diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e77e8e0..db076fc 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -36,11 +36,10 @@ jobs:
if: steps.cache.outputs.cache-hit != 'true'
uses: ./.github/actions/setup
- - name: Format & Lint & Typecheck
+ - name: Check & Typecheck
if: steps.cache.outputs.cache-hit != 'true'
run: |
- bun fmt
- bun lint
+ bun check
bun typecheck
build:
diff --git a/.gitignore b/.gitignore
index 3639bc5..844fa43 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,5 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+certificates
\ No newline at end of file
diff --git a/biome.jsonc b/biome.jsonc
index c67d6d7..c835a21 100644
--- a/biome.jsonc
+++ b/biome.jsonc
@@ -1,46 +1,48 @@
{
- "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
- "formatter": {
- "enabled": true,
- "formatWithErrors": false,
- "indentStyle": "space",
- "indentWidth": 2,
- "lineEnding": "lf",
- "lineWidth": 80,
- "attributePosition": "auto"
- },
- "assist": {
- "actions": {
- "source": {
- "organizeImports": "on"
- }
- }
- },
- "linter": {
- "enabled": true,
- "rules": {
- "recommended": false,
- "nursery": {
- "useSortedClasses": "error"
- }
- }
- },
- "javascript": {
- "formatter": {
- "jsxQuoteStyle": "double",
- "quoteProperties": "asNeeded",
- "trailingCommas": "all",
- "semicolons": "always",
- "arrowParentheses": "asNeeded",
- "bracketSpacing": true,
- "bracketSameLine": false,
- "quoteStyle": "double",
- "attributePosition": "auto"
- }
- },
- "json": {
- "formatter": {
- "trailingCommas": "none"
- }
- }
+ "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
+ "vcs": {
+ "enabled": true,
+ "clientKind": "git",
+ "useIgnoreFile": true
+ },
+ "files": { "ignoreUnknown": false, "includes": ["**"] },
+ "formatter": { "enabled": true },
+ "assist": { "actions": { "source": { "organizeImports": "on" } } },
+ "linter": {
+ "enabled": true,
+ "rules": {
+ "nursery": {
+ "useSortedClasses": {
+ "level": "warn",
+ "fix": "safe",
+ "options": {
+ "functions": ["clsx", "cva", "cn"]
+ }
+ }
+ },
+ "suspicious": {
+ "noArrayIndexKey": "off"
+ },
+ "a11y": {
+ "noSvgWithoutTitle": "off"
+ },
+ "correctness": {
+ "useExhaustiveDependencies": "off"
+ },
+ "recommended": true,
+ "style": {
+ "noNonNullAssertion": "off",
+ "noParameterAssign": "error",
+ "useAsConstAssertion": "error",
+ "useDefaultParameterLast": "error",
+ "useEnumInitializers": "error",
+ "useSelfClosingElements": "error",
+ "useSingleVarDeclarator": "error",
+ "noUnusedTemplateLiteral": "error",
+ "useNumberNamespace": "error",
+ "noInferrableTypes": "error",
+ "noUselessElse": "error"
+ }
+ }
+ }
}
diff --git a/bun.lock b/bun.lock
index a953374..be8f87e 100644
--- a/bun.lock
+++ b/bun.lock
@@ -4,40 +4,41 @@
"": {
"name": "sorting-algorithms",
"dependencies": {
- "@icons-pack/react-simple-icons": "^12.7.0",
- "@radix-ui/react-select": "^2.2.2",
- "@radix-ui/react-slider": "^1.3.2",
- "@radix-ui/react-slot": "^1.2.0",
- "@t3-oss/env-nextjs": "^0.12.0",
+ "@icons-pack/react-simple-icons": "^13.3.0",
+ "@radix-ui/react-select": "^2.2.5",
+ "@radix-ui/react-slider": "^1.3.5",
+ "@radix-ui/react-slot": "^1.2.3",
+ "@t3-oss/env-nextjs": "^0.13.8",
+ "@tanstack/react-query": "^5.81.5",
+ "@tanstack/react-query-devtools": "^5.81.5",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"d3": "^7.9.0",
- "lucide-react": "^0.503.0",
- "motion": "^12.7.4",
- "next": "15.3.1-canary.15",
- "next-intl": "^4.0.3",
+ "hast-util-to-jsx-runtime": "^2.3.6",
+ "lucide-react": "^0.525.0",
+ "motion": "^12.23.0",
+ "next": "15.4.0-canary.111",
+ "next-intl": "^4.3.4",
+ "nuqs": "^2.4.3",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-use-measure": "^2.1.7",
"server-only": "^0.0.1",
- "tailwind-merge": "^3.2.0",
- "tw-animate-css": "^1.2.7",
- "zod": "^3.24.3",
+ "shiki": "^3.7.0",
+ "tailwind-merge": "^3.3.1",
+ "tw-animate-css": "^1.3.5",
+ "zod": "^3.25.71",
},
"devDependencies": {
- "@biomejs/biome": "^2.0.0-beta.1",
- "@eslint/eslintrc": "^3.3.1",
- "@tailwindcss/postcss": "^4.1.4",
+ "@biomejs/biome": "^2.0.6",
+ "@tailwindcss/postcss": "^4.1.11",
"@types/d3": "^7.4.3",
- "@types/node": "^22.14.1",
- "@types/react": "^19.1.2",
- "@types/react-dom": "^19.1.2",
- "babel-plugin-react-compiler": "^19.0.0-beta-ebf51a3-20250411",
- "eslint": "^9.25.0",
- "eslint-config-next": "15.3.1",
- "eslint-plugin-react-compiler": "^19.0.0-beta-ebf51a3-20250411",
+ "@types/node": "^24.0.10",
+ "@types/react": "^19.1.8",
+ "@types/react-dom": "^19.1.6",
+ "babel-plugin-react-compiler": "^19.1.0-rc.2",
"postcss-load-config": "^6.0.1",
- "tailwindcss": "^4.1.4",
+ "tailwindcss": "^4.1.11",
"typescript": "^5.8.3",
},
},
@@ -47,101 +48,39 @@
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
- "@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
+ "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
- "@babel/compat-data": ["@babel/compat-data@7.26.8", "", {}, "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ=="],
+ "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="],
- "@babel/core": ["@babel/core@7.26.10", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.10", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", "@babel/helpers": "^7.26.10", "@babel/parser": "^7.26.10", "@babel/template": "^7.26.9", "@babel/traverse": "^7.26.10", "@babel/types": "^7.26.10", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ=="],
+ "@babel/types": ["@babel/types@7.28.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg=="],
- "@babel/generator": ["@babel/generator@7.27.0", "", { "dependencies": { "@babel/parser": "^7.27.0", "@babel/types": "^7.27.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw=="],
+ "@biomejs/biome": ["@biomejs/biome@2.0.6", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.0.6", "@biomejs/cli-darwin-x64": "2.0.6", "@biomejs/cli-linux-arm64": "2.0.6", "@biomejs/cli-linux-arm64-musl": "2.0.6", "@biomejs/cli-linux-x64": "2.0.6", "@biomejs/cli-linux-x64-musl": "2.0.6", "@biomejs/cli-win32-arm64": "2.0.6", "@biomejs/cli-win32-x64": "2.0.6" }, "bin": { "biome": "bin/biome" } }, "sha512-RRP+9cdh5qwe2t0gORwXaa27oTOiQRQvrFf49x2PA1tnpsyU7FIHX4ZOFMtBC4QNtyWsN7Dqkf5EDbg4X+9iqA=="],
- "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g=="],
+ "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.0.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-AzdiNNjNzsE6LfqWyBvcL29uWoIuZUkndu+wwlXW13EKcBHbbKjNQEZIJKYDc6IL+p7bmWGx3v9ZtcRyIoIz5A=="],
- "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.0", "", { "dependencies": { "@babel/compat-data": "^7.26.8", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA=="],
+ "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.0.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-wJjjP4E7bO4WJmiQaLnsdXMa516dbtC6542qeRkyJg0MqMXP0fvs4gdsHhZ7p9XWTAmGIjZHFKXdsjBvKGIJJQ=="],
- "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.27.0", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", "@babel/helper-replace-supers": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/traverse": "^7.27.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg=="],
+ "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.0.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZSVf6TYo5rNMUHIW1tww+rs/krol7U5A1Is/yzWyHVZguuB0lBnIodqyFuwCNqG9aJGyk7xIMS8HG0qGUPz0SA=="],
- "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ=="],
+ "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.0.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-CVPEMlin3bW49sBqLBg2x016Pws7eUXA27XYDFlEtponD0luYjg2zQaMJ2nOqlkKG9fqzzkamdYxHdMDc2gZFw=="],
- "@babel/helper-module-imports": ["@babel/helper-module-imports@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw=="],
+ "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.0.6", "", { "os": "linux", "cpu": "x64" }, "sha512-geM1MkHTV1Kh2Cs/Xzot9BOF3WBacihw6bkEmxkz4nSga8B9/hWy5BDiOG3gHDGIBa8WxT0nzsJs2f/hPqQIQw=="],
- "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.26.0", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw=="],
+ "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.0.6", "", { "os": "linux", "cpu": "x64" }, "sha512-mKHE/e954hR/hSnAcJSjkf4xGqZc/53Kh39HVW1EgO5iFi0JutTN07TSjEMg616julRtfSNJi0KNyxvc30Y4rQ=="],
- "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ=="],
+ "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.0.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-290V4oSFoKaprKE1zkYVsDfAdn0An5DowZ+GIABgjoq1ndhvNxkJcpxPsiYtT7slbVe3xmlT0ncdfOsN7KruzA=="],
- "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.26.5", "", {}, "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg=="],
-
- "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.26.5", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", "@babel/traverse": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg=="],
-
- "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA=="],
-
- "@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="],
-
- "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="],
-
- "@babel/helper-validator-option": ["@babel/helper-validator-option@7.25.9", "", {}, "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw=="],
-
- "@babel/helpers": ["@babel/helpers@7.27.0", "", { "dependencies": { "@babel/template": "^7.27.0", "@babel/types": "^7.27.0" } }, "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg=="],
-
- "@babel/parser": ["@babel/parser@7.27.0", "", { "dependencies": { "@babel/types": "^7.27.0" }, "bin": "./bin/babel-parser.js" }, "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg=="],
-
- "@babel/plugin-proposal-private-methods": ["@babel/plugin-proposal-private-methods@7.18.6", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA=="],
-
- "@babel/template": ["@babel/template@7.27.0", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/parser": "^7.27.0", "@babel/types": "^7.27.0" } }, "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA=="],
-
- "@babel/traverse": ["@babel/traverse@7.27.0", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.27.0", "@babel/parser": "^7.27.0", "@babel/template": "^7.27.0", "@babel/types": "^7.27.0", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA=="],
-
- "@babel/types": ["@babel/types@7.27.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" } }, "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg=="],
-
- "@biomejs/biome": ["@biomejs/biome@2.0.0-beta.1", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.0.0-beta.1", "@biomejs/cli-darwin-x64": "2.0.0-beta.1", "@biomejs/cli-linux-arm64": "2.0.0-beta.1", "@biomejs/cli-linux-arm64-musl": "2.0.0-beta.1", "@biomejs/cli-linux-x64": "2.0.0-beta.1", "@biomejs/cli-linux-x64-musl": "2.0.0-beta.1", "@biomejs/cli-win32-arm64": "2.0.0-beta.1", "@biomejs/cli-win32-x64": "2.0.0-beta.1" }, "bin": { "biome": "bin/biome" } }, "sha512-MqRoy9CbTkrS45zW+S4u8p4kQUIFx0mGUWi789W1R3b1kXYIudEqsTKgXKtTGsI0kWOlvnjuKqwTrabjaGchhQ=="],
-
- "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.0.0-beta.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RaGmpNLl5NFooXaoCwvgvcuU6Am/rMZ3R48pQeCVxjrCcz1BIlKLTai5UosiedazW7JbXAvgXdSNizYG7ITlAQ=="],
-
- "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.0.0-beta.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-sTzSshkne7HKZFNfiIhmAji7gjtCBXvkTujZELCZWIZC7oj1Tjw/gvAzbdFj2UyHd5/i90pND4ybFOLQZm9gpg=="],
-
- "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.0.0-beta.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-bxce2O4nooBmp20Ey0+IFIZyy/b0RVnciIQk9euCfAi9evq7SvFtMBYo3YUZej0KIvrau5H7tJk5OqmRJk2l+g=="],
-
- "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.0.0-beta.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-0MPUKzz9uBBxAYSJ+OlFi4+yGwiRcZeFqq39H0MxXCQ9MMpKJFH2Ek72fw8sXwG7Prn7EsW/3u1b7najyn1XGQ=="],
-
- "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.0.0-beta.1", "", { "os": "linux", "cpu": "x64" }, "sha512-6P/AtJv4hOH8mu8ez0c4UInUpiet9NEoF25+O7OPyb4w6ZHJMp2qzvayJS7TKrTQzE5KUvSiNsACGRz34DzUkg=="],
-
- "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.0.0-beta.1", "", { "os": "linux", "cpu": "x64" }, "sha512-dFvisnP1hFpVILNw0PZfs8piBwe8+aykO04Tb/4AJDVVzKkGgJfwSefwo4jqzO/Wk/Zruvhcp1nKbjgRXM+vDg=="],
-
- "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.0.0-beta.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-0C9YSqWHf2cJGnjKDbLi49xv6H9IfqbDsFav7X557PqwY64O6IKWqcmZzi/PkDFHjQM9opU6uhKapeGKGDxziQ=="],
-
- "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.0.0-beta.1", "", { "os": "win32", "cpu": "x64" }, "sha512-o8W6+DX0YRjt1kS8Y3ismq6EkjwiVDv7X0TEpfnFywoVG8HoJ7G7/m9r8LM1yE46WI3maPH2A0MoVpQ1ZNG++A=="],
-
- "@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" } }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
+ "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.0.6", "", { "os": "win32", "cpu": "x64" }, "sha512-bfM1Bce0d69Ao7pjTjUS+AWSZ02+5UHdiAP85Th8e9yV5xzw6JrHXbL5YWlcEKQ84FIZMdDc7ncuti1wd2sdbw=="],
"@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
- "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
-
- "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.6.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw=="],
-
- "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
-
- "@eslint/config-array": ["@eslint/config-array@0.20.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ=="],
-
- "@eslint/config-helpers": ["@eslint/config-helpers@0.2.1", "", {}, "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw=="],
+ "@floating-ui/core": ["@floating-ui/core@1.7.2", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw=="],
- "@eslint/core": ["@eslint/core@0.13.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw=="],
+ "@floating-ui/dom": ["@floating-ui/dom@1.7.2", "", { "dependencies": { "@floating-ui/core": "^1.7.2", "@floating-ui/utils": "^0.2.10" } }, "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA=="],
- "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
+ "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.4", "", { "dependencies": { "@floating-ui/dom": "^1.7.2" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-JbbpPhp38UmXDDAu60RJmbeme37Jbgsm7NrHGgzYYFKmblzRUh6Pa641dII6LsjwF4XlScDrde2UAzDo/b9KPw=="],
- "@eslint/js": ["@eslint/js@9.25.1", "", {}, "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg=="],
-
- "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
-
- "@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.8", "", { "dependencies": { "@eslint/core": "^0.13.0", "levn": "^0.4.1" } }, "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA=="],
-
- "@floating-ui/core": ["@floating-ui/core@1.6.9", "", { "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw=="],
-
- "@floating-ui/dom": ["@floating-ui/dom@1.6.13", "", { "dependencies": { "@floating-ui/core": "^1.6.0", "@floating-ui/utils": "^0.2.9" } }, "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w=="],
-
- "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.2", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A=="],
-
- "@floating-ui/utils": ["@floating-ui/utils@0.2.9", "", {}, "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="],
+ "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="],
"@formatjs/ecma402-abstract": ["@formatjs/ecma402-abstract@2.3.4", "", { "dependencies": { "@formatjs/fast-memoize": "2.2.7", "@formatjs/intl-localematcher": "0.6.1", "decimal.js": "^10.4.3", "tslib": "^2.8.0" } }, "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA=="],
@@ -153,19 +92,11 @@
"@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.5.10", "", { "dependencies": { "tslib": "2" } }, "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q=="],
- "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
-
- "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
-
- "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
+ "@icons-pack/react-simple-icons": ["@icons-pack/react-simple-icons@13.3.0", "", { "peerDependencies": { "react": "^16.13 || ^17 || ^18 || ^19" } }, "sha512-+7IxrUMLjfHBdDtW3aBlvELvjbQTX29cbrpc3UcCBhE51uL7GP1bkIEcaFM30f5rSB0oSqeg3MAmTIZV6nzY7A=="],
- "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.2", "", {}, "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ=="],
+ "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.2", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.1.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg=="],
- "@icons-pack/react-simple-icons": ["@icons-pack/react-simple-icons@12.7.0", "", { "peerDependencies": { "react": "^16.13 || ^17 || ^18 || ^19" } }, "sha512-XpG7e8buAco8AbvCar+4LX5do0L98d/NaeHoTuW4awyUH33id7HQiiMomK+cS1MRxfLNMmhW2W0223/fMC0unw=="],
-
- "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.1.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A=="],
-
- "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.1.0" }, "os": "darwin", "cpu": "x64" }, "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q=="],
+ "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.2", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.1.0" }, "os": "darwin", "cpu": "x64" }, "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g=="],
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.1.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA=="],
@@ -185,71 +116,61 @@
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.1.0", "", { "os": "linux", "cpu": "x64" }, "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A=="],
- "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.1.0" }, "os": "linux", "cpu": "arm" }, "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA=="],
-
- "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.1.0" }, "os": "linux", "cpu": "arm64" }, "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ=="],
-
- "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.1.0" }, "os": "linux", "cpu": "s390x" }, "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA=="],
+ "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.1.0" }, "os": "linux", "cpu": "arm" }, "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ=="],
- "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.1.0" }, "os": "linux", "cpu": "x64" }, "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA=="],
+ "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.1.0" }, "os": "linux", "cpu": "arm64" }, "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q=="],
- "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" }, "os": "linux", "cpu": "arm64" }, "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ=="],
+ "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.1.0" }, "os": "linux", "cpu": "s390x" }, "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw=="],
- "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.1", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.1.0" }, "os": "linux", "cpu": "x64" }, "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg=="],
+ "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.2", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.1.0" }, "os": "linux", "cpu": "x64" }, "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ=="],
- "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.1", "", { "dependencies": { "@emnapi/runtime": "^1.4.0" }, "cpu": "none" }, "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg=="],
+ "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.2", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" }, "os": "linux", "cpu": "arm64" }, "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA=="],
- "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw=="],
-
- "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.1", "", { "os": "win32", "cpu": "x64" }, "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw=="],
-
- "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
-
- "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
+ "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.2", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.1.0" }, "os": "linux", "cpu": "x64" }, "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA=="],
- "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
+ "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.2", "", { "dependencies": { "@emnapi/runtime": "^1.4.3" }, "cpu": "none" }, "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ=="],
- "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
+ "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ=="],
- "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
+ "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw=="],
- "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.9", "", { "dependencies": { "@emnapi/core": "^1.4.0", "@emnapi/runtime": "^1.4.0", "@tybys/wasm-util": "^0.9.0" } }, "sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg=="],
+ "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.2", "", { "os": "win32", "cpu": "x64" }, "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw=="],
- "@next/env": ["@next/env@15.3.1-canary.15", "", {}, "sha512-68oU7HJBCSeEp9ESMvrCSTKJN45s9ck7ZC/32x/HW9RfT8zr2osBiuOp4z2GYA+h+nUPu2oElfMpq7AamYR4Dw=="],
+ "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
- "@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.3.1", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-oEs4dsfM6iyER3jTzMm4kDSbrQJq8wZw5fmT6fg2V3SMo+kgG+cShzLfEV20senZzv8VF+puNLheiGPlBGsv2A=="],
+ "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.12", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg=="],
- "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.3.1-canary.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-OQCjX6Vte2k5OAPrWhPRtox2XnY1PwTvdr+bFVVrru6MSRbAqJW09u4GYK65at7hhILiAAMyhCk6yTq46beUaw=="],
+ "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
- "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.3.1-canary.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-KXP4QtAQKMJJ9yD9ALgvfdhZlOvSrHjh86dBaCn4pdnBQspyuv8bdvVFVQ72VBIKI/hi1I7Oy1tgNNz3YX1vxA=="],
+ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.4", "", {}, "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw=="],
- "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.3.1-canary.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-BuM+AzhU7ujlZguFArh55L3lseAyu6IpS6aZNAcM0mx+ug9hl3/K5DYEofr5hl36j6g4S04N6JMC2u9ThHlTbg=="],
+ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.29", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ=="],
- "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.3.1-canary.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-PqVdd1NhheFnVheEfAFdZGx2glpsoKG4Vjd2pl2MkCf/MEqUYFdzBtAaOcBlir1HN7eI2y7FixUbpjFQu2m7gg=="],
+ "@next/env": ["@next/env@15.4.0-canary.111", "", {}, "sha512-tpr5As3Eqbhv0mx4jO9D0k7gFZ/5rL0DB8NZXx5PVWUUJKv1N2vlHPqr6to97ERVmRofParTKZgQGGzY/los8A=="],
- "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.3.1-canary.15", "", { "os": "linux", "cpu": "x64" }, "sha512-JbtEqPbZoeWtpsDNwJxps+P24RKPQpE7eK8hRK7kGf9x2tAVVQZdOVVGjnzYC8WIibGi0FgXT7Q3UqtI5gR7JA=="],
+ "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.4.0-canary.111", "", { "os": "darwin", "cpu": "arm64" }, "sha512-24D4v+IB+b/A59z1B+8VJ0OMUHDpPxkTPU0n5qcJuf4UA9B6b7PZP6OeokSI/iJ2naTFwZcwi/0VE17uqEyklA=="],
- "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.3.1-canary.15", "", { "os": "linux", "cpu": "x64" }, "sha512-b8k1IIKSUYCsZFQFEr3iLskRDBATCc5lcpDRje2Z7PYV1Oe7vOzmf5gmtknn6YZdyFV1qNRhf9hHl6l4NPgyaw=="],
+ "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.4.0-canary.111", "", { "os": "darwin", "cpu": "x64" }, "sha512-WxRTH9FS15O6wbitxLKylO4j/l1qWEMWNSNlU7bLK3zwcFJjTdGD5vrjJc1vTk5dVnF1lJOecUWP8DRnzslJrA=="],
- "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.3.1-canary.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-I5SL0FCHiEfcewsTpxMGZoIu1sE5BLgUWiJbtqdkByfnAaP9XVIzaeBvW1IRBmNFjAcJmqwDTZtcKlsOTVHh+Q=="],
+ "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.4.0-canary.111", "", { "os": "linux", "cpu": "arm64" }, "sha512-8AU3nfOAWQHlv6PbPBTdOE7Bw+dOWI6odebAFiDJIzAYDZEztR8QmvP0fwVwwUJxgsEE31pB5NDBI3jkSgX+VA=="],
- "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.3.1-canary.15", "", { "os": "win32", "cpu": "x64" }, "sha512-2HkLFhKDJI3Y6LYMUHUgNmsKDpssVlfWQTxXEBoYOmaoX1A5R3OjZKnnKjjiRnppKC0rrP97dTgMnzN+e1tQew=="],
+ "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.4.0-canary.111", "", { "os": "linux", "cpu": "arm64" }, "sha512-6fbfZDEPTW+bv9HPhkorsN1UesniAbvVn3N+qtzJvPpNHjHcxfo5qs9Q2F/AcRWcPQnWiHdqCeT+qYNgMg5cpQ=="],
- "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
+ "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.4.0-canary.111", "", { "os": "linux", "cpu": "x64" }, "sha512-iuZRMRwxM9nTgOpKS9z/ANHGKy5xf0Xhigswj0hFgqttF8fGmaEuRoyTeKvMVwwGNd7tftpzcOxgKfQKCr9cPA=="],
- "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
+ "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.4.0-canary.111", "", { "os": "linux", "cpu": "x64" }, "sha512-1WHr+8ICeHb71bd5y60KvsrLsr0wz9+6H2sw8SuScrLuHWfTmbwYMue517EFV5RbO7dCxe/TIP9p1P0CxZ2uuQ=="],
- "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
+ "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.4.0-canary.111", "", { "os": "win32", "cpu": "arm64" }, "sha512-repGty63yqiAkaUEJaTyzb8/+E5t6fjkKo+g4U5snMkEC5HDpJJh0BVBbT/WUQKmByuqiL3hRQR4sRYLBqhclw=="],
- "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="],
+ "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.4.0-canary.111", "", { "os": "win32", "cpu": "x64" }, "sha512-yBC26Zq278JT2AwAKascL4CfphZsNULEmVuguBXTGTAtkVPpuJqZwHR5MW76l5ByptSbk9ZPYmiNhCeNEaCg7Q=="],
"@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="],
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="],
- "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qz+fxrqgNxG0dYew5l7qR3c7wdgRu1XVUHGnGYX7rg5HM4p9SWaRmJwfgR3J0SgyUKayLmzQIun+N6rWRgiRKw=="],
+ "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="],
- "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-slot": "1.2.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg=="],
+ "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="],
"@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
@@ -257,25 +178,25 @@
"@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="],
- "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.7", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-j5+WBUdhccJsmH5/H0K6RncjDtoALSEr6jbkaZu+bjw6hOPOhHycr6vEUujl+HBK8kjUfWcoCJXxP6e4lUlMZw=="],
+ "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ=="],
"@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA=="],
- "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-r2annK27lIW5w9Ho5NyQgqs0MmgZSTIKXWpVCJaLC1q2kZrZkcqnmHkCHMEmv8XLvsLlurKMPT+kbKkRkm/xVA=="],
+ "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="],
"@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
- "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.4", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.4", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3p2Rgm/a1cK0r/UVkx5F/K9v/EplfjAeIFCGOPYPO4lZ0jtg4iSQXt/YGTSLWaf4x7NG6Z4+uKFcylcTZjeqDA=="],
+ "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.7", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ=="],
- "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.6", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XmsIl2z1n/TsYFLIdYam2rmFwf9OC/Sh2avkbmVMDuBZIe7hSpM0cYnWPAo7nHOVx8zTuwDZGByfcqLdnzp3Vw=="],
+ "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="],
- "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.0", "", { "dependencies": { "@radix-ui/react-slot": "1.2.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw=="],
+ "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
- "@radix-ui/react-select": ["@radix-ui/react-select@2.2.2", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.4", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.7", "@radix-ui/react-focus-guards": "1.1.2", "@radix-ui/react-focus-scope": "1.1.4", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.4", "@radix-ui/react-portal": "1.1.6", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-slot": "1.2.0", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.0", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-HjkVHtBkuq+r3zUAZ/CvNWUGKPfuicGDbgtZgiQuFmNcV5F+Tgy24ep2nsAW2nFgvhGPJVqeBZa6KyVN0EyrBA=="],
+ "@radix-ui/react-select": ["@radix-ui/react-select@2.2.5", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.7", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA=="],
- "@radix-ui/react-slider": ["@radix-ui/react-slider@1.3.2", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.4", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.0", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oQnqfgSiYkxZ1MrF6672jw2/zZvpB+PJsrIc3Zm1zof1JHf/kj7WhmROw7JahLfOwYQ5/+Ip0rFORgF1tjSiaQ=="],
+ "@radix-ui/react-slider": ["@radix-ui/react-slider@1.3.5", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw=="],
- "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
+ "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
"@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
@@ -293,55 +214,69 @@
"@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="],
- "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.0", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-rQj0aAWOpCdCMRbI6pLQm8r7S2BM3YhTa0SzOYD55k+hJA8oo9J+H+9wLM9oMlZWOX/wJWPTzfDfmZkf7LvCfg=="],
+ "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="],
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
- "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="],
+ "@schummar/icu-type-parser": ["@schummar/icu-type-parser@1.21.5", "", {}, "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw=="],
+
+ "@shikijs/core": ["@shikijs/core@3.7.0", "", { "dependencies": { "@shikijs/types": "3.7.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-yilc0S9HvTPyahHpcum8eonYrQtmGTU0lbtwxhA6jHv4Bm1cAdlPFRCJX4AHebkCm75aKTjjRAW+DezqD1b/cg=="],
- "@rushstack/eslint-patch": ["@rushstack/eslint-patch@1.11.0", "", {}, "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ=="],
+ "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.7.0", "", { "dependencies": { "@shikijs/types": "3.7.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-0t17s03Cbv+ZcUvv+y33GtX75WBLQELgNdVghnsdhTgU3hVcWcMsoP6Lb0nDTl95ZJfbP1mVMO0p3byVh3uuzA=="],
- "@schummar/icu-type-parser": ["@schummar/icu-type-parser@1.21.5", "", {}, "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw=="],
+ "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.7.0", "", { "dependencies": { "@shikijs/types": "3.7.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-5BxcD6LjVWsGu4xyaBC5bu8LdNgPCVBnAkWTtOCs/CZxcB22L8rcoWfv7Hh/3WooVjBZmFtyxhgvkQFedPGnFw=="],
- "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="],
+ "@shikijs/langs": ["@shikijs/langs@3.7.0", "", { "dependencies": { "@shikijs/types": "3.7.0" } }, "sha512-1zYtdfXLr9xDKLTGy5kb7O0zDQsxXiIsw1iIBcNOO8Yi5/Y1qDbJ+0VsFoqTlzdmneO8Ij35g7QKF8kcLyznCQ=="],
+
+ "@shikijs/themes": ["@shikijs/themes@3.7.0", "", { "dependencies": { "@shikijs/types": "3.7.0" } }, "sha512-VJx8497iZPy5zLiiCTSIaOChIcKQwR0FebwE9S3rcN0+J/GTWwQ1v/bqhTbpbY3zybPKeO8wdammqkpXc4NVjQ=="],
+
+ "@shikijs/types": ["@shikijs/types@3.7.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg=="],
+
+ "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
- "@t3-oss/env-core": ["@t3-oss/env-core@0.12.0", "", { "peerDependencies": { "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0" }, "optionalPeers": ["typescript", "valibot", "zod"] }, "sha512-lOPj8d9nJJTt81mMuN9GMk8x5veOt7q9m11OSnCBJhwp1QrL/qR+M8Y467ULBSm9SunosryWNbmQQbgoiMgcdw=="],
+ "@t3-oss/env-core": ["@t3-oss/env-core@0.13.8", "", { "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0-beta.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-L1inmpzLQyYu4+Q1DyrXsGJYCXbtXjC4cICw1uAKv0ppYPQv656lhZPU91Qd1VS6SO/bou1/q5ufVzBGbNsUpw=="],
+
+ "@t3-oss/env-nextjs": ["@t3-oss/env-nextjs@0.13.8", "", { "dependencies": { "@t3-oss/env-core": "0.13.8" }, "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0-beta.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-QmTLnsdQJ8BiQad2W2nvV6oUpH4oMZMqnFEjhVpzU0h3sI9hn8zb8crjWJ1Amq453mGZs6A4v4ihIeBFDOrLeQ=="],
- "@t3-oss/env-nextjs": ["@t3-oss/env-nextjs@0.12.0", "", { "dependencies": { "@t3-oss/env-core": "0.12.0" }, "peerDependencies": { "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0" }, "optionalPeers": ["typescript", "valibot", "zod"] }, "sha512-rFnvYk1049RnNVUPvY8iQ55AuQh1Rr+qZzQBh3t++RttCGK4COpXGNxS4+45afuQq02lu+QAOy/5955aU8hRKw=="],
+ "@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="],
- "@tailwindcss/node": ["@tailwindcss/node@4.1.4", "", { "dependencies": { "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.29.2", "tailwindcss": "4.1.4" } }, "sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw=="],
+ "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.11", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.11", "@tailwindcss/oxide-darwin-arm64": "4.1.11", "@tailwindcss/oxide-darwin-x64": "4.1.11", "@tailwindcss/oxide-freebsd-x64": "4.1.11", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", "@tailwindcss/oxide-linux-x64-musl": "4.1.11", "@tailwindcss/oxide-wasm32-wasi": "4.1.11", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" } }, "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg=="],
- "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.4", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.4", "@tailwindcss/oxide-darwin-arm64": "4.1.4", "@tailwindcss/oxide-darwin-x64": "4.1.4", "@tailwindcss/oxide-freebsd-x64": "4.1.4", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.4", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.4", "@tailwindcss/oxide-linux-arm64-musl": "4.1.4", "@tailwindcss/oxide-linux-x64-gnu": "4.1.4", "@tailwindcss/oxide-linux-x64-musl": "4.1.4", "@tailwindcss/oxide-wasm32-wasi": "4.1.4", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.4", "@tailwindcss/oxide-win32-x64-msvc": "4.1.4" } }, "sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ=="],
+ "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.11", "", { "os": "android", "cpu": "arm64" }, "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg=="],
- "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.4", "", { "os": "android", "cpu": "arm64" }, "sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA=="],
+ "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ=="],
- "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg=="],
+ "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw=="],
- "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA=="],
+ "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA=="],
- "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA=="],
+ "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11", "", { "os": "linux", "cpu": "arm" }, "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg=="],
- "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.4", "", { "os": "linux", "cpu": "arm" }, "sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg=="],
+ "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ=="],
- "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww=="],
+ "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ=="],
- "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw=="],
+ "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg=="],
- "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ=="],
+ "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q=="],
- "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ=="],
+ "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.11", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g=="],
- "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.4", "", { "dependencies": { "@emnapi/core": "^1.4.0", "@emnapi/runtime": "^1.4.0", "@emnapi/wasi-threads": "^1.0.1", "@napi-rs/wasm-runtime": "^0.2.8", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q=="],
+ "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w=="],
- "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng=="],
+ "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.11", "", { "os": "win32", "cpu": "x64" }, "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg=="],
- "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.4", "", { "os": "win32", "cpu": "x64" }, "sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw=="],
+ "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.11", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.11", "@tailwindcss/oxide": "4.1.11", "postcss": "^8.4.41", "tailwindcss": "4.1.11" } }, "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA=="],
- "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.4", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.4", "@tailwindcss/oxide": "4.1.4", "postcss": "^8.4.41", "tailwindcss": "4.1.4" } }, "sha512-bjV6sqycCEa+AQSt2Kr7wpGF1bOZJ5wsqnLEkqSbM/JEHxx/yhMH8wHmdkPyApF9xhHeMSwnnkDUUMMM/hYnXw=="],
+ "@tanstack/query-core": ["@tanstack/query-core@5.81.5", "", {}, "sha512-ZJOgCy/z2qpZXWaj/oxvodDx07XcQa9BF92c0oINjHkoqUPsmm3uG08HpTaviviZ/N9eP1f9CM7mKSEkIo7O1Q=="],
- "@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
+ "@tanstack/query-devtools": ["@tanstack/query-devtools@5.81.2", "", {}, "sha512-jCeJcDCwKfoyyBXjXe9+Lo8aTkavygHHsUHAlxQKKaDeyT0qyQNLKl7+UyqYH2dDF6UN/14873IPBHchcsU+Zg=="],
+
+ "@tanstack/react-query": ["@tanstack/react-query@5.81.5", "", { "dependencies": { "@tanstack/query-core": "5.81.5" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-lOf2KqRRiYWpQT86eeeftAGnjuTR35myTP8MXyvHa81VlomoAWNEd8x5vkcAfQefu0qtYCvyqLropFZqgI2EQw=="],
+
+ "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.81.5", "", { "dependencies": { "@tanstack/query-devtools": "5.81.2" }, "peerDependencies": { "@tanstack/react-query": "^5.81.5", "react": "^18 || ^19" } }, "sha512-lCGMu4RX0uGnlrlLeSckBfnW/UV+KMlTBVqa97cwK7Z2ED5JKnZRSjNXwoma6sQBTJrcULvzgx2K6jEPvNUpDw=="],
"@types/d3": ["@types/d3@7.4.3", "", { "dependencies": { "@types/d3-array": "*", "@types/d3-axis": "*", "@types/d3-brush": "*", "@types/d3-chord": "*", "@types/d3-color": "*", "@types/d3-contour": "*", "@types/d3-delaunay": "*", "@types/d3-dispatch": "*", "@types/d3-drag": "*", "@types/d3-dsv": "*", "@types/d3-ease": "*", "@types/d3-fetch": "*", "@types/d3-force": "*", "@types/d3-format": "*", "@types/d3-geo": "*", "@types/d3-hierarchy": "*", "@types/d3-interpolate": "*", "@types/d3-path": "*", "@types/d3-polygon": "*", "@types/d3-quadtree": "*", "@types/d3-random": "*", "@types/d3-scale": "*", "@types/d3-scale-chromatic": "*", "@types/d3-selection": "*", "@types/d3-shape": "*", "@types/d3-time": "*", "@types/d3-time-format": "*", "@types/d3-timer": "*", "@types/d3-transition": "*", "@types/d3-zoom": "*" } }, "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww=="],
@@ -405,131 +340,47 @@
"@types/d3-zoom": ["@types/d3-zoom@3.0.8", "", { "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw=="],
- "@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
-
- "@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="],
-
- "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
-
- "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
-
- "@types/node": ["@types/node@22.14.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw=="],
-
- "@types/react": ["@types/react@19.1.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw=="],
-
- "@types/react-dom": ["@types/react-dom@19.1.2", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw=="],
-
- "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.31.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.31.0", "@typescript-eslint/type-utils": "8.31.0", "@typescript-eslint/utils": "8.31.0", "@typescript-eslint/visitor-keys": "8.31.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ=="],
-
- "@typescript-eslint/parser": ["@typescript-eslint/parser@8.31.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.31.0", "@typescript-eslint/types": "8.31.0", "@typescript-eslint/typescript-estree": "8.31.0", "@typescript-eslint/visitor-keys": "8.31.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw=="],
-
- "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.31.0", "", { "dependencies": { "@typescript-eslint/types": "8.31.0", "@typescript-eslint/visitor-keys": "8.31.0" } }, "sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw=="],
-
- "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.31.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.31.0", "@typescript-eslint/utils": "8.31.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg=="],
-
- "@typescript-eslint/types": ["@typescript-eslint/types@8.31.0", "", {}, "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ=="],
-
- "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.31.0", "", { "dependencies": { "@typescript-eslint/types": "8.31.0", "@typescript-eslint/visitor-keys": "8.31.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ=="],
-
- "@typescript-eslint/utils": ["@typescript-eslint/utils@8.31.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.31.0", "@typescript-eslint/types": "8.31.0", "@typescript-eslint/typescript-estree": "8.31.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww=="],
+ "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
- "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.31.0", "", { "dependencies": { "@typescript-eslint/types": "8.31.0", "eslint-visitor-keys": "^4.2.0" } }, "sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ=="],
+ "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
- "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.6.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ehtknxfSIlAIVFmQ9/yVbW4SzyjWuQpKAtRujNzuR0qS1avz4+BSmM0lVhl4OnU7nJaun/g+AM2FeaUY5KwZsg=="],
+ "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="],
- "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.6.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-CtPj8lqQNVaNjnURq4lCAsanQGN/zO8yFKbL8a7RKH4SU7EMYhOrK8JgW5mbcEDinB4hVuZdgsDCTA3x24CuVQ=="],
-
- "@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.6.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-N8UpCG5vis1srGACnJ03WG4N9YfkpzcF7Ooztv9uOE3IG7yjxT4wSVpfbTUof2kOM8TmVhgINoIDQ5wxo+CCxQ=="],
-
- "@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.6.4", "", { "os": "linux", "cpu": "arm" }, "sha512-Hllz4okH+R2P0YdFivGhrA1gjDLjQrhLmfu37TidpQpcp6tcTK40T9mt7SF8frXuPjd2/YNxXIyowOvswwnfOg=="],
-
- "@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.6.4", "", { "os": "linux", "cpu": "arm" }, "sha512-Mem13rJYfFvBj4xlkuok0zH5qn8vTm9FEm+FyiZeRK/6AFVPc/y596HihKcHIk7djvJ4BYXs7yIZo2ezabE7IA=="],
-
- "@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.6.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-kgyNRMgN7Z2pF2GJBHGIxhkN9e0rMOZwWORH3RjGHZnjtdrThxyQSMUGjK5MDM6+V3waPL0Kv9Y6pJnYxlvcXA=="],
-
- "@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.6.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-bLlGWp3Z7eiO6sytt5T3NFwiUfvIjYH9wGIVD01lnVOIBxHUjQQo+7Nv+SkZVP+Y7oySlyyrrzn5y9VFn1MLeQ=="],
-
- "@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.6.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Qt3g8MRemL9h51MCMh4BtQMNzK2JPo2CG8rVeTw8F2xKuUtLRqTsRGitOCbA6cuogv8EezBNyddKKT+bZ70W3g=="],
-
- "@unrs/resolver-binding-linux-riscv64-gnu": ["@unrs/resolver-binding-linux-riscv64-gnu@1.6.4", "", { "os": "linux", "cpu": "none" }, "sha512-MFMn6TCZZkaOt90lTC+OzfGuGTcOyNDDB6gqgmHEiNUAz8sfljbhKIyms8e792J/Dsq0H1LSWcNhtMjnRZtv8g=="],
-
- "@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.6.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-RGV8V4VjxH8WhcAqvVuHAv85nbdU87dbcJmarXYuAUPLWC76ptJ32eGY5CM4MmmdU8NA3m4EkiBilSvzSt+BMA=="],
-
- "@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.6.4", "", { "os": "linux", "cpu": "x64" }, "sha512-9SWe0F8kD7+4oD1dLvyHiVXN77PrBKbo46JVuwiCGtv3HnbSgNpjyl/9N4xqsXQScERwRWS6qjjA8fTaedwjRQ=="],
-
- "@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.6.4", "", { "os": "linux", "cpu": "x64" }, "sha512-EJP5VyeRTPHqm1CEVoeAcGY7z6fmvAl8MGi06NFxdvczRRwazg0SZre+kzYis/Px4jZY6nZwBXMsHamyY0CELg=="],
-
- "@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.6.4", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.9" }, "cpu": "none" }, "sha512-/Igzy4K6QTajH0m1PesWaYyor/USENYiX7PQQHHsVvewX9rx2mUwpH0ckOLXKpnLNghm+mzDcEufdgFsZQEK3A=="],
-
- "@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.6.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-MXx3CyX+XbNJm5HXgZrkiL1JbizaRbpEE1GnXYxIOjfBDFqzWl4tge5Fdp+sBtGeGPB42q6ZBnECEa/tzSWa6A=="],
-
- "@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.6.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-ZDIZ4HMZI8GNEUfBaM844O0LfguwDBvpu7orTv+9kxPOAW/6Cxyh768f/qlHIl8Xp0AHZOSJKLc1UneMdt9O6w=="],
-
- "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.6.4", "", { "os": "win32", "cpu": "x64" }, "sha512-jZIMKjruJy9ddDIZBLGzyi2rqfRzi3lNQkQTuaQkcpUMSy+HValMS/fvRHZIB0BGw/fdu2uCDfpxB6dNwB1Ung=="],
-
- "acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="],
-
- "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
-
- "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
-
- "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
-
- "aria-hidden": ["aria-hidden@1.2.4", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A=="],
-
- "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
-
- "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="],
-
- "array-includes": ["array-includes@3.1.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" } }, "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ=="],
-
- "array.prototype.findlast": ["array.prototype.findlast@1.2.5", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ=="],
-
- "array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-shim-unscopables": "^1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="],
-
- "array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="],
-
- "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="],
-
- "array.prototype.tosorted": ["array.prototype.tosorted@1.1.4", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3", "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA=="],
-
- "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="],
+ "@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="],
- "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="],
+ "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
- "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="],
+ "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
- "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
+ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
- "axe-core": ["axe-core@4.10.3", "", {}, "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg=="],
+ "@types/node": ["@types/node@24.0.10", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA=="],
- "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
+ "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="],
- "babel-plugin-react-compiler": ["babel-plugin-react-compiler@19.0.0-beta-ebf51a3-20250411", "", { "dependencies": { "@babel/types": "^7.26.0" } }, "sha512-q84bNR9JG1crykAlJUt5Ud0/5BUyMFuQww/mrwIQDFBaxsikqBDj3f/FNDsVd2iR26A1HvXKWPEIfgJDv8/V2g=="],
+ "@types/react-dom": ["@types/react-dom@19.1.6", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw=="],
- "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+ "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
- "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
+ "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
- "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
+ "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
- "browserslist": ["browserslist@4.24.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="],
+ "babel-plugin-react-compiler": ["babel-plugin-react-compiler@19.1.0-rc.2", "", { "dependencies": { "@babel/types": "^7.26.0" } }, "sha512-kSNA//p5fMO6ypG8EkEVPIqAjwIXm5tMjfD1XRPL/sRjYSbJ6UsvORfaeolNWnZ9n310aM0xJP7peW26BuCVzA=="],
- "busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="],
+ "caniuse-lite": ["caniuse-lite@1.0.30001726", "", {}, "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw=="],
- "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
+ "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
- "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
+ "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
- "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
+ "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
- "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
+ "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
- "caniuse-lite": ["caniuse-lite@1.0.30001715", "", {}, "sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw=="],
+ "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
- "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
+ "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
"class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
@@ -545,13 +396,9 @@
"color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
- "commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
-
- "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
+ "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
- "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
-
- "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
+ "commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
@@ -617,389 +464,193 @@
"d3-zoom": ["d3-zoom@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="],
- "damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="],
-
- "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
-
- "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
-
- "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="],
-
- "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
+ "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
"decimal.js": ["decimal.js@10.5.0", "", {}, "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw=="],
- "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
-
- "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
-
- "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
+ "decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="],
"delaunator": ["delaunator@5.0.1", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw=="],
+ "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
+
"detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
- "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
-
- "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
-
- "electron-to-chromium": ["electron-to-chromium@1.5.140", "", {}, "sha512-o82Rj+ONp4Ip7Cl1r7lrqx/pXhbp/lh9DpKcMNscFJdh8ebyRofnc7Sh01B4jx403RI0oqTBvlZ7OBIZLMr2+Q=="],
-
- "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
-
- "enhanced-resolve": ["enhanced-resolve@5.18.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="],
-
- "es-abstract": ["es-abstract@1.23.9", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.3", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.0", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-regex": "^1.2.1", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.0", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.3", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.3", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.18" } }, "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA=="],
-
- "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
-
- "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
-
- "es-iterator-helpers": ["es-iterator-helpers@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.6", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.4", "safe-array-concat": "^1.1.3" } }, "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w=="],
-
- "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
-
- "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
-
- "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="],
-
- "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="],
-
- "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
-
- "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
-
- "eslint": ["eslint@9.25.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", "@eslint/config-helpers": "^0.2.1", "@eslint/core": "^0.13.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.25.1", "@eslint/plugin-kit": "^0.2.8", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ=="],
-
- "eslint-config-next": ["eslint-config-next@15.3.1", "", { "dependencies": { "@next/eslint-plugin-next": "15.3.1", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^5.0.0" }, "peerDependencies": { "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-GnmyVd9TE/Ihe3RrvcafFhXErErtr2jS0JDeCSp3vWvy86AXwHsRBt0E3MqP/m8ACS1ivcsi5uaqjbhsG18qKw=="],
-
- "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="],
-
- "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.1", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.13", "unrs-resolver": "^1.6.2" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ=="],
-
- "eslint-module-utils": ["eslint-module-utils@2.12.0", "", { "dependencies": { "debug": "^3.2.7" } }, "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg=="],
-
- "eslint-plugin-import": ["eslint-plugin-import@2.31.0", "", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", "array.prototype.findlastindex": "^1.2.5", "array.prototype.flat": "^1.3.2", "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.0", "hasown": "^2.0.2", "is-core-module": "^2.15.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.0", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.8", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A=="],
-
- "eslint-plugin-jsx-a11y": ["eslint-plugin-jsx-a11y@6.10.2", "", { "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", "axe-core": "^4.10.0", "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "safe-regex-test": "^1.0.3", "string.prototype.includes": "^2.0.1" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q=="],
-
- "eslint-plugin-react": ["eslint-plugin-react@7.37.5", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="],
-
- "eslint-plugin-react-compiler": ["eslint-plugin-react-compiler@19.0.0-beta-ebf51a3-20250411", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "@babel/plugin-proposal-private-methods": "^7.18.6", "hermes-parser": "^0.25.1", "zod": "^3.22.4", "zod-validation-error": "^3.0.3" }, "peerDependencies": { "eslint": ">=7" } }, "sha512-R7ncuwbCPFAoeMlS56DGGSJFxmRtlWafYH/iWyep5Ks0RaPqTCL4k5gA87axUBBcITsaIgUGkbqAxDxl8Xfm5A=="],
+ "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
- "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="],
+ "enhanced-resolve": ["enhanced-resolve@5.18.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ=="],
- "eslint-scope": ["eslint-scope@8.3.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ=="],
+ "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="],
- "eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="],
-
- "espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="],
-
- "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
-
- "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
-
- "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
-
- "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
-
- "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
-
- "fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="],
-
- "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
-
- "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
-
- "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
-
- "fdir": ["fdir@6.4.4", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg=="],
-
- "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
-
- "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
-
- "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
-
- "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
-
- "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
-
- "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
-
- "framer-motion": ["framer-motion@12.7.4", "", { "dependencies": { "motion-dom": "^12.7.4", "motion-utils": "^12.7.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-jX0bPsTmU0oPZTYz/dVyD0dmOyEOEJvdn0TaZBE5I8g2GvVnnQnW9f65cJnoVfUkY3WZWNXGXnPbVA9YnaIfVA=="],
-
- "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
-
- "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="],
-
- "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
-
- "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
-
- "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
+ "framer-motion": ["framer-motion@12.23.0", "", { "dependencies": { "motion-dom": "^12.22.0", "motion-utils": "^12.19.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-xf6NxTGAyf7zR4r2KlnhFmsRfKIbjqeBupEDBAaEtVIBJX96sAon00kMlsKButSIRwPSHjbRrAPnYdJJ9kyhbA=="],
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
- "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
-
- "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="],
-
- "get-tsconfig": ["get-tsconfig@4.10.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A=="],
-
- "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
-
- "globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
-
- "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
-
- "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
-
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
- "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
-
- "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
-
- "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
+ "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
- "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
+ "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="],
- "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="],
+ "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
- "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
-
- "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
-
- "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
-
- "hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="],
-
- "hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="],
+ "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
- "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
-
- "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
-
- "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
-
- "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
+ "inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="],
"internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
"intl-messageformat": ["intl-messageformat@10.7.16", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.4", "@formatjs/fast-memoize": "2.2.7", "@formatjs/icu-messageformat-parser": "2.11.2", "tslib": "^2.8.0" } }, "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug=="],
- "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
-
- "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
-
- "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="],
-
- "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="],
-
- "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="],
-
- "is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "^7.7.1" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="],
-
- "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="],
-
- "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
-
- "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="],
+ "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
- "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="],
+ "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="],
- "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
-
- "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="],
-
- "is-generator-function": ["is-generator-function@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "get-proto": "^1.0.0", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ=="],
+ "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
- "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
+ "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
- "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="],
+ "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="],
- "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
+ "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
- "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
+ "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
- "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
+ "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
- "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="],
+ "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="],
- "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="],
+ "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="],
- "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="],
+ "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="],
- "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="],
+ "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="],
- "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="],
+ "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="],
- "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="],
+ "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="],
- "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="],
+ "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="],
- "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="],
+ "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="],
- "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
+ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="],
- "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
+ "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="],
- "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="],
+ "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
- "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
+ "lucide-react": ["lucide-react@0.525.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ=="],
- "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
+ "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
- "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
+ "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="],
- "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
+ "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="],
- "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
+ "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="],
- "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
+ "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="],
- "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
+ "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="],
- "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
+ "mdast-util-to-hast": ["mdast-util-to-hast@13.2.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA=="],
- "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="],
+ "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="],
- "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
+ "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
- "language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="],
+ "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="],
- "language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="],
+ "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
- "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
+ "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="],
- "lightningcss": ["lightningcss@1.29.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.29.2", "lightningcss-darwin-x64": "1.29.2", "lightningcss-freebsd-x64": "1.29.2", "lightningcss-linux-arm-gnueabihf": "1.29.2", "lightningcss-linux-arm64-gnu": "1.29.2", "lightningcss-linux-arm64-musl": "1.29.2", "lightningcss-linux-x64-gnu": "1.29.2", "lightningcss-linux-x64-musl": "1.29.2", "lightningcss-win32-arm64-msvc": "1.29.2", "lightningcss-win32-x64-msvc": "1.29.2" } }, "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA=="],
+ "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="],
- "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.29.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA=="],
+ "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="],
- "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.29.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w=="],
+ "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="],
- "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.29.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg=="],
+ "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="],
- "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.29.2", "", { "os": "linux", "cpu": "arm" }, "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg=="],
+ "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
- "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.29.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ=="],
+ "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="],
- "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.29.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ=="],
+ "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="],
- "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.29.2", "", { "os": "linux", "cpu": "x64" }, "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg=="],
+ "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="],
- "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.29.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w=="],
+ "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="],
- "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.29.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw=="],
+ "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="],
- "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.29.2", "", { "os": "win32", "cpu": "x64" }, "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA=="],
+ "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
- "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="],
+ "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="],
- "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
+ "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="],
- "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
+ "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="],
- "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
+ "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
- "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
+ "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="],
- "lucide-react": ["lucide-react@0.503.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-HGGkdlPWQ0vTF8jJ5TdIqhQXZi6uh3LnNgfZ8MHiuxFfX3RZeA79r2MW2tHAZKlAVfoNE8esm3p+O6VkIvpj6w=="],
+ "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
- "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
+ "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
- "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
+ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
- "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
+ "minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="],
- "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+ "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
- "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
+ "mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="],
- "motion": ["motion@12.7.4", "", { "dependencies": { "framer-motion": "^12.7.4", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-MBGrMbYageHw4iZJn+pGTr7abq5n53jCxYkhFC1It3vYukQPRWg5zij46MnwYGpLR8KG465MLHSASXot9edYOw=="],
+ "motion": ["motion@12.23.0", "", { "dependencies": { "framer-motion": "^12.23.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-PPNwblArRH9GRC4F3KtOTiIaYd+mtp324vYq3HIL+ueseoAVqPRK5TPFTAQBcIprfVd0NWo3DLzZSiyWaYFXXQ=="],
- "motion-dom": ["motion-dom@12.7.4", "", { "dependencies": { "motion-utils": "^12.7.2" } }, "sha512-1ZUHAoSUMMxP6jPqyxlk9XUfb6NxMsnWPnH2YGhrOhTURLcXWbETi6eemoKb60Pe32NVJYduL4B62VQSO5Jq8Q=="],
+ "motion-dom": ["motion-dom@12.22.0", "", { "dependencies": { "motion-utils": "^12.19.0" } }, "sha512-ooH7+/BPw9gOsL9VtPhEJHE2m4ltnhMlcGMhEqA0YGNhKof7jdaszvsyThXI6LVIKshJUZ9/CP6HNqQhJfV7kw=="],
- "motion-utils": ["motion-utils@12.7.2", "", {}, "sha512-XhZwqctxyJs89oX00zn3OGCuIIpVevbTa+u82usWBC6pSHUd2AoNWiYa7Du8tJxJy9TFbZ82pcn5t7NOm1PHAw=="],
+ "motion-utils": ["motion-utils@12.19.0", "", {}, "sha512-BuFTHINYmV07pdWs6lj6aI63vr2N4dg0vR+td0rtrdpWOhBzIkEklZyLcvKBoEtwSqx8Jg06vUB5RS0xDiUybw=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
- "napi-postinstall": ["napi-postinstall@0.1.5", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-HI5bHONOUYqV+FJvueOSgjRxHTLB25a3xIv59ugAxFe7xRNbW96hyYbMbsKzl+QvFV9mN/SrtHwiU+vYhMwA7Q=="],
-
- "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
-
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
- "next": ["next@15.3.1-canary.15", "", { "dependencies": { "@next/env": "15.3.1-canary.15", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.1-canary.15", "@next/swc-darwin-x64": "15.3.1-canary.15", "@next/swc-linux-arm64-gnu": "15.3.1-canary.15", "@next/swc-linux-arm64-musl": "15.3.1-canary.15", "@next/swc-linux-x64-gnu": "15.3.1-canary.15", "@next/swc-linux-x64-musl": "15.3.1-canary.15", "@next/swc-win32-arm64-msvc": "15.3.1-canary.15", "@next/swc-win32-x64-msvc": "15.3.1-canary.15", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-K04SKrZ+HKrx1+rma4y9nfxilL5HnQ5KiL0biKwyQyhiG5xFLUqaHGzpiYflbWW+ufJWejk3cJJkhK/DUuB37Q=="],
-
- "next-intl": ["next-intl@4.0.3", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.5.4", "negotiator": "^1.0.0", "use-intl": "^4.0.3" }, "peerDependencies": { "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0", "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-vw25WvRheZG6NzyXYePuhc/xrYHMhnu7cVcB49XoXaQsO4WMV0YDV6BllhRgI5Ne79hpoAQMyk/fxIKtkBVbNg=="],
-
- "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="],
-
- "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
-
- "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
-
- "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
-
- "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="],
-
- "object.entries": ["object.entries@1.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.1" } }, "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw=="],
+ "next": ["next@15.4.0-canary.111", "", { "dependencies": { "@next/env": "15.4.0-canary.111", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.0-canary.111", "@next/swc-darwin-x64": "15.4.0-canary.111", "@next/swc-linux-arm64-gnu": "15.4.0-canary.111", "@next/swc-linux-arm64-musl": "15.4.0-canary.111", "@next/swc-linux-x64-gnu": "15.4.0-canary.111", "@next/swc-linux-x64-musl": "15.4.0-canary.111", "@next/swc-win32-arm64-msvc": "15.4.0-canary.111", "@next/swc-win32-x64-msvc": "15.4.0-canary.111", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-Ar2qfxiFZIqfnZsHGrgq1iSQn9kMpj8eHwJWOYUx/f/v1xHU16XgVojufOg47xWMjHOGxal46FQv68U7MEKOFA=="],
- "object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="],
+ "next-intl": ["next-intl@4.3.4", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.5.4", "negotiator": "^1.0.0", "use-intl": "^4.3.4" }, "peerDependencies": { "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0", "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-VWLIDlGbnL/o4LnveJTJD1NOYN8lh3ZAGTWw2krhfgg53as3VsS4jzUVnArJdqvwtlpU/2BIDbWTZ7V4o1jFEw=="],
- "object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="],
+ "nuqs": ["nuqs@2.4.3", "", { "dependencies": { "mitt": "^3.0.1" }, "peerDependencies": { "@remix-run/react": ">=2", "next": ">=14.2.0", "react": ">=18.2.0 || ^19.0.0-0", "react-router": "^6 || ^7", "react-router-dom": "^6 || ^7" }, "optionalPeers": ["@remix-run/react", "next", "react-router", "react-router-dom"] }, "sha512-BgtlYpvRwLYiJuWzxt34q2bXu/AIS66sLU1QePIMr2LWkb+XH0vKXdbLSgn9t6p7QKzwI7f38rX3Wl9llTXQ8Q=="],
- "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="],
+ "oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="],
- "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
+ "oniguruma-to-es": ["oniguruma-to-es@4.3.3", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg=="],
- "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
-
- "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
-
- "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
-
- "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
-
- "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
-
- "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
-
- "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
+ "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
- "picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
-
- "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
-
- "postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
+ "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
"postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "^3.1.1" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "postcss", "tsx", "yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="],
- "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
-
- "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
-
- "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
-
- "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
+ "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
"react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
"react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
- "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
-
- "react-remove-scroll": ["react-remove-scroll@2.6.3", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="],
+ "react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="],
"react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
@@ -1007,202 +658,102 @@
"react-use-measure": ["react-use-measure@2.1.7", "", { "peerDependencies": { "react": ">=16.13", "react-dom": ">=16.13" }, "optionalPeers": ["react-dom"] }, "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg=="],
- "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
+ "regex": ["regex@6.0.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA=="],
- "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
+ "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
- "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
-
- "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
-
- "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
-
- "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
+ "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
"robust-predicates": ["robust-predicates@3.0.2", "", {}, "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="],
- "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
-
"rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="],
- "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="],
-
- "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="],
-
- "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
-
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
"scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
- "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
+ "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
"server-only": ["server-only@0.0.1", "", {}, "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="],
- "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
-
- "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
-
- "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="],
-
- "sharp": ["sharp@0.34.1", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.7.1" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.1", "@img/sharp-darwin-x64": "0.34.1", "@img/sharp-libvips-darwin-arm64": "1.1.0", "@img/sharp-libvips-darwin-x64": "1.1.0", "@img/sharp-libvips-linux-arm": "1.1.0", "@img/sharp-libvips-linux-arm64": "1.1.0", "@img/sharp-libvips-linux-ppc64": "1.1.0", "@img/sharp-libvips-linux-s390x": "1.1.0", "@img/sharp-libvips-linux-x64": "1.1.0", "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", "@img/sharp-libvips-linuxmusl-x64": "1.1.0", "@img/sharp-linux-arm": "0.34.1", "@img/sharp-linux-arm64": "0.34.1", "@img/sharp-linux-s390x": "0.34.1", "@img/sharp-linux-x64": "0.34.1", "@img/sharp-linuxmusl-arm64": "0.34.1", "@img/sharp-linuxmusl-x64": "0.34.1", "@img/sharp-wasm32": "0.34.1", "@img/sharp-win32-ia32": "0.34.1", "@img/sharp-win32-x64": "0.34.1" } }, "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg=="],
-
- "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
-
- "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
+ "sharp": ["sharp@0.34.2", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.4", "semver": "^7.7.2" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.2", "@img/sharp-darwin-x64": "0.34.2", "@img/sharp-libvips-darwin-arm64": "1.1.0", "@img/sharp-libvips-darwin-x64": "1.1.0", "@img/sharp-libvips-linux-arm": "1.1.0", "@img/sharp-libvips-linux-arm64": "1.1.0", "@img/sharp-libvips-linux-ppc64": "1.1.0", "@img/sharp-libvips-linux-s390x": "1.1.0", "@img/sharp-libvips-linux-x64": "1.1.0", "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", "@img/sharp-libvips-linuxmusl-x64": "1.1.0", "@img/sharp-linux-arm": "0.34.2", "@img/sharp-linux-arm64": "0.34.2", "@img/sharp-linux-s390x": "0.34.2", "@img/sharp-linux-x64": "0.34.2", "@img/sharp-linuxmusl-arm64": "0.34.2", "@img/sharp-linuxmusl-x64": "0.34.2", "@img/sharp-wasm32": "0.34.2", "@img/sharp-win32-arm64": "0.34.2", "@img/sharp-win32-ia32": "0.34.2", "@img/sharp-win32-x64": "0.34.2" } }, "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg=="],
- "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
-
- "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
-
- "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
-
- "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
+ "shiki": ["shiki@3.7.0", "", { "dependencies": { "@shikijs/core": "3.7.0", "@shikijs/engine-javascript": "3.7.0", "@shikijs/engine-oniguruma": "3.7.0", "@shikijs/langs": "3.7.0", "@shikijs/themes": "3.7.0", "@shikijs/types": "3.7.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-ZcI4UT9n6N2pDuM2n3Jbk0sR4Swzq43nLPgS/4h0E3B/NrFn2HKElrDtceSf8Zx/OWYOo7G1SAtBLypCp+YXqg=="],
"simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
- "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="],
-
- "streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="],
-
- "string.prototype.includes": ["string.prototype.includes@2.0.1", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3" } }, "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg=="],
-
- "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="],
-
- "string.prototype.repeat": ["string.prototype.repeat@1.0.0", "", { "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w=="],
-
- "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="],
+ "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
- "string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="],
+ "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
- "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="],
+ "style-to-js": ["style-to-js@1.1.17", "", { "dependencies": { "style-to-object": "1.0.9" } }, "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA=="],
- "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
-
- "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
+ "style-to-object": ["style-to-object@1.0.9", "", { "dependencies": { "inline-style-parser": "0.2.4" } }, "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw=="],
"styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
- "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
-
- "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
-
- "tailwind-merge": ["tailwind-merge@3.2.0", "", {}, "sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA=="],
-
- "tailwindcss": ["tailwindcss@4.1.4", "", {}, "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A=="],
+ "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="],
- "tapable": ["tapable@2.2.1", "", {}, "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="],
+ "tailwindcss": ["tailwindcss@4.1.11", "", {}, "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="],
- "tinyglobby": ["tinyglobby@0.2.13", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw=="],
+ "tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="],
- "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
+ "tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="],
- "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
-
- "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],
+ "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
- "tw-animate-css": ["tw-animate-css@1.2.8", "", {}, "sha512-AxSnYRvyFnAiZCUndS3zQZhNfV/B77ZhJ+O7d3K6wfg/jKJY+yv6ahuyXwnyaYA9UdLqnpCwhTRv9pPTBnPR2g=="],
-
- "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
-
- "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],
-
- "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="],
-
- "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="],
-
- "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="],
+ "tw-animate-css": ["tw-animate-css@1.3.5", "", {}, "sha512-t3u+0YNoloIhj1mMXs779P6MO9q3p3mvGn4k1n3nJPqJw/glZcuijG2qTSN4z4mgNRfW5ZC3aXJFLwDtiipZXA=="],
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
- "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
+ "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
- "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
+ "unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="],
- "unrs-resolver": ["unrs-resolver@1.6.4", "", { "dependencies": { "napi-postinstall": "^0.1.5" }, "optionalDependencies": { "@unrs/resolver-binding-darwin-arm64": "1.6.4", "@unrs/resolver-binding-darwin-x64": "1.6.4", "@unrs/resolver-binding-freebsd-x64": "1.6.4", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.6.4", "@unrs/resolver-binding-linux-arm-musleabihf": "1.6.4", "@unrs/resolver-binding-linux-arm64-gnu": "1.6.4", "@unrs/resolver-binding-linux-arm64-musl": "1.6.4", "@unrs/resolver-binding-linux-ppc64-gnu": "1.6.4", "@unrs/resolver-binding-linux-riscv64-gnu": "1.6.4", "@unrs/resolver-binding-linux-s390x-gnu": "1.6.4", "@unrs/resolver-binding-linux-x64-gnu": "1.6.4", "@unrs/resolver-binding-linux-x64-musl": "1.6.4", "@unrs/resolver-binding-wasm32-wasi": "1.6.4", "@unrs/resolver-binding-win32-arm64-msvc": "1.6.4", "@unrs/resolver-binding-win32-ia32-msvc": "1.6.4", "@unrs/resolver-binding-win32-x64-msvc": "1.6.4" } }, "sha512-Fb6KH4pQK0XjR5PdRW8BEzsQmbYjkeRHF3IIIZtOVXVFM6Nh+Gb2fQh23Ba7qaYloDp+Aa8/JeNqyImJ8xHlkQ=="],
+ "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="],
- "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
+ "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
- "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
+ "unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
+
+ "unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="],
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
- "use-intl": ["use-intl@4.0.3", "", { "dependencies": { "@formatjs/fast-memoize": "^2.2.0", "@schummar/icu-type-parser": "1.21.5", "intl-messageformat": "^10.5.14" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" } }, "sha512-AeXB+5gPORJHj1BPCj0aNga0y4gpucHcqNk6B+xRSLlBt0Bz+QVtMmdtV1nvxG308rWqqkmfo6RVu9yIcZt+Hw=="],
+ "use-intl": ["use-intl@4.3.4", "", { "dependencies": { "@formatjs/fast-memoize": "^2.2.0", "@schummar/icu-type-parser": "1.21.5", "intl-messageformat": "^10.5.14" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" } }, "sha512-sHfiU0QeJ1rirNWRxvCyvlSh9+NczcOzRnPyMeo2rtHXhVnBsvMRjE+UG4eh3lRhCxrvcqei/I0lBxsc59on1w=="],
"use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
- "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
-
- "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
-
- "which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="],
-
- "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="],
-
- "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="],
-
- "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
+ "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
- "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
+ "vfile-message": ["vfile-message@4.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="],
- "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
+ "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
- "zod": ["zod@3.24.3", "", {}, "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg=="],
+ "zod": ["zod@3.25.71", "", {}, "sha512-BsBc/NPk7h8WsUWYWYL+BajcJPY8YhjelaWu2NMLuzgraKAz4Lb4/6K11g9jpuDetjMiqhZ6YaexFLOC0Ogi3Q=="],
- "zod-validation-error": ["zod-validation-error@3.4.0", "", { "peerDependencies": { "zod": "^3.18.0" } }, "sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ=="],
-
- "@babel/traverse/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="],
-
- "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
+ "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
"@formatjs/ecma402-abstract/@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.6.1", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg=="],
- "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
-
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
- "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.9", "", { "dependencies": { "@emnapi/core": "^1.4.0", "@emnapi/runtime": "^1.4.0", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg=="],
+ "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA=="],
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
- "@typescript-eslint/typescript-estree/fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
-
- "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
-
- "@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
-
- "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
-
- "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
-
- "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
-
- "eslint-plugin-react/resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="],
-
- "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
-
- "is-bun-module/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
-
- "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
-
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
- "sharp/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
-
- "tsconfig-paths/json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="],
-
- "@typescript-eslint/typescript-estree/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
-
- "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
+ "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
}
}
diff --git a/components.json b/components.json
index 067cea3..48a9965 100644
--- a/components.json
+++ b/components.json
@@ -1,21 +1,21 @@
{
- "$schema": "https://ui.shadcn.com/schema.json",
- "style": "default",
- "rsc": true,
- "tsx": true,
- "tailwind": {
- "config": "",
- "css": "src/app/globals.css",
- "baseColor": "slate",
- "cssVariables": true,
- "prefix": ""
- },
- "aliases": {
- "components": "@/components",
- "utils": "@/lib/utils",
- "ui": "@/components/ui",
- "lib": "@/lib",
- "hooks": "@/hooks"
- },
- "iconLibrary": "lucide"
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "default",
+ "rsc": true,
+ "tsx": true,
+ "tailwind": {
+ "config": "",
+ "css": "src/app/globals.css",
+ "baseColor": "slate",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "iconLibrary": "lucide"
}
diff --git a/eslint.config.mjs b/eslint.config.mjs
deleted file mode 100644
index a5abc97..0000000
--- a/eslint.config.mjs
+++ /dev/null
@@ -1,62 +0,0 @@
-// @ts-check
-
-import { FlatCompat } from "@eslint/eslintrc";
-
-const compat = new FlatCompat({
- baseDirectory: import.meta.dirname,
-});
-
-/** @type {import("eslint").Linter.Config[]} */
-const config = [
- {
- languageOptions: {
- parserOptions: {
- project: true,
- ecmaFeatures: {
- jsx: true,
- },
- },
- },
- },
- ...compat.config({
- extends: ["next/core-web-vitals", "next/typescript"],
- plugins: ["react-compiler"],
-
- rules: {
- "react-compiler/react-compiler": "error",
- "react/no-unescaped-entities": "off",
-
- "@typescript-eslint/consistent-type-imports": [
- "error",
- {
- prefer: "type-imports",
- fixStyle: "inline-type-imports",
- },
- ],
-
- "@typescript-eslint/no-unused-vars": [
- "error",
- {
- argsIgnorePattern: "^_",
- varsIgnorePattern: "^_",
- destructuredArrayIgnorePattern: "^_",
- },
- ],
-
- "@typescript-eslint/no-misused-promises": [
- "error",
- {
- checksVoidReturn: {
- attributes: false,
- },
- },
- ],
-
- "@typescript-eslint/no-require-imports": ["off"],
-
- "react-hooks/exhaustive-deps": ["off"],
- },
- }),
-];
-
-export default config;
diff --git a/global.ts b/global.ts
index a7ebdeb..efe587f 100644
--- a/global.ts
+++ b/global.ts
@@ -2,8 +2,8 @@ import type { routing } from "@/i18n/routing";
import type messages from "./messages/en.json";
declare module "next-intl" {
- interface AppConfig {
- Locale: (typeof routing.locales)[number];
- Messages: typeof messages;
- }
+ interface AppConfig {
+ Locale: (typeof routing.locales)[number];
+ Messages: typeof messages;
+ }
}
diff --git a/messages/en.json b/messages/en.json
index bc7d1d8..432dca5 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -1,150 +1,150 @@
{
- "home": {
- "hero": {
- "title": "Sorting Algorithms Visualized",
- "about": "Explore and understand how different sorting algorithms work through interactive visualizations",
- "call-to-action": "Start Exploring"
- },
- "info": {
- "title": "What are Sorting Algorithms?",
- "description": "Sorting algorithms are methods for reorganizing a sequence of items in a specific order, typically in ascending or descending sequence. These algorithms are fundamental to computer science and form the backbone of many complex systems. From organizing databases to optimizing search functions, sorting algorithms play a crucial role in software development. Understanding how they work and their efficiency characteristics helps developers choose the right algorithm for specific tasks. ",
- "title2": "Why Visualize Them?",
- "description2": "Visual learning enhances understanding of complex concepts Compare algorithm efficiency in real-time Identify patterns and behaviors in different sorting methods Perfect for students, educators, and curious minds "
- },
- "algorithms": {
- "title": "Explore Popular Algorithms",
- "description": "Select an algorithm to visualize its sorting process in the interactive playground",
- "call-to-action": "Visualize",
- "call-to-action2": "View All Algorithms",
- "bubble-sort": {
- "title": "Bubble Sort",
- "description": "Simple comparison-based algorithm"
- },
- "selection-sort": {
- "title": "Selection Sort",
- "description": "In-place comparison sorting algorithm"
- },
- "insertion-sort": {
- "title": "Insertion Sort",
- "description": "Builds sorted array one item at a time"
- },
- "quick-sort": {
- "title": "Quick Sort",
- "description": "Efficient divide-and-conquer algorithm"
- }
- }
- },
- "metadata": {
- "title": "Sorting Algorithm Visualization",
- "description": "A simple visualization of some basic sorting algorithms"
- },
- "not-found": {
- "title": "Oops! This page has been sucked into the void ",
- "description": "The page you're looking for seems to have been pulled into the event horizon. Even light can't escape, but you still can!",
- "link": "Escape to Safety"
- },
- "locale-switcher": {
- "label": "Change Language",
- "locale": "{locale, select, en {English} sk {Slovak} other {Unknown}}",
- "locale-short": "{locale, select, en {EN} sk {SK} other {XX}}"
- },
- "navigation": {
- "home": "Home",
- "playground": "Playground"
- },
- "footer": {
- "title": "Sorting Algorithm Visualizer",
- "description": "Learn through interactive visualization",
- "copyright": "All rights reserved."
- },
- "visualizer": {
- "start": "Start Sorting",
- "resume": "Resume",
- "pause": "Pause",
- "reset": "Reset",
- "new-data": "New Data",
- "speed": "Speed",
- "select-algorithm": "Select Algorithm",
- "unsorted": "Unsorted",
- "comparing": "Comparing",
- "pivot": "Pivot",
- "current": "Current Element",
- "sorted": "Sorted",
- "length": "Length"
- },
- "algorithm-explanation": {
- "stats": {
- "title": "Statistics",
- "iterations": "Iterations",
- "swaps": "Swaps"
- },
- "complexity": {
- "title": "Complexity Analysis",
- "case": "Case",
- "best": "Best",
- "average": "Average",
- "worst": "Worst",
- "time": "Time",
- "space": "Space"
- },
- "explanation": {
- "title": "How It Works",
- "steps": "Steps",
- "cases": "Cases",
- "code": "Code"
- },
- "bubble": {
- "title": "Bubble Sort",
- "description": "A simple comparison-based algorithm that repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order.",
- "steps": [
- "Start at the beginning of the array",
- "Compare adjacent elements. If the first is greater than the second, swap them",
- "Move to the next pair of elements and repeat the comparison and swap if necessary",
- "After one complete pass, the largest element will be at the end. Repeat the process for the remaining elements"
- ],
- "best-case": "When the array is already sorted, bubble sort makes only one pass through the array with no swaps.",
- "average-case": "For random arrays, bubble sort still requires quadratic time.",
- "worst-case": "When the array is sorted in reverse order, bubble sort must make the maximum number of comparisons and swaps."
- },
- "selection": {
- "title": "Selection Sort",
- "description": "A simple in-place comparison sorting algorithm that divides the input list into two parts: a sorted sublist and an unsorted sublist.",
- "steps": [
- "Find the minimum element in the unsorted part of the array",
- "Swap it with the element at the beginning of the unsorted part",
- "Move the boundary between the sorted and unsorted parts one element to the right",
- "Repeat until the entire array is sorted"
- ],
- "best-case": "Selection sort always performs the same number of comparisons regardless of the input array's order.",
- "average-case": "For random arrays, selection sort requires quadratic time.",
- "worst-case": "Selection sort always performs the same number of comparisons and swaps regardless of the input array's order."
- },
- "insertion": {
- "title": "Insertion Sort",
- "description": "A simple sorting algorithm that builds the final sorted array one item at a time, similar to how you might sort playing cards in your hand.",
- "steps": [
- "Start with the second element (assume the first element is already sorted)",
- "Compare the current element with the previous elements",
- "If the previous element is greater than the current element, move the previous element one position ahead",
- "Repeat until the correct position for the current element is found, then insert it"
- ],
- "best-case": "When the array is already sorted, insertion sort makes only one comparison per element.",
- "average-case": "For random arrays, insertion sort requires quadratic time.",
- "worst-case": "When the array is sorted in reverse order, insertion sort must shift each element to the beginning of the array."
- },
- "quicksort": {
- "title": "Quick Sort",
- "description": "An efficient, divide-and-conquer sorting algorithm that works by selecting a 'pivot' element and partitioning the array around the pivot.",
- "steps": [
- "Choose a pivot element from the array",
- "Partition the array: items less than the pivot go to the left, items greater go to the right",
- "The pivot is now in its final sorted position",
- "Recursively apply the above steps to the sub-arrays on the left and right of the pivot",
- "The base case is when a sub-array has 0 or 1 elements (already sorted)"
- ],
- "best-case": "When the pivot always divides the array into nearly equal halves, quick sort achieves its best performance.",
- "average-case": "For random arrays, quick sort is very efficient with O(n log n) time complexity.",
- "worst-case": "When the pivot is always the smallest or largest element (e.g., in already sorted arrays), quick sort degrades to O(n²) performance."
- }
- }
+ "home": {
+ "hero": {
+ "title": "Sorting Algorithms Visualized",
+ "about": "Explore and understand how different sorting algorithms work through interactive visualizations",
+ "call-to-action": "Start Exploring"
+ },
+ "info": {
+ "title": "What are Sorting Algorithms?",
+ "description": "Sorting algorithms are methods for reorganizing a sequence of items in a specific order, typically in ascending or descending sequence. These algorithms are fundamental to computer science and form the backbone of many complex systems. From organizing databases to optimizing search functions, sorting algorithms play a crucial role in software development. Understanding how they work and their efficiency characteristics helps developers choose the right algorithm for specific tasks. ",
+ "title2": "Why Visualize Them?",
+ "description2": "Visual learning enhances understanding of complex concepts Compare algorithm efficiency in real-time Identify patterns and behaviors in different sorting methods Perfect for students, educators, and curious minds "
+ },
+ "algorithms": {
+ "title": "Explore Popular Algorithms",
+ "description": "Select an algorithm to visualize its sorting process in the interactive playground",
+ "call-to-action": "Visualize",
+ "call-to-action2": "View All Algorithms",
+ "bubble-sort": {
+ "title": "Bubble Sort",
+ "description": "Simple comparison-based algorithm"
+ },
+ "selection-sort": {
+ "title": "Selection Sort",
+ "description": "In-place comparison sorting algorithm"
+ },
+ "insertion-sort": {
+ "title": "Insertion Sort",
+ "description": "Builds sorted array one item at a time"
+ },
+ "quick-sort": {
+ "title": "Quick Sort",
+ "description": "Efficient divide-and-conquer algorithm"
+ }
+ }
+ },
+ "metadata": {
+ "title": "Sorting Algorithm Visualization",
+ "description": "A simple visualization of some basic sorting algorithms"
+ },
+ "not-found": {
+ "title": "Oops! This page has been sucked into the void ",
+ "description": "The page you're looking for seems to have been pulled into the event horizon. Even light can't escape, but you still can!",
+ "link": "Escape to Safety"
+ },
+ "locale-switcher": {
+ "label": "Change Language",
+ "locale": "{locale, select, en {English} sk {Slovak} other {Unknown}}",
+ "locale-short": "{locale, select, en {EN} sk {SK} other {XX}}"
+ },
+ "navigation": {
+ "home": "Home",
+ "playground": "Playground"
+ },
+ "footer": {
+ "title": "Sorting Algorithm Visualizer",
+ "description": "Learn through interactive visualization",
+ "copyright": "All rights reserved."
+ },
+ "visualizer": {
+ "start": "Start Sorting",
+ "resume": "Resume",
+ "pause": "Pause",
+ "reset": "Reset",
+ "new-data": "New Data",
+ "speed": "Speed",
+ "select-algorithm": "Select Algorithm",
+ "unsorted": "Unsorted",
+ "comparing": "Comparing",
+ "pivot": "Pivot",
+ "current": "Current Element",
+ "sorted": "Sorted",
+ "length": "Length"
+ },
+ "algorithm-explanation": {
+ "stats": {
+ "title": "Statistics",
+ "iterations": "Iterations",
+ "swaps": "Swaps"
+ },
+ "complexity": {
+ "title": "Complexity Analysis",
+ "case": "Case",
+ "best": "Best",
+ "average": "Average",
+ "worst": "Worst",
+ "time": "Time",
+ "space": "Space"
+ },
+ "explanation": {
+ "title": "How It Works",
+ "steps": "Steps",
+ "cases": "Cases",
+ "code": "Code"
+ },
+ "bubble": {
+ "title": "Bubble Sort",
+ "description": "A simple comparison-based algorithm that repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order.",
+ "steps": [
+ "Start at the beginning of the array",
+ "Compare adjacent elements. If the first is greater than the second, swap them",
+ "Move to the next pair of elements and repeat the comparison and swap if necessary",
+ "After one complete pass, the largest element will be at the end. Repeat the process for the remaining elements"
+ ],
+ "best-case": "When the array is already sorted, bubble sort makes only one pass through the array with no swaps.",
+ "average-case": "For random arrays, bubble sort still requires quadratic time.",
+ "worst-case": "When the array is sorted in reverse order, bubble sort must make the maximum number of comparisons and swaps."
+ },
+ "selection": {
+ "title": "Selection Sort",
+ "description": "A simple in-place comparison sorting algorithm that divides the input list into two parts: a sorted sublist and an unsorted sublist.",
+ "steps": [
+ "Find the minimum element in the unsorted part of the array",
+ "Swap it with the element at the beginning of the unsorted part",
+ "Move the boundary between the sorted and unsorted parts one element to the right",
+ "Repeat until the entire array is sorted"
+ ],
+ "best-case": "Selection sort always performs the same number of comparisons regardless of the input array's order.",
+ "average-case": "For random arrays, selection sort requires quadratic time.",
+ "worst-case": "Selection sort always performs the same number of comparisons and swaps regardless of the input array's order."
+ },
+ "insertion": {
+ "title": "Insertion Sort",
+ "description": "A simple sorting algorithm that builds the final sorted array one item at a time, similar to how you might sort playing cards in your hand.",
+ "steps": [
+ "Start with the second element (assume the first element is already sorted)",
+ "Compare the current element with the previous elements",
+ "If the previous element is greater than the current element, move the previous element one position ahead",
+ "Repeat until the correct position for the current element is found, then insert it"
+ ],
+ "best-case": "When the array is already sorted, insertion sort makes only one comparison per element.",
+ "average-case": "For random arrays, insertion sort requires quadratic time.",
+ "worst-case": "When the array is sorted in reverse order, insertion sort must shift each element to the beginning of the array."
+ },
+ "quicksort": {
+ "title": "Quick Sort",
+ "description": "An efficient, divide-and-conquer sorting algorithm that works by selecting a 'pivot' element and partitioning the array around the pivot.",
+ "steps": [
+ "Choose a pivot element from the array",
+ "Partition the array: items less than the pivot go to the left, items greater go to the right",
+ "The pivot is now in its final sorted position",
+ "Recursively apply the above steps to the sub-arrays on the left and right of the pivot",
+ "The base case is when a sub-array has 0 or 1 elements (already sorted)"
+ ],
+ "best-case": "When the pivot always divides the array into nearly equal halves, quick sort achieves its best performance.",
+ "average-case": "For random arrays, quick sort is very efficient with O(n log n) time complexity.",
+ "worst-case": "When the pivot is always the smallest or largest element (e.g., in already sorted arrays), quick sort degrades to O(n²) performance."
+ }
+ }
}
diff --git a/messages/sk.json b/messages/sk.json
index 2b1004c..638aded 100644
--- a/messages/sk.json
+++ b/messages/sk.json
@@ -1,151 +1,151 @@
{
- "home": {
- "hero": {
- "title": "Algoritmy Triedení Vizualizované",
- "about": "Preskúmaj a pochop, ako fungujú rôzne algoritmy triedení cez interaktívne vizualizácie",
- "call-to-action": "Chcem Vyskúšať"
- },
- "info": {
- "title": "Čo sú Triediace algoritmy?",
- "description": "Triediace algoritmy sú metódy pre preorganizovanie sekvencie položiek v konkrétnom poradí, obvykle v poradí vzostupne alebo zostupu. Tieto algoritmy sú základné v informatike a tvoria základ pre mnoho zložitejších systémov. Od organizácie databáz cez optimalizáciu vyhľadávacích funkcií, triediace algoritmy sú zásadné vo vývoji softvéru. Poznanie ich funkcií a účinností nám pomôže vybrať správny algoritmus pre konkrétnu úlohu. ",
- "title2": "Prečo ich vizualizovať?",
- "description2": "Vizualizácia pomáha pri pochopení zložitých konceptov Porovnávaj efektívnosť algoritmov Zisti vzory a správanie v rôznych triediacich algoritmoch Ideálne pre študentov, učiteľov a zvedavé hlavy "
- },
- "algorithms": {
- "title": "Pozri si Populárne Algoritmy",
- "description": "Vyber si algoritmus, ktorý chceš vizualizovať v interaktívnom prehrávaní",
- "call-to-action": "Vizualizovať",
- "call-to-action2": "Pozrieť všetky algoritmy",
- "bubble-sort": {
- "title": "Bubble Sort",
- "description": "Jednoduchý porovnávací algoritmus"
- },
- "selection-sort": {
- "title": "Selection Sort",
- "description": "Porovnávací algoritmus na mieste v pamäti"
- },
- "insertion-sort": {
- "title": "Insertion Sort",
- "description": "Vytvára zoradený zoznam jednou položkou naraz"
- },
- "quick-sort": {
- "title": "Quick Sort",
- "description": "Efektívny algoritmus rozdelenia a zlúčenia"
- }
- }
- },
- "metadata": {
- "title": "Vizualizácia algoritmov pre triedenie",
- "description": "Jednoduchá vizualizácia niektorých základných algoritmov triedenia"
- },
- "not-found": {
- "title": "Oops! Stránka bola zhltnutá do prázdnoty ",
- "description": "Stránka, ktorú hľadáte, bola vtiahnutá do horizontu. Ani svetlo nevie uniknúť, ale vy stále môžete!",
- "link": "Späť do bezpečia"
- },
- "locale-switcher": {
- "label": "Zmeniť jazyk",
- "locale": "{locale, select, en {Angličtina} sk {Slovenčina} other {Neznámy}}",
- "locale-short": "{locale, select, en {EN} sk {SK} other {XX}}"
- },
- "navigation": {
- "home": "Domov",
- "playground": "Vizualizácia"
- },
- "footer": {
- "title": "Vizualizácia algoritmov pre triedenie",
- "description": "Nauč sa cez interaktívne vizualizácie",
+ "home": {
+ "hero": {
+ "title": "Algoritmy Triedení Vizualizované",
+ "about": "Preskúmaj a pochop, ako fungujú rôzne algoritmy triedení cez interaktívne vizualizácie",
+ "call-to-action": "Chcem Vyskúšať"
+ },
+ "info": {
+ "title": "Čo sú Triediace algoritmy?",
+ "description": "Triediace algoritmy sú metódy pre preorganizovanie sekvencie položiek v konkrétnom poradí, obvykle v poradí vzostupne alebo zostupu. Tieto algoritmy sú základné v informatike a tvoria základ pre mnoho zložitejších systémov. Od organizácie databáz cez optimalizáciu vyhľadávacích funkcií, triediace algoritmy sú zásadné vo vývoji softvéru. Poznanie ich funkcií a účinností nám pomôže vybrať správny algoritmus pre konkrétnu úlohu. ",
+ "title2": "Prečo ich vizualizovať?",
+ "description2": "Vizualizácia pomáha pri pochopení zložitých konceptov Porovnávaj efektívnosť algoritmov Zisti vzory a správanie v rôznych triediacich algoritmoch Ideálne pre študentov, učiteľov a zvedavé hlavy "
+ },
+ "algorithms": {
+ "title": "Pozri si Populárne Algoritmy",
+ "description": "Vyber si algoritmus, ktorý chceš vizualizovať v interaktívnom prehrávaní",
+ "call-to-action": "Vizualizovať",
+ "call-to-action2": "Pozrieť všetky algoritmy",
+ "bubble-sort": {
+ "title": "Bubble Sort",
+ "description": "Jednoduchý porovnávací algoritmus"
+ },
+ "selection-sort": {
+ "title": "Selection Sort",
+ "description": "Porovnávací algoritmus na mieste v pamäti"
+ },
+ "insertion-sort": {
+ "title": "Insertion Sort",
+ "description": "Vytvára zoradený zoznam jednou položkou naraz"
+ },
+ "quick-sort": {
+ "title": "Quick Sort",
+ "description": "Efektívny algoritmus rozdelenia a zlúčenia"
+ }
+ }
+ },
+ "metadata": {
+ "title": "Vizualizácia algoritmov pre triedenie",
+ "description": "Jednoduchá vizualizácia niektorých základných algoritmov triedenia"
+ },
+ "not-found": {
+ "title": "Oops! Stránka bola zhltnutá do prázdnoty ",
+ "description": "Stránka, ktorú hľadáte, bola vtiahnutá do horizontu. Ani svetlo nevie uniknúť, ale vy stále môžete!",
+ "link": "Späť do bezpečia"
+ },
+ "locale-switcher": {
+ "label": "Zmeniť jazyk",
+ "locale": "{locale, select, en {Angličtina} sk {Slovenčina} other {Neznámy}}",
+ "locale-short": "{locale, select, en {EN} sk {SK} other {XX}}"
+ },
+ "navigation": {
+ "home": "Domov",
+ "playground": "Vizualizácia"
+ },
+ "footer": {
+ "title": "Vizualizácia algoritmov pre triedenie",
+ "description": "Nauč sa cez interaktívne vizualizácie",
- "copyright": "Všetky práva vyhradené."
- },
- "visualizer": {
- "start": "Začať triedenie",
- "resume": "Pokračovať",
- "pause": "Zastaviť",
- "reset": "Resetovať",
- "new-data": "Nové dáta",
- "speed": "Rýchlosť",
- "select-algorithm": "Vyber algoritmus",
- "unsorted": "Nevytriedené",
- "comparing": "Porovnávanie",
- "pivot": "Pivot",
- "current": "Aktuálny prvok",
- "sorted": "Vytriedené",
- "length": "Dĺžka"
- },
- "algorithm-explanation": {
- "stats": {
- "title": "Štatistiky",
- "iterations": "Iterácie",
- "swaps": "Výmeny"
- },
- "complexity": {
- "title": "Analýza zložitosti",
- "case": "Prípad",
- "best": "Najlepší",
- "average": "Priemerný",
- "worst": "Najhorší",
- "time": "Čas",
- "space": "Priestor"
- },
- "explanation": {
- "title": "Ako to funguje",
- "steps": "Kroky",
- "cases": "Prípady",
- "code": "Kód"
- },
- "bubble": {
- "title": "Bubble Sort",
- "description": "Jednoduchý porovnávací algoritmus, ktorý opakovane prechádza zoznamom, porovnáva susedné prvky a vymieňa ich, ak sú v nesprávnom poradí.",
- "steps": [
- "Začnite na začiatku poľa",
- "Porovnajte susedné prvky. Ak je prvý väčší ako druhý, vymeňte ich",
- "Prejdite na ďalší pár prvkov a opakujte porovnanie a výmenu, ak je to potrebné",
- "Po jednom úplnom prechode bude najväčší prvok na konci. Opakujte proces pre zostávajúce prvky"
- ],
- "best-case": "Keď je pole už zoradené, bubble sort urobí iba jeden prechod poľom bez výmen.",
- "average-case": "Pre náhodné polia bubble sort stále vyžaduje kvadratický čas.",
- "worst-case": "Keď je pole zoradené v opačnom poradí, bubble sort musí urobiť maximálny počet porovnaní a výmen."
- },
- "selection": {
- "title": "Selection Sort",
- "description": "Jednoduchý porovnávací algoritmus triedenia na mieste, ktorý rozdeľuje vstupný zoznam na dve časti: zoradenú časť a nezoradenú časť.",
- "steps": [
- "Nájdite minimálny prvok v nezotriedenej časti poľa",
- "Vymeňte ho s prvkom na začiatku nezotriedenej časti",
- "Posuňte hranicu medzi zotriedenou a nezotriedenou časťou o jeden prvok doprava",
- "Opakujte, kým nie je celé pole zotriedené"
- ],
- "best-case": "Selection sort vždy vykonáva rovnaký počet porovnaní bez ohľadu na poradie vstupného poľa.",
- "average-case": "Pre náhodné polia selection sort vyžaduje kvadratický čas.",
- "worst-case": "Selection sort vždy vykonáva rovnaký počet porovnaní a výmen bez ohľadu na poradie vstupného poľa."
- },
- "insertion": {
- "title": "Insertion Sort",
- "description": "Jednoduchý triediaci algoritmus, ktorý buduje konečné zoradené pole po jednom prvku, podobne ako by ste mohli triediť hracie karty v ruke.",
- "steps": [
- "Začnite s druhým prvkom (predpokladajte, že prvý prvok je už zotriedený)",
- "Porovnajte aktuálny prvok s predchádzajúcimi prvkami",
- "Ak je predchádzajúci prvok väčší ako aktuálny prvok, posuňte predchádzajúci prvok o jednu pozíciu dopredu",
- "Opakujte, kým sa nenájde správna pozícia pre aktuálny prvok, potom ho vložte"
- ],
- "best-case": "Keď je pole už zoradené, insertion sort robí iba jedno porovnanie na prvok.",
- "average-case": "Pre náhodné polia insertion sort vyžaduje kvadratický čas.",
- "worst-case": "Keď je pole zoradené v opačnom poradí, insertion sort musí posunúť každý prvok na začiatok poľa."
- },
- "quicksort": {
- "title": "Quick Sort",
- "description": "Efektívny algoritmus triedenia typu rozdeľuj a panuj, ktorý funguje výberom 'pivotného' prvku a rozdelením poľa okolo pivotu.",
- "steps": [
- "Vyberte pivotný prvok z poľa",
- "Rozdeľte pole: prvky menšie ako pivot idú doľava, väčšie doprava",
- "Pivot je teraz na svojej konečnej zotriedenej pozícii",
- "Rekurzívne aplikujte vyššie uvedené kroky na podpolia naľavo a napravo od pivotu",
- "Základný prípad je, keď podpole má 0 alebo 1 prvok (už zotriedené)"
- ],
- "best-case": "Keď pivot vždy rozdelí pole na takmer rovnaké polovice, quick sort dosahuje najlepší výkon.",
- "average-case": "Pre náhodné polia je quick sort veľmi efektívny s časovou zložitosťou O(n log n).",
- "worst-case": "Keď je pivot vždy najmenší alebo najväčší prvok (napr. v už zoradených poliach), výkon quick sortu sa zhoršuje na O(n²)."
- }
- }
+ "copyright": "Všetky práva vyhradené."
+ },
+ "visualizer": {
+ "start": "Začať triedenie",
+ "resume": "Pokračovať",
+ "pause": "Zastaviť",
+ "reset": "Resetovať",
+ "new-data": "Nové dáta",
+ "speed": "Rýchlosť",
+ "select-algorithm": "Vyber algoritmus",
+ "unsorted": "Nevytriedené",
+ "comparing": "Porovnávanie",
+ "pivot": "Pivot",
+ "current": "Aktuálny prvok",
+ "sorted": "Vytriedené",
+ "length": "Dĺžka"
+ },
+ "algorithm-explanation": {
+ "stats": {
+ "title": "Štatistiky",
+ "iterations": "Iterácie",
+ "swaps": "Výmeny"
+ },
+ "complexity": {
+ "title": "Analýza zložitosti",
+ "case": "Prípad",
+ "best": "Najlepší",
+ "average": "Priemerný",
+ "worst": "Najhorší",
+ "time": "Čas",
+ "space": "Priestor"
+ },
+ "explanation": {
+ "title": "Ako to funguje",
+ "steps": "Kroky",
+ "cases": "Prípady",
+ "code": "Kód"
+ },
+ "bubble": {
+ "title": "Bubble Sort",
+ "description": "Jednoduchý porovnávací algoritmus, ktorý opakovane prechádza zoznamom, porovnáva susedné prvky a vymieňa ich, ak sú v nesprávnom poradí.",
+ "steps": [
+ "Začnite na začiatku poľa",
+ "Porovnajte susedné prvky. Ak je prvý väčší ako druhý, vymeňte ich",
+ "Prejdite na ďalší pár prvkov a opakujte porovnanie a výmenu, ak je to potrebné",
+ "Po jednom úplnom prechode bude najväčší prvok na konci. Opakujte proces pre zostávajúce prvky"
+ ],
+ "best-case": "Keď je pole už zoradené, bubble sort urobí iba jeden prechod poľom bez výmen.",
+ "average-case": "Pre náhodné polia bubble sort stále vyžaduje kvadratický čas.",
+ "worst-case": "Keď je pole zoradené v opačnom poradí, bubble sort musí urobiť maximálny počet porovnaní a výmen."
+ },
+ "selection": {
+ "title": "Selection Sort",
+ "description": "Jednoduchý porovnávací algoritmus triedenia na mieste, ktorý rozdeľuje vstupný zoznam na dve časti: zoradenú časť a nezoradenú časť.",
+ "steps": [
+ "Nájdite minimálny prvok v nezotriedenej časti poľa",
+ "Vymeňte ho s prvkom na začiatku nezotriedenej časti",
+ "Posuňte hranicu medzi zotriedenou a nezotriedenou časťou o jeden prvok doprava",
+ "Opakujte, kým nie je celé pole zotriedené"
+ ],
+ "best-case": "Selection sort vždy vykonáva rovnaký počet porovnaní bez ohľadu na poradie vstupného poľa.",
+ "average-case": "Pre náhodné polia selection sort vyžaduje kvadratický čas.",
+ "worst-case": "Selection sort vždy vykonáva rovnaký počet porovnaní a výmen bez ohľadu na poradie vstupného poľa."
+ },
+ "insertion": {
+ "title": "Insertion Sort",
+ "description": "Jednoduchý triediaci algoritmus, ktorý buduje konečné zoradené pole po jednom prvku, podobne ako by ste mohli triediť hracie karty v ruke.",
+ "steps": [
+ "Začnite s druhým prvkom (predpokladajte, že prvý prvok je už zotriedený)",
+ "Porovnajte aktuálny prvok s predchádzajúcimi prvkami",
+ "Ak je predchádzajúci prvok väčší ako aktuálny prvok, posuňte predchádzajúci prvok o jednu pozíciu dopredu",
+ "Opakujte, kým sa nenájde správna pozícia pre aktuálny prvok, potom ho vložte"
+ ],
+ "best-case": "Keď je pole už zoradené, insertion sort robí iba jedno porovnanie na prvok.",
+ "average-case": "Pre náhodné polia insertion sort vyžaduje kvadratický čas.",
+ "worst-case": "Keď je pole zoradené v opačnom poradí, insertion sort musí posunúť každý prvok na začiatok poľa."
+ },
+ "quicksort": {
+ "title": "Quick Sort",
+ "description": "Efektívny algoritmus triedenia typu rozdeľuj a panuj, ktorý funguje výberom 'pivotného' prvku a rozdelením poľa okolo pivotu.",
+ "steps": [
+ "Vyberte pivotný prvok z poľa",
+ "Rozdeľte pole: prvky menšie ako pivot idú doľava, väčšie doprava",
+ "Pivot je teraz na svojej konečnej zotriedenej pozícii",
+ "Rekurzívne aplikujte vyššie uvedené kroky na podpolia naľavo a napravo od pivotu",
+ "Základný prípad je, keď podpole má 0 alebo 1 prvok (už zotriedené)"
+ ],
+ "best-case": "Keď pivot vždy rozdelí pole na takmer rovnaké polovice, quick sort dosahuje najlepší výkon.",
+ "average-case": "Pre náhodné polia je quick sort veľmi efektívny s časovou zložitosťou O(n log n).",
+ "worst-case": "Keď je pivot vždy najmenší alebo najväčší prvok (napr. v už zoradených poliach), výkon quick sortu sa zhoršuje na O(n²)."
+ }
+ }
}
diff --git a/next.config.ts b/next.config.ts
index 9173624..bad3fc6 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -8,17 +8,14 @@ import createNextIntlPlugin from "next-intl/plugin";
import "@/env";
const nextConfig: NextConfig = {
- reactStrictMode: true,
- experimental: {
- reactCompiler: true,
- ppr: true,
- },
- typescript: {
- ignoreBuildErrors: true,
- },
- eslint: {
- ignoreDuringBuilds: true,
- },
+ reactStrictMode: true,
+ experimental: {
+ reactCompiler: true,
+ ppr: true,
+ },
+ typescript: {
+ ignoreBuildErrors: true,
+ },
};
const withNextIntl = createNextIntlPlugin();
diff --git a/package.json b/package.json
index c78c866..db11af2 100644
--- a/package.json
+++ b/package.json
@@ -1,53 +1,53 @@
{
- "name": "sorting-algorithms",
- "version": "0.1.0",
- "private": true,
- "scripts": {
- "dev": "next dev --turbopack",
- "build": "next build",
- "build:turbo": "next build --turbopack",
- "start": "next start",
- "lint": "next lint",
- "lint:fix": "eslint . --fix",
- "format": "biome format ./src --write && biome check ./src --write --unsafe && biome lint ./src --write --unsafe",
- "fmt": "bun format",
- "typecheck": "tsc --noEmit"
- },
- "dependencies": {
- "@icons-pack/react-simple-icons": "^12.7.0",
- "@radix-ui/react-select": "^2.2.2",
- "@radix-ui/react-slider": "^1.3.2",
- "@radix-ui/react-slot": "^1.2.0",
- "@t3-oss/env-nextjs": "^0.12.0",
- "class-variance-authority": "^0.7.1",
- "clsx": "^2.1.1",
- "d3": "^7.9.0",
- "lucide-react": "^0.503.0",
- "motion": "^12.7.4",
- "next": "15.3.1-canary.15",
- "next-intl": "^4.0.3",
- "react": "^19.1.0",
- "react-dom": "^19.1.0",
- "react-use-measure": "^2.1.7",
- "server-only": "^0.0.1",
- "tailwind-merge": "^3.2.0",
- "tw-animate-css": "^1.2.7",
- "zod": "^3.24.3"
- },
- "devDependencies": {
- "@biomejs/biome": "^2.0.0-beta.1",
- "@eslint/eslintrc": "^3.3.1",
- "@tailwindcss/postcss": "^4.1.4",
- "@types/d3": "^7.4.3",
- "@types/node": "^22.14.1",
- "@types/react": "^19.1.2",
- "@types/react-dom": "^19.1.2",
- "babel-plugin-react-compiler": "^19.0.0-beta-ebf51a3-20250411",
- "eslint": "^9.25.0",
- "eslint-config-next": "15.3.1",
- "eslint-plugin-react-compiler": "^19.0.0-beta-ebf51a3-20250411",
- "postcss-load-config": "^6.0.1",
- "tailwindcss": "^4.1.4",
- "typescript": "^5.8.3"
- }
+ "name": "sorting-algorithms",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev --turbopack",
+ "build": "next build",
+ "build:turbo": "next build --turbopack",
+ "start": "next start",
+ "check": "biome check .",
+ "check:write": "biome check --write .",
+ "check:unsafe": "biome check --write --unsafe .",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@icons-pack/react-simple-icons": "^13.3.0",
+ "@radix-ui/react-select": "^2.2.5",
+ "@radix-ui/react-slider": "^1.3.5",
+ "@radix-ui/react-slot": "^1.2.3",
+ "@t3-oss/env-nextjs": "^0.13.8",
+ "@tanstack/react-query": "^5.81.5",
+ "@tanstack/react-query-devtools": "^5.81.5",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "d3": "^7.9.0",
+ "hast-util-to-jsx-runtime": "^2.3.6",
+ "lucide-react": "^0.525.0",
+ "motion": "^12.23.0",
+ "next": "15.4.0-canary.111",
+ "next-intl": "^4.3.4",
+ "nuqs": "^2.4.3",
+ "react": "^19.1.0",
+ "react-dom": "^19.1.0",
+ "react-use-measure": "^2.1.7",
+ "server-only": "^0.0.1",
+ "shiki": "^3.7.0",
+ "tailwind-merge": "^3.3.1",
+ "tw-animate-css": "^1.3.5",
+ "zod": "^3.25.71"
+ },
+ "devDependencies": {
+ "@biomejs/biome": "^2.0.6",
+ "@tailwindcss/postcss": "^4.1.11",
+ "@types/d3": "^7.4.3",
+ "@types/node": "^24.0.10",
+ "@types/react": "^19.1.8",
+ "@types/react-dom": "^19.1.6",
+ "babel-plugin-react-compiler": "^19.1.0-rc.2",
+ "postcss-load-config": "^6.0.1",
+ "tailwindcss": "^4.1.11",
+ "typescript": "^5.8.3"
+ }
}
diff --git a/postcss.config.mjs b/postcss.config.mjs
index 28c5766..32fd279 100644
--- a/postcss.config.mjs
+++ b/postcss.config.mjs
@@ -1,6 +1,6 @@
/** @type {import('postcss-load-config').Config} */
const config = {
- plugins: ["@tailwindcss/postcss"],
+ plugins: ["@tailwindcss/postcss"],
};
export default config;
diff --git a/src/app/[locale]/[...rest]/page.tsx b/src/app/[locale]/[...rest]/page.tsx
index 2dc2960..27cba95 100644
--- a/src/app/[locale]/[...rest]/page.tsx
+++ b/src/app/[locale]/[...rest]/page.tsx
@@ -1,5 +1,5 @@
import { notFound } from "next/navigation";
export default function CatchAllPage() {
- notFound();
+ notFound();
}
diff --git a/src/app/[locale]/globals.css b/src/app/[locale]/globals.css
index 2d66f06..2321084 100644
--- a/src/app/[locale]/globals.css
+++ b/src/app/[locale]/globals.css
@@ -4,125 +4,138 @@
@custom-variant dark (&:is(.dark *));
@theme inline {
- --color-background: var(--background);
- --color-foreground: var(--foreground);
- --font-sans: var(--font-geist-sans);
- --font-mono: var(--font-geist-mono);
- --color-sidebar-ring: var(--sidebar-ring);
- --color-sidebar-border: var(--sidebar-border);
- --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
- --color-sidebar-accent: var(--sidebar-accent);
- --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
- --color-sidebar-primary: var(--sidebar-primary);
- --color-sidebar-foreground: var(--sidebar-foreground);
- --color-sidebar: var(--sidebar);
- --color-chart-5: var(--chart-5);
- --color-chart-4: var(--chart-4);
- --color-chart-3: var(--chart-3);
- --color-chart-2: var(--chart-2);
- --color-chart-1: var(--chart-1);
- --color-ring: var(--ring);
- --color-input: var(--input);
- --color-border: var(--border);
- --color-destructive: var(--destructive);
- --color-accent-foreground: var(--accent-foreground);
- --color-accent: var(--accent);
- --color-muted-foreground: var(--muted-foreground);
- --color-muted: var(--muted);
- --color-secondary-foreground: var(--secondary-foreground);
- --color-secondary: var(--secondary);
- --color-primary-foreground: var(--primary-foreground);
- --color-primary: var(--primary);
- --color-popover-foreground: var(--popover-foreground);
- --color-popover: var(--popover);
- --color-card-foreground: var(--card-foreground);
- --color-card: var(--card);
- --radius-sm: calc(var(--radius) - 4px);
- --radius-md: calc(var(--radius) - 2px);
- --radius-lg: var(--radius);
- --radius-xl: calc(var(--radius) + 4px);
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --font-sans: var(--font-geist-sans);
+ --font-mono: var(--font-geist-mono);
+ --color-sidebar-ring: var(--sidebar-ring);
+ --color-sidebar-border: var(--sidebar-border);
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
+ --color-sidebar-accent: var(--sidebar-accent);
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
+ --color-sidebar-primary: var(--sidebar-primary);
+ --color-sidebar-foreground: var(--sidebar-foreground);
+ --color-sidebar: var(--sidebar);
+ --color-chart-5: var(--chart-5);
+ --color-chart-4: var(--chart-4);
+ --color-chart-3: var(--chart-3);
+ --color-chart-2: var(--chart-2);
+ --color-chart-1: var(--chart-1);
+ --color-ring: var(--ring);
+ --color-input: var(--input);
+ --color-border: var(--border);
+ --color-destructive: var(--destructive);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-accent: var(--accent);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-muted: var(--muted);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-secondary: var(--secondary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-primary: var(--primary);
+ --color-popover-foreground: var(--popover-foreground);
+ --color-popover: var(--popover);
+ --color-card-foreground: var(--card-foreground);
+ --color-card: var(--card);
+ --radius-sm: calc(var(--radius) - 4px);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) + 4px);
- --color-bluesky-blue: var(--bluesky-blue);
- --color-discord-blue: var(--discord-blue);
+ --color-bluesky-blue: var(--bluesky-blue);
+ --color-discord-blue: var(--discord-blue);
+
+ --ease-snappy: cubic-bezier(0.2, 0.4, 0.1, 0.95);
+}
+
+@custom-variant hover-only {
+ @media (hover: hover) {
+ @slot;
+ }
+}
+@custom-variant touch-only {
+ @media (hover: none) {
+ @slot;
+ }
}
:root {
- --radius: 0.5rem;
- --background: oklch(1 0 0);
- --foreground: oklch(0.141 0.005 285.823);
- --card: oklch(1 0 0);
- --card-foreground: oklch(0.141 0.005 285.823);
- --popover: oklch(1 0 0);
- --popover-foreground: oklch(0.141 0.005 285.823);
- --primary: oklch(0.623 0.214 259.815);
- --primary-foreground: oklch(0.97 0.014 254.604);
- --secondary: oklch(0.967 0.001 286.375);
- --secondary-foreground: oklch(0.21 0.006 285.885);
- --muted: oklch(0.967 0.001 286.375);
- --muted-foreground: oklch(0.552 0.016 285.938);
- --accent: oklch(0.967 0.001 286.375);
- --accent-foreground: oklch(0.21 0.006 285.885);
- --destructive: oklch(0.577 0.245 27.325);
- --border: oklch(0.92 0.004 286.32);
- --input: oklch(0.92 0.004 286.32);
- --ring: oklch(0.623 0.214 259.815);
- --chart-1: oklch(0.646 0.222 41.116);
- --chart-2: oklch(0.6 0.118 184.704);
- --chart-3: oklch(0.398 0.07 227.392);
- --chart-4: oklch(0.828 0.189 84.429);
- --chart-5: oklch(0.769 0.188 70.08);
- --sidebar: oklch(0.985 0 0);
- --sidebar-foreground: oklch(0.141 0.005 285.823);
- --sidebar-primary: oklch(0.623 0.214 259.815);
- --sidebar-primary-foreground: oklch(0.97 0.014 254.604);
- --sidebar-accent: oklch(0.967 0.001 286.375);
- --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
- --sidebar-border: oklch(0.92 0.004 286.32);
- --sidebar-ring: oklch(0.623 0.214 259.815);
+ --radius: 0.5rem;
+ --background: oklch(1 0 0);
+ --foreground: oklch(0.141 0.005 285.823);
+ --card: oklch(1 0 0);
+ --card-foreground: oklch(0.141 0.005 285.823);
+ --popover: oklch(1 0 0);
+ --popover-foreground: oklch(0.141 0.005 285.823);
+ --primary: oklch(0.623 0.214 259.815);
+ --primary-foreground: oklch(0.97 0.014 254.604);
+ --secondary: oklch(0.967 0.001 286.375);
+ --secondary-foreground: oklch(0.21 0.006 285.885);
+ --muted: oklch(0.967 0.001 286.375);
+ --muted-foreground: oklch(0.552 0.016 285.938);
+ --accent: oklch(0.967 0.001 286.375);
+ --accent-foreground: oklch(0.21 0.006 285.885);
+ --destructive: oklch(0.577 0.245 27.325);
+ --border: oklch(0.92 0.004 286.32);
+ --input: oklch(0.92 0.004 286.32);
+ --ring: oklch(0.623 0.214 259.815);
+ --chart-1: oklch(0.646 0.222 41.116);
+ --chart-2: oklch(0.6 0.118 184.704);
+ --chart-3: oklch(0.398 0.07 227.392);
+ --chart-4: oklch(0.828 0.189 84.429);
+ --chart-5: oklch(0.769 0.188 70.08);
+ --sidebar: oklch(0.985 0 0);
+ --sidebar-foreground: oklch(0.141 0.005 285.823);
+ --sidebar-primary: oklch(0.623 0.214 259.815);
+ --sidebar-primary-foreground: oklch(0.97 0.014 254.604);
+ --sidebar-accent: oklch(0.967 0.001 286.375);
+ --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
+ --sidebar-border: oklch(0.92 0.004 286.32);
+ --sidebar-ring: oklch(0.623 0.214 259.815);
- --bluesky-blue: #0285ff;
- --discord-blue: #5865f2;
+ --bluesky-blue: #0285ff;
+ --discord-blue: #5865f2;
}
.dark {
- --background: oklch(0.141 0.005 285.823);
- --foreground: oklch(0.985 0 0);
- --card: oklch(0.21 0.006 285.885);
- --card-foreground: oklch(0.985 0 0);
- --popover: oklch(0.21 0.006 285.885);
- --popover-foreground: oklch(0.985 0 0);
- --primary: oklch(0.546 0.245 262.881);
- --primary-foreground: oklch(0.985 0 0);
- --secondary: oklch(0.274 0.006 286.033);
- --secondary-foreground: oklch(0.985 0 0);
- --muted: oklch(0.274 0.006 286.033);
- --muted-foreground: oklch(0.705 0.015 286.067);
- --accent: oklch(0.274 0.006 286.033);
- --accent-foreground: oklch(0.985 0 0);
- --destructive: oklch(0.704 0.191 22.216);
- --border: oklch(1 0 0 / 10%);
- --input: oklch(1 0 0 / 15%);
- --ring: oklch(0.488 0.243 264.376);
- --chart-1: oklch(0.488 0.243 264.376);
- --chart-2: oklch(0.696 0.17 162.48);
- --chart-3: oklch(0.769 0.188 70.08);
- --chart-4: oklch(0.627 0.265 303.9);
- --chart-5: oklch(0.645 0.246 16.439);
- --sidebar: oklch(0.21 0.006 285.885);
- --sidebar-foreground: oklch(0.985 0 0);
- --sidebar-primary: oklch(0.546 0.245 262.881);
- --sidebar-primary-foreground: oklch(0.379 0.146 265.522);
- --sidebar-accent: oklch(0.274 0.006 286.033);
- --sidebar-accent-foreground: oklch(0.985 0 0);
- --sidebar-border: oklch(1 0 0 / 10%);
- --sidebar-ring: oklch(0.488 0.243 264.376);
+ --background: oklch(0.141 0.005 285.823);
+ --foreground: oklch(0.985 0 0);
+ --card: oklch(0.21 0.006 285.885);
+ --card-foreground: oklch(0.985 0 0);
+ --popover: oklch(0.21 0.006 285.885);
+ --popover-foreground: oklch(0.985 0 0);
+ --primary: oklch(0.546 0.245 262.881);
+ --primary-foreground: oklch(0.985 0 0);
+ --secondary: oklch(0.274 0.006 286.033);
+ --secondary-foreground: oklch(0.985 0 0);
+ --muted: oklch(0.274 0.006 286.033);
+ --muted-foreground: oklch(0.705 0.015 286.067);
+ --accent: oklch(0.274 0.006 286.033);
+ --accent-foreground: oklch(0.985 0 0);
+ --destructive: oklch(0.704 0.191 22.216);
+ --border: oklch(1 0 0 / 10%);
+ --input: oklch(1 0 0 / 15%);
+ --ring: oklch(0.488 0.243 264.376);
+ --chart-1: oklch(0.488 0.243 264.376);
+ --chart-2: oklch(0.696 0.17 162.48);
+ --chart-3: oklch(0.769 0.188 70.08);
+ --chart-4: oklch(0.627 0.265 303.9);
+ --chart-5: oklch(0.645 0.246 16.439);
+ --sidebar: oklch(0.21 0.006 285.885);
+ --sidebar-foreground: oklch(0.985 0 0);
+ --sidebar-primary: oklch(0.546 0.245 262.881);
+ --sidebar-primary-foreground: oklch(0.379 0.146 265.522);
+ --sidebar-accent: oklch(0.274 0.006 286.033);
+ --sidebar-accent-foreground: oklch(0.985 0 0);
+ --sidebar-border: oklch(1 0 0 / 10%);
+ --sidebar-ring: oklch(0.488 0.243 264.376);
}
@layer base {
- * {
- @apply border-border outline-ring/50;
- }
- body {
- @apply bg-background text-foreground;
- }
+ * {
+ @apply border-border outline-ring/50;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
}
diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx
index 2d7542c..a27da00 100644
--- a/src/app/[locale]/layout.tsx
+++ b/src/app/[locale]/layout.tsx
@@ -6,59 +6,62 @@ import { routing } from "@/i18n/routing";
import "./globals.css";
import Footer from "@/components/footer";
import Navigation from "@/components/navigation";
+import Providers from "@/components/providers";
const geistSans = Geist({
- variable: "--font-geist-sans",
- subsets: ["latin"],
+ variable: "--font-geist-sans",
+ subsets: ["latin"],
});
const geistMono = Geist_Mono({
- variable: "--font-geist-mono",
- subsets: ["latin"],
+ variable: "--font-geist-mono",
+ subsets: ["latin"],
});
export function generateStaticParams() {
- return routing.locales.map(locale => ({ locale }));
+ return routing.locales.map((locale) => ({ locale }));
}
export async function generateMetadata({
- params,
+ params,
}: {
- params: Promise<{ locale: Locale }>;
+ params: Promise<{ locale: Locale }>;
}) {
- const { locale } = await params;
- const t = await getTranslations({ locale, namespace: "metadata" });
+ const { locale } = await params;
+ const t = await getTranslations({ locale, namespace: "metadata" });
- return {
- title: t("title"),
- description: t("description"),
- };
+ return {
+ title: t("title"),
+ description: t("description"),
+ };
}
export default async function LocaleLayout({
- children,
- params,
+ children,
+ params,
}: {
- children: React.ReactNode;
- params: Promise<{ locale: Locale }>;
+ children: React.ReactNode;
+ params: Promise<{ locale: Locale }>;
}) {
- const { locale } = await params;
- if (!hasLocale(routing.locales, locale)) notFound();
+ const { locale } = await params;
+ if (!hasLocale(routing.locales, locale)) notFound();
- // Enable static rendering
- setRequestLocale(locale);
+ // Enable static rendering
+ setRequestLocale(locale);
- return (
-
-
-
-
- {children}
-
-
-
-
- );
+ return (
+
+
+
+
+
+ {children}
+
+
+
+
+
+ );
}
diff --git a/src/app/[locale]/not-found.tsx b/src/app/[locale]/not-found.tsx
index c6a3e32..9191132 100644
--- a/src/app/[locale]/not-found.tsx
+++ b/src/app/[locale]/not-found.tsx
@@ -8,70 +8,70 @@ import { HoleBackground } from "@/components/animate-ui/hole-background";
import { Link } from "@/i18n/navigation";
export default function NotFoundPage() {
- const t = useTranslations("not-found");
+ const t = useTranslations("not-found");
- return (
-
-
+ return (
+
+
-
-
-
-
-
-
- {t.rich("title", {
- newline: chunks => (
- {chunks}
- ),
- })}
-
-
- {t("description")}
-
-
+
+
+
+
+
+
+ {t.rich("title", {
+ newline: (chunks) => (
+ {chunks}
+ ),
+ })}
+
+
+ {t("description")}
+
+
-
-
-
-
-
-
-
-
- );
+
+
+
+
+
+
+
+
+ );
}
diff --git a/src/app/[locale]/page.tsx b/src/app/[locale]/page.tsx
index 3cdbd2b..5eba0df 100644
--- a/src/app/[locale]/page.tsx
+++ b/src/app/[locale]/page.tsx
@@ -1,12 +1,12 @@
"use client";
import {
- ArrowRight,
- BarChart3,
- Layers,
- Shuffle,
- SortAsc,
- Split,
+ ArrowRight,
+ BarChart3,
+ Layers,
+ Shuffle,
+ SortAsc,
+ Split,
} from "lucide-react";
import { motion } from "motion/react";
import Link from "next/link";
@@ -16,226 +16,236 @@ import { GradientText } from "@/components/animate-ui/gradient-text";
import { RippleButton } from "@/components/animate-ui/ripple-button";
export default function HomePage() {
- const t = useTranslations("home");
+ const t = useTranslations("home");
- return (
-
- {/* Hero Section */}
-
-
-
-
+ return (
+
+ {/* Hero Section */}
+
+
+
+
-
-
-
- {t("hero.title")
- .split(" ")
- .map((word, index, words) => (
-
-
- {index === words.length - 1 ? (
-
- ) : (
- {word}
- )}
-
-
- ))}
-
+
+
+
+ {t("hero.title")
+ .split(" ")
+ .map((word, index, words) => (
+
+
+ {index === words.length - 1 ? (
+
+ ) : (
+ {word}
+ )}
+
+
+ ))}
+
-
- {t("hero.about")}
-
+
+ {t("hero.about")}
+
-
-
-
- {t("hero.call-to-action")}
-
-
-
-
-
-
+
+
+
+ {t("hero.call-to-action")}
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
- {/* Information Section */}
-
-
-
-
- {t("info.title")}
-
-
- {t.rich("info.description", {
- paragraph: chunks =>
{chunks}
,
- })}
-
-
+ {/* Information Section */}
+
+
+
+
+ {t("info.title")}
+
+
+ {t.rich("info.description", {
+ paragraph: (chunks) =>
{chunks}
,
+ })}
+
+
-
-
- {t("info.title2")}
-
-
- {t.rich("info.description2", {
- point: chunks => {chunks} ,
- })}
-
-
-
-
+
+
+ {t("info.title2")}
+
+
+ {t.rich("info.description2", {
+ point: (chunks) => {chunks} ,
+ })}
+
+
+
+
- {/* Algorithms Section */}
-
-
-
-
- {t("algorithms.title")}
-
-
- {t("algorithms.description")}
-
-
+ {/* Algorithms Section */}
+
+
+
+
+ {t("algorithms.title")}
+
+
+ {t("algorithms.description")}
+
+
-
- {[
- {
- name: t("algorithms.bubble-sort.title"),
- icon: (
-
- ),
- description: t("algorithms.bubble-sort.description"),
- },
- {
- name: t("algorithms.selection-sort.title"),
- icon: (
-
- ),
- description: t("algorithms.selection-sort.description"),
- },
- {
- name: t("algorithms.insertion-sort.title"),
- icon: (
-
- ),
- description: t("algorithms.insertion-sort.description"),
- },
- {
- name: t("algorithms.quick-sort.title"),
- icon: (
-
- ),
- description: t("algorithms.quick-sort.description"),
- },
- ].map((algorithm, index) => (
-
-
-
-
- {algorithm.icon}
-
-
- {algorithm.name}
-
-
- {algorithm.description}
-
-
-
{t("algorithms.call-to-action")}
-
-
-
-
-
- ))}
-
+
+ {[
+ {
+ name: t("algorithms.bubble-sort.title"),
+ icon: (
+
+ ),
+ description: t("algorithms.bubble-sort.description"),
+ queryParam: "bubble",
+ },
+ {
+ name: t("algorithms.selection-sort.title"),
+ icon: (
+
+ ),
+ description: t("algorithms.selection-sort.description"),
+ queryParam: "selection",
+ },
+ {
+ name: t("algorithms.insertion-sort.title"),
+ icon: (
+
+ ),
+ description: t("algorithms.insertion-sort.description"),
+ queryParam: "insertion",
+ },
+ {
+ name: t("algorithms.quick-sort.title"),
+ icon: (
+
+ ),
+ description: t("algorithms.quick-sort.description"),
+ queryParam: "quicksort",
+ },
+ ].map((algorithm, index) => (
+
+
+
+
+ {algorithm.icon}
+
+
+ {algorithm.name}
+
+
+ {algorithm.description}
+
+
+
{t("algorithms.call-to-action")}
+
+
+
+
+
+ ))}
+
-
-
-
- {t("algorithms.call-to-action2")}
-
-
-
-
-
-
-
- );
+
+
+
+ {t("algorithms.call-to-action2")}
+
+
+
+
+
+
+
+ );
}
diff --git a/src/app/[locale]/playground/page.tsx b/src/app/[locale]/playground/page.tsx
index 2d1336c..a6e4f50 100644
--- a/src/app/[locale]/playground/page.tsx
+++ b/src/app/[locale]/playground/page.tsx
@@ -1,43 +1,27 @@
-"use client";
-
-import { useState } from "react";
-import AlgorithmExplanation from "@/components/algorithm-explanation";
+import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
+import type { SearchParams } from "nuqs/server";
import { HexagonBackground } from "@/components/animate-ui/hexagon-background";
-import SortingVisualizer from "@/components/sorting-visualizer";
+import PlaygroundState from "@/components/playground-state";
+import { getQueryClient } from "@/lib/get-query-client";
+import { codeHighlightOptions, loadSearchParams } from "@/lib/utils";
-export default function Playground() {
- const [algorithm, setAlgorithm] = useState<
- "bubble" | "selection" | "insertion" | "quicksort"
- >("bubble");
- const [iterations, setIterations] = useState(0);
- const [swaps, setSwaps] = useState(0);
+export default async function Playground({
+ searchParams,
+}: {
+ searchParams: Promise;
+}) {
+ const { algorithm } = await loadSearchParams(searchParams);
+ const queryClient = getQueryClient();
- return (
-
-
-
+ await queryClient.prefetchQuery(codeHighlightOptions(algorithm));
-
-
-
-
+ return (
+
- );
+
+
+
+
+ );
}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 7bb5def..f370b30 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,9 +1,9 @@
// Since we have a `not-found.tsx` page on the root, a layout file
// is required, even if it's just passing children through.
export default function RootLayout({
- children,
+ children,
}: Readonly<{
- children: React.ReactNode;
+ children: React.ReactNode;
}>) {
- return children;
+ return children;
}
diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx
index 1fd9497..891c466 100644
--- a/src/app/not-found.tsx
+++ b/src/app/not-found.tsx
@@ -1,17 +1,17 @@
"use client";
-import Error from "next/error";
+import NextError from "next/error";
// This page renders when a route like `/unknown.txt` is requested.
// In this case, the layout at `app/[locale]/layout.tsx` receives
// an invalid value as the `[locale]` param and calls `notFound()`.
export default function GlobalNotFound() {
- return (
-
-
- ;
-
-
- );
+ return (
+
+
+
+
+
+ );
}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 089107b..9c9a026 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -2,5 +2,5 @@ import { redirect } from "next/navigation";
// This page only renders when the app is built statically (output: 'export')
export default function RootPage() {
- redirect("/en");
+ redirect("/en");
}
diff --git a/src/components/algorithm-explanation.tsx b/src/components/algorithm-explanation.tsx
index 99568b1..17014d6 100644
--- a/src/components/algorithm-explanation.tsx
+++ b/src/components/algorithm-explanation.tsx
@@ -1,357 +1,313 @@
"use client";
+import { useQuery } from "@tanstack/react-query";
+import { Check, Copy, Loader2 } from "lucide-react";
import { useTranslations } from "next-intl";
+import { useState } from "react";
import {
- Tabs,
- TabsContent,
- TabsContents,
- TabsList,
- TabsTrigger,
+ Tabs,
+ TabsContent,
+ TabsContents,
+ TabsList,
+ TabsTrigger,
} from "@/components/animate-ui/tabs";
import {
- Card,
- CardContent,
- CardDescription,
- CardHeader,
- CardTitle,
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
} from "@/components/ui/card";
-import { cn } from "@/lib/utils";
+import {
+ type Algorithm,
+ cn,
+ codeHighlightOptions,
+ getAlgorithmCode,
+} from "@/lib/utils";
+import { RippleButton } from "./animate-ui/ripple-button";
interface AlgorithmExplanationProps {
- algorithm: "bubble" | "selection" | "insertion" | "quicksort";
- iterations: number;
- swaps: number;
- className?: string;
+ algorithm: Algorithm;
+ iterations: number;
+ swaps: number;
+ className?: string;
}
export default function AlgorithmExplanation({
- algorithm,
- iterations,
- swaps,
- className,
+ algorithm,
+ iterations,
+ swaps,
+ className,
}: AlgorithmExplanationProps) {
- const t = useTranslations("algorithm-explanation");
-
- const algorithmData = {
- bubble: {
- title: t("bubble.title"),
- description: t("bubble.description"),
- steps: [
- t("bubble.steps.0"),
- t("bubble.steps.1"),
- t("bubble.steps.2"),
- t("bubble.steps.3"),
- ],
- timeComplexity: {
- best: "O(n)",
- average: "O(n²)",
- worst: "O(n²)",
- },
- spaceComplexity: "O(1)",
- bestCase: t("bubble.best-case"),
- averageCase: t("bubble.average-case"),
- worstCase: t("bubble.worst-case"),
- },
- selection: {
- title: t("selection.title"),
- description: t("selection.description"),
- steps: [
- t("selection.steps.0"),
- t("selection.steps.1"),
- t("selection.steps.2"),
- t("selection.steps.3"),
- ],
- timeComplexity: {
- best: "O(n²)",
- average: "O(n²)",
- worst: "O(n²)",
- },
- spaceComplexity: "O(1)",
- bestCase: t("selection.best-case"),
- averageCase: t("selection.average-case"),
- worstCase: t("selection.worst-case"),
- },
- insertion: {
- title: t("insertion.title"),
- description: t("insertion.description"),
- steps: [
- t("insertion.steps.0"),
- t("insertion.steps.1"),
- t("insertion.steps.2"),
- t("insertion.steps.3"),
- ],
- timeComplexity: {
- best: "O(n)",
- average: "O(n²)",
- worst: "O(n²)",
- },
- spaceComplexity: "O(1)",
- bestCase: t("insertion.best-case"),
- averageCase: t("insertion.average-case"),
- worstCase: t("insertion.worst-case"),
- },
- quicksort: {
- title: t("quicksort.title"),
- description: t("quicksort.description"),
- steps: [
- t("quicksort.steps.0"),
- t("quicksort.steps.1"),
- t("quicksort.steps.2"),
- t("quicksort.steps.3"),
- t("quicksort.steps.4"),
- ],
- timeComplexity: {
- best: "O(n log n)",
- average: "O(n log n)",
- worst: "O(n²)",
- },
- spaceComplexity: "O(log n)",
- bestCase: t("quicksort.best-case"),
- averageCase: t("quicksort.average-case"),
- worstCase: t("quicksort.worst-case"),
- },
- };
-
- const currentAlgorithm = algorithmData[algorithm];
-
- return (
-
-
-
- {currentAlgorithm.title}
- {currentAlgorithm.description}
-
-
-
- {/* Stats Section */}
-
-
{t("stats.title")}
-
-
-
-
- {t("stats.iterations")}
-
-
- {iterations}
-
-
-
-
- {t("stats.swaps")}
-
-
{swaps}
-
-
+ const t = useTranslations("algorithm-explanation");
-
-
{t("complexity.title")}
-
-
-
{t("complexity.case")}
-
{t("complexity.best")}
-
{t("complexity.average")}
-
{t("complexity.worst")}
-
-
-
{t("complexity.time")}
-
- {currentAlgorithm.timeComplexity.best}
-
-
- {currentAlgorithm.timeComplexity.average}
-
-
- {currentAlgorithm.timeComplexity.worst}
-
-
-
-
{t("complexity.space")}
-
- {currentAlgorithm.spaceComplexity}
-
-
-
-
-
+ const { data, isPending, isError } = useQuery(
+ codeHighlightOptions(algorithm),
+ );
- {/* Explanation Section */}
-
-
- {t("explanation.title")}
-
+ const [copied, setCopied] = useState
(null);
-
-
-
- {t("explanation.steps")}
-
-
- {t("explanation.cases")}
-
-
- {t("explanation.code")}
-
-
-
-
-
-
- {currentAlgorithm.steps.map((step, index) => (
- {step}
- ))}
-
-
-
-
-
-
-
- {t("complexity.best")} (
- {currentAlgorithm.timeComplexity.best})
-
-
- {currentAlgorithm.bestCase}
-
-
-
-
- {t("complexity.average")} (
- {currentAlgorithm.timeComplexity.average})
-
-
- {currentAlgorithm.averageCase}
-
-
-
-
- {t("complexity.worst")} (
- {currentAlgorithm.timeComplexity.worst})
-
-
- {currentAlgorithm.worstCase}
-
-
-
-
-
-
-
- {getAlgorithmCode(algorithm)}
-
-
-
-
-
-
-
-
-
- );
-}
+ const copyCode = (code: string) => {
+ navigator.clipboard.writeText(code);
+ setCopied(code);
-function getAlgorithmCode(algorithm: string): string {
- switch (algorithm) {
- case "bubble":
- return `function bubbleSort(arr) {
- const n = arr.length;
+ setTimeout(() => {
+ setCopied(null);
+ }, 2000);
+ };
- for (let i = 0; i < n - 1; i++) {
- let swapped = false;
+ const algorithmData = {
+ bubble: {
+ title: t("bubble.title"),
+ description: t("bubble.description"),
+ steps: [
+ t("bubble.steps.0"),
+ t("bubble.steps.1"),
+ t("bubble.steps.2"),
+ t("bubble.steps.3"),
+ ],
+ timeComplexity: {
+ best: "O(n)",
+ average: "O(n²)",
+ worst: "O(n²)",
+ },
+ spaceComplexity: "O(1)",
+ bestCase: t("bubble.best-case"),
+ averageCase: t("bubble.average-case"),
+ worstCase: t("bubble.worst-case"),
+ },
+ selection: {
+ title: t("selection.title"),
+ description: t("selection.description"),
+ steps: [
+ t("selection.steps.0"),
+ t("selection.steps.1"),
+ t("selection.steps.2"),
+ t("selection.steps.3"),
+ ],
+ timeComplexity: {
+ best: "O(n²)",
+ average: "O(n²)",
+ worst: "O(n²)",
+ },
+ spaceComplexity: "O(1)",
+ bestCase: t("selection.best-case"),
+ averageCase: t("selection.average-case"),
+ worstCase: t("selection.worst-case"),
+ },
+ insertion: {
+ title: t("insertion.title"),
+ description: t("insertion.description"),
+ steps: [
+ t("insertion.steps.0"),
+ t("insertion.steps.1"),
+ t("insertion.steps.2"),
+ t("insertion.steps.3"),
+ ],
+ timeComplexity: {
+ best: "O(n)",
+ average: "O(n²)",
+ worst: "O(n²)",
+ },
+ spaceComplexity: "O(1)",
+ bestCase: t("insertion.best-case"),
+ averageCase: t("insertion.average-case"),
+ worstCase: t("insertion.worst-case"),
+ },
+ quicksort: {
+ title: t("quicksort.title"),
+ description: t("quicksort.description"),
+ steps: [
+ t("quicksort.steps.0"),
+ t("quicksort.steps.1"),
+ t("quicksort.steps.2"),
+ t("quicksort.steps.3"),
+ t("quicksort.steps.4"),
+ ],
+ timeComplexity: {
+ best: "O(n log n)",
+ average: "O(n log n)",
+ worst: "O(n²)",
+ },
+ spaceComplexity: "O(log n)",
+ bestCase: t("quicksort.best-case"),
+ averageCase: t("quicksort.average-case"),
+ worstCase: t("quicksort.worst-case"),
+ },
+ };
- for (let j = 0; j < n - i - 1; j++) {
- if (arr[j] > arr[j + 1]) {
- // Swap elements
- [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
- swapped = true;
- }
- }
+ const currentAlgorithm = algorithmData[algorithm];
- // If no swapping occurred in this pass,
- // array is sorted
- if (!swapped) break;
- }
+ return (
+
+
+
+ {currentAlgorithm.title}
+ {currentAlgorithm.description}
+
+
+
+ {/* Stats Section */}
+
+
{t("stats.title")}
- return arr;
-}`;
- case "selection":
- return `function selectionSort(arr) {
- const n = arr.length;
+
+
+
+ {t("stats.iterations")}
+
+
+ {iterations}
+
+
+
+
+ {t("stats.swaps")}
+
+
{swaps}
+
+
- for (let i = 0; i < n - 1; i++) {
- // Find minimum element in unsorted part
- let minIndex = i;
+
+
{t("complexity.title")}
+
+
+
{t("complexity.case")}
+
{t("complexity.best")}
+
{t("complexity.average")}
+
{t("complexity.worst")}
+
+
+
{t("complexity.time")}
+
+ {currentAlgorithm.timeComplexity.best}
+
+
+ {currentAlgorithm.timeComplexity.average}
+
+
+ {currentAlgorithm.timeComplexity.worst}
+
+
+
+
{t("complexity.space")}
+
+ {currentAlgorithm.spaceComplexity}
+
+
+
+
+
- for (let j = i + 1; j < n; j++) {
- if (arr[j] < arr[minIndex]) {
- minIndex = j;
- }
- }
+ {/* Explanation Section */}
+
+
+ {t("explanation.title")}
+
- // Swap found minimum with first element
- if (minIndex !== i) {
- [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
- }
- }
-
- return arr;
-}`;
- case "insertion":
- return `function insertionSort(arr) {
- const n = arr.length;
-
- for (let i = 1; i < n; i++) {
- // Store current element to be inserted
- let key = arr[i];
- let j = i - 1;
-
- // Move elements greater than key
- // to one position ahead
- while (j >= 0 && arr[j] > key) {
- arr[j + 1] = arr[j];
- j--;
- }
-
- // Insert the key at correct position
- arr[j + 1] = key;
- }
-
- return arr;
-}`;
- case "quicksort":
- return `function quickSort(arr, low = 0, high = arr.length - 1) {
- if (low < high) {
- // Find pivot element such that
- // elements smaller than pivot are on the left
- // elements greater than pivot are on the right
- const pivotIndex = partition(arr, low, high);
-
- // Recursively sort elements before and after pivot
- quickSort(arr, low, pivotIndex - 1);
- quickSort(arr, pivotIndex + 1, high);
- }
-
- return arr;
-}
+
+
+
+ {t("explanation.steps")}
+
+
+ {t("explanation.cases")}
+
+
+ {t("explanation.code")}
+
+
-function partition(arr, low, high) {
- // Choose rightmost element as pivot
- const pivot = arr[high];
- let i = low - 1;
+
+
+
+ {currentAlgorithm.steps.map((step, index) => (
+ {step}
+ ))}
+
+
- // Compare each element with pivot
- for (let j = low; j < high; j++) {
- if (arr[j] <= pivot) {
- i++;
- [arr[i], arr[j]] = [arr[j], arr[i]];
- }
- }
+
+
+
+
+ {t("complexity.best")} (
+ {currentAlgorithm.timeComplexity.best})
+
+
+ {currentAlgorithm.bestCase}
+
+
+
+
+ {t("complexity.average")} (
+ {currentAlgorithm.timeComplexity.average})
+
+
+ {currentAlgorithm.averageCase}
+
+
+
+
+ {t("complexity.worst")} (
+ {currentAlgorithm.timeComplexity.worst})
+
+
+ {currentAlgorithm.worstCase}
+
+
+
+
- // Place pivot in its final position
- [arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];
- return i + 1;
-}`;
- default:
- return "";
- }
+
+ {isPending ? (
+
+
+
+ ) : isError ? (
+
+ There was an error loading the code.
+
+ ) : (
+ data
+ )}
+ {data && (
+ copyCode(getAlgorithmCode(algorithm))}
+ variant="ghost"
+ size="sm"
+ className={cn(
+ "absolute top-1 right-1 flex size-8 scale-0 items-center justify-center bg-background/60 p-0 transition-all duration-200 ease-snappy touch-only:group-focus-within:scale-100 touch-only:group-focus-within:opacity-100 group-hover:scale-100 group-hover:opacity-100",
+ copied === getAlgorithmCode(algorithm) &&
+ "scale-100 opacity-100",
+ )}
+ type="button"
+ >
+
+
+
+
+
+ )}
+
+
+
+
+
+
+
+
+ );
}
diff --git a/src/components/animate-ui/bubble-background.tsx b/src/components/animate-ui/bubble-background.tsx
index 4fafdcd..6f8c9c8 100644
--- a/src/components/animate-ui/bubble-background.tsx
+++ b/src/components/animate-ui/bubble-background.tsx
@@ -1,85 +1,85 @@
"use client";
import {
- motion,
- type SpringOptions,
- useMotionValue,
- useSpring,
+ motion,
+ type SpringOptions,
+ useMotionValue,
+ useSpring,
} from "motion/react";
import * as React from "react";
import { useIsMobile } from "@/hooks";
import { cn } from "@/lib/utils";
type BubbleBackgroundProps = React.ComponentProps<"div"> & {
- interactive?: boolean;
- transition?: SpringOptions;
- colors?: {
- first: string;
- second: string;
- third: string;
- fourth: string;
- fifth: string;
- sixth: string;
- };
+ interactive?: boolean;
+ transition?: SpringOptions;
+ colors?: {
+ first: string;
+ second: string;
+ third: string;
+ fourth: string;
+ fifth: string;
+ sixth: string;
+ };
};
function BubbleBackground({
- ref,
- className,
- children,
- interactive = false,
- transition = { stiffness: 100, damping: 20 },
- colors = {
- first: "18,113,255",
- second: "221,74,255",
- third: "0,220,255",
- fourth: "200,50,50",
- fifth: "180,180,50",
- sixth: "140,100,255",
- },
- ...props
+ ref,
+ className,
+ children,
+ interactive = false,
+ transition = { stiffness: 100, damping: 20 },
+ colors = {
+ first: "18,113,255",
+ second: "221,74,255",
+ third: "0,220,255",
+ fourth: "200,50,50",
+ fifth: "180,180,50",
+ sixth: "140,100,255",
+ },
+ ...props
}: BubbleBackgroundProps) {
- const isMobile = useIsMobile();
-
- const containerRef = React.useRef(null);
- React.useImperativeHandle(ref, () => containerRef.current as HTMLDivElement);
-
- const mouseX = useMotionValue(0);
- const mouseY = useMotionValue(0);
- const springX = useSpring(mouseX, transition);
- const springY = useSpring(mouseY, transition);
-
- React.useEffect(() => {
- if (!interactive || isMobile) return;
-
- const currentContainer = containerRef.current;
- if (!currentContainer) return;
-
- const handleMouseMove = (e: MouseEvent) => {
- const rect = currentContainer.getBoundingClientRect();
- const centerX = rect.left + rect.width / 2;
- const centerY = rect.top + rect.height / 2;
- mouseX.set(e.clientX - centerX);
- mouseY.set(e.clientY - centerY);
- };
-
- currentContainer?.addEventListener("mousemove", handleMouseMove);
- return () =>
- currentContainer?.removeEventListener("mousemove", handleMouseMove);
- }, [interactive, mouseX, mouseY, isMobile]);
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {interactive && (
-
- )}
-
-
- {children}
-
- );
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {interactive && (
+
+ )}
+
+
+ {children}
+
+ );
}
export { BubbleBackground, type BubbleBackgroundProps };
diff --git a/src/components/animate-ui/github-stars-button.tsx b/src/components/animate-ui/github-stars-button.tsx
index 19689a3..01b706d 100644
--- a/src/components/animate-ui/github-stars-button.tsx
+++ b/src/components/animate-ui/github-stars-button.tsx
@@ -2,14 +2,14 @@
import { Star } from "lucide-react";
import {
- AnimatePresence,
- type HTMLMotionProps,
- motion,
- type SpringOptions,
- type UseInViewOptions,
- useInView,
- useMotionValue,
- useSpring,
+ AnimatePresence,
+ type HTMLMotionProps,
+ motion,
+ type SpringOptions,
+ type UseInViewOptions,
+ useInView,
+ useMotionValue,
+ useSpring,
} from "motion/react";
import * as React from "react";
import { SlidingNumber } from "@/components/animate-ui/sliding-number";
@@ -18,244 +18,247 @@ import { cn } from "@/lib/utils";
type FormatNumberResult = { number: string[]; unit: string };
function formatNumber(num: number, formatted: boolean): FormatNumberResult {
- if (formatted) {
- if (num < 1000) {
- return { number: [num.toString()], unit: "" };
- }
- const units = ["k", "M", "B", "T"];
- let unitIndex = 0;
- let n = num;
- while (n >= 1000 && unitIndex < units.length) {
- n /= 1000;
- unitIndex++;
- }
- const finalNumber = Math.floor(n).toString();
- return { number: [finalNumber], unit: units[unitIndex - 1]! };
- } else {
- return { number: num.toLocaleString("en-US").split(","), unit: "" };
- }
+ if (formatted) {
+ if (num < 1000) {
+ return { number: [num.toString()], unit: "" };
+ }
+ const units = ["k", "M", "B", "T"];
+ let unitIndex = 0;
+ let n = num;
+ while (n >= 1000 && unitIndex < units.length) {
+ n /= 1000;
+ unitIndex++;
+ }
+ const finalNumber = Math.floor(n).toString();
+ return { number: [finalNumber], unit: units[unitIndex - 1]! };
+ }
+ return { number: num.toLocaleString("en-US").split(","), unit: "" };
}
const animations = {
- pulse: {
- initial: { scale: 1.2, opacity: 0 },
- animate: { scale: [1.2, 1.8, 1.2], opacity: [0, 0.3, 0] },
- transition: { duration: 1.2, ease: "easeInOut" },
- },
- glow: {
- initial: { scale: 1, opacity: 0 },
- animate: { scale: [1, 1.5], opacity: [0.8, 0] },
- transition: { duration: 0.8, ease: "easeOut" },
- },
- particle: (index: number) => ({
- initial: { x: "50%", y: "50%", scale: 0, opacity: 0 },
- animate: {
- x: `calc(50% + ${Math.cos((index * Math.PI) / 3) * 30}px)`,
- y: `calc(50% + ${Math.sin((index * Math.PI) / 3) * 30}px)`,
- scale: [0, 1, 0],
- opacity: [0, 1, 0],
- },
- transition: { duration: 0.8, delay: index * 0.05, ease: "easeOut" },
- }),
+ pulse: {
+ initial: { scale: 1.2, opacity: 0 },
+ animate: { scale: [1.2, 1.8, 1.2], opacity: [0, 0.3, 0] },
+ transition: { duration: 1.2, ease: "easeInOut" } as const,
+ },
+ glow: {
+ initial: { scale: 1, opacity: 0 },
+ animate: { scale: [1, 1.5], opacity: [0.8, 0] },
+ transition: { duration: 0.8, ease: "easeOut" } as const,
+ },
+ particle: (index: number) => ({
+ initial: { x: "50%", y: "50%", scale: 0, opacity: 0 },
+ animate: {
+ x: `calc(50% + ${Math.cos((index * Math.PI) / 3) * 30}px)`,
+ y: `calc(50% + ${Math.sin((index * Math.PI) / 3) * 30}px)`,
+ scale: [0, 1, 0],
+ opacity: [0, 1, 0],
+ },
+ transition: {
+ duration: 0.8,
+ delay: index * 0.05,
+ ease: "easeOut",
+ } as const,
+ }),
};
type GitHubStarsButtonProps = HTMLMotionProps<"a"> & {
- username: string;
- repo: string;
- transition?: SpringOptions;
- formatted?: boolean;
- inView?: boolean;
- inViewMargin?: UseInViewOptions["margin"];
- inViewOnce?: boolean;
+ username: string;
+ repo: string;
+ transition?: SpringOptions;
+ formatted?: boolean;
+ inView?: boolean;
+ inViewMargin?: UseInViewOptions["margin"];
+ inViewOnce?: boolean;
};
function GitHubStarsButton({
- ref,
- username,
- repo,
- transition = { stiffness: 90, damping: 50 },
- formatted = false,
- inView = false,
- inViewOnce = true,
- inViewMargin = "0px",
- className,
- ...props
+ ref,
+ username,
+ repo,
+ transition = { stiffness: 90, damping: 50 },
+ formatted = false,
+ inView = false,
+ inViewOnce = true,
+ inViewMargin = "0px",
+ className,
+ ...props
}: GitHubStarsButtonProps) {
- const motionVal = useMotionValue(0);
- const springVal = useSpring(motionVal, transition);
- const motionNumberRef = React.useRef(0);
- const isCompletedRef = React.useRef(false);
- const [, forceRender] = React.useReducer(x => x + 1, 0);
- const [stars, setStars] = React.useState(0);
- const [isCompleted, setIsCompleted] = React.useState(false);
- const [displayParticles, setDisplayParticles] = React.useState(false);
- const [isLoading, setIsLoading] = React.useState(true);
+ const motionVal = useMotionValue(0);
+ const springVal = useSpring(motionVal, transition);
+ const motionNumberRef = React.useRef(0);
+ const isCompletedRef = React.useRef(false);
+ const [, forceRender] = React.useReducer((x) => x + 1, 0);
+ const [stars, setStars] = React.useState(0);
+ const [isCompleted, setIsCompleted] = React.useState(false);
+ const [displayParticles, setDisplayParticles] = React.useState(false);
+ const [isLoading, setIsLoading] = React.useState(true);
- const repoUrl = React.useMemo(
- () => `https://github.com/${username}/${repo}`,
- [username, repo],
- );
+ const repoUrl = React.useMemo(
+ () => `https://github.com/${username}/${repo}`,
+ [username, repo],
+ );
- React.useEffect(() => {
- fetch(`https://api.github.com/repos/${username}/${repo}`)
- .then(response => response.json())
- .then(data => {
- if (data && typeof data.stargazers_count === "number") {
- setStars(data.stargazers_count);
- }
- })
- .catch(console.error)
- .finally(() => setIsLoading(false));
- }, [username, repo]);
+ React.useEffect(() => {
+ fetch(`https://api.github.com/repos/${username}/${repo}`)
+ .then((response) => response.json())
+ .then((data) => {
+ if (data && typeof data.stargazers_count === "number") {
+ setStars(data.stargazers_count);
+ }
+ })
+ .catch(console.error)
+ .finally(() => setIsLoading(false));
+ }, [username, repo]);
- const handleDisplayParticles = React.useCallback(() => {
- setDisplayParticles(true);
- setTimeout(() => setDisplayParticles(false), 1500);
- }, []);
+ const handleDisplayParticles = React.useCallback(() => {
+ setDisplayParticles(true);
+ setTimeout(() => setDisplayParticles(false), 1500);
+ }, []);
- const localRef = React.useRef(null);
- React.useImperativeHandle(ref, () => localRef.current as HTMLAnchorElement);
+ const localRef = React.useRef(null);
+ React.useImperativeHandle(ref, () => localRef.current as HTMLAnchorElement);
- const inViewResult = useInView(localRef, {
- once: inViewOnce,
- margin: inViewMargin,
- });
- const isComponentInView = !inView || inViewResult;
+ const inViewResult = useInView(localRef, {
+ once: inViewOnce,
+ margin: inViewMargin,
+ });
+ const isComponentInView = !inView || inViewResult;
- React.useEffect(() => {
- const unsubscribe = springVal.on("change", (latest: number) => {
- const newValue = Math.round(latest);
- if (motionNumberRef.current !== newValue) {
- motionNumberRef.current = newValue;
- forceRender();
- }
- if (stars !== 0 && newValue >= stars && !isCompletedRef.current) {
- isCompletedRef.current = true;
- setIsCompleted(true);
- handleDisplayParticles();
- }
- });
- return () => unsubscribe();
- }, [springVal, stars, handleDisplayParticles]);
+ React.useEffect(() => {
+ const unsubscribe = springVal.on("change", (latest: number) => {
+ const newValue = Math.round(latest);
+ if (motionNumberRef.current !== newValue) {
+ motionNumberRef.current = newValue;
+ forceRender();
+ }
+ if (stars !== 0 && newValue >= stars && !isCompletedRef.current) {
+ isCompletedRef.current = true;
+ setIsCompleted(true);
+ handleDisplayParticles();
+ }
+ });
+ return () => unsubscribe();
+ }, [springVal, stars, handleDisplayParticles]);
- React.useEffect(() => {
- if (stars > 0 && isComponentInView) motionVal.set(stars);
- }, [motionVal, stars, isComponentInView]);
+ React.useEffect(() => {
+ if (stars > 0 && isComponentInView) motionVal.set(stars);
+ }, [motionVal, stars, isComponentInView]);
- const fillPercentage = Math.min(100, (motionNumberRef.current / stars) * 100);
- const formattedResult = formatNumber(motionNumberRef.current, formatted);
- const ghostFormattedNumber = formatNumber(stars, formatted);
+ const fillPercentage = Math.min(100, (motionNumberRef.current / stars) * 100);
+ const formattedResult = formatNumber(motionNumberRef.current, formatted);
+ const ghostFormattedNumber = formatNumber(stars, formatted);
- const renderNumberSegments = (
- segments: string[],
- unit: string,
- isGhost: boolean,
- ) => (
-
- {segments.map((segment, index) => (
-
- {Array.from(segment).map((digit, digitIndex) => (
-
- ))}
- {index < segments.length - 1 && , }
-
- ))}
+ const renderNumberSegments = (
+ segments: string[],
+ unit: string,
+ isGhost: boolean,
+ ) => (
+
+ {segments.map((segment, index) => (
+
+ {Array.from(segment).map((digit, digitIndex) => (
+
+ ))}
+ {index < segments.length - 1 && , }
+
+ ))}
- {formatted && unit && {unit} }
-
- );
+ {formatted && unit && {unit} }
+
+ );
- const handleClick = React.useCallback(
- (e: React.MouseEvent) => {
- e.preventDefault();
- handleDisplayParticles();
- setTimeout(() => window.open(repoUrl, "_blank"), 500);
- },
- [handleDisplayParticles, repoUrl],
- );
+ const handleClick = React.useCallback(
+ (e: React.MouseEvent) => {
+ e.preventDefault();
+ handleDisplayParticles();
+ setTimeout(() => window.open(repoUrl, "_blank"), 500);
+ },
+ [handleDisplayParticles, repoUrl],
+ );
- if (isLoading) return null;
+ if (isLoading) return null;
- return (
- svg]:px-3 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-[18px] [&_svg]:pointer-events-none [&_svg]:shrink-0",
- className,
- )}
- {...props}
- >
-
-
-
- GitHub Stars
-
-
-
-
- {displayParticles && (
- <>
-
-
- {[...Array(6)].map((_, i) => (
-
- ))}
- >
- )}
-
-
-
- {renderNumberSegments(
- ghostFormattedNumber.number,
- ghostFormattedNumber.unit,
- true,
- )}
- {renderNumberSegments(
- formattedResult.number,
- formattedResult.unit,
- false,
- )}
-
-
- );
+ return (
+ svg]:px-3 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-[18px] [&_svg]:pointer-events-none [&_svg]:shrink-0",
+ className,
+ )}
+ {...props}
+ >
+
+
+
+ GitHub Stars
+
+
+
+
+ {displayParticles && (
+ <>
+
+
+ {[...Array(6)].map((_, i) => (
+
+ ))}
+ >
+ )}
+
+
+
+ {renderNumberSegments(
+ ghostFormattedNumber.number,
+ ghostFormattedNumber.unit,
+ true,
+ )}
+ {renderNumberSegments(
+ formattedResult.number,
+ formattedResult.unit,
+ false,
+ )}
+
+
+ );
}
export { GitHubStarsButton, type GitHubStarsButtonProps };
diff --git a/src/components/animate-ui/gradient-text.tsx b/src/components/animate-ui/gradient-text.tsx
index 5ecaf02..c149a2a 100644
--- a/src/components/animate-ui/gradient-text.tsx
+++ b/src/components/animate-ui/gradient-text.tsx
@@ -1,58 +1,62 @@
"use client";
import { motion, type Transition } from "motion/react";
-import * as React from "react";
+import type * as React from "react";
import { cn } from "@/lib/utils";
type GradientTextProps = React.ComponentProps<"span"> & {
- text: string;
- gradient?: string;
- neon?: boolean;
- transition?: Transition;
+ text: string;
+ gradient?: string;
+ neon?: boolean;
+ transition?: Transition;
};
function GradientText({
- text,
- className,
- gradient = "linear-gradient(90deg, #3b82f6 0%, #a855f7 20%, #ec4899 50%, #a855f7 80%, #3b82f6 100%)",
- neon = false,
- transition = { duration: 50, repeat: Infinity, ease: "linear" },
- ...props
+ text,
+ className,
+ gradient = "linear-gradient(90deg, #3b82f6 0%, #a855f7 20%, #ec4899 50%, #a855f7 80%, #3b82f6 100%)",
+ neon = false,
+ transition = {
+ duration: 50,
+ repeat: Number.POSITIVE_INFINITY,
+ ease: "linear",
+ },
+ ...props
}: GradientTextProps) {
- const baseStyle: React.CSSProperties = {
- backgroundImage: gradient,
- };
+ const baseStyle: React.CSSProperties = {
+ backgroundImage: gradient,
+ };
- return (
-
-
- {text}
-
+ return (
+
+
+ {text}
+
- {neon && (
-
- {text}
-
- )}
-
- );
+ {neon && (
+
+ {text}
+
+ )}
+
+ );
}
export { GradientText, type GradientTextProps };
diff --git a/src/components/animate-ui/hexagon-background.tsx b/src/components/animate-ui/hexagon-background.tsx
index 4d538f5..f9cb887 100644
--- a/src/components/animate-ui/hexagon-background.tsx
+++ b/src/components/animate-ui/hexagon-background.tsx
@@ -5,106 +5,105 @@ import * as React from "react";
import { cn } from "@/lib/utils";
interface HexagonBackgroundProps extends React.HTMLAttributes {
- children?: React.ReactNode;
- hexagonProps?: React.HTMLAttributes;
- hexagonSize?: number; // value greater than 50
- hexagonMargin?: number;
+ children?: React.ReactNode;
+ hexagonProps?: React.HTMLAttributes;
+ hexagonSize?: number; // value greater than 50
+ hexagonMargin?: number;
}
-const HexagonBackground = React.forwardRef<
- HTMLDivElement,
- HexagonBackgroundProps
->(
- (
- {
- className,
- children,
- hexagonProps,
- hexagonSize = 75,
- hexagonMargin = 3,
- ...props
- },
- ref,
- ) => {
- const hexagonWidth = hexagonSize;
- const hexagonHeight = hexagonSize * 1.1;
- const rowSpacing = hexagonSize * 0.8;
- const baseMarginTop = -36 - 0.275 * (hexagonSize - 100);
- const computedMarginTop = baseMarginTop + hexagonMargin;
- const oddRowMarginLeft = -(hexagonSize / 2);
- const evenRowMarginLeft = hexagonMargin / 2;
+const HexagonBackground = ({
+ className,
+ children,
+ hexagonProps,
+ hexagonSize = 75,
+ hexagonMargin = 3,
+ ...props
+}: HexagonBackgroundProps) => {
+ const hexagonWidth = hexagonSize;
+ const hexagonHeight = hexagonSize * 1.1;
+ const rowSpacing = hexagonSize * 0.8;
+ const baseMarginTop = -36 - 0.275 * (hexagonSize - 100);
+ const computedMarginTop = baseMarginTop + hexagonMargin;
+ const oddRowMarginLeft = -(hexagonSize / 2);
+ const evenRowMarginLeft = hexagonMargin / 2;
- const [gridDimensions, setGridDimensions] = React.useState({
- rows: 0,
- columns: 0,
- });
+ const ref = React.useRef(null);
- const updateGridDimensions = React.useCallback(() => {
- const rows = Math.ceil(window.innerHeight / rowSpacing);
- const columns = Math.ceil(window.innerWidth / hexagonWidth) + 1;
- setGridDimensions({ rows, columns });
- }, [rowSpacing, hexagonWidth]);
+ const [gridDimensions, setGridDimensions] = React.useState({
+ rows: 0,
+ columns: 0,
+ });
- React.useEffect(() => {
- updateGridDimensions();
- window.addEventListener("resize", updateGridDimensions);
- return () => window.removeEventListener("resize", updateGridDimensions);
- }, [updateGridDimensions]);
+ const updateGridDimensions = React.useCallback(() => {
+ if (!ref.current) {
+ return;
+ }
+ const boundingRect = ref.current.getBoundingClientRect();
- return (
-
-
-
- {Array.from({ length: gridDimensions.rows }).map((_, rowIndex) => (
-
- {Array.from({ length: gridDimensions.columns }).map(
- (_, colIndex) => (
-
- ),
- )}
-
- ))}
-
- {children}
-
- );
- },
-);
+ const rows = Math.ceil(boundingRect.height / rowSpacing);
+ const columns = Math.ceil(boundingRect.width / hexagonWidth) + 1;
+ setGridDimensions({ rows, columns });
+ }, [rowSpacing, hexagonWidth]);
+
+ React.useEffect(() => {
+ updateGridDimensions();
+ window.addEventListener("resize", updateGridDimensions);
+ return () => window.removeEventListener("resize", updateGridDimensions);
+ }, [updateGridDimensions]);
+
+ return (
+
+
+
+ {Array.from({ length: gridDimensions.rows }).map((_, rowIndex) => (
+
+ {Array.from({ length: gridDimensions.columns }).map(
+ (_, colIndex) => (
+
+ ),
+ )}
+
+ ))}
+
+ {children}
+
+ );
+};
HexagonBackground.displayName = "HexagonBackground";
diff --git a/src/components/animate-ui/hole-background.tsx b/src/components/animate-ui/hole-background.tsx
index 24a1474..1ba5305 100644
--- a/src/components/animate-ui/hole-background.tsx
+++ b/src/components/animate-ui/hole-background.tsx
@@ -6,379 +6,382 @@ import * as React from "react";
import { cn } from "@/lib/utils";
type HoleBackgroundProps = React.ComponentProps<"div"> & {
- strokeColor?: string;
- numberOfLines?: number;
- numberOfDiscs?: number;
- particleRGBColor?: [number, number, number];
+ strokeColor?: string;
+ numberOfLines?: number;
+ numberOfDiscs?: number;
+ particleRGBColor?: [number, number, number];
};
function HoleBackground({
- strokeColor = "#737373",
- numberOfLines = 50,
- numberOfDiscs = 50,
- particleRGBColor = [255, 255, 255],
- className,
- children,
- ...props
+ strokeColor = "#737373",
+ numberOfLines = 50,
+ numberOfDiscs = 50,
+ particleRGBColor = [255, 255, 255],
+ className,
+ children,
+ ...props
}: HoleBackgroundProps) {
- const canvasRef = React.useRef(null);
- const animationFrameIdRef = React.useRef(0);
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const stateRef = React.useRef({
- discs: [],
- lines: [],
- particles: [],
- clip: {},
- startDisc: {},
- endDisc: {},
- rect: { width: 0, height: 0 },
- render: { width: 0, height: 0, dpi: 1 },
- particleArea: {},
- linesCanvas: null,
- });
+ const canvasRef = React.useRef(null);
+ const animationFrameIdRef = React.useRef(0);
+ // biome-ignore lint/suspicious/noExplicitAny: Autogenerated by animate-ui
+ const stateRef = React.useRef({
+ discs: [],
+ lines: [],
+ particles: [],
+ clip: {},
+ startDisc: {},
+ endDisc: {},
+ rect: { width: 0, height: 0 },
+ render: { width: 0, height: 0, dpi: 1 },
+ particleArea: {},
+ linesCanvas: null,
+ });
- const linear = (p: number) => p;
- const easeInExpo = (p: number) => (p === 0 ? 0 : Math.pow(2, 10 * (p - 1)));
+ const linear = (p: number) => p;
+ const easeInExpo = (p: number) => (p === 0 ? 0 : 2 ** (10 * (p - 1)));
- const tweenValue = React.useCallback(
- (start: number, end: number, p: number, ease: "inExpo" | null = null) => {
- const delta = end - start;
- const easeFn = ease === "inExpo" ? easeInExpo : linear;
- return start + delta * easeFn(p);
- },
- // biome-ignore lint/correctness/useExhaustiveDependencies: Not needed with React Compiler (https://github.com/biomejs/biome/issues/5293)
- [easeInExpo, linear],
- );
+ const tweenValue = React.useCallback(
+ (start: number, end: number, p: number, ease: "inExpo" | null = null) => {
+ const delta = end - start;
+ const easeFn = ease === "inExpo" ? easeInExpo : linear;
+ return start + delta * easeFn(p);
+ },
+ [easeInExpo, linear],
+ );
- const tweenDisc = React.useCallback(
- (disc: { x: number; y: number; w: number; h: number; p: number }) => {
- const { startDisc, endDisc } = stateRef.current;
- disc.x = tweenValue(startDisc.x, endDisc.x, disc.p);
- disc.y = tweenValue(startDisc.y, endDisc.y, disc.p, "inExpo");
- disc.w = tweenValue(startDisc.w, endDisc.w, disc.p);
- disc.h = tweenValue(startDisc.h, endDisc.h, disc.p);
- },
- [tweenValue],
- );
+ const tweenDisc = React.useCallback(
+ (disc: { x: number; y: number; w: number; h: number; p: number }) => {
+ const { startDisc, endDisc } = stateRef.current;
+ disc.x = tweenValue(startDisc.x, endDisc.x, disc.p);
+ disc.y = tweenValue(startDisc.y, endDisc.y, disc.p, "inExpo");
+ disc.w = tweenValue(startDisc.w, endDisc.w, disc.p);
+ disc.h = tweenValue(startDisc.h, endDisc.h, disc.p);
+ },
+ [tweenValue],
+ );
- const setSize = React.useCallback(() => {
- const canvas = canvasRef.current;
- if (!canvas) return;
- const rect = canvas.getBoundingClientRect();
- stateRef.current.rect = { width: rect.width, height: rect.height };
- stateRef.current.render = {
- width: rect.width,
- height: rect.height,
- dpi: window.devicePixelRatio || 1,
- };
- canvas.width = stateRef.current.render.width * stateRef.current.render.dpi;
- canvas.height =
- stateRef.current.render.height * stateRef.current.render.dpi;
- }, []);
+ const setSize = React.useCallback(() => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+ const rect = canvas.getBoundingClientRect();
+ stateRef.current.rect = { width: rect.width, height: rect.height };
+ stateRef.current.render = {
+ width: rect.width,
+ height: rect.height,
+ dpi: window.devicePixelRatio || 1,
+ };
+ canvas.width = stateRef.current.render.width * stateRef.current.render.dpi;
+ canvas.height =
+ stateRef.current.render.height * stateRef.current.render.dpi;
+ }, []);
- const setDiscs = React.useCallback(() => {
- const { width, height } = stateRef.current.rect;
- stateRef.current.discs = [];
- stateRef.current.startDisc = {
- x: width * 0.5,
- y: height * 0.45,
- w: width * 0.75,
- h: height * 0.7,
- };
- stateRef.current.endDisc = {
- x: width * 0.5,
- y: height * 0.95,
- w: 0,
- h: 0,
- };
- let prevBottom = height;
- stateRef.current.clip = {};
- for (let i = 0; i < numberOfDiscs; i++) {
- const p = i / numberOfDiscs;
- const disc = { p, x: 0, y: 0, w: 0, h: 0 };
- tweenDisc(disc);
- const bottom = disc.y + disc.h;
- if (bottom <= prevBottom) {
- stateRef.current.clip = { disc: { ...disc }, i };
- }
- prevBottom = bottom;
- stateRef.current.discs.push(disc);
- }
- const clipPath = new Path2D();
- const disc = stateRef.current.clip.disc;
- clipPath.ellipse(disc.x, disc.y, disc.w, disc.h, 0, 0, Math.PI * 2);
- clipPath.rect(disc.x - disc.w, 0, disc.w * 2, disc.y);
- stateRef.current.clip.path = clipPath;
- }, [tweenDisc, numberOfDiscs]);
+ const setDiscs = React.useCallback(() => {
+ const { width, height } = stateRef.current.rect;
+ stateRef.current.discs = [];
+ stateRef.current.startDisc = {
+ x: width * 0.5,
+ y: height * 0.45,
+ w: width * 0.75,
+ h: height * 0.7,
+ };
+ stateRef.current.endDisc = {
+ x: width * 0.5,
+ y: height * 0.95,
+ w: 0,
+ h: 0,
+ };
+ let prevBottom = height;
+ stateRef.current.clip = {};
+ for (let i = 0; i < numberOfDiscs; i++) {
+ const p = i / numberOfDiscs;
+ const disc = { p, x: 0, y: 0, w: 0, h: 0 };
+ tweenDisc(disc);
+ const bottom = disc.y + disc.h;
+ if (bottom <= prevBottom) {
+ stateRef.current.clip = { disc: { ...disc }, i };
+ }
+ prevBottom = bottom;
+ stateRef.current.discs.push(disc);
+ }
+ const clipPath = new Path2D();
+ const disc = stateRef.current.clip.disc;
+ clipPath.ellipse(disc.x, disc.y, disc.w, disc.h, 0, 0, Math.PI * 2);
+ clipPath.rect(disc.x - disc.w, 0, disc.w * 2, disc.y);
+ stateRef.current.clip.path = clipPath;
+ }, [tweenDisc, numberOfDiscs]);
- const setLines = React.useCallback(() => {
- const { width, height } = stateRef.current.rect;
- stateRef.current.lines = [];
- const linesAngle = (Math.PI * 2) / numberOfLines;
- for (let i = 0; i < numberOfLines; i++) {
- stateRef.current.lines.push([]);
- }
- stateRef.current.discs.forEach(
- (disc: { x: number; y: number; w: number; h: number }) => {
- for (let i = 0; i < numberOfLines; i++) {
- const angle = i * linesAngle;
- const p = {
- x: disc.x + Math.cos(angle) * disc.w,
- y: disc.y + Math.sin(angle) * disc.h,
- };
- stateRef.current.lines[i].push(p);
- }
- },
- );
- const offCanvas = document.createElement("canvas");
- offCanvas.width = width;
- offCanvas.height = height;
- const ctx = offCanvas.getContext("2d");
- if (!ctx) return;
- stateRef.current.lines.forEach((line: { x: number; y: number }[]) => {
- ctx.save();
- let lineIsIn = false;
- line.forEach((p1, j) => {
- if (j === 0) return;
- const p0 = line[j - 1]!;
- if (
- !lineIsIn &&
- (ctx.isPointInPath(stateRef.current.clip.path, p1.x, p1.y) ||
- ctx.isPointInStroke(stateRef.current.clip.path, p1.x, p1.y))
- ) {
- lineIsIn = true;
- } else if (lineIsIn) {
- ctx.clip(stateRef.current.clip.path);
- }
- ctx.beginPath();
- ctx.moveTo(p0.x, p0.y);
- ctx.lineTo(p1.x, p1.y);
- ctx.strokeStyle = strokeColor;
- ctx.lineWidth = 2;
- ctx.stroke();
- ctx.closePath();
- });
- ctx.restore();
- });
- stateRef.current.linesCanvas = offCanvas;
- }, [strokeColor, numberOfLines]);
+ const setLines = React.useCallback(() => {
+ const { width, height } = stateRef.current.rect;
+ stateRef.current.lines = [];
+ const linesAngle = (Math.PI * 2) / numberOfLines;
+ for (let i = 0; i < numberOfLines; i++) {
+ stateRef.current.lines.push([]);
+ }
+ stateRef.current.discs.forEach(
+ (disc: { x: number; y: number; w: number; h: number }) => {
+ for (let i = 0; i < numberOfLines; i++) {
+ const angle = i * linesAngle;
+ const p = {
+ x: disc.x + Math.cos(angle) * disc.w,
+ y: disc.y + Math.sin(angle) * disc.h,
+ };
+ stateRef.current.lines[i].push(p);
+ }
+ },
+ );
+ const offCanvas = document.createElement("canvas");
+ offCanvas.width = width;
+ offCanvas.height = height;
+ const ctx = offCanvas.getContext("2d");
+ if (!ctx) return;
+ stateRef.current.lines.forEach((line: { x: number; y: number }[]) => {
+ ctx.save();
+ let lineIsIn = false;
+ line.forEach((p1, j) => {
+ if (j === 0) return;
+ const p0 = line[j - 1]!;
+ if (
+ !lineIsIn &&
+ (ctx.isPointInPath(stateRef.current.clip.path, p1.x, p1.y) ||
+ ctx.isPointInStroke(stateRef.current.clip.path, p1.x, p1.y))
+ ) {
+ lineIsIn = true;
+ } else if (lineIsIn) {
+ ctx.clip(stateRef.current.clip.path);
+ }
+ ctx.beginPath();
+ ctx.moveTo(p0.x, p0.y);
+ ctx.lineTo(p1.x, p1.y);
+ ctx.strokeStyle = strokeColor;
+ ctx.lineWidth = 2;
+ ctx.stroke();
+ ctx.closePath();
+ });
+ ctx.restore();
+ });
+ stateRef.current.linesCanvas = offCanvas;
+ }, [strokeColor, numberOfLines]);
- const initParticle = React.useCallback(
- (start: boolean = false) => {
- const sx =
- stateRef.current.particleArea.sx +
- stateRef.current.particleArea.sw * Math.random();
- const ex =
- stateRef.current.particleArea.ex +
- stateRef.current.particleArea.ew * Math.random();
- const dx = ex - sx;
- const y = start
- ? stateRef.current.particleArea.h * Math.random()
- : stateRef.current.particleArea.h;
- const r = 0.5 + Math.random() * 4;
- const vy = 0.5 + Math.random();
- return {
- x: sx,
- sx,
- dx,
- y,
- vy,
- p: 0,
- r,
- c: `rgba(${particleRGBColor[0]}, ${particleRGBColor[1]}, ${particleRGBColor[2]}, ${Math.random()})`,
- };
- },
- [particleRGBColor[0]],
- );
+ const initParticle = React.useCallback(
+ (start = false) => {
+ const sx =
+ stateRef.current.particleArea.sx +
+ stateRef.current.particleArea.sw * Math.random();
+ const ex =
+ stateRef.current.particleArea.ex +
+ stateRef.current.particleArea.ew * Math.random();
+ const dx = ex - sx;
+ const y = start
+ ? stateRef.current.particleArea.h * Math.random()
+ : stateRef.current.particleArea.h;
+ const r = 0.5 + Math.random() * 4;
+ const vy = 0.5 + Math.random();
+ return {
+ x: sx,
+ sx,
+ dx,
+ y,
+ vy,
+ p: 0,
+ r,
+ c: `rgba(${particleRGBColor[0]}, ${particleRGBColor[1]}, ${particleRGBColor[2]}, ${Math.random()})`,
+ };
+ },
+ [particleRGBColor[0]],
+ );
- const setParticles = React.useCallback(() => {
- const { width, height } = stateRef.current.rect;
- stateRef.current.particles = [];
- const disc = stateRef.current.clip.disc;
- stateRef.current.particleArea = {
- sw: disc.w * 0.5,
- ew: disc.w * 2,
- h: height * 0.85,
- };
- stateRef.current.particleArea.sx =
- (width - stateRef.current.particleArea.sw) / 2;
- stateRef.current.particleArea.ex =
- (width - stateRef.current.particleArea.ew) / 2;
- const totalParticles = 100;
- for (let i = 0; i < totalParticles; i++) {
- stateRef.current.particles.push(initParticle(true));
- }
- }, [initParticle]);
+ const setParticles = React.useCallback(() => {
+ const { width, height } = stateRef.current.rect;
+ stateRef.current.particles = [];
+ const disc = stateRef.current.clip.disc;
+ stateRef.current.particleArea = {
+ sw: disc.w * 0.5,
+ ew: disc.w * 2,
+ h: height * 0.85,
+ };
+ stateRef.current.particleArea.sx =
+ (width - stateRef.current.particleArea.sw) / 2;
+ stateRef.current.particleArea.ex =
+ (width - stateRef.current.particleArea.ew) / 2;
+ const totalParticles = 100;
+ for (let i = 0; i < totalParticles; i++) {
+ stateRef.current.particles.push(initParticle(true));
+ }
+ }, [initParticle]);
- const drawDiscs = React.useCallback(
- (ctx: CanvasRenderingContext2D) => {
- ctx.strokeStyle = strokeColor;
- ctx.lineWidth = 2;
- const outerDisc = stateRef.current.startDisc;
- ctx.beginPath();
- ctx.ellipse(
- outerDisc.x,
- outerDisc.y,
- outerDisc.w,
- outerDisc.h,
- 0,
- 0,
- Math.PI * 2,
- );
- ctx.stroke();
- ctx.closePath();
- stateRef.current.discs.forEach(
- (
- disc: {
- x: number;
- y: number;
- w: number;
- h: number;
- },
- i: number,
- ) => {
- if (i % 5 !== 0) return;
- if (disc.w < stateRef.current.clip.disc.w - 5) {
- ctx.save();
- ctx.clip(stateRef.current.clip.path);
- }
- ctx.beginPath();
- ctx.ellipse(disc.x, disc.y, disc.w, disc.h, 0, 0, Math.PI * 2);
- ctx.stroke();
- ctx.closePath();
- if (disc.w < stateRef.current.clip.disc.w - 5) {
- ctx.restore();
- }
- },
- );
- },
- [strokeColor],
- );
+ const drawDiscs = React.useCallback(
+ (ctx: CanvasRenderingContext2D) => {
+ ctx.strokeStyle = strokeColor;
+ ctx.lineWidth = 2;
+ const outerDisc = stateRef.current.startDisc;
+ ctx.beginPath();
+ ctx.ellipse(
+ outerDisc.x,
+ outerDisc.y,
+ outerDisc.w,
+ outerDisc.h,
+ 0,
+ 0,
+ Math.PI * 2,
+ );
+ ctx.stroke();
+ ctx.closePath();
+ stateRef.current.discs.forEach(
+ (
+ disc: {
+ x: number;
+ y: number;
+ w: number;
+ h: number;
+ },
+ i: number,
+ ) => {
+ if (i % 5 !== 0) return;
+ if (disc.w < stateRef.current.clip.disc.w - 5) {
+ ctx.save();
+ ctx.clip(stateRef.current.clip.path);
+ }
+ ctx.beginPath();
+ ctx.ellipse(disc.x, disc.y, disc.w, disc.h, 0, 0, Math.PI * 2);
+ ctx.stroke();
+ ctx.closePath();
+ if (disc.w < stateRef.current.clip.disc.w - 5) {
+ ctx.restore();
+ }
+ },
+ );
+ },
+ [strokeColor],
+ );
- const drawLines = React.useCallback((ctx: CanvasRenderingContext2D) => {
- if (stateRef.current.linesCanvas) {
- ctx.drawImage(stateRef.current.linesCanvas, 0, 0);
- }
- }, []);
+ const drawLines = React.useCallback((ctx: CanvasRenderingContext2D) => {
+ if (stateRef.current.linesCanvas) {
+ ctx.drawImage(stateRef.current.linesCanvas, 0, 0);
+ }
+ }, []);
- const drawParticles = React.useCallback((ctx: CanvasRenderingContext2D) => {
- ctx.save();
- ctx.clip(stateRef.current.clip.path);
- stateRef.current.particles.forEach(
- (particle: { x: number; y: number; r: number; c: string }) => {
- ctx.fillStyle = particle.c;
- ctx.beginPath();
- ctx.rect(particle.x, particle.y, particle.r, particle.r);
- ctx.closePath();
- ctx.fill();
- },
- );
- ctx.restore();
- }, []);
+ const drawParticles = React.useCallback((ctx: CanvasRenderingContext2D) => {
+ ctx.save();
+ ctx.clip(stateRef.current.clip.path);
+ stateRef.current.particles.forEach(
+ (particle: { x: number; y: number; r: number; c: string }) => {
+ ctx.fillStyle = particle.c;
+ ctx.beginPath();
+ ctx.rect(particle.x, particle.y, particle.r, particle.r);
+ ctx.closePath();
+ ctx.fill();
+ },
+ );
+ ctx.restore();
+ }, []);
- const moveDiscs = React.useCallback(() => {
- stateRef.current.discs.forEach(
- (disc: { x: number; y: number; w: number; h: number; p: number }) => {
- disc.p = (disc.p + 0.001) % 1;
- tweenDisc(disc);
- },
- );
- }, [tweenDisc]);
+ const moveDiscs = React.useCallback(() => {
+ stateRef.current.discs.forEach(
+ (disc: { x: number; y: number; w: number; h: number; p: number }) => {
+ disc.p = (disc.p + 0.001) % 1;
+ tweenDisc(disc);
+ },
+ );
+ }, [tweenDisc]);
- const moveParticles = React.useCallback(() => {
- stateRef.current.particles.forEach(
- (
- particle: {
- x: number;
- sx: number;
- dx: number;
- y: number;
- vy: number;
- p: number;
- r: number;
- c: string;
- },
- idx: number,
- ) => {
- particle.p = 1 - particle.y / stateRef.current.particleArea.h;
- particle.x = particle.sx + particle.dx * particle.p;
- particle.y -= particle.vy;
- if (particle.y < 0) {
- stateRef.current.particles[idx] = initParticle();
- }
- },
- );
- }, [initParticle]);
+ const moveParticles = React.useCallback(() => {
+ stateRef.current.particles.forEach(
+ (
+ particle: {
+ x: number;
+ sx: number;
+ dx: number;
+ y: number;
+ vy: number;
+ p: number;
+ r: number;
+ c: string;
+ },
+ idx: number,
+ ) => {
+ particle.p = 1 - particle.y / stateRef.current.particleArea.h;
+ particle.x = particle.sx + particle.dx * particle.p;
+ particle.y -= particle.vy;
+ if (particle.y < 0) {
+ stateRef.current.particles[idx] = initParticle();
+ }
+ },
+ );
+ }, [initParticle]);
- const tick = React.useCallback(() => {
- const canvas = canvasRef.current;
- if (!canvas) return;
- const ctx = canvas.getContext("2d");
- if (!ctx) return;
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.save();
- ctx.scale(stateRef.current.render.dpi, stateRef.current.render.dpi);
- moveDiscs();
- moveParticles();
- drawDiscs(ctx);
- drawLines(ctx);
- drawParticles(ctx);
- ctx.restore();
- animationFrameIdRef.current = requestAnimationFrame(tick);
- }, [moveDiscs, moveParticles, drawDiscs, drawLines, drawParticles]);
+ const tick = React.useCallback(() => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+ const ctx = canvas.getContext("2d");
+ if (!ctx) return;
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.save();
+ ctx.scale(stateRef.current.render.dpi, stateRef.current.render.dpi);
+ moveDiscs();
+ moveParticles();
+ drawDiscs(ctx);
+ drawLines(ctx);
+ drawParticles(ctx);
+ ctx.restore();
+ animationFrameIdRef.current = requestAnimationFrame(tick);
+ }, [moveDiscs, moveParticles, drawDiscs, drawLines, drawParticles]);
- const init = React.useCallback(() => {
- setSize();
- setDiscs();
- setLines();
- setParticles();
- }, [setSize, setDiscs, setLines, setParticles]);
+ const init = React.useCallback(() => {
+ setSize();
+ setDiscs();
+ setLines();
+ setParticles();
+ }, [setSize, setDiscs, setLines, setParticles]);
- React.useEffect(() => {
- const canvas = canvasRef.current;
- if (!canvas) return;
- init();
- tick();
- const handleResize = () => {
- setSize();
- setDiscs();
- setLines();
- setParticles();
- };
- window.addEventListener("resize", handleResize);
- return () => {
- window.removeEventListener("resize", handleResize);
- cancelAnimationFrame(animationFrameIdRef.current);
- };
- }, [init, tick, setSize, setDiscs, setLines, setParticles]);
+ React.useEffect(() => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+ init();
+ tick();
+ const handleResize = () => {
+ setSize();
+ setDiscs();
+ setLines();
+ setParticles();
+ };
+ window.addEventListener("resize", handleResize);
+ return () => {
+ window.removeEventListener("resize", handleResize);
+ cancelAnimationFrame(animationFrameIdRef.current);
+ };
+ }, [init, tick, setSize, setDiscs, setLines, setParticles]);
- return (
-
- );
+ return (
+
+ );
}
export { HoleBackground, type HoleBackgroundProps };
diff --git a/src/components/animate-ui/motion-highlight.tsx b/src/components/animate-ui/motion-highlight.tsx
index 680a43c..dc3f764 100644
--- a/src/components/animate-ui/motion-highlight.tsx
+++ b/src/components/animate-ui/motion-highlight.tsx
@@ -8,579 +8,577 @@ import { cn } from "@/lib/utils";
type MotionHighlightMode = "children" | "parent";
type Bounds = {
- top: number;
- left: number;
- width: number;
- height: number;
+ top: number;
+ left: number;
+ width: number;
+ height: number;
};
type MotionHighlightContextType = {
- mode: MotionHighlightMode;
- activeValue: string | null;
- setActiveValue: (value: string | null) => void;
- setBounds: (bounds: DOMRect) => void;
- clearBounds: () => void;
- id: string;
- hover: boolean;
- className?: string;
- activeClassName?: string;
- setActiveClassName: (className: string) => void;
- transition?: Transition;
- disabled?: boolean;
- enabled?: boolean;
- exitDelay?: number;
- forceUpdateBounds?: boolean;
+ mode: MotionHighlightMode;
+ activeValue: string | null;
+ setActiveValue: (value: string | null) => void;
+ setBounds: (bounds: DOMRect) => void;
+ clearBounds: () => void;
+ id: string;
+ hover: boolean;
+ className?: string;
+ activeClassName?: string;
+ setActiveClassName: (className: string) => void;
+ transition?: Transition;
+ disabled?: boolean;
+ enabled?: boolean;
+ exitDelay?: number;
+ forceUpdateBounds?: boolean;
};
const MotionHighlightContext = React.createContext<
- MotionHighlightContextType | undefined
+ MotionHighlightContextType | undefined
>(undefined);
const useMotionHighlight = (): MotionHighlightContextType => {
- const context = React.useContext(MotionHighlightContext);
- if (!context) {
- throw new Error(
- "useMotionHighlight must be used within a MotionHighlightProvider",
- );
- }
- return context;
+ const context = React.useContext(MotionHighlightContext);
+ if (!context) {
+ throw new Error(
+ "useMotionHighlight must be used within a MotionHighlightProvider",
+ );
+ }
+ return context;
};
type BaseMotionHighlightProps = {
- mode?: MotionHighlightMode;
- value?: string | null;
- defaultValue?: string | null;
- onValueChange?: (value: string | null) => void;
- className?: string;
- transition?: Transition;
- hover?: boolean;
- disabled?: boolean;
- enabled?: boolean;
- exitDelay?: number;
+ mode?: MotionHighlightMode;
+ value?: string | null;
+ defaultValue?: string | null;
+ onValueChange?: (value: string | null) => void;
+ className?: string;
+ transition?: Transition;
+ hover?: boolean;
+ disabled?: boolean;
+ enabled?: boolean;
+ exitDelay?: number;
};
type ParentModeMotionHighlightProps = {
- boundsOffset?: Partial;
- containerClassName?: string;
- forceUpdateBounds?: boolean;
+ boundsOffset?: Partial;
+ containerClassName?: string;
+ forceUpdateBounds?: boolean;
};
type ControlledParentModeMotionHighlightProps = BaseMotionHighlightProps &
- ParentModeMotionHighlightProps & {
- mode: "parent";
- controlledItems: true;
- children: React.ReactNode;
- };
+ ParentModeMotionHighlightProps & {
+ mode: "parent";
+ controlledItems: true;
+ children: React.ReactNode;
+ };
type ControlledChildrenModeMotionHighlightProps = BaseMotionHighlightProps & {
- mode?: "children" | undefined;
- controlledItems: true;
- children: React.ReactNode;
+ mode?: "children" | undefined;
+ controlledItems: true;
+ children: React.ReactNode;
};
type UncontrolledParentModeMotionHighlightProps = BaseMotionHighlightProps &
- ParentModeMotionHighlightProps & {
- mode: "parent";
- controlledItems?: false;
- itemsClassName?: string;
- children: React.ReactElement | React.ReactElement[];
- };
+ ParentModeMotionHighlightProps & {
+ mode: "parent";
+ controlledItems?: false;
+ itemsClassName?: string;
+ children: React.ReactElement | React.ReactElement[];
+ };
type UncontrolledChildrenModeMotionHighlightProps = BaseMotionHighlightProps & {
- mode?: "children";
- controlledItems?: false;
- itemsClassName?: string;
- children: React.ReactElement | React.ReactElement[];
+ mode?: "children";
+ controlledItems?: false;
+ itemsClassName?: string;
+ children: React.ReactElement | React.ReactElement[];
};
type MotionHighlightProps = React.ComponentProps<"div"> &
- (
- | ControlledParentModeMotionHighlightProps
- | ControlledChildrenModeMotionHighlightProps
- | UncontrolledParentModeMotionHighlightProps
- | UncontrolledChildrenModeMotionHighlightProps
- );
+ (
+ | ControlledParentModeMotionHighlightProps
+ | ControlledChildrenModeMotionHighlightProps
+ | UncontrolledParentModeMotionHighlightProps
+ | UncontrolledChildrenModeMotionHighlightProps
+ );
function MotionHighlight({ ref, ...props }: MotionHighlightProps) {
- const {
- children,
- value,
- defaultValue,
- onValueChange,
- className,
- transition = { type: "spring", stiffness: 350, damping: 35 },
- hover = false,
- enabled = true,
- controlledItems,
- disabled = false,
- exitDelay = 0.2,
- mode = "children",
- } = props;
-
- const localRef = React.useRef(null);
- React.useImperativeHandle(ref, () => localRef.current as HTMLDivElement);
-
- const [activeValue, setActiveValue] = React.useState(
- value ?? defaultValue ?? null,
- );
- const [boundsState, setBoundsState] = React.useState(null);
- const [activeClassNameState, setActiveClassNameState] =
- React.useState("");
-
- const safeSetActiveValue = React.useCallback(
- (id: string | null) => {
- setActiveValue(prev => (prev === id ? prev : id));
- if (id !== activeValue) onValueChange?.(id);
- },
- [activeValue, onValueChange],
- );
-
- const safeSetBounds = React.useCallback(
- (bounds: DOMRect) => {
- if (!localRef.current) return;
-
- const boundsOffset = (props as ParentModeMotionHighlightProps)
- ?.boundsOffset ?? {
- top: 0,
- left: 0,
- width: 0,
- height: 0,
- };
-
- const containerRect = localRef.current.getBoundingClientRect();
- const newBounds: Bounds = {
- top: bounds.top - containerRect.top + (boundsOffset.top ?? 0),
- left: bounds.left - containerRect.left + (boundsOffset.left ?? 0),
- width: bounds.width + (boundsOffset.width ?? 0),
- height: bounds.height + (boundsOffset.height ?? 0),
- };
-
- setBoundsState(prev => {
- if (
- prev &&
- prev.top === newBounds.top &&
- prev.left === newBounds.left &&
- prev.width === newBounds.width &&
- prev.height === newBounds.height
- ) {
- return prev;
- }
- return newBounds;
- });
- },
- // biome-ignore lint/correctness/useExhaustiveDependencies: Not needed with React Compiler (https://github.com/biomejs/biome/issues/5293)
- [props],
- );
-
- const clearBounds = React.useCallback(() => {
- setBoundsState(prev => (prev === null ? prev : null));
- }, []);
-
- React.useEffect(() => {
- if (value !== undefined) setActiveValue(value);
- else if (defaultValue !== undefined) setActiveValue(defaultValue);
- }, [value, defaultValue]);
-
- const id = React.useId();
-
- React.useEffect(() => {
- if (mode !== "parent") return;
- const container = localRef.current;
- if (!container) return;
-
- const onScroll = () => {
- if (!activeValue) return;
- const activeEl = container.querySelector(
- `[data-value="${activeValue}"][data-highlight="true"]`,
- );
- if (activeEl) safeSetBounds(activeEl.getBoundingClientRect());
- };
-
- container.addEventListener("scroll", onScroll, { passive: true });
- return () => container.removeEventListener("scroll", onScroll);
- }, [mode, activeValue, safeSetBounds]);
-
- const render = React.useCallback(
- (children: React.ReactNode) => {
- if (mode === "parent") {
- return (
-
-
- {boundsState && (
-
- )}
-
- {children}
-
- );
- }
-
- return children;
- },
- [
- mode,
- // biome-ignore lint/correctness/useExhaustiveDependencies: Not needed with React Compiler (https://github.com/biomejs/biome/issues/5293)
- props,
- boundsState,
- transition,
- exitDelay,
- className,
- activeClassNameState,
- ],
- );
-
- return (
-
- {enabled
- ? controlledItems
- ? render(children)
- : render(
- React.Children.map(children, (child, index) => (
-
- {child}
-
- )),
- )
- : children}
-
- );
+ const {
+ children,
+ value,
+ defaultValue,
+ onValueChange,
+ className,
+ transition = { type: "spring", stiffness: 350, damping: 35 },
+ hover = false,
+ enabled = true,
+ controlledItems,
+ disabled = false,
+ exitDelay = 0.2,
+ mode = "children",
+ } = props;
+
+ const localRef = React.useRef(null);
+ React.useImperativeHandle(ref, () => localRef.current as HTMLDivElement);
+
+ const [activeValue, setActiveValue] = React.useState(
+ value ?? defaultValue ?? null,
+ );
+ const [boundsState, setBoundsState] = React.useState(null);
+ const [activeClassNameState, setActiveClassNameState] =
+ React.useState("");
+
+ const safeSetActiveValue = React.useCallback(
+ (id: string | null) => {
+ setActiveValue((prev) => (prev === id ? prev : id));
+ if (id !== activeValue) onValueChange?.(id);
+ },
+ [activeValue, onValueChange],
+ );
+
+ const safeSetBounds = React.useCallback(
+ (bounds: DOMRect) => {
+ if (!localRef.current) return;
+
+ const boundsOffset = (props as ParentModeMotionHighlightProps)
+ ?.boundsOffset ?? {
+ top: 0,
+ left: 0,
+ width: 0,
+ height: 0,
+ };
+
+ const containerRect = localRef.current.getBoundingClientRect();
+ const newBounds: Bounds = {
+ top: bounds.top - containerRect.top + (boundsOffset.top ?? 0),
+ left: bounds.left - containerRect.left + (boundsOffset.left ?? 0),
+ width: bounds.width + (boundsOffset.width ?? 0),
+ height: bounds.height + (boundsOffset.height ?? 0),
+ };
+
+ setBoundsState((prev) => {
+ if (
+ prev &&
+ prev.top === newBounds.top &&
+ prev.left === newBounds.left &&
+ prev.width === newBounds.width &&
+ prev.height === newBounds.height
+ ) {
+ return prev;
+ }
+ return newBounds;
+ });
+ },
+ [props],
+ );
+
+ const clearBounds = React.useCallback(() => {
+ setBoundsState((prev) => (prev === null ? prev : null));
+ }, []);
+
+ React.useEffect(() => {
+ if (value !== undefined) setActiveValue(value);
+ else if (defaultValue !== undefined) setActiveValue(defaultValue);
+ }, [value, defaultValue]);
+
+ const id = React.useId();
+
+ React.useEffect(() => {
+ if (mode !== "parent") return;
+ const container = localRef.current;
+ if (!container) return;
+
+ const onScroll = () => {
+ if (!activeValue) return;
+ const activeEl = container.querySelector(
+ `[data-value="${activeValue}"][data-highlight="true"]`,
+ );
+ if (activeEl) safeSetBounds(activeEl.getBoundingClientRect());
+ };
+
+ container.addEventListener("scroll", onScroll, { passive: true });
+ return () => container.removeEventListener("scroll", onScroll);
+ }, [mode, activeValue, safeSetBounds]);
+
+ const render = React.useCallback(
+ (children: React.ReactNode) => {
+ if (mode === "parent") {
+ return (
+
+
+ {boundsState && (
+
+ )}
+
+ {children}
+
+ );
+ }
+
+ return children;
+ },
+ [
+ mode,
+ props,
+ boundsState,
+ transition,
+ exitDelay,
+ className,
+ activeClassNameState,
+ ],
+ );
+
+ return (
+
+ {enabled
+ ? controlledItems
+ ? render(children)
+ : render(
+ React.Children.map(children, (child, index) => (
+
+ {child}
+
+ )),
+ )
+ : children}
+
+ );
}
function getNonOverridingDataAttributes(
- element: React.ReactElement,
- dataAttributes: Record,
+ element: React.ReactElement,
+ dataAttributes: Record,
): Record {
- return Object.keys(dataAttributes).reduce>(
- (acc, key) => {
- if ((element.props as Record)[key] === undefined) {
- acc[key] = dataAttributes[key];
- }
- return acc;
- },
- {},
- );
+ return Object.keys(dataAttributes).reduce>(
+ (acc, key) => {
+ if ((element.props as Record)[key] === undefined) {
+ acc[key] = dataAttributes[key];
+ }
+ return acc;
+ },
+ {},
+ );
}
type ExtendedChildProps = React.ComponentProps<"div"> & {
- id?: string;
- ref?: React.Ref;
- "data-active"?: string;
- "data-value"?: string;
- "data-disabled"?: boolean;
- "data-highlight"?: boolean;
- "data-slot"?: string;
+ id?: string;
+ ref?: React.Ref;
+ "data-active"?: string;
+ "data-value"?: string;
+ "data-disabled"?: boolean;
+ "data-highlight"?: boolean;
+ "data-slot"?: string;
};
type MotionHighlightItemProps = React.ComponentProps<"div"> & {
- children: React.ReactElement;
- id?: string;
- value?: string;
- className?: string;
- transition?: Transition;
- activeClassName?: string;
- disabled?: boolean;
- exitDelay?: number;
- asChild?: boolean;
- forceUpdateBounds?: boolean;
+ children: React.ReactElement;
+ id?: string;
+ value?: string;
+ className?: string;
+ transition?: Transition;
+ activeClassName?: string;
+ disabled?: boolean;
+ exitDelay?: number;
+ asChild?: boolean;
+ forceUpdateBounds?: boolean;
};
function MotionHighlightItem({
- ref,
- children,
- id,
- value,
- className,
- transition,
- disabled = false,
- activeClassName,
- exitDelay,
- asChild = false,
- forceUpdateBounds,
- ...props
+ ref,
+ children,
+ id,
+ value,
+ className,
+ transition,
+ disabled = false,
+ activeClassName,
+ exitDelay,
+ asChild = false,
+ forceUpdateBounds,
+ ...props
}: MotionHighlightItemProps) {
- const itemId = React.useId();
- const {
- activeValue,
- setActiveValue,
- mode,
- setBounds,
- clearBounds,
- hover,
- enabled,
- className: contextClassName,
- transition: contextTransition,
- id: contextId,
- disabled: contextDisabled,
- exitDelay: contextExitDelay,
- forceUpdateBounds: contextForceUpdateBounds,
- setActiveClassName,
- } = useMotionHighlight();
-
- const element = children as React.ReactElement;
- const childValue =
- id ?? value ?? element.props?.["data-value"] ?? element.props?.id ?? itemId;
- const isActive = activeValue === childValue;
- const isDisabled = disabled === undefined ? contextDisabled : disabled;
- const itemTransition = transition ?? contextTransition;
-
- const localRef = React.useRef(null);
- React.useImperativeHandle(ref, () => localRef.current as HTMLDivElement);
-
- React.useEffect(() => {
- if (mode !== "parent") return;
- let rafId: number;
- let previousBounds: Bounds | null = null;
- const shouldUpdateBounds =
- forceUpdateBounds === true ||
- (contextForceUpdateBounds && forceUpdateBounds !== false);
-
- const updateBounds = () => {
- if (!localRef.current) return;
-
- const bounds = localRef.current.getBoundingClientRect();
-
- if (shouldUpdateBounds) {
- if (
- previousBounds &&
- previousBounds.top === bounds.top &&
- previousBounds.left === bounds.left &&
- previousBounds.width === bounds.width &&
- previousBounds.height === bounds.height
- ) {
- rafId = requestAnimationFrame(updateBounds);
- return;
- }
- previousBounds = bounds;
- rafId = requestAnimationFrame(updateBounds);
- }
-
- setBounds(bounds);
- };
-
- if (isActive) {
- updateBounds();
- setActiveClassName(activeClassName ?? "");
- } else if (!activeValue) clearBounds();
-
- if (shouldUpdateBounds) return () => cancelAnimationFrame(rafId);
- }, [
- mode,
- isActive,
- activeValue,
- setBounds,
- clearBounds,
- activeClassName,
- setActiveClassName,
- forceUpdateBounds,
- contextForceUpdateBounds,
- ]);
-
- if (!React.isValidElement(children)) return children;
-
- const dataAttributes = {
- "data-active": isActive ? "true" : "false",
- "aria-selected": isActive,
- "data-disabled": isDisabled,
- "data-value": childValue,
- "data-highlight": true,
- };
-
- const commonHandlers = hover
- ? {
- onMouseEnter: (e: React.MouseEvent) => {
- setActiveValue(childValue);
- element.props.onMouseEnter?.(e);
- },
- onMouseLeave: (e: React.MouseEvent) => {
- setActiveValue(null);
- element.props.onMouseLeave?.(e);
- },
- }
- : {
- onClick: (e: React.MouseEvent) => {
- setActiveValue(childValue);
- element.props.onClick?.(e);
- },
- };
-
- if (asChild) {
- if (mode === "children") {
- return React.cloneElement(
- element,
- {
- key: childValue,
- ref: localRef,
- className: cn("relative", element.props.className),
- ...getNonOverridingDataAttributes(element, {
- ...dataAttributes,
- "data-slot": "motion-highlight-item-container",
- }),
- ...commonHandlers,
- ...props,
- },
- <>
-
- {isActive && !isDisabled && (
-
- )}
-
-
-
- {children}
-
- >,
- );
- }
-
- return React.cloneElement(element, {
- ref: localRef,
- ...getNonOverridingDataAttributes(element, {
- ...dataAttributes,
- "data-slot": "motion-highlight-item",
- }),
- ...commonHandlers,
- });
- }
-
- return enabled ? (
-
- {mode === "children" && (
-
- {isActive && !isDisabled && (
-
- )}
-
- )}
-
- {React.cloneElement(element, {
- className: cn("relative z-[1]", element.props.className),
- ...getNonOverridingDataAttributes(element, {
- ...dataAttributes,
- "data-slot": "motion-highlight-item",
- }),
- })}
-
- ) : (
- children
- );
+ const itemId = React.useId();
+ const {
+ activeValue,
+ setActiveValue,
+ mode,
+ setBounds,
+ clearBounds,
+ hover,
+ enabled,
+ className: contextClassName,
+ transition: contextTransition,
+ id: contextId,
+ disabled: contextDisabled,
+ exitDelay: contextExitDelay,
+ forceUpdateBounds: contextForceUpdateBounds,
+ setActiveClassName,
+ } = useMotionHighlight();
+
+ const element = children as React.ReactElement;
+ const childValue =
+ id ?? value ?? element.props?.["data-value"] ?? element.props?.id ?? itemId;
+ const isActive = activeValue === childValue;
+ const isDisabled = disabled === undefined ? contextDisabled : disabled;
+ const itemTransition = transition ?? contextTransition;
+
+ const localRef = React.useRef(null);
+ React.useImperativeHandle(ref, () => localRef.current as HTMLDivElement);
+
+ React.useEffect(() => {
+ if (mode !== "parent") return;
+ let rafId: number;
+ let previousBounds: Bounds | null = null;
+ const shouldUpdateBounds =
+ forceUpdateBounds === true ||
+ (contextForceUpdateBounds && forceUpdateBounds !== false);
+
+ const updateBounds = () => {
+ if (!localRef.current) return;
+
+ const bounds = localRef.current.getBoundingClientRect();
+
+ if (shouldUpdateBounds) {
+ if (
+ previousBounds &&
+ previousBounds.top === bounds.top &&
+ previousBounds.left === bounds.left &&
+ previousBounds.width === bounds.width &&
+ previousBounds.height === bounds.height
+ ) {
+ rafId = requestAnimationFrame(updateBounds);
+ return;
+ }
+ previousBounds = bounds;
+ rafId = requestAnimationFrame(updateBounds);
+ }
+
+ setBounds(bounds);
+ };
+
+ if (isActive) {
+ updateBounds();
+ setActiveClassName(activeClassName ?? "");
+ } else if (!activeValue) clearBounds();
+
+ if (shouldUpdateBounds) return () => cancelAnimationFrame(rafId);
+ }, [
+ mode,
+ isActive,
+ activeValue,
+ setBounds,
+ clearBounds,
+ activeClassName,
+ setActiveClassName,
+ forceUpdateBounds,
+ contextForceUpdateBounds,
+ ]);
+
+ if (!React.isValidElement(children)) return children;
+
+ const dataAttributes = {
+ "data-active": isActive ? "true" : "false",
+ "aria-selected": isActive,
+ "data-disabled": isDisabled,
+ "data-value": childValue,
+ "data-highlight": true,
+ };
+
+ const commonHandlers = hover
+ ? {
+ onMouseEnter: (e: React.MouseEvent) => {
+ setActiveValue(childValue);
+ element.props.onMouseEnter?.(e);
+ },
+ onMouseLeave: (e: React.MouseEvent) => {
+ setActiveValue(null);
+ element.props.onMouseLeave?.(e);
+ },
+ }
+ : {
+ onClick: (e: React.MouseEvent) => {
+ setActiveValue(childValue);
+ element.props.onClick?.(e);
+ },
+ };
+
+ if (asChild) {
+ if (mode === "children") {
+ return React.cloneElement(
+ element,
+ {
+ key: childValue,
+ ref: localRef,
+ className: cn("relative", element.props.className),
+ ...getNonOverridingDataAttributes(element, {
+ ...dataAttributes,
+ "data-slot": "motion-highlight-item-container",
+ }),
+ ...commonHandlers,
+ ...props,
+ },
+ <>
+
+ {isActive && !isDisabled && (
+
+ )}
+
+
+
+ {children}
+
+ >,
+ );
+ }
+
+ return React.cloneElement(element, {
+ ref: localRef,
+ ...getNonOverridingDataAttributes(element, {
+ ...dataAttributes,
+ "data-slot": "motion-highlight-item",
+ }),
+ ...commonHandlers,
+ });
+ }
+
+ return enabled ? (
+
+ {mode === "children" && (
+
+ {isActive && !isDisabled && (
+
+ )}
+
+ )}
+
+ {React.cloneElement(element, {
+ className: cn("relative z-[1]", element.props.className),
+ ...getNonOverridingDataAttributes(element, {
+ ...dataAttributes,
+ "data-slot": "motion-highlight-item",
+ }),
+ })}
+
+ ) : (
+ children
+ );
}
export {
- MotionHighlight,
- MotionHighlightItem,
- useMotionHighlight,
- type MotionHighlightProps,
- type MotionHighlightItemProps,
+ MotionHighlight,
+ MotionHighlightItem,
+ useMotionHighlight,
+ type MotionHighlightProps,
+ type MotionHighlightItemProps,
};
diff --git a/src/components/animate-ui/ripple-button.tsx b/src/components/animate-ui/ripple-button.tsx
index c865358..8c8c5be 100644
--- a/src/components/animate-ui/ripple-button.tsx
+++ b/src/components/animate-ui/ripple-button.tsx
@@ -3,143 +3,144 @@
import { cva, type VariantProps } from "class-variance-authority";
import { type HTMLMotionProps, motion, type Transition } from "motion/react";
import * as React from "react";
-import { cn } from "@/lib/utils";
-
-interface Ripple {
- id: number;
- x: number;
- y: number;
-}
-interface RippleButtonProps extends HTMLMotionProps<"button"> {
- children: React.ReactNode;
- rippleClassName?: string;
- scale?: number;
- transition?: Transition;
-}
+import { cn } from "@/lib/utils";
const buttonVariants = cva(
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive overflow-hidden",
- {
- variants: {
- variant: {
- default:
- "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
- destructive:
- "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
- outline:
- "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
- secondary:
- "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
- ghost:
- "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
- link: "text-primary underline-offset-4 hover:underline",
- },
- size: {
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
- sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
- icon: "size-9",
- },
- },
- defaultVariants: {
- variant: "default",
- size: "default",
- },
- },
+ `relative border cursor-pointer inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive overflow-hidden`,
+ {
+ variants: {
+ variant: {
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
+ outline:
+ "border bg-background hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
+ secondary:
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ ghost:
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
+ },
+ size: {
+ default: "h-10 px-4 py-2 has-[>svg]:px-3",
+ sm: "h-9 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
+ lg: "h-11 rounded-md px-8 has-[>svg]:px-6",
+ icon: "size-10",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ },
);
-const RippleButton = React.forwardRef<
- HTMLButtonElement,
- RippleButtonProps & VariantProps
->(
- (
- {
- children,
- onClick,
- className,
- rippleClassName,
- scale = 10,
- transition = { duration: 0.6, ease: "easeOut" },
- variant,
- size,
- ...props
- },
- ref,
- ) => {
- const [ripples, setRipples] = React.useState([]);
- const buttonRef = React.useRef(null);
- React.useImperativeHandle(
- ref,
- () => buttonRef.current as HTMLButtonElement,
- );
+const rippleVariants = cva("pointer-events-none absolute size-5 rounded-full", {
+ variants: {
+ variant: {
+ default: "bg-primary-foreground",
+ destructive: "bg-destructive",
+ outline: "bg-input",
+ secondary: "bg-secondary",
+ ghost: "bg-accent",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+});
- const createRipple = React.useCallback(
- (event: React.MouseEvent) => {
- const button = buttonRef.current;
- if (!button) return;
+type Ripple = {
+ id: number;
+ x: number;
+ y: number;
+};
- const rect = button.getBoundingClientRect();
- const x = event.clientX - rect.left;
- const y = event.clientY - rect.top;
+type RippleButtonProps = HTMLMotionProps<"button"> & {
+ children: React.ReactNode;
+ rippleClassName?: string;
+ scale?: number;
+ transition?: Transition;
+} & VariantProps;
- const newRipple: Ripple = {
- id: Date.now(),
- x,
- y,
- };
+function RippleButton({
+ ref,
+ children,
+ onClick,
+ className,
+ rippleClassName,
+ variant,
+ size,
+ scale = 10,
+ transition = { duration: 0.6, ease: "easeOut" },
+ ...props
+}: RippleButtonProps) {
+ const [ripples, setRipples] = React.useState([]);
+ const buttonRef = React.useRef(null);
+ React.useImperativeHandle(ref, () => buttonRef.current as HTMLButtonElement);
- setRipples(prev => [...prev, newRipple]);
+ const createRipple = React.useCallback(
+ (event: React.MouseEvent) => {
+ const button = buttonRef.current;
+ if (!button) return;
- setTimeout(() => {
- setRipples(prev => prev.filter(r => r.id !== newRipple.id));
- }, 600);
- },
- [],
- );
+ const rect = button.getBoundingClientRect();
+ const x = event.clientX - rect.left;
+ const y = event.clientY - rect.top;
- const handleClick = React.useCallback(
- (event: React.MouseEvent) => {
- createRipple(event);
- if (onClick) {
- onClick(event);
- }
- },
- [createRipple, onClick],
- );
+ const newRipple: Ripple = {
+ id: Date.now(),
+ x,
+ y,
+ };
- return (
-
- {children}
- {ripples.map(ripple => (
-
- ))}
-
- );
- },
-);
+ setRipples((prev) => [...prev, newRipple]);
+
+ setTimeout(() => {
+ setRipples((prev) => prev.filter((r) => r.id !== newRipple.id));
+ }, 600);
+ },
+ [],
+ );
-RippleButton.displayName = "RippleButton";
+ const handleClick = React.useCallback(
+ (event: React.MouseEvent) => {
+ createRipple(event);
+ if (onClick) {
+ onClick(event);
+ }
+ },
+ [createRipple, onClick],
+ );
+
+ return (
+
+ {children}
+ {ripples.map((ripple) => (
+
+ ))}
+
+ );
+}
export { RippleButton, type RippleButtonProps };
diff --git a/src/components/animate-ui/sliding-number.tsx b/src/components/animate-ui/sliding-number.tsx
index 198c59e..9165ccb 100644
--- a/src/components/animate-ui/sliding-number.tsx
+++ b/src/components/animate-ui/sliding-number.tsx
@@ -1,13 +1,13 @@
"use client";
import {
- type MotionValue,
- motion,
- type SpringOptions,
- type UseInViewOptions,
- useInView,
- useSpring,
- useTransform,
+ type MotionValue,
+ motion,
+ type SpringOptions,
+ type UseInViewOptions,
+ useInView,
+ useSpring,
+ useTransform,
} from "motion/react";
import * as React from "react";
import useMeasure from "react-use-measure";
@@ -15,219 +15,223 @@ import useMeasure from "react-use-measure";
import { cn } from "@/lib/utils";
interface NumberProps {
- prevValue: number;
- value: number;
- place: number;
- transition: SpringOptions;
+ prevValue: number;
+ value: number;
+ place: number;
+ transition: SpringOptions;
}
const NumberRoller = ({ prevValue, value, place, transition }: NumberProps) => {
- const startNumber = Math.floor(prevValue / place) % 10;
- const targetNumber = Math.floor(value / place) % 10;
- const animatedValue = useSpring(startNumber, transition);
-
- React.useEffect(() => {
- animatedValue.set(targetNumber);
- }, [targetNumber, animatedValue]);
-
- const [measureRef, { height }] = useMeasure();
-
- return (
-
-
0
- {Array.from({ length: 10 }, (_, i) => (
-
- ))}
-
- );
+ const startNumber = Math.floor(prevValue / place) % 10;
+ const targetNumber = Math.floor(value / place) % 10;
+ const animatedValue = useSpring(startNumber, transition);
+
+ React.useEffect(() => {
+ animatedValue.set(targetNumber);
+ }, [targetNumber, animatedValue]);
+
+ const [measureRef, { height }] = useMeasure();
+
+ return (
+
+
0
+ {Array.from({ length: 10 }, (_, i) => (
+
+ ))}
+
+ );
};
interface NumberDisplayProps {
- motionValue: MotionValue;
- number: number;
- height: number;
- transition: SpringOptions;
+ motionValue: MotionValue;
+ number: number;
+ height: number;
+ transition: SpringOptions;
}
const NumberDisplay = ({
- motionValue,
- number,
- height,
- transition,
+ motionValue,
+ number,
+ height,
+ transition,
}: NumberDisplayProps) => {
- const y = useTransform(motionValue, latest => {
- if (!height) return 0;
- const currentNumber = latest % 10;
- const offset = (10 + number - currentNumber) % 10;
- let translateY = offset * height;
- if (offset > 5) translateY -= 10 * height;
- return translateY;
- });
-
- if (!height) {
- return {number} ;
- }
-
- return (
-
- {number}
-
- );
+ const y = useTransform(motionValue, (latest) => {
+ if (!height) return 0;
+ const currentNumber = latest % 10;
+ const offset = (10 + number - currentNumber) % 10;
+ let translateY = offset * height;
+ if (offset > 5) translateY -= 10 * height;
+ return translateY;
+ });
+
+ if (!height) {
+ return {number} ;
+ }
+
+ return (
+
+ {number}
+
+ );
};
interface SlidingNumberProps extends React.HTMLAttributes {
- number: number | string;
- inView?: boolean;
- inViewMargin?: UseInViewOptions["margin"];
- inViewOnce?: boolean;
- padStart?: boolean;
- decimalSeparator?: string;
- decimalPlaces?: number;
- transition?: SpringOptions;
+ number: number | string;
+ inView?: boolean;
+ inViewMargin?: UseInViewOptions["margin"];
+ inViewOnce?: boolean;
+ padStart?: boolean;
+ decimalSeparator?: string;
+ decimalPlaces?: number;
+ transition?: SpringOptions;
}
const SlidingNumber = React.forwardRef(
- (
- {
- number,
- className,
- inView = false,
- inViewMargin = "0px",
- inViewOnce = true,
- padStart = false,
- decimalSeparator = ".",
- decimalPlaces = 0,
- transition = {
- stiffness: 200,
- damping: 20,
- mass: 0.4,
- },
- ...props
- },
- ref,
- ) => {
- const localRef = React.useRef(null);
- React.useImperativeHandle(ref, () => localRef.current!);
-
- const inViewResult = useInView(localRef, {
- once: inViewOnce,
- margin: inViewMargin,
- });
- const isInView = !inView || inViewResult;
-
- const prevNumberRef = React.useRef(0);
-
- const effectiveNumber = React.useMemo(
- () => (!isInView ? 0 : Math.abs(Number(number))),
- [number, isInView],
- );
-
- const formatNumber = React.useCallback(
- (num: number) =>
- decimalPlaces != null ? num.toFixed(decimalPlaces) : num.toString(),
- [decimalPlaces],
- );
-
- const numberStr = formatNumber(effectiveNumber);
- const [newIntStrRaw, newDecStrRaw = ""] = numberStr.split(".");
- const newIntStr =
- padStart && newIntStrRaw!.length === 1
- ? "0" + newIntStrRaw
- : newIntStrRaw;
-
- const prevFormatted = formatNumber(prevNumberRef.current);
- const [prevIntStrRaw = "", prevDecStrRaw = ""] = prevFormatted.split(".");
- const prevIntStr =
- padStart && prevIntStrRaw.length === 1
- ? "0" + prevIntStrRaw
- : prevIntStrRaw;
-
- const adjustedPrevInt = React.useMemo(() => {
- return prevIntStr.length > newIntStr!.length
- ? prevIntStr.slice(-newIntStr!.length)
- : prevIntStr.padStart(newIntStr!.length, "0");
- }, [prevIntStr, newIntStr]);
-
- const adjustedPrevDec = React.useMemo(() => {
- if (!newDecStrRaw) return "";
- return prevDecStrRaw.length > newDecStrRaw.length
- ? prevDecStrRaw.slice(0, newDecStrRaw.length)
- : prevDecStrRaw.padEnd(newDecStrRaw.length, "0");
- }, [prevDecStrRaw, newDecStrRaw]);
-
- React.useEffect(() => {
- if (isInView) prevNumberRef.current = effectiveNumber;
- }, [effectiveNumber, isInView]);
-
- const intDigitCount = newIntStr!.length;
- const intPlaces = React.useMemo(
- () =>
- Array.from({ length: intDigitCount }, (_, i) =>
- Math.pow(10, intDigitCount - i - 1),
- ),
- [intDigitCount],
- );
- const decPlaces = React.useMemo(
- () =>
- newDecStrRaw
- ? Array.from({ length: newDecStrRaw.length }, (_, i) =>
- Math.pow(10, newDecStrRaw.length - i - 1),
- )
- : [],
- [newDecStrRaw],
- );
-
- const newDecValue = newDecStrRaw ? parseInt(newDecStrRaw, 10) : 0;
- const prevDecValue = adjustedPrevDec ? parseInt(adjustedPrevDec, 10) : 0;
-
- return (
-
- {isInView && Number(number) < 0 && - }
-
- {intPlaces.map(place => (
-
- ))}
-
- {newDecStrRaw && (
- <>
- {decimalSeparator}
- {decPlaces.map(place => (
-
- ))}
- >
- )}
-
- );
- },
+ (
+ {
+ number,
+ className,
+ inView = false,
+ inViewMargin = "0px",
+ inViewOnce = true,
+ padStart = false,
+ decimalSeparator = ".",
+ decimalPlaces = 0,
+ transition = {
+ stiffness: 200,
+ damping: 20,
+ mass: 0.4,
+ },
+ ...props
+ },
+ ref,
+ ) => {
+ const localRef = React.useRef(null);
+ React.useImperativeHandle(ref, () => localRef.current!);
+
+ const inViewResult = useInView(localRef, {
+ once: inViewOnce,
+ margin: inViewMargin,
+ });
+ const isInView = !inView || inViewResult;
+
+ const prevNumberRef = React.useRef(0);
+
+ const effectiveNumber = React.useMemo(
+ () => (!isInView ? 0 : Math.abs(Number(number))),
+ [number, isInView],
+ );
+
+ const formatNumber = React.useCallback(
+ (num: number) =>
+ decimalPlaces != null ? num.toFixed(decimalPlaces) : num.toString(),
+ [decimalPlaces],
+ );
+
+ const numberStr = formatNumber(effectiveNumber);
+ const [newIntStrRaw, newDecStrRaw = ""] = numberStr.split(".");
+ const newIntStr =
+ padStart && newIntStrRaw?.length === 1
+ ? `0${newIntStrRaw}`
+ : newIntStrRaw;
+
+ const prevFormatted = formatNumber(prevNumberRef.current);
+ const [prevIntStrRaw = "", prevDecStrRaw = ""] = prevFormatted.split(".");
+ const prevIntStr =
+ padStart && prevIntStrRaw.length === 1
+ ? `0${prevIntStrRaw}`
+ : prevIntStrRaw;
+
+ const adjustedPrevInt = React.useMemo(() => {
+ return prevIntStr.length > (newIntStr?.length ?? 0)
+ ? prevIntStr.slice(-(newIntStr?.length ?? 0))
+ : prevIntStr.padStart(newIntStr?.length ?? 0, "0");
+ }, [prevIntStr, newIntStr]);
+
+ const adjustedPrevDec = React.useMemo(() => {
+ if (!newDecStrRaw) return "";
+ return prevDecStrRaw.length > newDecStrRaw.length
+ ? prevDecStrRaw.slice(0, newDecStrRaw.length)
+ : prevDecStrRaw.padEnd(newDecStrRaw.length, "0");
+ }, [prevDecStrRaw, newDecStrRaw]);
+
+ React.useEffect(() => {
+ if (isInView) prevNumberRef.current = effectiveNumber;
+ }, [effectiveNumber, isInView]);
+
+ const intDigitCount = newIntStr?.length ?? 0;
+ const intPlaces = React.useMemo(
+ () =>
+ Array.from(
+ { length: intDigitCount },
+ (_, i) => 10 ** (intDigitCount - i - 1),
+ ),
+ [intDigitCount],
+ );
+ const decPlaces = React.useMemo(
+ () =>
+ newDecStrRaw
+ ? Array.from(
+ { length: newDecStrRaw.length },
+ (_, i) => 10 ** (newDecStrRaw.length - i - 1),
+ )
+ : [],
+ [newDecStrRaw],
+ );
+
+ const newDecValue = newDecStrRaw ? Number.parseInt(newDecStrRaw, 10) : 0;
+ const prevDecValue = adjustedPrevDec
+ ? Number.parseInt(adjustedPrevDec, 10)
+ : 0;
+
+ return (
+
+ {isInView && Number(number) < 0 && - }
+
+ {intPlaces.map((place) => (
+
+ ))}
+
+ {newDecStrRaw && (
+ <>
+ {decimalSeparator}
+ {decPlaces.map((place) => (
+
+ ))}
+ >
+ )}
+
+ );
+ },
);
SlidingNumber.displayName = "SlidingNumber";
diff --git a/src/components/animate-ui/tabs.tsx b/src/components/animate-ui/tabs.tsx
index 66a6e47..7b14a7b 100644
--- a/src/components/animate-ui/tabs.tsx
+++ b/src/components/animate-ui/tabs.tsx
@@ -3,280 +3,280 @@
import { type HTMLMotionProps, motion, type Transition } from "motion/react";
import * as React from "react";
import {
- MotionHighlight,
- MotionHighlightItem,
+ MotionHighlight,
+ MotionHighlightItem,
} from "@/components/animate-ui/motion-highlight";
import { cn } from "@/lib/utils";
type TabsContextType = {
- activeValue: string;
- handleValueChange: (value: string) => void;
- registerTrigger: (value: string, node: HTMLElement | null) => void;
+ activeValue: string;
+ handleValueChange: (value: string) => void;
+ registerTrigger: (value: string, node: HTMLElement | null) => void;
};
const TabsContext = React.createContext(undefined);
const useTabs = (): TabsContextType => {
- const context = React.useContext(TabsContext);
- if (!context) {
- throw new Error("useTabs must be used within a TabsProvider");
- }
- return context;
+ const context = React.useContext(TabsContext);
+ if (!context) {
+ throw new Error("useTabs must be used within a TabsProvider");
+ }
+ return context;
};
type BaseTabsProps = React.ComponentProps<"div"> & {
- children: React.ReactNode;
+ children: React.ReactNode;
};
type UnControlledTabsProps = BaseTabsProps & {
- defaultValue?: string;
- value?: never;
- onValueChange?: never;
+ defaultValue?: string;
+ value?: never;
+ onValueChange?: never;
};
type ControlledTabsProps = BaseTabsProps & {
- value: string;
- onValueChange?: (value: string) => void;
- defaultValue?: never;
+ value: string;
+ onValueChange?: (value: string) => void;
+ defaultValue?: never;
};
type TabsProps = UnControlledTabsProps | ControlledTabsProps;
function Tabs({
- defaultValue,
- value,
- onValueChange,
- children,
- className,
- ...props
+ defaultValue,
+ value,
+ onValueChange,
+ children,
+ className,
+ ...props
}: TabsProps) {
- const [activeValue, setActiveValue] = React.useState(defaultValue);
- const triggersRef = React.useRef(new Map());
- const initialSet = React.useRef(false);
- const isControlled = value !== undefined;
+ const [activeValue, setActiveValue] = React.useState(defaultValue);
+ const triggersRef = React.useRef(new Map());
+ const initialSet = React.useRef(false);
+ const isControlled = value !== undefined;
- React.useEffect(() => {
- if (
- !isControlled &&
- activeValue === undefined &&
- triggersRef.current.size > 0 &&
- !initialSet.current
- ) {
- const firstTab = Array.from(triggersRef.current.keys())[0];
- setActiveValue(firstTab);
- initialSet.current = true;
- }
- }, [activeValue, isControlled]);
+ React.useEffect(() => {
+ if (
+ !isControlled &&
+ activeValue === undefined &&
+ triggersRef.current.size > 0 &&
+ !initialSet.current
+ ) {
+ const firstTab = Array.from(triggersRef.current.keys())[0];
+ setActiveValue(firstTab);
+ initialSet.current = true;
+ }
+ }, [activeValue, isControlled]);
- const registerTrigger = (value: string, node: HTMLElement | null) => {
- if (node) {
- triggersRef.current.set(value, node);
- if (!isControlled && activeValue === undefined && !initialSet.current) {
- setActiveValue(value);
- initialSet.current = true;
- }
- } else {
- triggersRef.current.delete(value);
- }
- };
+ const registerTrigger = (value: string, node: HTMLElement | null) => {
+ if (node) {
+ triggersRef.current.set(value, node);
+ if (!isControlled && activeValue === undefined && !initialSet.current) {
+ setActiveValue(value);
+ initialSet.current = true;
+ }
+ } else {
+ triggersRef.current.delete(value);
+ }
+ };
- const handleValueChange = (val: string) => {
- if (!isControlled) setActiveValue(val);
- else onValueChange?.(val);
- };
+ const handleValueChange = (val: string) => {
+ if (!isControlled) setActiveValue(val);
+ else onValueChange?.(val);
+ };
- return (
-
-
- {children}
-
-
- );
+ return (
+
+
+ {children}
+
+
+ );
}
type TabsListProps = React.ComponentProps<"div"> & {
- children: React.ReactNode;
- activeClassName?: string;
- transition?: Transition;
+ children: React.ReactNode;
+ activeClassName?: string;
+ transition?: Transition;
};
function TabsList({
- children,
- className,
- activeClassName,
- transition = {
- type: "spring",
- stiffness: 200,
- damping: 25,
- },
- ...props
+ children,
+ className,
+ activeClassName,
+ transition = {
+ type: "spring",
+ stiffness: 200,
+ damping: 25,
+ },
+ ...props
}: TabsListProps) {
- const { activeValue } = useTabs();
+ const { activeValue } = useTabs();
- return (
-
-
- {children}
-
-
- );
+ return (
+
+
+ {children}
+
+
+ );
}
type TabsTriggerProps = HTMLMotionProps<"button"> & {
- value: string;
- children: React.ReactNode;
+ value: string;
+ children: React.ReactNode;
};
function TabsTrigger({
- ref,
- value,
- children,
- className,
- ...props
+ ref,
+ value,
+ children,
+ className,
+ ...props
}: TabsTriggerProps) {
- const { activeValue, handleValueChange, registerTrigger } = useTabs();
+ const { activeValue, handleValueChange, registerTrigger } = useTabs();
- const localRef = React.useRef(null);
- React.useImperativeHandle(ref, () => localRef.current as HTMLButtonElement);
+ const localRef = React.useRef(null);
+ React.useImperativeHandle(ref, () => localRef.current as HTMLButtonElement);
- React.useEffect(() => {
- registerTrigger(value, localRef.current);
- return () => registerTrigger(value, null);
- }, [value, registerTrigger]);
+ React.useEffect(() => {
+ registerTrigger(value, localRef.current);
+ return () => registerTrigger(value, null);
+ }, [value, registerTrigger]);
- return (
-
- handleValueChange(value)}
- data-state={activeValue === value ? "active" : "inactive"}
- className={cn(
- "z-[1] inline-flex size-full cursor-pointer items-center justify-center whitespace-nowrap rounded-sm px-2 py-1 font-medium text-sm ring-offset-background transition-transform focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:text-foreground",
- className,
- )}
- {...props}
- >
- {children}
-
-
- );
+ return (
+
+ handleValueChange(value)}
+ data-state={activeValue === value ? "active" : "inactive"}
+ className={cn(
+ "z-[1] inline-flex size-full cursor-pointer items-center justify-center whitespace-nowrap rounded-sm px-2 py-1 font-medium text-sm ring-offset-background transition-transform focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:text-foreground",
+ className,
+ )}
+ {...props}
+ >
+ {children}
+
+
+ );
}
type TabsContentsProps = React.ComponentProps<"div"> & {
- children: React.ReactNode;
- transition?: Transition;
+ children: React.ReactNode;
+ transition?: Transition;
};
function TabsContents({
- children,
- className,
- transition = {
- type: "spring",
- stiffness: 300,
- damping: 30,
- bounce: 0,
- restDelta: 0.01,
- },
- ...props
+ children,
+ className,
+ transition = {
+ type: "spring",
+ stiffness: 300,
+ damping: 30,
+ bounce: 0,
+ restDelta: 0.01,
+ },
+ ...props
}: TabsContentsProps) {
- const { activeValue } = useTabs();
- const childrenArray = React.Children.toArray(children);
- const activeIndex = childrenArray.findIndex(
- (child): child is React.ReactElement<{ value: string }> =>
- React.isValidElement(child) &&
- typeof child.props === "object" &&
- child.props !== null &&
- "value" in child.props &&
- child.props.value === activeValue,
- );
+ const { activeValue } = useTabs();
+ const childrenArray = React.Children.toArray(children);
+ const activeIndex = childrenArray.findIndex(
+ (child): child is React.ReactElement<{ value: string }> =>
+ React.isValidElement(child) &&
+ typeof child.props === "object" &&
+ child.props !== null &&
+ "value" in child.props &&
+ child.props.value === activeValue,
+ );
- return (
-
-
- {childrenArray.map((child, index) => (
-
- {child}
-
- ))}
-
-
- );
+ return (
+
+
+ {childrenArray.map((child, index) => (
+
+ {child}
+
+ ))}
+
+
+ );
}
type TabsContentProps = HTMLMotionProps<"div"> & {
- value: string;
- children: React.ReactNode;
+ value: string;
+ children: React.ReactNode;
};
function TabsContent({
- children,
- value,
- className,
- ...props
+ children,
+ value,
+ className,
+ ...props
}: TabsContentProps) {
- const { activeValue } = useTabs();
- const isActive = activeValue === value;
- return (
-
- {children}
-
- );
+ const { activeValue } = useTabs();
+ const isActive = activeValue === value;
+ return (
+
+ {children}
+
+ );
}
export {
- Tabs,
- TabsList,
- TabsTrigger,
- TabsContents,
- TabsContent,
- useTabs,
- type TabsContextType,
- type TabsProps,
- type TabsListProps,
- type TabsTriggerProps,
- type TabsContentsProps,
- type TabsContentProps,
+ Tabs,
+ TabsList,
+ TabsTrigger,
+ TabsContents,
+ TabsContent,
+ useTabs,
+ type TabsContextType,
+ type TabsProps,
+ type TabsListProps,
+ type TabsTriggerProps,
+ type TabsContentsProps,
+ type TabsContentProps,
};
diff --git a/src/components/footer.tsx b/src/components/footer.tsx
index af961f7..92c5299 100644
--- a/src/components/footer.tsx
+++ b/src/components/footer.tsx
@@ -4,78 +4,78 @@ import { useTranslations } from "next-intl";
import { GitHubStarsButton } from "./animate-ui/github-stars-button";
export default function Footer() {
- const t = useTranslations("footer");
+ const t = useTranslations("footer");
- return (
-
-
-
-
-
- {t("title")}
-
-
- {t("description")}
-
-
+ return (
+
- );
+
+
+
+
+ );
}
function FooterEntry({
- href,
- children,
+ href,
+ children,
}: {
- href: string;
- children: React.ReactNode;
+ href: string;
+ children: React.ReactNode;
}) {
- return (
-
- {children}
-
- );
+ return (
+
+ {children}
+
+ );
}
diff --git a/src/components/locale-switcher.tsx b/src/components/locale-switcher.tsx
index 63ac78c..83268dc 100644
--- a/src/components/locale-switcher.tsx
+++ b/src/components/locale-switcher.tsx
@@ -2,57 +2,56 @@
import { Globe } from "lucide-react";
import { useParams } from "next/navigation";
-import { useLocale, useTranslations } from "next-intl";
-import { type Locale } from "next-intl";
+import { type Locale, useLocale, useTranslations } from "next-intl";
import { useTransition } from "react";
import { useIsMobile } from "@/hooks";
import { usePathname, useRouter } from "@/i18n/navigation";
import { routing } from "@/i18n/routing";
import { cn } from "@/lib/utils";
import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
} from "./ui/select";
export default function LocaleSwitcher({ className }: { className?: string }) {
- const t = useTranslations("locale-switcher");
- const locale = useLocale();
+ const t = useTranslations("locale-switcher");
+ const locale = useLocale();
- const router = useRouter();
- const [isPending, startTransition] = useTransition();
- const pathname = usePathname();
- const params = useParams();
+ const router = useRouter();
+ const [isPending, startTransition] = useTransition();
+ const pathname = usePathname();
+ const params = useParams();
- const isMobile = useIsMobile();
+ const isMobile = useIsMobile();
- function onSelectChange(val: Locale) {
- startTransition(() => {
- router.replace({ pathname, query: params }, { locale: val });
- });
- }
+ function onSelectChange(val: Locale) {
+ startTransition(() => {
+ router.replace({ pathname, query: params }, { locale: val });
+ });
+ }
- return (
-
-
-
-
-
- {routing.locales.map(cur => (
-
-
- {t(isMobile ? "locale-short" : "locale", { locale: cur })}
-
- ))}
-
-
- );
+ return (
+
+
+
+
+
+ {routing.locales.map((cur) => (
+
+
+ {t(isMobile ? "locale-short" : "locale", { locale: cur })}
+
+ ))}
+
+
+ );
}
diff --git a/src/components/navigation.tsx b/src/components/navigation.tsx
index d85f5e3..993c6a7 100644
--- a/src/components/navigation.tsx
+++ b/src/components/navigation.tsx
@@ -9,98 +9,99 @@ import { cn } from "@/lib/utils";
import LocaleSwitcher from "./locale-switcher";
export default function Navbar() {
- const [scrolled, setScrolled] = useState(false);
- const pathname = usePathname();
+ const [scrolled, setScrolled] = useState(false);
+ const pathname = usePathname();
- useEffect(() => {
- const handleScroll = () => {
- setScrolled(window.scrollY > 10);
- };
+ useEffect(() => {
+ const handleScroll = () => {
+ setScrolled(window.scrollY > 10);
+ };
- window.addEventListener("scroll", handleScroll);
- return () => window.removeEventListener("scroll", handleScroll);
- }, []);
+ handleScroll(); // Initial check on mount
+ window.addEventListener("scroll", handleScroll);
+ return () => window.removeEventListener("scroll", handleScroll);
+ }, []);
- return (
-
-
-
- {/* Logo */}
-
-
-
+ return (
+
+
+
+ {/* Logo */}
+
+
+
- {/* Navigation */}
-
-
-
-
+ {/* Navigation */}
+
+
+
+
- {/* Language Selector */}
-
-
-
-
- );
+ {/* Language Selector */}
+
+
+
+
+ );
}
function NavLink({
- name,
- href,
- isActive,
- className,
+ name,
+ href,
+ isActive,
+ className,
}: {
- name: "home" | "playground";
- href: string;
- isActive: boolean;
- className?: string;
+ name: "home" | "playground";
+ href: string;
+ isActive: boolean;
+ className?: string;
}) {
- const t = useTranslations("navigation");
+ const t = useTranslations("navigation");
- return (
-
- {t(name)}
- {isActive && (
-
- )}
-
- );
+ return (
+
+ {t(name)}
+ {isActive && (
+
+ )}
+
+ );
}
diff --git a/src/components/playground-state.tsx b/src/components/playground-state.tsx
new file mode 100644
index 0000000..059df18
--- /dev/null
+++ b/src/components/playground-state.tsx
@@ -0,0 +1,33 @@
+"use client";
+
+import { useQueryState } from "nuqs";
+import { useState } from "react";
+import { playgroundSearchParams } from "@/lib/utils";
+import AlgorithmExplanation from "./algorithm-explanation";
+import SortingVisualizer from "./sorting-visualizer";
+
+export default function PlaygroundState() {
+ const [algorithm, setAlgorithm] = useQueryState(
+ "algorithm",
+ playgroundSearchParams.algorithm,
+ );
+ const [iterations, setIterations] = useState(0);
+ const [swaps, setSwaps] = useState(0);
+
+ return (
+
+ );
+}
diff --git a/src/components/providers.tsx b/src/components/providers.tsx
new file mode 100644
index 0000000..ad7c44e
--- /dev/null
+++ b/src/components/providers.tsx
@@ -0,0 +1,21 @@
+"use client";
+
+import { QueryClientProvider } from "@tanstack/react-query";
+import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
+import { NuqsAdapter } from "nuqs/adapters/next/app";
+import { getQueryClient } from "@/lib/get-query-client";
+
+export default function Providers({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ const queryClient = getQueryClient();
+
+ return (
+
+ {children}
+
+
+ );
+}
diff --git a/src/components/sorting-visualizer.tsx b/src/components/sorting-visualizer.tsx
index 1422f41..d35d3c1 100644
--- a/src/components/sorting-visualizer.tsx
+++ b/src/components/sorting-visualizer.tsx
@@ -3,893 +3,895 @@
import * as d3 from "d3";
import { Pause, Play, RefreshCw, RotateCcw } from "lucide-react";
import { useTranslations } from "next-intl";
+import { useQueryState } from "nuqs";
import { useCallback, useEffect, useRef, useState } from "react";
import { RippleButton } from "@/components/animate-ui/ripple-button";
import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
} from "@/components/ui/select";
import { Slider } from "@/components/ui/slider";
-import { cn } from "@/lib/utils";
+import { type Algorithm, cn, playgroundSearchParams } from "@/lib/utils";
import { SlidingNumber } from "./animate-ui/sliding-number";
// Define the types for our data and animation states
type BarData = {
- value: number;
- index: number;
- state: "default" | "comparing" | "sorted" | "current";
- originalIndex: number; // To track original position
+ value: number;
+ index: number;
+ state: "default" | "comparing" | "sorted" | "current";
+ originalIndex: number; // To track original position
};
+type NumberSetState = (val: ((prev: number) => number) | number) => void;
+
export default function SortingVisualizer({
- algorithm,
- setAlgorithm,
- setIterations,
- setSwaps,
- className,
+ algorithm,
+ setAlgorithm,
+ setIterations,
+ setSwaps,
+ className,
}: {
- algorithm: "bubble" | "selection" | "insertion" | "quicksort";
- setAlgorithm: (
- algorithm: "bubble" | "selection" | "insertion" | "quicksort",
- ) => void;
- setIterations: (val: ((prev: number) => number) | number) => void;
- setSwaps: (val: ((prev: number) => number) | number) => void;
- className?: string;
+ algorithm: Algorithm;
+ setAlgorithm: (algorithm: Algorithm) => void;
+ setIterations: NumberSetState;
+ setSwaps: NumberSetState;
+ className?: string;
}) {
- const t = useTranslations("visualizer");
-
- const [arrayLength, setArrayLength] = useState(20);
-
- const [data, setData] = useState([]);
- const [originalData, setOriginalData] = useState([]);
-
- const [isSorting, setIsSorting] = useState(false);
- const [isPaused, setIsPaused] = useState(false);
- const pauseRef = useRef(false);
- const cancelRef = useRef(false);
-
- const [speed, setSpeed] = useState(50);
- // A ref is needed because the the state in useState would be encapsulated in the sorting algorithm closures
- const speedRef = useRef(speed);
-
- const svgRef = useRef(null);
- const animationFrameId = useRef(null);
-
- // Generate random data
- const generateRandomData = useCallback((count: number) => {
- const newData: BarData[] = [];
- for (let i = 0; i < count; i++) {
- newData.push({
- value: Math.floor(Math.random() * 90) + 10,
- index: i,
- state: "default",
- originalIndex: i,
- });
- }
- setData([...newData]);
- setOriginalData(JSON.parse(JSON.stringify(newData)));
- }, []);
-
- // Initialize data on component mount
- useEffect(() => {
- generateRandomData(arrayLength);
- return () => {
- // Cleanup any animations on unmount
- if (animationFrameId.current) {
- cancelAnimationFrame(animationFrameId.current);
- }
- };
- }, [arrayLength, generateRandomData]);
-
- useEffect(() => {
- d3.select(svgRef.current).selectAll("*").remove();
- // biome-ignore lint/correctness/useExhaustiveDependencies: This is an explicit effect
- }, [arrayLength]);
-
- // Draw the visualization whenever data changes
- useEffect(() => {
- if (!svgRef.current || data.length === 0) {
- // Optionally clear SVG if data becomes empty
- if (svgRef.current) {
- d3.select(svgRef.current).selectAll("*").remove();
- }
- return;
- }
-
- const svg = d3.select(svgRef.current);
- const width = svgRef.current.clientWidth;
- const height = svgRef.current.clientHeight - 10; // Account for text above bars
-
- // Calculate bar dimensions based on current data length
- const barWidth = (width / data.length) * 0.8;
- const barPadding = (width / data.length) * 0.2;
-
- // --- D3 Update Pattern ---
-
- // Bind data to group elements (g) with class "bar"
- // Use originalIndex as the key function to track individual bars
- const bars = svg
- .selectAll(".bar") // Explicitly type selection
- .data(data, d => d.originalIndex.toString()); // Key must be a string
-
- // Exit selection: Elements that are no longer in the data
- bars
- .exit()
- .transition() // Start transition on exiting elements
- .duration(300) // Animation duration for exit
- .attr("opacity", 0) // Fade out
- .remove(); // Remove the element after the transition finishes
-
- // Enter selection: New elements that are in the data but not on screen
- const enterBars = bars
- .enter()
- .append("g") // Append a group for each bar
- .attr("class", "bar")
- .attr("opacity", 0); // Start invisible for the enter transition
-
- // Append rect and text to the entering groups
- enterBars
- .append("rect")
- .attr("width", barWidth) // Initial width
- .attr("rx", 2);
-
- enterBars
- .append("text")
- .attr("x", barWidth / 2) // Text horizontal position relative to group
- .attr("text-anchor", "middle")
- .attr("font-size", "10px")
- .attr("fill", "currentColor");
-
- // Update + Enter selection: Elements that are on screen (either new or existing)
- // This is where you define the final state and transitions
- const mergedBars = enterBars.merge(bars); // Combine enter and update selections
-
- // Transition for the group (position and opacity)
- mergedBars
- .transition()
- .duration(300) // Animation duration for position and appearance
- .ease(d3.easeCubicInOut) // Add easing for smoother motion
- // Use the index 'i' from the *current* data array for the position
- .attr(
- "transform",
- (_d, i) => `translate(${i * (barWidth + barPadding)}, 0)`,
- )
- .attr("opacity", 1); // Fade in new elements
-
- // Transition for the rectangle within the group (height, y-position, color)
- mergedBars
- .select("rect")
- .transition()
- .duration(300) // Match group duration
- .ease(d3.easeCubicInOut)
- .attr("y", d => height - (d.value * height) / 110) // Animate from old y to new y
- .attr("height", d => (d.value * height) / 110) // Animate from old height to new height
- .attr("fill", d => {
- // Animate color changes
- switch (d.state) {
- case "comparing":
- return "#FF5733"; // Orange for comparing
- case "sorted":
- return "#33FF57"; // Green for sorted
- case "current":
- return "#FFD700"; // Gold for current element
- default:
- return "#3B82F6"; // Blue for default
- }
- });
-
- // Transition for the text within the group (y-position and text content)
- mergedBars
- .select("text")
- .transition()
- .duration(300) // Match group duration
- .ease(d3.easeCubicInOut)
- .text(d => d.value) // Update text content (instantly)
- .attr("y", d => height - (d.value * height) / 110 - 5); // Animate text position
- }, [data]); // Rerun effect when data changes
-
- // Helper function to wait for a specified time
- const sleep = async (ms: number) => {
- return new Promise(resolve => {
- const startTime = Date.now();
-
- const checkPause = () => {
- if (cancelRef.current) {
- resolve();
- return;
- }
-
- if (pauseRef.current) {
- animationFrameId.current = requestAnimationFrame(checkPause);
- return;
- }
-
- const elapsedTime = Date.now() - startTime;
- if (elapsedTime >= ms) {
- resolve();
- } else {
- animationFrameId.current = requestAnimationFrame(checkPause);
- }
- };
-
- checkPause();
- });
- };
-
- // Helper function to get animation delay based on speed
- const getDelay = () => Math.max(50, 1000 - speedRef.current * 9);
-
- // Bubble sort implementation with animation
- const bubbleSort = async () => {
- if (isSorting) return;
-
- setIsSorting(true);
- pauseRef.current = false;
- cancelRef.current = false;
-
- // Create a working copy of the data
- let workingData = [...data];
- const n = workingData.length;
-
- // Reset all states
- workingData = workingData.map(item => ({
- ...item,
- state: "default",
- }));
-
- setData([...workingData]);
- await sleep(getDelay() / 2);
-
- // Main bubble sort loop
- for (let i = 0; i < n - 1; i++) {
- let swapped = false;
-
- for (let j = 0; j < n - i - 1; j++) {
- if (cancelRef.current) {
- setIsSorting(false);
- return;
- }
-
- // Mark elements being compared
- workingData[j]!.state = "comparing";
- workingData[j + 1]!.state = "comparing";
- setIterations(prev => prev + 1);
- setData([...workingData]);
- await sleep(getDelay());
-
- // Compare and swap if needed
- if (workingData[j]!.value > workingData[j + 1]!.value) {
- // Swap elements
- const temp = workingData[j]!;
- workingData[j] = workingData[j + 1]!;
- workingData[j + 1] = temp;
- swapped = true;
-
- // Update visualization
- setData([...workingData]);
- setSwaps(prev => prev + 1);
- await sleep(getDelay());
- }
-
- // Reset comparison state
- workingData[j]!.state = "default";
- workingData[j + 1]!.state = j === n - i - 2 ? "sorted" : "default";
- setData([...workingData]);
- }
-
- if (!swapped) {
- // If no swaps were made, the array is sorted
- break;
- }
- }
-
- // Mark all elements as sorted
- workingData = workingData.map(item => ({
- ...item,
- state: "sorted",
- }));
- setData([...workingData]);
-
- setIsSorting(false);
- };
-
- // Selection sort implementation with animation
- const selectionSort = async () => {
- if (isSorting) return;
-
- setIsSorting(true);
- pauseRef.current = false;
- cancelRef.current = false;
-
- // Create a working copy of the data
- let workingData = [...data];
- const n = workingData.length;
-
- // Reset all states
- workingData = workingData.map(item => ({
- ...item,
- state: "default",
- }));
-
- setData([...workingData]);
- await sleep(getDelay() / 2);
-
- // Main selection sort loop
- for (let i = 0; i < n - 1; i++) {
- if (cancelRef.current) {
- setIsSorting(false);
- return;
- }
-
- // Assume the minimum is the first unsorted element
- let minIdx = i;
- workingData[i]!.state = "comparing";
- setData([...workingData]);
- setIterations(prev => prev + 1);
- await sleep(getDelay());
-
- // Find the minimum element in the unsorted part
- for (let j = i + 1; j < n; j++) {
- if (cancelRef.current) {
- setIsSorting(false);
- return;
- }
-
- // Mark current element being compared
- workingData[j]!.state = "comparing";
- setData([...workingData]);
- setIterations(prev => prev + 1);
- await sleep(getDelay());
-
- if (workingData[j]!.value < workingData[minIdx]!.value) {
- // Reset previous minimum if it's not the initial position
- if (minIdx !== i) {
- workingData[minIdx]!.state = "default";
- }
-
- minIdx = j;
- workingData[j]!.state = "current"; // Mark as current minimum
- } else {
- // Reset if not the minimum
- workingData[j]!.state = "default";
- }
-
- setData([...workingData]);
- await sleep(getDelay() / 2);
- }
-
- // Swap the found minimum with the first unsorted element
- if (minIdx !== i) {
- // Reset states before swap
- workingData[i]!.state = "comparing";
- workingData[minIdx]!.state = "comparing";
- setData([...workingData]);
- await sleep(getDelay());
-
- // Perform swap
- const temp = workingData[i]!;
- workingData[i] = workingData[minIdx]!;
- workingData[minIdx] = temp;
-
- // Update visualization
- setData([...workingData]);
- setSwaps(prev => prev + 1);
- await sleep(getDelay());
- }
-
- // Mark current position as sorted
- workingData[i]!.state = "sorted";
-
- // Reset any remaining comparing states
- for (let j = i + 1; j < n; j++) {
- if (
- workingData[j]!.state === "comparing" ||
- workingData[j]!.state === "current"
- ) {
- workingData[j]!.state = "default";
- }
- }
-
- setData([...workingData]);
- await sleep(getDelay() / 2);
- }
-
- // Mark the last element as sorted
- workingData[n - 1]!.state = "sorted";
- setData([...workingData]);
-
- setIsSorting(false);
- };
-
- // Insertion sort implementation with animation
- const insertionSort = async () => {
- if (isSorting) return;
-
- setIsSorting(true);
- pauseRef.current = false;
- cancelRef.current = false;
-
- // Create a working copy of the data
- let workingData = [...data];
- const n = workingData.length;
-
- // Reset all states
- workingData = workingData.map(item => ({
- ...item,
- state: "default",
- }));
-
- // Mark the first element as sorted initially
- workingData[0]!.state = "sorted";
- setData([...workingData]);
- setIterations(prev => prev + 1);
- await sleep(getDelay());
-
- // Main insertion sort loop
- for (let i = 1; i < n; i++) {
- if (cancelRef.current) {
- setIsSorting(false);
- return;
- }
-
- // Store the current element to be inserted
- const key = workingData[i]!.value;
- const currentItem = { ...workingData[i]! };
- currentItem.state = "comparing";
-
- // Mark current element
- workingData[i]!.state = "comparing";
- setData([...workingData]);
- setIterations(prev => prev + 1);
- await sleep(getDelay());
-
- // Find the position to insert the current element
- let j = i - 1;
-
- // Compare with each element in the sorted part
- while (j >= 0 && workingData[j]!.value > key) {
- if (cancelRef.current) {
- setIsSorting(false);
- return;
- }
-
- const temp = workingData[j]!;
- workingData[j] = { ...currentItem, state: "current" };
-
- // Mark element being compared
- setData([...workingData]);
-
- // Shift element to the right
- workingData[j + 1] = { ...temp, state: "sorted" };
-
- j--;
-
- // Update visualization with the shift
- setData([...workingData]);
- setIterations(prev => prev + 1);
- setSwaps(prev => prev + 1);
- await sleep(getDelay());
- }
-
- // Insert the current element at the correct position
- workingData[j + 1] = { ...currentItem, state: "sorted" };
-
- // Update visualization with the insertion
- setData([...workingData]);
- await sleep(getDelay());
- }
-
- setIsSorting(false);
- };
-
- // Quicksort implementation with animation
- const quickSort = async () => {
- if (isSorting) return;
-
- setIsSorting(true);
- pauseRef.current = false;
- cancelRef.current = false;
-
- // Create a working copy of the data
- let workingData = [...data];
- const n = workingData.length;
-
- // Reset all states
- workingData = workingData.map(item => ({
- ...item,
- state: "default",
- }));
-
- setData([...workingData]);
- await sleep(getDelay() / 2);
-
- // Recursive quicksort function
- const quickSortRecursive = async (left: number, right: number) => {
- if (cancelRef.current) {
- return;
- }
-
- if (left < right) {
- // Partition the array
- await medianOfThree(left, right);
-
- const pivotIndex = await partition(left, right);
-
- // Mark pivot as sorted
- workingData[pivotIndex]!.state = "sorted";
- setData([...workingData]);
- await sleep(getDelay());
-
- // Recursively sort the sub-arrays
- await quickSortRecursive(left, pivotIndex - 1);
- await quickSortRecursive(pivotIndex + 1, right);
- } else if (left === right) {
- // Single element is already sorted
- workingData[left]!.state = "sorted";
- setData([...workingData]);
- await sleep(getDelay() / 2);
- }
- };
-
- // Partition function for quicksort
- const partition = async (left: number, right: number) => {
- const pivotValue = workingData[right]!.value;
- let i = left - 1;
-
- // Highlight the pivot
- workingData[right]!.state = "current";
- setData([...workingData]);
- await sleep(getDelay());
-
- for (let j = left; j < right; j++) {
- if (cancelRef.current) {
- return left;
- }
-
- // Mark current element being compared
- workingData[j]!.state = "comparing";
- setData([...workingData]);
- setIterations(prev => prev + 1);
- await sleep(getDelay());
-
- if (workingData[j]!.value < pivotValue) {
- i++;
-
- // Highlight elements to be swapped
- if (i !== j) {
- workingData[i]!.state = "comparing";
- setData([...workingData]);
- await sleep(getDelay());
-
- // Swap elements
- const temp = workingData[i]!;
- workingData[i] = workingData[j]!;
- workingData[j] = temp;
-
- setData([...workingData]);
- setSwaps(prev => prev + 1);
- await sleep(getDelay());
- }
- }
-
- // Reset current element state
- workingData[j]!.state = "default";
- setData([...workingData]);
- }
-
- // Swap pivot with element at i+1
- i++;
-
- // Highlight elements to be swapped
- workingData[i]!.state = "comparing";
- setData([...workingData]);
- await sleep(getDelay());
-
- // Perform the swap
- const temp = workingData[i]!;
- workingData[i] = workingData[right]!;
- workingData[right] = temp;
-
- // Update visualization
- setData([...workingData]);
- setSwaps(prev => prev + 1);
- await sleep(getDelay());
-
- // Reset states except for the pivot position
- for (let j = left; j <= right; j++) {
- if (j !== i) {
- workingData[j]!.state = "default";
- }
- }
-
- setData([...workingData]);
- await sleep(getDelay() / 2);
-
- return i;
- };
-
- const medianOfThree = async (left: number, right: number) => {
- const mid = Math.floor((left + right) / 2);
-
- // Sort the three elements
- workingData[left]!.state = "comparing";
- workingData[mid]!.state = "comparing";
- setData([...workingData]);
- setIterations(prev => prev + 1);
- await sleep(getDelay());
- if (workingData[left]!.value > workingData[mid]!.value) {
- const temp = workingData[left]!;
- workingData[left] = workingData[mid]!;
- workingData[mid] = temp;
- setSwaps(prev => prev + 1);
- }
- workingData[left]!.state = "default";
- workingData[mid]!.state = "default";
- setData([...workingData]);
- await sleep(getDelay());
-
- workingData[left]!.state = "comparing";
- workingData[right]!.state = "comparing";
- setData([...workingData]);
- setIterations(prev => prev + 1);
- await sleep(getDelay());
- if (workingData[left]!.value > workingData[right]!.value) {
- const temp = workingData[left]!;
- workingData[left] = workingData[right]!;
- workingData[right] = temp;
- setSwaps(prev => prev + 1);
- }
- workingData[left]!.state = "default";
- workingData[right]!.state = "default";
- setData([...workingData]);
- await sleep(getDelay());
-
- workingData[mid]!.state = "comparing";
- workingData[right]!.state = "comparing";
- setData([...workingData]);
- setIterations(prev => prev + 1);
- await sleep(getDelay());
- if (workingData[mid]!.value > workingData[right]!.value) {
- const temp = workingData[mid]!;
- workingData[mid] = workingData[right]!;
- workingData[right] = temp;
- setSwaps(prev => prev + 1);
- }
- workingData[mid]!.state = "default";
- workingData[right]!.state = "default";
- setData([...workingData]);
- await sleep(getDelay());
-
- workingData[mid]!.state = "comparing";
- workingData[right]!.state = "comparing";
- setData([...workingData]);
- setIterations(prev => prev + 1);
- await sleep(getDelay());
- const temp = workingData[mid]!;
- workingData[mid] = workingData[right]!;
- workingData[right] = temp;
- workingData[mid]!.state = "default";
- workingData[right]!.state = "default";
- setSwaps(prev => prev + 1);
- setData([...workingData]);
- await sleep(getDelay());
-
- return right;
- };
-
- await quickSortRecursive(0, n - 1);
-
- // Mark all elements as sorted when done
- workingData = workingData.map(item => ({
- ...item,
- state: "sorted",
- }));
- setData([...workingData]);
- setIsSorting(false);
- };
-
- // Reset to original unsorted state
- const resetToOriginal = () => {
- // Cancel any ongoing sorting
- cancelRef.current = true;
- pauseRef.current = false;
-
- if (animationFrameId.current) {
- cancelAnimationFrame(animationFrameId.current);
- animationFrameId.current = null;
- }
-
- setIsSorting(false);
- setIsPaused(false);
- setIterations(0);
- setSwaps(0);
-
- // Reset to original data with default states
- const resetData = originalData.map(item => ({
- ...item,
- state: "default" as const,
- }));
-
- setData([...resetData]);
- };
-
- // Generate new random data
- const generateNew = () => {
- // Cancel any ongoing sorting
- cancelRef.current = true;
- pauseRef.current = false;
-
- if (animationFrameId.current) {
- cancelAnimationFrame(animationFrameId.current);
- animationFrameId.current = null;
- }
-
- setIsSorting(false);
- setIsPaused(false);
- setIterations(0);
- setSwaps(0);
-
- // Generate new data
- generateRandomData(arrayLength);
- };
-
- // Toggle pause/resume
- const togglePause = () => {
- const newPausedState = !isPaused;
- setIsPaused(newPausedState);
- pauseRef.current = newPausedState;
- };
-
- // Start sorting based on selected algorithm
- const startSorting = () => {
- if (isSorting) return;
-
- switch (algorithm) {
- case "bubble":
- bubbleSort();
- break;
- case "selection":
- selectionSort();
- break;
- case "insertion":
- insertionSort();
- break;
- case "quicksort":
- quickSort();
- break;
- default:
- break;
- }
- };
-
- return (
-
-
-
- {!isSorting ? (
-
-
- {t("start")}
-
- ) : (
-
- {isPaused ? (
-
- ) : (
-
- )}
- {isPaused ? "Resume" : "Pause"}
-
- )}
-
-
-
- {t("reset")}
-
-
-
-
- {t("new-data")}
-
-
-
-
-
- {t("speed")}:
- {
- setSpeed(value[0]!);
- speedRef.current = value[0]!;
- }}
- min={1}
- max={100}
- step={1}
- className="w-32"
- disabled={!isPaused && isSorting}
- />
-
-
-
-
- setAlgorithm(
- val as "bubble" | "selection" | "insertion" | "quicksort",
- )
- }
- disabled={isSorting}
- >
-
-
-
-
- Bubble Sort
- Selection Sort
- Insertion Sort
- Quick Sort
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {algorithm === "quicksort" ? t("pivot") : t("current")}
-
-
-
-
-
- {t("length")}:
- setArrayLength(value[0]!)}
- min={10}
- max={100}
- step={1}
- className="w-32"
- disabled={isSorting}
- />
-
-
-
-
- );
+ const t = useTranslations("visualizer");
+
+ const [arrayLength, setArrayLength] = useQueryState(
+ "length",
+ playgroundSearchParams.length,
+ );
+
+ const [data, setData] = useState([]);
+ const [originalData, setOriginalData] = useState([]);
+
+ const [isSorting, setIsSorting] = useState(false);
+ const [isPaused, setIsPaused] = useState(false);
+ const pauseRef = useRef(false);
+ const cancelRef = useRef(false);
+
+ const [speed, setSpeed] = useQueryState(
+ "speed",
+ playgroundSearchParams.speed,
+ );
+ // A ref is needed because the the state in useState would be encapsulated in the sorting algorithm closures
+ const speedRef = useRef(speed);
+
+ const svgRef = useRef(null);
+ const animationFrameId = useRef(null);
+
+ // Generate random data
+ const generateRandomData = useCallback((count: number) => {
+ const newData: BarData[] = [];
+ for (let i = 0; i < count; i++) {
+ newData.push({
+ value: Math.floor(Math.random() * 90) + 10,
+ index: i,
+ state: "default",
+ originalIndex: i,
+ });
+ }
+ setData([...newData]);
+ setOriginalData(JSON.parse(JSON.stringify(newData)));
+ }, []);
+
+ // Initialize data on component mount
+ useEffect(() => {
+ generateRandomData(arrayLength);
+ return () => {
+ // Cleanup any animations on unmount
+ if (animationFrameId.current) {
+ cancelAnimationFrame(animationFrameId.current);
+ }
+ };
+ }, [arrayLength, generateRandomData]);
+
+ useEffect(() => {
+ d3.select(svgRef.current).selectAll("*").remove();
+ }, [arrayLength]);
+
+ // Draw the visualization whenever data changes
+ useEffect(() => {
+ if (!svgRef.current || data.length === 0) {
+ // Optionally clear SVG if data becomes empty
+ if (svgRef.current) {
+ d3.select(svgRef.current).selectAll("*").remove();
+ }
+ return;
+ }
+
+ const svg = d3.select(svgRef.current);
+ const width = svgRef.current.clientWidth;
+ const height = svgRef.current.clientHeight - 10; // Account for text above bars
+
+ // Calculate bar dimensions based on current data length
+ const barWidth = (width / data.length) * 0.8;
+ const barPadding = (width / data.length) * 0.2;
+
+ // --- D3 Update Pattern ---
+
+ // Bind data to group elements (g) with class "bar"
+ // Use originalIndex as the key function to track individual bars
+ const bars = svg
+ .selectAll(".bar") // Explicitly type selection
+ .data(data, (d) => d.originalIndex.toString()); // Key must be a string
+
+ // Exit selection: Elements that are no longer in the data
+ bars
+ .exit()
+ .transition() // Start transition on exiting elements
+ .duration(300) // Animation duration for exit
+ .attr("opacity", 0) // Fade out
+ .remove(); // Remove the element after the transition finishes
+
+ // Enter selection: New elements that are in the data but not on screen
+ const enterBars = bars
+ .enter()
+ .append("g") // Append a group for each bar
+ .attr("class", "bar")
+ .attr("opacity", 0); // Start invisible for the enter transition
+
+ // Append rect and text to the entering groups
+ enterBars
+ .append("rect")
+ .attr("width", barWidth) // Initial width
+ .attr("rx", 2);
+
+ enterBars
+ .append("text")
+ .attr("x", barWidth / 2) // Text horizontal position relative to group
+ .attr("text-anchor", "middle")
+ .attr("font-size", "10px")
+ .attr("fill", "currentColor");
+
+ // Update + Enter selection: Elements that are on screen (either new or existing)
+ // This is where you define the final state and transitions
+ const mergedBars = enterBars.merge(bars); // Combine enter and update selections
+
+ // Transition for the group (position and opacity)
+ mergedBars
+ .transition()
+ .duration(300) // Animation duration for position and appearance
+ .ease(d3.easeCubicInOut) // Add easing for smoother motion
+ // Use the index 'i' from the *current* data array for the position
+ .attr(
+ "transform",
+ (_d, i) => `translate(${i * (barWidth + barPadding)}, 0)`,
+ )
+ .attr("opacity", 1); // Fade in new elements
+
+ // Transition for the rectangle within the group (height, y-position, color)
+ mergedBars
+ .select("rect")
+ .transition()
+ .duration(300) // Match group duration
+ .ease(d3.easeCubicInOut)
+ .attr("y", (d) => height - (d.value * height) / 110) // Animate from old y to new y
+ .attr("height", (d) => (d.value * height) / 110) // Animate from old height to new height
+ .attr("fill", (d) => {
+ // Animate color changes
+ switch (d.state) {
+ case "comparing":
+ return "#FF5733"; // Orange for comparing
+ case "sorted":
+ return "#33FF57"; // Green for sorted
+ case "current":
+ return "#FFD700"; // Gold for current element
+ default:
+ return "#3B82F6"; // Blue for default
+ }
+ });
+
+ // Transition for the text within the group (y-position and text content)
+ mergedBars
+ .select("text")
+ .transition()
+ .duration(300) // Match group duration
+ .ease(d3.easeCubicInOut)
+ .text((d) => d.value) // Update text content (instantly)
+ .attr("y", (d) => height - (d.value * height) / 110 - 5); // Animate text position
+ }, [data]); // Rerun effect when data changes
+
+ // Helper function to wait for a specified time
+ const sleep = async (ms: number) => {
+ return new Promise((resolve) => {
+ const startTime = Date.now();
+
+ const checkPause = () => {
+ if (cancelRef.current) {
+ resolve();
+ return;
+ }
+
+ if (pauseRef.current) {
+ animationFrameId.current = requestAnimationFrame(checkPause);
+ return;
+ }
+
+ const elapsedTime = Date.now() - startTime;
+ if (elapsedTime >= ms) {
+ resolve();
+ } else {
+ animationFrameId.current = requestAnimationFrame(checkPause);
+ }
+ };
+
+ checkPause();
+ });
+ };
+
+ // Helper function to get animation delay based on speed
+ const getDelay = () => Math.max(50, 1000 - speedRef.current * 9);
+
+ // Bubble sort implementation with animation
+ const bubbleSort = async () => {
+ if (isSorting) return;
+
+ setIsSorting(true);
+ pauseRef.current = false;
+ cancelRef.current = false;
+
+ // Create a working copy of the data
+ let workingData = [...data];
+ const n = workingData.length;
+
+ // Reset all states
+ workingData = workingData.map((item) => ({
+ ...item,
+ state: "default",
+ }));
+
+ setData([...workingData]);
+ await sleep(getDelay() / 2);
+
+ // Main bubble sort loop
+ for (let i = 0; i < n - 1; i++) {
+ let swapped = false;
+
+ for (let j = 0; j < n - i - 1; j++) {
+ if (cancelRef.current) {
+ setIsSorting(false);
+ return;
+ }
+
+ // Mark elements being compared
+ workingData[j]!.state = "comparing";
+ workingData[j + 1]!.state = "comparing";
+ setIterations((prev) => prev + 1);
+ setData([...workingData]);
+ await sleep(getDelay());
+
+ // Compare and swap if needed
+ if (workingData[j]!.value > workingData[j + 1]!.value) {
+ // Swap elements
+ const temp = workingData[j]!;
+ workingData[j] = workingData[j + 1]!;
+ workingData[j + 1] = temp;
+ swapped = true;
+
+ // Update visualization
+ setData([...workingData]);
+ setSwaps((prev) => prev + 1);
+ await sleep(getDelay());
+ }
+
+ // Reset comparison state
+ workingData[j]!.state = "default";
+ workingData[j + 1]!.state = j === n - i - 2 ? "sorted" : "default";
+ setData([...workingData]);
+ }
+
+ if (!swapped) {
+ // If no swaps were made, the array is sorted
+ break;
+ }
+ }
+
+ // Mark all elements as sorted
+ workingData = workingData.map((item) => ({
+ ...item,
+ state: "sorted",
+ }));
+ setData([...workingData]);
+
+ setIsSorting(false);
+ };
+
+ // Selection sort implementation with animation
+ const selectionSort = async () => {
+ if (isSorting) return;
+
+ setIsSorting(true);
+ pauseRef.current = false;
+ cancelRef.current = false;
+
+ // Create a working copy of the data
+ let workingData = [...data];
+ const n = workingData.length;
+
+ // Reset all states
+ workingData = workingData.map((item) => ({
+ ...item,
+ state: "default",
+ }));
+
+ setData([...workingData]);
+ await sleep(getDelay() / 2);
+
+ // Main selection sort loop
+ for (let i = 0; i < n - 1; i++) {
+ if (cancelRef.current) {
+ setIsSorting(false);
+ return;
+ }
+
+ // Assume the minimum is the first unsorted element
+ let minIdx = i;
+ workingData[i]!.state = "comparing";
+ setData([...workingData]);
+ setIterations((prev) => prev + 1);
+ await sleep(getDelay());
+
+ // Find the minimum element in the unsorted part
+ for (let j = i + 1; j < n; j++) {
+ if (cancelRef.current) {
+ setIsSorting(false);
+ return;
+ }
+
+ // Mark current element being compared
+ workingData[j]!.state = "comparing";
+ setData([...workingData]);
+ setIterations((prev) => prev + 1);
+ await sleep(getDelay());
+
+ if (workingData[j]!.value < workingData[minIdx]!.value) {
+ // Reset previous minimum if it's not the initial position
+ if (minIdx !== i) {
+ workingData[minIdx]!.state = "default";
+ }
+
+ minIdx = j;
+ workingData[j]!.state = "current"; // Mark as current minimum
+ } else {
+ // Reset if not the minimum
+ workingData[j]!.state = "default";
+ }
+
+ setData([...workingData]);
+ await sleep(getDelay() / 2);
+ }
+
+ // Swap the found minimum with the first unsorted element
+ if (minIdx !== i) {
+ // Reset states before swap
+ workingData[i]!.state = "comparing";
+ workingData[minIdx]!.state = "comparing";
+ setData([...workingData]);
+ await sleep(getDelay());
+
+ // Perform swap
+ const temp = workingData[i]!;
+ workingData[i] = workingData[minIdx]!;
+ workingData[minIdx] = temp;
+
+ // Update visualization
+ setData([...workingData]);
+ setSwaps((prev) => prev + 1);
+ await sleep(getDelay());
+ }
+
+ // Mark current position as sorted
+ workingData[i]!.state = "sorted";
+
+ // Reset any remaining comparing states
+ for (let j = i + 1; j < n; j++) {
+ if (
+ workingData[j]?.state === "comparing" ||
+ workingData[j]?.state === "current"
+ ) {
+ workingData[j]!.state = "default";
+ }
+ }
+
+ setData([...workingData]);
+ await sleep(getDelay() / 2);
+ }
+
+ // Mark the last element as sorted
+ workingData[n - 1]!.state = "sorted";
+ setData([...workingData]);
+
+ setIsSorting(false);
+ };
+
+ // Insertion sort implementation with animation
+ const insertionSort = async () => {
+ if (isSorting) return;
+
+ setIsSorting(true);
+ pauseRef.current = false;
+ cancelRef.current = false;
+
+ // Create a working copy of the data
+ let workingData = [...data];
+ const n = workingData.length;
+
+ // Reset all states
+ workingData = workingData.map((item) => ({
+ ...item,
+ state: "default",
+ }));
+
+ // Mark the first element as sorted initially
+ workingData[0]!.state = "sorted";
+ setData([...workingData]);
+ setIterations((prev) => prev + 1);
+ await sleep(getDelay());
+
+ // Main insertion sort loop
+ for (let i = 1; i < n; i++) {
+ if (cancelRef.current) {
+ setIsSorting(false);
+ return;
+ }
+
+ // Store the current element to be inserted
+ const key = workingData[i]!.value;
+ const currentItem = { ...workingData[i]! };
+ currentItem.state = "comparing";
+
+ // Mark current element
+ workingData[i]!.state = "comparing";
+ setData([...workingData]);
+ setIterations((prev) => prev + 1);
+ await sleep(getDelay());
+
+ // Find the position to insert the current element
+ let j = i - 1;
+
+ // Compare with each element in the sorted part
+ while (j >= 0 && workingData[j]!.value > key) {
+ if (cancelRef.current) {
+ setIsSorting(false);
+ return;
+ }
+
+ const temp = workingData[j]!;
+ workingData[j] = { ...currentItem, state: "current" };
+
+ // Mark element being compared
+ setData([...workingData]);
+
+ // Shift element to the right
+ workingData[j + 1] = { ...temp, state: "sorted" };
+
+ j--;
+
+ // Update visualization with the shift
+ setData([...workingData]);
+ setIterations((prev) => prev + 1);
+ setSwaps((prev) => prev + 1);
+ await sleep(getDelay());
+ }
+
+ // Insert the current element at the correct position
+ workingData[j + 1] = { ...currentItem, state: "sorted" };
+
+ // Update visualization with the insertion
+ setData([...workingData]);
+ await sleep(getDelay());
+ }
+
+ setIsSorting(false);
+ };
+
+ // Quicksort implementation with animation
+ const quickSort = async () => {
+ if (isSorting) return;
+
+ setIsSorting(true);
+ pauseRef.current = false;
+ cancelRef.current = false;
+
+ // Create a working copy of the data
+ let workingData = [...data];
+ const n = workingData.length;
+
+ // Reset all states
+ workingData = workingData.map((item) => ({
+ ...item,
+ state: "default",
+ }));
+
+ setData([...workingData]);
+ await sleep(getDelay() / 2);
+
+ // Recursive quicksort function
+ const quickSortRecursive = async (left: number, right: number) => {
+ if (cancelRef.current) {
+ return;
+ }
+
+ if (left < right) {
+ // Partition the array
+ await medianOfThree(left, right);
+
+ const pivotIndex = await partition(left, right);
+
+ // Mark pivot as sorted
+ workingData[pivotIndex]!.state = "sorted";
+ setData([...workingData]);
+ await sleep(getDelay());
+
+ // Recursively sort the sub-arrays
+ await quickSortRecursive(left, pivotIndex - 1);
+ await quickSortRecursive(pivotIndex + 1, right);
+ } else if (left === right) {
+ // Single element is already sorted
+ workingData[left]!.state = "sorted";
+ setData([...workingData]);
+ await sleep(getDelay() / 2);
+ }
+ };
+
+ // Partition function for quicksort
+ const partition = async (left: number, right: number) => {
+ const pivotValue = workingData[right]!.value;
+ let i = left - 1;
+
+ // Highlight the pivot
+ workingData[right]!.state = "current";
+ setData([...workingData]);
+ await sleep(getDelay());
+
+ for (let j = left; j < right; j++) {
+ if (cancelRef.current) {
+ return left;
+ }
+
+ // Mark current element being compared
+ workingData[j]!.state = "comparing";
+ setData([...workingData]);
+ setIterations((prev) => prev + 1);
+ await sleep(getDelay());
+
+ if (workingData[j]!.value < pivotValue) {
+ i++;
+
+ // Highlight elements to be swapped
+ if (i !== j) {
+ workingData[i]!.state = "comparing";
+ setData([...workingData]);
+ await sleep(getDelay());
+
+ // Swap elements
+ const temp = workingData[i]!;
+ workingData[i] = workingData[j]!;
+ workingData[j] = temp;
+
+ setData([...workingData]);
+ setSwaps((prev) => prev + 1);
+ await sleep(getDelay());
+ }
+ }
+
+ // Reset current element state
+ workingData[j]!.state = "default";
+ setData([...workingData]);
+ }
+
+ // Swap pivot with element at i+1
+ i++;
+
+ // Highlight elements to be swapped
+ workingData[i]!.state = "comparing";
+ setData([...workingData]);
+ await sleep(getDelay());
+
+ // Perform the swap
+ const temp = workingData[i]!;
+ workingData[i] = workingData[right]!;
+ workingData[right] = temp;
+
+ // Update visualization
+ setData([...workingData]);
+ setSwaps((prev) => prev + 1);
+ await sleep(getDelay());
+
+ // Reset states except for the pivot position
+ for (let j = left; j <= right; j++) {
+ if (j !== i) {
+ workingData[j]!.state = "default";
+ }
+ }
+
+ setData([...workingData]);
+ await sleep(getDelay() / 2);
+
+ return i;
+ };
+
+ const medianOfThree = async (left: number, right: number) => {
+ const mid = Math.floor((left + right) / 2);
+
+ // Sort the three elements
+ workingData[left]!.state = "comparing";
+ workingData[mid]!.state = "comparing";
+ setData([...workingData]);
+ setIterations((prev) => prev + 1);
+ await sleep(getDelay());
+ if (workingData[left]!.value > workingData[mid]!.value) {
+ const temp = workingData[left]!;
+ workingData[left] = workingData[mid]!;
+ workingData[mid] = temp;
+ setSwaps((prev) => prev + 1);
+ }
+ workingData[left]!.state = "default";
+ workingData[mid]!.state = "default";
+ setData([...workingData]);
+ await sleep(getDelay());
+
+ workingData[left]!.state = "comparing";
+ workingData[right]!.state = "comparing";
+ setData([...workingData]);
+ setIterations((prev) => prev + 1);
+ await sleep(getDelay());
+ if (workingData[left]!.value > workingData[right]!.value) {
+ const temp = workingData[left]!;
+ workingData[left] = workingData[right]!;
+ workingData[right] = temp;
+ setSwaps((prev) => prev + 1);
+ }
+ workingData[left]!.state = "default";
+ workingData[right]!.state = "default";
+ setData([...workingData]);
+ await sleep(getDelay());
+
+ workingData[mid]!.state = "comparing";
+ workingData[right]!.state = "comparing";
+ setData([...workingData]);
+ setIterations((prev) => prev + 1);
+ await sleep(getDelay());
+ if (workingData[mid]!.value > workingData[right]!.value) {
+ const temp = workingData[mid]!;
+ workingData[mid] = workingData[right]!;
+ workingData[right] = temp;
+ setSwaps((prev) => prev + 1);
+ }
+ workingData[mid]!.state = "default";
+ workingData[right]!.state = "default";
+ setData([...workingData]);
+ await sleep(getDelay());
+
+ workingData[mid]!.state = "comparing";
+ workingData[right]!.state = "comparing";
+ setData([...workingData]);
+ setIterations((prev) => prev + 1);
+ await sleep(getDelay());
+ const temp = workingData[mid]!;
+ workingData[mid] = workingData[right]!;
+ workingData[right] = temp;
+ workingData[mid]!.state = "default";
+ workingData[right]!.state = "default";
+ setSwaps((prev) => prev + 1);
+ setData([...workingData]);
+ await sleep(getDelay());
+
+ return right;
+ };
+
+ await quickSortRecursive(0, n - 1);
+
+ // Mark all elements as sorted when done
+ workingData = workingData.map((item) => ({
+ ...item,
+ state: "sorted",
+ }));
+ setData([...workingData]);
+ setIsSorting(false);
+ };
+
+ // Reset to original unsorted state
+ const resetToOriginal = () => {
+ // Cancel any ongoing sorting
+ cancelRef.current = true;
+ pauseRef.current = false;
+
+ if (animationFrameId.current) {
+ cancelAnimationFrame(animationFrameId.current);
+ animationFrameId.current = null;
+ }
+
+ setIsSorting(false);
+ setIsPaused(false);
+ setIterations(0);
+ setSwaps(0);
+
+ // Reset to original data with default states
+ const resetData = originalData.map((item) => ({
+ ...item,
+ state: "default" as const,
+ }));
+
+ setData([...resetData]);
+ };
+
+ // Generate new random data
+ const generateNew = () => {
+ // Cancel any ongoing sorting
+ cancelRef.current = true;
+ pauseRef.current = false;
+
+ if (animationFrameId.current) {
+ cancelAnimationFrame(animationFrameId.current);
+ animationFrameId.current = null;
+ }
+
+ setIsSorting(false);
+ setIsPaused(false);
+ setIterations(0);
+ setSwaps(0);
+
+ // Generate new data
+ generateRandomData(arrayLength);
+ };
+
+ // Toggle pause/resume
+ const togglePause = () => {
+ const newPausedState = !isPaused;
+ setIsPaused(newPausedState);
+ pauseRef.current = newPausedState;
+ };
+
+ // Start sorting based on selected algorithm
+ const startSorting = () => {
+ if (isSorting) return;
+
+ switch (algorithm) {
+ case "bubble":
+ bubbleSort();
+ break;
+ case "selection":
+ selectionSort();
+ break;
+ case "insertion":
+ insertionSort();
+ break;
+ case "quicksort":
+ quickSort();
+ break;
+ default:
+ break;
+ }
+ };
+
+ return (
+
+
+
+ {!isSorting ? (
+
+
+ {t("start")}
+
+ ) : (
+
+ {isPaused ? (
+
+ ) : (
+
+ )}
+ {isPaused ? "Resume" : "Pause"}
+
+ )}
+
+
+
+ {t("reset")}
+
+
+
+
+ {t("new-data")}
+
+
+
+
+
+ {t("speed")}:
+ {
+ setSpeed(value[0]!);
+ speedRef.current = value[0]!;
+ }}
+ min={1}
+ max={100}
+ step={1}
+ className="w-32"
+ disabled={!isPaused && isSorting}
+ />
+
+
+
+
setAlgorithm(val as Algorithm)}
+ disabled={isSorting}
+ >
+
+
+
+
+ Bubble Sort
+ Selection Sort
+ Insertion Sort
+ Quick Sort
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {algorithm === "quicksort" ? t("pivot") : t("current")}
+
+
+
+
+
+ {t("length")}:
+ setArrayLength(value[0]!)}
+ min={10}
+ max={100}
+ step={1}
+ className="w-32"
+ disabled={isSorting}
+ />
+
+
+
+
+ );
}
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
index 9a611f7..ad2e04f 100644
--- a/src/components/ui/button.tsx
+++ b/src/components/ui/button.tsx
@@ -1,59 +1,59 @@
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
-import * as React from "react";
+import type * as React from "react";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
- {
- variants: {
- variant: {
- default:
- "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
- destructive:
- "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
- outline:
- "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
- secondary:
- "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
- ghost:
- "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
- link: "text-primary underline-offset-4 hover:underline",
- },
- size: {
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
- sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
- icon: "size-9",
- },
- },
- defaultVariants: {
- variant: "default",
- size: "default",
- },
- },
+ "inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
+ {
+ variants: {
+ variant: {
+ default:
+ "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
+ outline:
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
+ secondary:
+ "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
+ ghost:
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
+ sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
+ icon: "size-9",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ },
);
function Button({
- className,
- variant,
- size,
- asChild = false,
- ...props
+ className,
+ variant,
+ size,
+ asChild = false,
+ ...props
}: React.ComponentProps<"button"> &
- VariantProps & {
- asChild?: boolean;
- }) {
- const Comp = asChild ? Slot : "button";
+ VariantProps & {
+ asChild?: boolean;
+ }) {
+ const Comp = asChild ? Slot : "button";
- return (
-
- );
+ return (
+
+ );
}
export { Button, buttonVariants };
diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx
index 966d538..d60c9d4 100644
--- a/src/components/ui/card.tsx
+++ b/src/components/ui/card.tsx
@@ -1,92 +1,92 @@
-import * as React from "react";
+import type * as React from "react";
import { cn } from "@/lib/utils";
function Card({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
+ return (
+
+ );
}
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
+ return (
+
+ );
}
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
+ return (
+
+ );
}
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
+ return (
+
+ );
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
+ return (
+
+ );
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
+ return (
+
+ );
}
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
+ return (
+
+ );
}
export {
- Card,
- CardHeader,
- CardFooter,
- CardTitle,
- CardAction,
- CardDescription,
- CardContent,
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardAction,
+ CardDescription,
+ CardContent,
};
diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx
index 523ffab..97a81c6 100644
--- a/src/components/ui/select.tsx
+++ b/src/components/ui/select.tsx
@@ -2,184 +2,184 @@
import * as SelectPrimitive from "@radix-ui/react-select";
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
-import * as React from "react";
+import type * as React from "react";
import { cn } from "@/lib/utils";
function Select({
- ...props
+ ...props
}: React.ComponentProps) {
- return ;
+ return ;
}
function SelectGroup({
- ...props
+ ...props
}: React.ComponentProps) {
- return ;
+ return ;
}
function SelectValue({
- ...props
+ ...props
}: React.ComponentProps) {
- return ;
+ return ;
}
function SelectTrigger({
- className,
- size = "default",
- children,
- ...props
+ className,
+ size = "default",
+ children,
+ ...props
}: React.ComponentProps & {
- size?: "sm" | "default";
+ size?: "sm" | "default";
}) {
- return (
-
- {children}
-
-
-
-
- );
+ return (
+
+ {children}
+
+
+
+
+ );
}
function SelectContent({
- className,
- children,
- position = "popper",
- ...props
+ className,
+ children,
+ position = "popper",
+ ...props
}: React.ComponentProps) {
- return (
-
-
-
-
- {children}
-
-
-
-
- );
+ return (
+
+
+
+
+ {children}
+
+
+
+
+ );
}
function SelectLabel({
- className,
- ...props
+ className,
+ ...props
}: React.ComponentProps) {
- return (
-
- );
+ return (
+
+ );
}
function SelectItem({
- className,
- children,
- ...props
+ className,
+ children,
+ ...props
}: React.ComponentProps) {
- return (
-
-
-
-
-
-
- {children}
-
- );
+ return (
+
+
+
+
+
+
+ {children}
+
+ );
}
function SelectSeparator({
- className,
- ...props
+ className,
+ ...props
}: React.ComponentProps) {
- return (
-
- );
+ return (
+
+ );
}
function SelectScrollUpButton({
- className,
- ...props
+ className,
+ ...props
}: React.ComponentProps) {
- return (
-
-
-
- );
+ return (
+
+
+
+ );
}
function SelectScrollDownButton({
- className,
- ...props
+ className,
+ ...props
}: React.ComponentProps) {
- return (
-
-
-
- );
+ return (
+
+
+
+ );
}
export {
- Select,
- SelectContent,
- SelectGroup,
- SelectItem,
- SelectLabel,
- SelectScrollDownButton,
- SelectScrollUpButton,
- SelectSeparator,
- SelectTrigger,
- SelectValue,
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectLabel,
+ SelectScrollDownButton,
+ SelectScrollUpButton,
+ SelectSeparator,
+ SelectTrigger,
+ SelectValue,
};
diff --git a/src/components/ui/slider.tsx b/src/components/ui/slider.tsx
index 2adaa9c..eccc534 100644
--- a/src/components/ui/slider.tsx
+++ b/src/components/ui/slider.tsx
@@ -6,58 +6,58 @@ import * as React from "react";
import { cn } from "@/lib/utils";
function Slider({
- className,
- defaultValue,
- value,
- min = 0,
- max = 100,
- ...props
+ className,
+ defaultValue,
+ value,
+ min = 0,
+ max = 100,
+ ...props
}: React.ComponentProps) {
- const _values = React.useMemo(
- () =>
- Array.isArray(value)
- ? value
- : Array.isArray(defaultValue)
- ? defaultValue
- : [min, max],
- [value, defaultValue, min, max],
- );
+ const _values = React.useMemo(
+ () =>
+ Array.isArray(value)
+ ? value
+ : Array.isArray(defaultValue)
+ ? defaultValue
+ : [min, max],
+ [value, defaultValue, min, max],
+ );
- return (
-
-
-
-
- {Array.from({ length: _values.length }, (_, index) => (
-
- ))}
-
- );
+ return (
+
+
+
+
+ {Array.from({ length: _values.length }, (_, index) => (
+
+ ))}
+
+ );
}
export { Slider };
diff --git a/src/env.ts b/src/env.ts
index 400878a..5a7dbd0 100644
--- a/src/env.ts
+++ b/src/env.ts
@@ -2,47 +2,47 @@ import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
export const env = createEnv({
- /**
- * Specify your server-side environment variables schema here. This way you can ensure the app
- * isn't built with invalid env vars.
- */
- server: {
- NODE_ENV: z
- .enum(["development", "test", "production"])
- .default("development"),
- VERCEL_ENV: z
- .enum(["production", "preview", "development"])
- .default("development"),
- },
+ /**
+ * Specify your server-side environment variables schema here. This way you can ensure the app
+ * isn't built with invalid env vars.
+ */
+ server: {
+ NODE_ENV: z
+ .enum(["development", "test", "production"])
+ .default("development"),
+ VERCEL_ENV: z
+ .enum(["production", "preview", "development"])
+ .default("development"),
+ },
- /**
- * Specify your client-side environment variables schema here. This way you can ensure the app
- * isn't built with invalid env vars. To expose them to the client, prefix them with
- * `NEXT_PUBLIC_`.
- */
- client: {
- NEXT_PUBLIC_POSTHOG_HOST: z.string().url(),
- NEXT_PUBLIC_POSTHOG_KEY: z.string(),
- },
+ /**
+ * Specify your client-side environment variables schema here. This way you can ensure the app
+ * isn't built with invalid env vars. To expose them to the client, prefix them with
+ * `NEXT_PUBLIC_`.
+ */
+ client: {
+ NEXT_PUBLIC_POSTHOG_HOST: z.string().url(),
+ NEXT_PUBLIC_POSTHOG_KEY: z.string(),
+ },
- /**
- * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g.
- * middlewares) or client-side so we need to destruct manually.
- */
- runtimeEnv: {
- NODE_ENV: process.env.NODE_ENV,
- VERCEL_ENV: process.env.VERCEL_ENV,
- NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST,
- NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
- },
- /**
- * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation.
- * This is especially useful for Docker builds.
- */
- skipValidation: !!process.env.SKIP_ENV_VALIDATION,
- /**
- * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and
- * `SOME_VAR=''` will throw an error.
- */
- emptyStringAsUndefined: true,
+ /**
+ * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g.
+ * middlewares) or client-side so we need to destruct manually.
+ */
+ runtimeEnv: {
+ NODE_ENV: process.env.NODE_ENV,
+ VERCEL_ENV: process.env.VERCEL_ENV,
+ NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST,
+ NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
+ },
+ /**
+ * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation.
+ * This is especially useful for Docker builds.
+ */
+ skipValidation: !!process.env.SKIP_ENV_VALIDATION,
+ /**
+ * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and
+ * `SOME_VAR=''` will throw an error.
+ */
+ emptyStringAsUndefined: true,
});
diff --git a/src/hooks.ts b/src/hooks.ts
index 199b6dd..5791ccd 100644
--- a/src/hooks.ts
+++ b/src/hooks.ts
@@ -1,18 +1,18 @@
import { useEffect, useState } from "react";
export function useIsMobile() {
- const [isMobile, setIsMobile] = useState(false);
+ const [isMobile, setIsMobile] = useState(false);
- useEffect(() => {
- const handleResize = () => {
- setIsMobile(window.innerWidth < 768);
- };
+ useEffect(() => {
+ const handleResize = () => {
+ setIsMobile(window.innerWidth < 768);
+ };
- handleResize(); // Check on mount
- window.addEventListener("resize", handleResize);
+ handleResize(); // Check on mount
+ window.addEventListener("resize", handleResize);
- return () => window.removeEventListener("resize", handleResize);
- }, []);
+ return () => window.removeEventListener("resize", handleResize);
+ }, []);
- return isMobile;
+ return isMobile;
}
diff --git a/src/i18n/navigation.ts b/src/i18n/navigation.ts
index 2c786c5..f7578b6 100644
--- a/src/i18n/navigation.ts
+++ b/src/i18n/navigation.ts
@@ -2,4 +2,4 @@ import { createNavigation } from "next-intl/navigation";
import { routing } from "./routing";
export const { Link, redirect, usePathname, useRouter, getPathname } =
- createNavigation(routing);
+ createNavigation(routing);
diff --git a/src/i18n/request.ts b/src/i18n/request.ts
index 94482a0..80fc1da 100644
--- a/src/i18n/request.ts
+++ b/src/i18n/request.ts
@@ -3,14 +3,14 @@ import { getRequestConfig } from "next-intl/server";
import { routing } from "./routing";
export default getRequestConfig(async ({ requestLocale }) => {
- // Typically corresponds to the `[locale]` segment
- const requested = await requestLocale;
- const locale = hasLocale(routing.locales, requested)
- ? requested
- : routing.defaultLocale;
+ // Typically corresponds to the `[locale]` segment
+ const requested = await requestLocale;
+ const locale = hasLocale(routing.locales, requested)
+ ? requested
+ : routing.defaultLocale;
- return {
- locale,
- messages: (await import(`../../messages/${locale}.json`)).default,
- };
+ return {
+ locale,
+ messages: (await import(`../../messages/${locale}.json`)).default,
+ };
});
diff --git a/src/i18n/routing.ts b/src/i18n/routing.ts
index 561126b..dea19e6 100644
--- a/src/i18n/routing.ts
+++ b/src/i18n/routing.ts
@@ -1,6 +1,6 @@
import { defineRouting } from "next-intl/routing";
export const routing = defineRouting({
- locales: ["en", "sk"],
- defaultLocale: "en",
+ locales: ["en", "sk"],
+ defaultLocale: "en",
});
diff --git a/src/lib/get-query-client.ts b/src/lib/get-query-client.ts
new file mode 100644
index 0000000..ab41ee2
--- /dev/null
+++ b/src/lib/get-query-client.ts
@@ -0,0 +1,36 @@
+import {
+ defaultShouldDehydrateQuery,
+ isServer,
+ QueryClient,
+} from "@tanstack/react-query";
+
+function makeQueryClient() {
+ return new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: 60 * 1000,
+ },
+ dehydrate: {
+ // include pending queries in dehydration
+ shouldDehydrateQuery: (query) =>
+ defaultShouldDehydrateQuery(query) ||
+ query.state.status === "pending",
+ },
+ },
+ });
+}
+
+let browserQueryClient: QueryClient | undefined;
+
+export function getQueryClient() {
+ if (isServer) {
+ // Server: always make a new query client
+ return makeQueryClient();
+ }
+ // Browser: make a new query client if we don't already have one
+ // This is very important, so we don't re-make a new client if React
+ // suspends during the initial render. This may not be needed if we
+ // have a suspense boundary BELOW the creation of the query client
+ if (!browserQueryClient) browserQueryClient = makeQueryClient();
+ return browserQueryClient;
+}
diff --git a/src/lib/highlight.tsx b/src/lib/highlight.tsx
new file mode 100644
index 0000000..19f5280
--- /dev/null
+++ b/src/lib/highlight.tsx
@@ -0,0 +1,34 @@
+import { toJsxRuntime } from "hast-util-to-jsx-runtime";
+import { Fragment, type JSX } from "react";
+import { jsx, jsxs } from "react/jsx-runtime";
+import { codeToHast } from "shiki";
+import type { BundledLanguage } from "shiki/bundle/web";
+import { cn } from "@/lib/utils";
+
+export async function highlight(code: string, lang: BundledLanguage) {
+ const out = await codeToHast(code, {
+ lang,
+ theme: "github-dark",
+ });
+
+ return toJsxRuntime(out, {
+ Fragment,
+ jsx,
+ jsxs,
+ components: {
+ pre: (props) => {
+ const { className, ...rest } = props;
+ return (
+
+ );
+ },
+ },
+ }) as JSX.Element;
+}
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 365058c..001e362 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -1,6 +1,143 @@
+import { queryOptions } from "@tanstack/react-query";
import { type ClassValue, clsx } from "clsx";
+import {
+ createLoader,
+ parseAsInteger,
+ parseAsStringLiteral,
+} from "nuqs/server";
import { twMerge } from "tailwind-merge";
+import { highlight } from "./highlight";
export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs));
+ return twMerge(clsx(inputs));
+}
+
+export const ALGORITMS = [
+ "bubble",
+ "selection",
+ "insertion",
+ "quicksort",
+] as const;
+export type Algorithm = (typeof ALGORITMS)[number];
+
+export const playgroundSearchParams = {
+ algorithm: parseAsStringLiteral(ALGORITMS).withDefault("bubble"),
+ speed: parseAsInteger.withDefault(50),
+ length: parseAsInteger.withDefault(20),
+};
+export const loadSearchParams = createLoader(playgroundSearchParams);
+
+export function codeHighlightOptions(algorithm: Algorithm) {
+ return queryOptions({
+ queryKey: ["code-highlight", algorithm],
+ queryFn: () => {
+ return highlight(getAlgorithmCode(algorithm), "ts");
+ },
+ });
+}
+
+export function getAlgorithmCode(algorithm: Algorithm): string {
+ switch (algorithm) {
+ case "bubble":
+ return `function bubbleSort(arr) {
+ const n = arr.length;
+
+ for (let i = 0; i < n - 1; i++) {
+ let swapped = false;
+
+ for (let j = 0; j < n - i - 1; j++) {
+ if (arr[j] > arr[j + 1]) {
+ // Swap elements
+ [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
+ swapped = true;
+ }
+ }
+
+ // If no swapping occurred in this pass,
+ // array is sorted
+ if (!swapped) break;
+ }
+
+ return arr;
+}`;
+ case "selection":
+ return `function selectionSort(arr) {
+ const n = arr.length;
+
+ for (let i = 0; i < n - 1; i++) {
+ // Find minimum element in unsorted part
+ let minIndex = i;
+
+ for (let j = i + 1; j < n; j++) {
+ if (arr[j] < arr[minIndex]) {
+ minIndex = j;
+ }
+ }
+
+ // Swap found minimum with first element
+ if (minIndex !== i) {
+ [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
+ }
+ }
+
+ return arr;
+}`;
+ case "insertion":
+ return `function insertionSort(arr) {
+ const n = arr.length;
+
+ for (let i = 1; i < n; i++) {
+ // Store current element to be inserted
+ let key = arr[i];
+ let j = i - 1;
+
+ // Move elements greater than key
+ // to one position ahead
+ while (j >= 0 && arr[j] > key) {
+ arr[j + 1] = arr[j];
+ j--;
+ }
+
+ // Insert the key at correct position
+ arr[j + 1] = key;
+ }
+
+ return arr;
+}`;
+ case "quicksort":
+ return `function quickSort(arr, low = 0, high = arr.length - 1) {
+ if (low < high) {
+ // Find pivot element such that
+ // elements smaller than pivot are on the left
+ // elements greater than pivot are on the right
+ const pivotIndex = partition(arr, low, high);
+
+ // Recursively sort elements before and after pivot
+ quickSort(arr, low, pivotIndex - 1);
+ quickSort(arr, pivotIndex + 1, high);
+ }
+
+ return arr;
+}
+
+function partition(arr, low, high) {
+ // Choose rightmost element as pivot
+ const pivot = arr[high];
+ let i = low - 1;
+
+ // Compare each element with pivot
+ for (let j = low; j < high; j++) {
+ if (arr[j] <= pivot) {
+ i++;
+ [arr[i], arr[j]] = [arr[j], arr[i]];
+ }
+ }
+
+ // Place pivot in its final position
+ [arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];
+ return i + 1;
+}`;
+ default:
+ return "";
+ }
}
diff --git a/src/middleware.ts b/src/middleware.ts
index ed0e55f..40d6d21 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -4,8 +4,8 @@ import { routing } from "./i18n/routing";
export default createMiddleware(routing);
export const config = {
- // Match all pathnames except for
- // - … if they start with `/api`, `/trpc`, `/_next` or `/_vercel`
- // - … the ones containing a dot (e.g. `favicon.ico`)
- matcher: "/((?!api|trpc|_next|_vercel|.*\\..*).*)",
+ // Match all pathnames except for
+ // - … if they start with `/api`, `/trpc`, `/_next` or `/_vercel`
+ // - … the ones containing a dot (e.g. `favicon.ico`)
+ matcher: "/((?!api|trpc|_next|_vercel|.*\\..*).*)",
};
diff --git a/tsconfig.json b/tsconfig.json
index f7f35b8..6dfb0eb 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,45 +1,45 @@
{
- "$schema": "https://json.schemastore.org/tsconfig",
- "compilerOptions": {
- "lib": ["dom", "dom.iterable", "esnext"],
- "allowJs": true,
- "skipLibCheck": true,
- "strict": true,
- "noEmit": true,
- "esModuleInterop": true,
- "module": "esnext",
- "moduleResolution": "bundler",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "jsx": "preserve",
- "incremental": true,
- "forceConsistentCasingInFileNames": true,
- "noUncheckedIndexedAccess": true,
- "verbatimModuleSyntax": true,
- "noImplicitAny": true,
- "noFallthroughCasesInSwitch": true,
- "noImplicitOverride": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "allowUnreachableCode": false,
- "allowUnusedLabels": false,
- "plugins": [
- {
- "name": "next"
- }
- ],
- "paths": {
- "@/*": ["./src/*"]
- },
- "target": "ES2017"
- },
- "include": [
- "next-env.d.ts",
- "**/*.ts",
- "**/*.tsx",
- "**/*.cjs",
- "**/*.mjs",
- ".next/types/**/*.ts"
- ],
- "exclude": ["node_modules"]
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "compilerOptions": {
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "forceConsistentCasingInFileNames": true,
+ "noUncheckedIndexedAccess": true,
+ "verbatimModuleSyntax": true,
+ "noImplicitAny": true,
+ "noFallthroughCasesInSwitch": true,
+ "noImplicitOverride": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "allowUnreachableCode": false,
+ "allowUnusedLabels": false,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+ "target": "ES2017"
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ "**/*.cjs",
+ "**/*.mjs",
+ ".next/types/**/*.ts"
+ ],
+ "exclude": ["node_modules"]
}