From b1db390c06f81a0ceae21d4f804775021dc02e40 Mon Sep 17 00:00:00 2001 From: emil28092005 Date: Sat, 9 May 2026 01:57:12 +0300 Subject: [PATCH 01/44] Initial commit --- .astro/settings.json | 5 + .astro/types.d.ts | 1 + .dockerignore | 5 + .gitignore | 4 + Dockerfile | 14 + astro.config.mjs | 8 + nginx.conf | 18 + package-lock.json | 6498 +++++++++++++++++ package.json | 15 + public/favicon.svg | 4 + src/components/AdBanner.astro | 24 + src/components/GeneratorCard.astro | 53 + .../generators/NumberGenerator.astro | 177 + src/data/generators.ts | 52 + src/env.d.ts | 1 + src/layouts/BaseLayout.astro | 54 + src/pages/generators/numbers.astro | 63 + src/pages/index.astro | 43 + tsconfig.json | 9 + 19 files changed, 7048 insertions(+) create mode 100644 .astro/settings.json create mode 100644 .astro/types.d.ts create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 astro.config.mjs create mode 100644 nginx.conf create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 public/favicon.svg create mode 100644 src/components/AdBanner.astro create mode 100644 src/components/GeneratorCard.astro create mode 100644 src/components/generators/NumberGenerator.astro create mode 100644 src/data/generators.ts create mode 100644 src/env.d.ts create mode 100644 src/layouts/BaseLayout.astro create mode 100644 src/pages/generators/numbers.astro create mode 100644 src/pages/index.astro create mode 100644 tsconfig.json diff --git a/.astro/settings.json b/.astro/settings.json new file mode 100644 index 0000000..a24f29b --- /dev/null +++ b/.astro/settings.json @@ -0,0 +1,5 @@ +{ + "_variables": { + "lastUpdateCheck": 1778278468140 + } +} \ No newline at end of file diff --git a/.astro/types.d.ts b/.astro/types.d.ts new file mode 100644 index 0000000..f964fe0 --- /dev/null +++ b/.astro/types.d.ts @@ -0,0 +1 @@ +/// diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..beed316 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules +dist +.git +.env +*.log diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa0926a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +.env +*.log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a2b3021 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +# Stage 1 — build +FROM node:20-alpine AS builder +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build + +# Stage 2 — serve +FROM nginx:alpine AS runner +COPY --from=builder /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000..2b45c70 --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,8 @@ +import { defineConfig } from 'astro/config'; +import tailwindcss from '@tailwindcss/vite'; + +export default defineConfig({ + vite: { + plugins: [tailwindcss()], + }, +}); diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..7c1669d --- /dev/null +++ b/nginx.conf @@ -0,0 +1,18 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + absolute_redirect off; + + location / { + try_files $uri $uri/index.html $uri.html =404; + } + + error_page 404 /404.html; + + gzip on; + gzip_types text/plain text/css application/javascript application/json image/svg+xml; + gzip_min_length 1024; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d2564ee --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6498 @@ +{ + "name": "randify", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "randify", + "version": "0.0.1", + "dependencies": { + "@tailwindcss/vite": "^4.0.0", + "astro": "^4.16.0", + "tailwindcss": "^4.0.0" + } + }, + "node_modules/@astrojs/compiler": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz", + "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==" + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.4.1.tgz", + "integrity": "sha512-bMf9jFihO8YP940uD70SI/RDzIhUHJAolWVcO1v5PUivxGKvfLZTLTVVxEYzGYyPsA3ivdLNqMnL5VgmQySa+g==" + }, + "node_modules/@astrojs/markdown-remark": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-5.3.0.tgz", + "integrity": "sha512-r0Ikqr0e6ozPb5bvhup1qdWnSPUvQu6tub4ZLYaKyG50BXZ0ej6FhGz3GpChKpH7kglRFPObJd/bDyf2VM9pkg==", + "dependencies": { + "@astrojs/prism": "3.1.0", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-text": "^4.0.2", + "import-meta-resolve": "^4.1.0", + "mdast-util-definitions": "^6.0.0", + "rehype-raw": "^7.0.0", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.1", + "remark-smartypants": "^3.0.2", + "shiki": "^1.22.0", + "unified": "^11.0.5", + "unist-util-remove-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.1", + "vfile": "^6.0.3" + } + }, + "node_modules/@astrojs/prism": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.1.0.tgz", + "integrity": "sha512-Z9IYjuXSArkAUx3N6xj6+Bnvx8OdUSHA8YoOgyepp3+zJmtVYJIl/I18GozdJVW1p5u/CNpl3Km7/gwTJK85cw==", + "dependencies": { + "prismjs": "^1.29.0" + }, + "engines": { + "node": "^18.17.1 || ^20.3.0 || >=21.0.0" + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.1.0.tgz", + "integrity": "sha512-/ca/+D8MIKEC8/A9cSaPUqQNZm+Es/ZinRv0ZAzvu2ios7POQSsVD+VOj7/hypWNsNM3T7RpfgNq7H2TU1KEHA==", + "dependencies": { + "ci-info": "^4.0.0", + "debug": "^4.3.4", + "dlv": "^1.1.3", + "dset": "^3.1.3", + "is-docker": "^3.0.0", + "is-wsl": "^3.0.0", + "which-pm-runs": "^1.1.0" + }, + "engines": { + "node": "^18.17.1 || ^20.3.0 || >=21.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", + "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-syntax-jsx": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openharmony" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==" + }, + "node_modules/@oxc-project/types": { + "version": "0.128.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.128.0.tgz", + "integrity": "sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.18.tgz", + "integrity": "sha512-lIDyUAfD7U3+BWKzdxMbJcsYHuqXqmGz40aeRqvuAm3y5TkJSYTBW2RDrn65DJFPQqVjUAUqq5uz8urzQ8aBdQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.18.tgz", + "integrity": "sha512-apJq2ktnGp27nSInMR5Vcj8kY6xJzDAvfdIFlpDcAK/w4cDO58qVoi1YQsES/SKiFNge/6e4CUzgjfHduYqWpQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.18.tgz", + "integrity": "sha512-5Ofot8xbs+pxRHJqm9/9N/4sTQOvdrwEsmPE9pdLEEoAbdZtG6F2LMDfO1sp6ZAtXJuJV/21ew2srq3W8NXB5g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.18.tgz", + "integrity": "sha512-7h8eeOTT1eyqJyx64BFCnWZpNm486hGWt2sqeLLgDxA0xI1oGZ9H7gK1S85uNGmBhkdPwa/6reTxfFFKvIsebw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.18.tgz", + "integrity": "sha512-eRcm/HVt9U/JFu5RKAEKwGQYtDCKWLiaH6wOnsSEp6NMBb/3Os8LgHZlNyzMpFVNmiiMFlfb2zEnebfzJrHFmg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-SOrT/cT4ukTmgnrEz/Hg3m7LBnuCLW9psDeMKrimRWY4I8DmnO7Lco8W2vtqPmMkbVu8iJ+g4GFLVLLOVjJ9DQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.18.tgz", + "integrity": "sha512-QWjdxN1HJCpBTAcZ5N5F7wju3gVPzRzSpmGzx7na0c/1qpN9CFil+xt+l9lV/1M6/gqHSNXCiqPfwhVJPeLnug==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-ugCOyj7a4d9h3q9B+wXmf6g3a68UsjGh6dob5DHevHGMwDUbhsYNbSPxJsENcIttJZ9jv7qGM2UesLw5jqIhdg==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-kKWRhbsotpXkGbcd5dllUWg5gEXcDAa8u5YnP9AV5DYNbvJHGzzuwv7dpmhc8NqKMJldl0a+x76IHbspEpEmdA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-uCo8ElcCIAMyYAZyuIZ81oFkhTSIllNvUCHCAlbhlN4ji3uC28h7IIdlXyIvGO7HsuqnV9p3rD/bpH7XhIyhRw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.18.tgz", + "integrity": "sha512-XNOQZtuE6yUIvx4rwGemwh8kpL1xvU41FXy/s9K7T/3JVcqGzo3NfKM2HrbrGgfPYGFW42f07Wk++aOC6B9NWA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.18.tgz", + "integrity": "sha512-tSn/kzrfa7tNOXr7sEacDBN4YsIqTyLqh45IO0nHDwtpKIDNDJr+VFojt+4klSpChxB29JLyduSsE0MKEwa65A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openharmony" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.18.tgz", + "integrity": "sha512-+J9YGmc+czgqlhYmwun3S3O0FIZhsH8ep2456xwjAdIOmuJxM7xz4P4PtrxU+Bz17a/5bqPA8o3HAAoX0teUdg==", + "cpu": [ + "wasm32" + ], + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.18.tgz", + "integrity": "sha512-zsu47DgU0FQzSwi6sU9dZoEdUv7pc1AptSEz/Z8HBg54sV0Pbs3N0+CrIbTsgiu6EyoaNN9CHboqbLaz9lhOyQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.18.tgz", + "integrity": "sha512-7H+3yqGgmnlDTRRhw/xpYY9J1kf4GC681nVc4GqKhExZTDrVVrV2tsOR9kso0fvgBdcTCcQShx4SLLoHgaLwhg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.18.tgz", + "integrity": "sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==", + "peer": true + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.3.tgz", + "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.3.tgz", + "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.3.tgz", + "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.3.tgz", + "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.3.tgz", + "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.3.tgz", + "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.3.tgz", + "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.3.tgz", + "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.3.tgz", + "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.3.tgz", + "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.3.tgz", + "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.3.tgz", + "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.3.tgz", + "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.3.tgz", + "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.3.tgz", + "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.3.tgz", + "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.3.tgz", + "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.3.tgz", + "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.3.tgz", + "integrity": "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.3.tgz", + "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.3.tgz", + "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.3.tgz", + "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.3.tgz", + "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.3.tgz", + "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.3.tgz", + "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.29.2.tgz", + "integrity": "sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ==", + "dependencies": { + "@shikijs/engine-javascript": "1.29.2", + "@shikijs/engine-oniguruma": "1.29.2", + "@shikijs/types": "1.29.2", + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.4" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.29.2.tgz", + "integrity": "sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A==", + "dependencies": { + "@shikijs/types": "1.29.2", + "@shikijs/vscode-textmate": "^10.0.1", + "oniguruma-to-es": "^2.2.0" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.29.2.tgz", + "integrity": "sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==", + "dependencies": { + "@shikijs/types": "1.29.2", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "node_modules/@shikijs/langs": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-1.29.2.tgz", + "integrity": "sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ==", + "dependencies": { + "@shikijs/types": "1.29.2" + } + }, + "node_modules/@shikijs/themes": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-1.29.2.tgz", + "integrity": "sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g==", + "dependencies": { + "@shikijs/types": "1.29.2" + } + }, + "node_modules/@shikijs/types": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.29.2.tgz", + "integrity": "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==" + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.21.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-freebsd-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-musl": "4.3.0", + "@tailwindcss/oxide-wasm32-wasi": "4.3.0", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz", + "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==", + "dependencies": { + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "tailwindcss": "4.3.0" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-iterate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", + "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/astro": { + "version": "4.16.19", + "resolved": "https://registry.npmjs.org/astro/-/astro-4.16.19.tgz", + "integrity": "sha512-baeSswPC5ZYvhGDoj25L2FuzKRWMgx105FetOPQVJFMCAp0o08OonYC7AhwsFdhvp7GapqjnC1Fe3lKb2lupYw==", + "dependencies": { + "@astrojs/compiler": "^2.10.3", + "@astrojs/internal-helpers": "0.4.1", + "@astrojs/markdown-remark": "5.3.0", + "@astrojs/telemetry": "3.1.0", + "@babel/core": "^7.26.0", + "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/types": "^7.26.0", + "@oslojs/encoding": "^1.1.0", + "@rollup/pluginutils": "^5.1.3", + "@types/babel__core": "^7.20.5", + "@types/cookie": "^0.6.0", + "acorn": "^8.14.0", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "boxen": "8.0.1", + "ci-info": "^4.1.0", + "clsx": "^2.1.1", + "common-ancestor-path": "^1.0.1", + "cookie": "^0.7.2", + "cssesc": "^3.0.0", + "debug": "^4.3.7", + "deterministic-object-hash": "^2.0.2", + "devalue": "^5.1.1", + "diff": "^5.2.0", + "dlv": "^1.1.3", + "dset": "^3.1.4", + "es-module-lexer": "^1.5.4", + "esbuild": "^0.21.5", + "estree-walker": "^3.0.3", + "fast-glob": "^3.3.2", + "flattie": "^1.1.1", + "github-slugger": "^2.0.0", + "gray-matter": "^4.0.3", + "html-escaper": "^3.0.3", + "http-cache-semantics": "^4.1.1", + "js-yaml": "^4.1.0", + "kleur": "^4.1.5", + "magic-string": "^0.30.14", + "magicast": "^0.3.5", + "micromatch": "^4.0.8", + "mrmime": "^2.0.0", + "neotraverse": "^0.6.18", + "ora": "^8.1.1", + "p-limit": "^6.1.0", + "p-queue": "^8.0.1", + "preferred-pm": "^4.0.0", + "prompts": "^2.4.2", + "rehype": "^13.0.2", + "semver": "^7.6.3", + "shiki": "^1.23.1", + "tinyexec": "^0.3.1", + "tsconfck": "^3.1.4", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.3", + "vite": "^5.4.11", + "vitefu": "^1.0.4", + "which-pm": "^3.0.0", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^21.1.1", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.23.5", + "zod-to-ts": "^1.2.0" + }, + "bin": { + "astro": "astro.js" + }, + "engines": { + "node": "^18.17.1 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "optionalDependencies": { + "sharp": "^0.33.3" + } + }, + "node_modules/astro/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/astro/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/astro/node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/base-64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", + "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.28", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.28.tgz", + "integrity": "sha512-Ic44hnOtFIgravCunj1ifSoQPSUrkNiJuH9Mf6jr2jjoA74icqV8wU0KuadXeOR8zuIJMOoTv0GuQjZ9ZYNMeA==", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/boxen": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", + "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^8.0.0", + "chalk": "^5.3.0", + "cli-boxes": "^3.0.0", + "string-width": "^7.2.0", + "type-fest": "^4.21.0", + "widest-line": "^5.0.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001792", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", + "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "optional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "optional": true + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/common-ancestor-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", + "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/deterministic-object-hash": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz", + "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==", + "dependencies": { + "base-64": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/devalue": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.0.tgz", + "integrity": "sha512-2zA9pFEsnp7vWBZbXF5JAgAq0fsUIt/1XPbRiAmRV3lp/2C3upzH+sADiyy66aFCihoLEsrQHxNM5w1gIDfsBg==" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.353", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.353.tgz", + "integrity": "sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==" + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==" + }, + "node_modules/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==" + }, + "node_modules/enhanced-resolve": { + "version": "5.21.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.2.tgz", + "integrity": "sha512-xe9vQb5kReirPUxgQrXA3ihgbCqssmTiM7cOZ+Gzu+VeGWgpV98lLZvp0dl4yriyAePcewxGUs9UpKD8PET9KQ==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==" + }, + "node_modules/esbuild": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", + "hasInstallScript": true, + "optional": true, + "peer": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "peer": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-yarn-workspace-root2": { + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz", + "integrity": "sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==", + "dependencies": { + "micromatch": "^4.0.2", + "pkg-dir": "^4.2.0" + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", + "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==" + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gray-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gray-matter/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==" + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "optional": true + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/load-yaml-file": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/load-yaml-file/-/load-yaml-file-0.2.0.tgz", + "integrity": "sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==", + "dependencies": { + "graceful-fs": "^4.1.5", + "js-yaml": "^3.13.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/load-yaml-file/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/load-yaml-file/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-definitions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", + "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "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" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "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" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "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" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "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" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "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" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neotraverse": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", + "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-releases": { + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==" + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/oniguruma-to-es": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-2.3.0.tgz", + "integrity": "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==", + "dependencies": { + "emoji-regex-xs": "^1.0.0", + "regex": "^5.1.1", + "regex-recursion": "^5.1.1" + } + }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", + "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", + "dependencies": { + "yocto-queue": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.1.tgz", + "integrity": "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^6.1.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", + "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-latin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", + "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/preferred-pm": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/preferred-pm/-/preferred-pm-4.1.1.tgz", + "integrity": "sha512-rU+ZAv1Ur9jAUZtGPebQVQPzdGhNzaEiQ7VL9+cjsAWPHFYOccNXPNiev1CCDSOg/2j7UujM7ojNhpkuILEVNQ==", + "dependencies": { + "find-up-simple": "^1.0.0", + "find-yarn-workspace-root2": "1.2.16", + "which-pm": "^3.0.1" + }, + "engines": { + "node": ">=18.12" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "engines": { + "node": ">=6" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/regex": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/regex/-/regex-5.1.1.tgz", + "integrity": "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-5.1.1.tgz", + "integrity": "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==", + "dependencies": { + "regex": "^5.1.1", + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==" + }, + "node_modules/rehype": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", + "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", + "dependencies": { + "@types/hast": "^3.0.0", + "rehype-parse": "^9.0.0", + "rehype-stringify": "^10.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", + "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-html": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-smartypants": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", + "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", + "dependencies": { + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retext": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", + "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", + "dependencies": { + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", + "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", + "dependencies": { + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-stringify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", + "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.18.tgz", + "integrity": "sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg==", + "peer": true, + "dependencies": { + "@oxc-project/types": "=0.128.0", + "@rolldown/pluginutils": "1.0.0-rc.18" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.18", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.18", + "@rolldown/binding-darwin-x64": "1.0.0-rc.18", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.18", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.18", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.18", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.18", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.18", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.18", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.18", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.18" + } + }, + "node_modules/rollup": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.3.tgz", + "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.3", + "@rollup/rollup-android-arm64": "4.60.3", + "@rollup/rollup-darwin-arm64": "4.60.3", + "@rollup/rollup-darwin-x64": "4.60.3", + "@rollup/rollup-freebsd-arm64": "4.60.3", + "@rollup/rollup-freebsd-x64": "4.60.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", + "@rollup/rollup-linux-arm-musleabihf": "4.60.3", + "@rollup/rollup-linux-arm64-gnu": "4.60.3", + "@rollup/rollup-linux-arm64-musl": "4.60.3", + "@rollup/rollup-linux-loong64-gnu": "4.60.3", + "@rollup/rollup-linux-loong64-musl": "4.60.3", + "@rollup/rollup-linux-ppc64-gnu": "4.60.3", + "@rollup/rollup-linux-ppc64-musl": "4.60.3", + "@rollup/rollup-linux-riscv64-gnu": "4.60.3", + "@rollup/rollup-linux-riscv64-musl": "4.60.3", + "@rollup/rollup-linux-s390x-gnu": "4.60.3", + "@rollup/rollup-linux-x64-gnu": "4.60.3", + "@rollup/rollup-linux-x64-musl": "4.60.3", + "@rollup/rollup-openbsd-x64": "4.60.3", + "@rollup/rollup-openharmony-arm64": "4.60.3", + "@rollup/rollup-win32-arm64-msvc": "4.60.3", + "@rollup/rollup-win32-ia32-msvc": "4.60.3", + "@rollup/rollup-win32-x64-gnu": "4.60.3", + "@rollup/rollup-win32-x64-msvc": "4.60.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/shiki": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.29.2.tgz", + "integrity": "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg==", + "dependencies": { + "@shikijs/core": "1.29.2", + "@shikijs/engine-javascript": "1.29.2", + "@shikijs/engine-oniguruma": "1.29.2", + "@shikijs/langs": "1.29.2", + "@shikijs/themes": "1.29.2", + "@shikijs/types": "1.29.2", + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "optional": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==" + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "peer": true, + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tsconfck": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", + "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "optional": true + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-modify-children": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", + "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-children": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", + "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.11.tgz", + "integrity": "sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow==", + "peer": true, + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.14", + "rolldown": "1.0.0-rc.18", + "tinyglobby": "^0.2.16" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/which-pm": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-3.0.1.tgz", + "integrity": "sha512-v2JrMq0waAI4ju1xU5x3blsxBBMgdgZve580iYMN5frDaLGjbA24fok7wKCsya8KLVO19Ju4XDc5+zTZCJkQfg==", + "dependencies": { + "load-yaml-file": "^0.2.0" + }, + "engines": { + "node": ">=18.12" + } + }, + "node_modules/which-pm-runs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", + "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/widest-line": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", + "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", + "dependencies": { + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + }, + "node_modules/zod-to-ts": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz", + "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==", + "peerDependencies": { + "typescript": "^4.9.4 || ^5.0.2", + "zod": "^3" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..fb80cc2 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "randify", + "type": "module", + "version": "0.0.1", + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview" + }, + "dependencies": { + "astro": "^4.16.0", + "@tailwindcss/vite": "^4.0.0", + "tailwindcss": "^4.0.0" + } +} diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..d127fdd --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,4 @@ + + + # + diff --git a/src/components/AdBanner.astro b/src/components/AdBanner.astro new file mode 100644 index 0000000..f516d91 --- /dev/null +++ b/src/components/AdBanner.astro @@ -0,0 +1,24 @@ +--- +interface Props { + size: 'leaderboard' | 'rectangle' | 'tile'; +} + +const { size } = Astro.props; + +const specs = { + leaderboard: { label: '728 × 90', class: 'w-full h-[90px]' }, + rectangle: { label: '300 × 250', class: 'w-full max-w-[300px] h-[250px]' }, + tile: { label: '300 × 250', class: 'w-full h-[250px]' }, +}; + +const { label, class: sizeClass } = specs[size]; +--- + + diff --git a/src/components/GeneratorCard.astro b/src/components/GeneratorCard.astro new file mode 100644 index 0000000..c2a8823 --- /dev/null +++ b/src/components/GeneratorCard.astro @@ -0,0 +1,53 @@ +--- +import type { Generator } from '../data/generators'; + +interface Props { + generator: Generator; +} + +const { generator } = Astro.props; +const { slug, title, description, icon, status } = generator; +const isLive = status === 'live'; +const href = `/generators/${slug}/`; + +const icons: Record = { + hash: ``, + palette: ``, + lock: ``, + ticket: ``, + 'dice-6': ``, + 'square-stack': ``, +}; +--- + +{isLive ? ( + +
+ +

+ {title} +

+

{description}

+
+) : ( +
+
+ +

{title}

+

{description}

+
+)} diff --git a/src/components/generators/NumberGenerator.astro b/src/components/generators/NumberGenerator.astro new file mode 100644 index 0000000..b717633 --- /dev/null +++ b/src/components/generators/NumberGenerator.astro @@ -0,0 +1,177 @@ +--- +// Interactive number generator — runs on the client via inline script +--- + +
+
+
+ + +
+
+ + +
+
+ + + +
+ + + Copied +
+ +
+ +
+
+ + diff --git a/src/data/generators.ts b/src/data/generators.ts new file mode 100644 index 0000000..df3b080 --- /dev/null +++ b/src/data/generators.ts @@ -0,0 +1,52 @@ +export interface Generator { + slug: string; + title: string; + description: string; + icon: string; + status: 'live' | 'coming-soon'; +} + +export const generators: Generator[] = [ + { + slug: 'numbers', + title: 'Number', + description: 'Pick a random number within any range you choose.', + icon: 'hash', + status: 'live', + }, + { + slug: 'colors', + title: 'Color', + description: 'Generate a random color in HEX, RGB, or HSL format.', + icon: 'palette', + status: 'coming-soon', + }, + { + slug: 'password', + title: 'Password', + description: 'Create a strong, random password with custom rules.', + icon: 'lock', + status: 'coming-soon', + }, + { + slug: 'lottery', + title: 'Lottery', + description: 'Draw a set of unique numbers for lottery-style picks.', + icon: 'ticket', + status: 'coming-soon', + }, + { + slug: 'dice', + title: 'Dice', + description: 'Roll one or more dice with any number of sides.', + icon: 'dice-6', + status: 'coming-soon', + }, + { + slug: 'cards', + title: 'Card', + description: 'Draw a random playing card from a standard deck.', + icon: 'square-stack', + status: 'coming-soon', + }, +]; diff --git a/src/env.d.ts b/src/env.d.ts new file mode 100644 index 0000000..9bc5cb4 --- /dev/null +++ b/src/env.d.ts @@ -0,0 +1 @@ +/// \ No newline at end of file diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro new file mode 100644 index 0000000..be61d27 --- /dev/null +++ b/src/layouts/BaseLayout.astro @@ -0,0 +1,54 @@ +--- +interface Props { + title?: string; + description?: string; +} + +const { + title = 'Randify — Random Value Generators', + description = 'Simple tools for raffles, games, and everything that needs randomness.', +} = Astro.props; +--- + + + + + + + + + + + + {title} + + + + + + + diff --git a/src/pages/generators/numbers.astro b/src/pages/generators/numbers.astro new file mode 100644 index 0000000..260d39d --- /dev/null +++ b/src/pages/generators/numbers.astro @@ -0,0 +1,63 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import NumberGenerator from '../../components/generators/NumberGenerator.astro'; +import AdBanner from '../../components/AdBanner.astro'; +import { generators } from '../../data/generators'; + +const generator = generators.find((g) => g.slug === 'numbers')!; +--- + + +
+ + +
+
+ + randify +
+

+ {generator.title} Generator +

+

{generator.description}

+
+ +
+ +
+ +
+ + + +
+
+
diff --git a/src/pages/index.astro b/src/pages/index.astro new file mode 100644 index 0000000..2137549 --- /dev/null +++ b/src/pages/index.astro @@ -0,0 +1,43 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +import GeneratorCard from '../components/GeneratorCard.astro'; +import AdBanner from '../components/AdBanner.astro'; +import { generators } from '../data/generators'; +--- + + +
+
+
+ + randify +
+

+ Random generators for every occasion +

+

+ Simple tools for raffles, games, and everything that needs randomness. +

+
+ + + +
+
+ {generators.map((generator) => ( +
+ +
+ ))} +
+
+
+
diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c5450d3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "astro/tsconfigs/strict", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + } +} From 6a8630a00d347a3a70aaeb6c172e6a7edab67ab2 Mon Sep 17 00:00:00 2001 From: emil28092005 Date: Sat, 9 May 2026 13:30:43 +0300 Subject: [PATCH 02/44] Add colours generator. --- .../generators/ColorGenerator.astro | 134 ++++++++++++++++++ src/data/generators.ts | 2 +- src/pages/generators/colors.astro | 63 ++++++++ 3 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 src/components/generators/ColorGenerator.astro create mode 100644 src/pages/generators/colors.astro diff --git a/src/components/generators/ColorGenerator.astro b/src/components/generators/ColorGenerator.astro new file mode 100644 index 0000000..38e6b76 --- /dev/null +++ b/src/components/generators/ColorGenerator.astro @@ -0,0 +1,134 @@ +--- +--- + +
+ + +
+ +
+ + +
+ {[ + { id: 'cg-hex', label: 'HEX' }, + { id: 'cg-rgb', label: 'RGB' }, + { id: 'cg-hsl', label: 'HSL' }, + ].map(({ id, label }) => ( +
+ + {label} + + + +
+ ))} +
+ + +
+ +
+
+ + diff --git a/src/data/generators.ts b/src/data/generators.ts index df3b080..91e039a 100644 --- a/src/data/generators.ts +++ b/src/data/generators.ts @@ -19,7 +19,7 @@ export const generators: Generator[] = [ title: 'Color', description: 'Generate a random color in HEX, RGB, or HSL format.', icon: 'palette', - status: 'coming-soon', + status: 'live', }, { slug: 'password', diff --git a/src/pages/generators/colors.astro b/src/pages/generators/colors.astro new file mode 100644 index 0000000..fb9f4b9 --- /dev/null +++ b/src/pages/generators/colors.astro @@ -0,0 +1,63 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import ColorGenerator from '../../components/generators/ColorGenerator.astro'; +import AdBanner from '../../components/AdBanner.astro'; +import { generators } from '../../data/generators'; + +const generator = generators.find((g) => g.slug === 'colors')!; +--- + + +
+ + +
+
+ + randify +
+

+ {generator.title} Generator +

+

{generator.description}

+
+ +
+ +
+ +
+ + + +
+
+
From 3b7b1382b436d99dad2876f0a04aeab231b8597f Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 13:32:35 +0300 Subject: [PATCH 03/44] feat: add password generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds /generators/password/ with length slider (4–64), uppercase/lowercase/digits/symbols toggles, guaranteed character-type coverage, Fisher-Yates shuffle, and clipboard copy. Sets password status to live in the generator catalog. Also adds CLAUDE.md with project architecture notes. Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 37 ++++ .../generators/PasswordGenerator.astro | 201 ++++++++++++++++++ src/data/generators.ts | 2 +- src/pages/generators/password.astro | 63 ++++++ 4 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 CLAUDE.md create mode 100644 src/components/generators/PasswordGenerator.astro create mode 100644 src/pages/generators/password.astro diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..a2e2dce --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,37 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this is + +Randify is a static site (Astro 4 + Tailwind CSS 4) that hosts random-value generators. Only the number generator is live; the rest are catalogued in `src/data/generators.ts` with `status: 'coming-soon'`. + +## Commands + +```bash +npm run dev # Dev server (localhost:4321) +npm run build # Static build → dist/ +npm run preview # Serve the built dist/ locally +``` + +**Docker (production):** multi-stage build compiles with Node 20 then serves via `nginx:alpine`. The nginx config handles pretty URLs via `try_files $uri $uri/index.html`. + +```bash +docker build -t randify . +docker run -p 8080:80 randify +``` + +## Architecture + +**Adding a new generator** involves three steps: + +1. Add an entry to `src/data/generators.ts` (set `status: 'live'` when ready). +2. Create `src/components/generators/Generator.astro` — interactive logic goes in an inline ` diff --git a/src/data/generators.ts b/src/data/generators.ts index 91e039a..7579d87 100644 --- a/src/data/generators.ts +++ b/src/data/generators.ts @@ -26,7 +26,7 @@ export const generators: Generator[] = [ title: 'Password', description: 'Create a strong, random password with custom rules.', icon: 'lock', - status: 'coming-soon', + status: 'live', }, { slug: 'lottery', diff --git a/src/pages/generators/password.astro b/src/pages/generators/password.astro new file mode 100644 index 0000000..1613a4e --- /dev/null +++ b/src/pages/generators/password.astro @@ -0,0 +1,63 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import PasswordGenerator from '../../components/generators/PasswordGenerator.astro'; +import AdBanner from '../../components/AdBanner.astro'; +import { generators } from '../../data/generators'; + +const generator = generators.find((g) => g.slug === 'password')!; +--- + + +
+ + +
+
+ + randify +
+

+ {generator.title} Generator +

+

{generator.description}

+
+ +
+ +
+ +
+ + + +
+
+
From c3a3693016b639a3f1175a5dbfd170ffd19655ea Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 13:35:59 +0300 Subject: [PATCH 04/44] chore: update package-lock.json after npm install Co-Authored-By: Claude Sonnet 4.6 --- package-lock.json | 458 ---------------------------------------------- 1 file changed, 458 deletions(-) diff --git a/package-lock.json b/package-lock.json index d2564ee..676afb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -379,422 +379,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", - "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "aix" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", - "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", - "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", - "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", - "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", - "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", - "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", - "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", - "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", - "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", - "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", - "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", - "cpu": [ - "loong64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", - "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", - "cpu": [ - "mips64el" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", - "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", - "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", - "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", - "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", - "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "netbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", - "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "netbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", - "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "openbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", - "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "openbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", - "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "openharmony" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", - "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "sunos" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", - "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", - "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", - "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", @@ -3288,48 +2872,6 @@ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==" }, - "node_modules/esbuild": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", - "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", - "hasInstallScript": true, - "optional": true, - "peer": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.0", - "@esbuild/android-arm": "0.28.0", - "@esbuild/android-arm64": "0.28.0", - "@esbuild/android-x64": "0.28.0", - "@esbuild/darwin-arm64": "0.28.0", - "@esbuild/darwin-x64": "0.28.0", - "@esbuild/freebsd-arm64": "0.28.0", - "@esbuild/freebsd-x64": "0.28.0", - "@esbuild/linux-arm": "0.28.0", - "@esbuild/linux-arm64": "0.28.0", - "@esbuild/linux-ia32": "0.28.0", - "@esbuild/linux-loong64": "0.28.0", - "@esbuild/linux-mips64el": "0.28.0", - "@esbuild/linux-ppc64": "0.28.0", - "@esbuild/linux-riscv64": "0.28.0", - "@esbuild/linux-s390x": "0.28.0", - "@esbuild/linux-x64": "0.28.0", - "@esbuild/netbsd-arm64": "0.28.0", - "@esbuild/netbsd-x64": "0.28.0", - "@esbuild/openbsd-arm64": "0.28.0", - "@esbuild/openbsd-x64": "0.28.0", - "@esbuild/openharmony-arm64": "0.28.0", - "@esbuild/sunos-x64": "0.28.0", - "@esbuild/win32-arm64": "0.28.0", - "@esbuild/win32-ia32": "0.28.0", - "@esbuild/win32-x64": "0.28.0" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", From 09f593a06235e2541387191d2c70e9935f7956c2 Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 13:40:58 +0300 Subject: [PATCH 05/44] feat: add lottery generator Adds /generators/lottery/ with configurable range and pick count. Partial Fisher-Yates shuffle guarantees unique draws, results sorted ascending and displayed as numbered balls with clipboard copy. Co-Authored-By: Claude Sonnet 4.6 --- .../generators/LotteryGenerator.astro | 179 ++++++++++++++++++ src/data/generators.ts | 2 +- src/pages/generators/lottery.astro | 63 ++++++ 3 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 src/components/generators/LotteryGenerator.astro create mode 100644 src/pages/generators/lottery.astro diff --git a/src/components/generators/LotteryGenerator.astro b/src/components/generators/LotteryGenerator.astro new file mode 100644 index 0000000..a055a9a --- /dev/null +++ b/src/components/generators/LotteryGenerator.astro @@ -0,0 +1,179 @@ +--- +// Interactive lottery generator — runs on the client via inline script +--- + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + + +
+
+ + +
+ +
+ +
+
+ + diff --git a/src/data/generators.ts b/src/data/generators.ts index 7579d87..012f729 100644 --- a/src/data/generators.ts +++ b/src/data/generators.ts @@ -33,7 +33,7 @@ export const generators: Generator[] = [ title: 'Lottery', description: 'Draw a set of unique numbers for lottery-style picks.', icon: 'ticket', - status: 'coming-soon', + status: 'live', }, { slug: 'dice', diff --git a/src/pages/generators/lottery.astro b/src/pages/generators/lottery.astro new file mode 100644 index 0000000..a27974f --- /dev/null +++ b/src/pages/generators/lottery.astro @@ -0,0 +1,63 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import LotteryGenerator from '../../components/generators/LotteryGenerator.astro'; +import AdBanner from '../../components/AdBanner.astro'; +import { generators } from '../../data/generators'; + +const generator = generators.find((g) => g.slug === 'lottery')!; +--- + + +
+ + +
+
+ + randify +
+

+ {generator.title} Generator +

+

{generator.description}

+
+ +
+ +
+ +
+ + + +
+
+
From 01ddfc8428a3bd1090678c6b4e499c83158ee597 Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 13:42:16 +0300 Subject: [PATCH 06/44] feat: add dice generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds /generators/dice/ with configurable count (1–20) and standard RPG sides (d4/d6/d8/d10/d12/d20/d100). Shows individual rolls with colour-coded min/max, running total, and clipboard copy in NdS=total format. Co-Authored-By: Claude Sonnet 4.6 --- src/components/generators/DiceGenerator.astro | 170 ++++++++++++++++++ src/data/generators.ts | 2 +- src/pages/generators/dice.astro | 63 +++++++ 3 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 src/components/generators/DiceGenerator.astro create mode 100644 src/pages/generators/dice.astro diff --git a/src/components/generators/DiceGenerator.astro b/src/components/generators/DiceGenerator.astro new file mode 100644 index 0000000..42543dd --- /dev/null +++ b/src/components/generators/DiceGenerator.astro @@ -0,0 +1,170 @@ +--- +// Interactive dice generator — runs on the client via inline script +--- + +
+
+
+ + +
+
+ +
+ {['4','6','8','10','12','20','100'].map((s) => ( + + ))} +
+
+
+ + + +
+
+ + + + +
+ +
+ +
+
+ + diff --git a/src/data/generators.ts b/src/data/generators.ts index 012f729..ba12ae7 100644 --- a/src/data/generators.ts +++ b/src/data/generators.ts @@ -40,7 +40,7 @@ export const generators: Generator[] = [ title: 'Dice', description: 'Roll one or more dice with any number of sides.', icon: 'dice-6', - status: 'coming-soon', + status: 'live', }, { slug: 'cards', diff --git a/src/pages/generators/dice.astro b/src/pages/generators/dice.astro new file mode 100644 index 0000000..0c505d3 --- /dev/null +++ b/src/pages/generators/dice.astro @@ -0,0 +1,63 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import DiceGenerator from '../../components/generators/DiceGenerator.astro'; +import AdBanner from '../../components/AdBanner.astro'; +import { generators } from '../../data/generators'; + +const generator = generators.find((g) => g.slug === 'dice')!; +--- + + +
+ + +
+
+ + randify +
+

+ {generator.title} Generator +

+

{generator.description}

+
+ +
+ +
+ +
+ + + +
+
+
From d3434bc9287564b34b88ea95acabf2087279e2e3 Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 13:47:10 +0300 Subject: [PATCH 07/44] feat: add card generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds /generators/cards/ — draws 1–52 cards from a standard 52-card deck. Supports unique draws (Fisher-Yates) and with-replacement mode. Cards rendered as mini playing-card tiles with red suits coloured. Clipboard copy in rank+suit format (e.g. A♠, K♥). Co-Authored-By: Claude Sonnet 4.6 --- src/components/generators/CardGenerator.astro | 187 ++++++++++++++++++ src/data/generators.ts | 2 +- src/pages/generators/cards.astro | 63 ++++++ 3 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 src/components/generators/CardGenerator.astro create mode 100644 src/pages/generators/cards.astro diff --git a/src/components/generators/CardGenerator.astro b/src/components/generators/CardGenerator.astro new file mode 100644 index 0000000..3217ba8 --- /dev/null +++ b/src/components/generators/CardGenerator.astro @@ -0,0 +1,187 @@ +--- +// Interactive card generator — runs on the client via inline script +--- + +
+
+
+ + +
+ +
+ + + +
+
+ + +
+ +
+ +
+
+ + diff --git a/src/data/generators.ts b/src/data/generators.ts index ba12ae7..1a9c824 100644 --- a/src/data/generators.ts +++ b/src/data/generators.ts @@ -47,6 +47,6 @@ export const generators: Generator[] = [ title: 'Card', description: 'Draw a random playing card from a standard deck.', icon: 'square-stack', - status: 'coming-soon', + status: 'live', }, ]; diff --git a/src/pages/generators/cards.astro b/src/pages/generators/cards.astro new file mode 100644 index 0000000..f606a11 --- /dev/null +++ b/src/pages/generators/cards.astro @@ -0,0 +1,63 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import CardGenerator from '../../components/generators/CardGenerator.astro'; +import AdBanner from '../../components/AdBanner.astro'; +import { generators } from '../../data/generators'; + +const generator = generators.find((g) => g.slug === 'cards')!; +--- + + +
+ + +
+
+ + randify +
+

+ {generator.title} Generator +

+

{generator.description}

+
+ +
+ +
+ +
+ + + +
+
+
From c4c900232e74d56a8eef0e0c0617812aadcdb6ea Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 14:01:28 +0300 Subject: [PATCH 08/44] ci: add GitHub Actions deploy workflow On push to main: builds Astro, deploys dist/ to reg.ru via rsync over SSH. Requires DEPLOY_KEY, DEPLOY_HOST, DEPLOY_USER secrets. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/deploy.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..771c4ac --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,33 @@ +name: Deploy + +on: + push: + branches: [main] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - run: npm ci + - run: npm run build + + - name: Setup SSH key + run: | + mkdir -p ~/.ssh + echo "${{ secrets.DEPLOY_KEY }}" > ~/.ssh/deploy_key + chmod 600 ~/.ssh/deploy_key + ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts + + - name: Deploy via rsync + run: | + rsync -avz --delete \ + -e "ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no" \ + dist/ \ + ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:~/www/randify.pro/ From 9a5b85f32f23f75b4f5d428f5a8a16fcce43acf8 Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 14:07:22 +0300 Subject: [PATCH 09/44] feat: add coin flip, list picker, uuid/token generators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Coin flip: slider 1–20 coins, heads/tails display with H/T count summary - List picker: textarea input, configurable pick count, unique or with-replacement, Ctrl+Enter shortcut - UUID/Token: UUID v4 via crypto, hex and base64 modes with configurable byte length, up to 20 at once Co-Authored-By: Claude Sonnet 4.6 --- src/components/GeneratorCard.astro | 3 + src/components/generators/CoinGenerator.astro | 136 +++++++++++++ src/components/generators/ListGenerator.astro | 174 ++++++++++++++++ src/components/generators/UuidGenerator.astro | 185 ++++++++++++++++++ src/data/generators.ts | 21 ++ src/pages/generators/coin.astro | 40 ++++ src/pages/generators/list.astro | 40 ++++ src/pages/generators/uuid.astro | 40 ++++ 8 files changed, 639 insertions(+) create mode 100644 src/components/generators/CoinGenerator.astro create mode 100644 src/components/generators/ListGenerator.astro create mode 100644 src/components/generators/UuidGenerator.astro create mode 100644 src/pages/generators/coin.astro create mode 100644 src/pages/generators/list.astro create mode 100644 src/pages/generators/uuid.astro diff --git a/src/components/GeneratorCard.astro b/src/components/GeneratorCard.astro index c2a8823..b301bdb 100644 --- a/src/components/GeneratorCard.astro +++ b/src/components/GeneratorCard.astro @@ -17,6 +17,9 @@ const icons: Record = { ticket: ``, 'dice-6': ``, 'square-stack': ``, + 'circle-dollar-sign': ``, + 'list': ``, + 'fingerprint': ``, }; --- diff --git a/src/components/generators/CoinGenerator.astro b/src/components/generators/CoinGenerator.astro new file mode 100644 index 0000000..1e43b88 --- /dev/null +++ b/src/components/generators/CoinGenerator.astro @@ -0,0 +1,136 @@ +--- +// Interactive coin flip generator — runs on the client via inline script +--- + +
+
+
+ + 1 +
+ +
+ 120 +
+
+ +
+
+ + + + +
+ +
+ +
+
+ + diff --git a/src/components/generators/ListGenerator.astro b/src/components/generators/ListGenerator.astro new file mode 100644 index 0000000..c3faec1 --- /dev/null +++ b/src/components/generators/ListGenerator.astro @@ -0,0 +1,174 @@ +--- +// Interactive list picker — runs on the client via inline script +--- + +
+
+
+ + +
+ +
+
+ + +
+ +
+
+ + + +
+
+ + +
+ +
+ +
+
+ + diff --git a/src/components/generators/UuidGenerator.astro b/src/components/generators/UuidGenerator.astro new file mode 100644 index 0000000..945f5d4 --- /dev/null +++ b/src/components/generators/UuidGenerator.astro @@ -0,0 +1,185 @@ +--- +// Interactive UUID / token generator — runs on the client via inline script +--- + +
+
+
+ +
+ {[ + { value: 'uuid', label: 'UUID v4' }, + { value: 'hex', label: 'Hex' }, + { value: 'base64', label: 'Base64' }, + ].map(({ value, label }) => ( + + ))} +
+
+ +
+ +
+ + +
+
+
+ +
+
+ + +
+ +
+ +
+
+ + diff --git a/src/data/generators.ts b/src/data/generators.ts index 1a9c824..257eda7 100644 --- a/src/data/generators.ts +++ b/src/data/generators.ts @@ -49,4 +49,25 @@ export const generators: Generator[] = [ icon: 'square-stack', status: 'live', }, + { + slug: 'coin', + title: 'Coin Flip', + description: 'Flip one or more coins and see heads or tails.', + icon: 'circle-dollar-sign', + status: 'live', + }, + { + slug: 'list', + title: 'List Picker', + description: 'Paste a list of items and pick random winners.', + icon: 'list', + status: 'live', + }, + { + slug: 'uuid', + title: 'UUID / Token', + description: 'Generate UUIDs, hex tokens, or random base64 strings.', + icon: 'fingerprint', + status: 'live', + }, ]; diff --git a/src/pages/generators/coin.astro b/src/pages/generators/coin.astro new file mode 100644 index 0000000..f60389d --- /dev/null +++ b/src/pages/generators/coin.astro @@ -0,0 +1,40 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import CoinGenerator from '../../components/generators/CoinGenerator.astro'; +import AdBanner from '../../components/AdBanner.astro'; +import { generators } from '../../data/generators'; + +const generator = generators.find((g) => g.slug === 'coin')!; +--- + + +
+ +
+
+ + randify +
+

{generator.title}

+

{generator.description}

+
+
+
+ +
+
+
diff --git a/src/pages/generators/list.astro b/src/pages/generators/list.astro new file mode 100644 index 0000000..7804085 --- /dev/null +++ b/src/pages/generators/list.astro @@ -0,0 +1,40 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import ListGenerator from '../../components/generators/ListGenerator.astro'; +import AdBanner from '../../components/AdBanner.astro'; +import { generators } from '../../data/generators'; + +const generator = generators.find((g) => g.slug === 'list')!; +--- + + +
+ +
+
+ + randify +
+

{generator.title}

+

{generator.description}

+
+
+
+ +
+
+
diff --git a/src/pages/generators/uuid.astro b/src/pages/generators/uuid.astro new file mode 100644 index 0000000..49a8e87 --- /dev/null +++ b/src/pages/generators/uuid.astro @@ -0,0 +1,40 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import UuidGenerator from '../../components/generators/UuidGenerator.astro'; +import AdBanner from '../../components/AdBanner.astro'; +import { generators } from '../../data/generators'; + +const generator = generators.find((g) => g.slug === 'uuid')!; +--- + + +
+ +
+
+ + randify +
+

{generator.title}

+

{generator.description}

+
+
+
+ +
+
+
From ff40720df2c73b33347eb5ab171a497c3c9ee55d Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 14:10:23 +0300 Subject: [PATCH 10/44] fix: use printf to preserve SSH key newlines in deploy workflow echo strips trailing newlines from multiline secrets, breaking PEM format. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 771c4ac..df3e3f3 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -21,7 +21,7 @@ jobs: - name: Setup SSH key run: | mkdir -p ~/.ssh - echo "${{ secrets.DEPLOY_KEY }}" > ~/.ssh/deploy_key + printf '%s\n' "${{ secrets.DEPLOY_KEY }}" > ~/.ssh/deploy_key chmod 600 ~/.ssh/deploy_key ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts From 3d9b9ca4988fdeab5bfa61e772a513d963b6a934 Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 14:13:10 +0300 Subject: [PATCH 11/44] ci: trigger deploy after SSH key fix From ddcae5094a77ea4bd02312a756c572b55988454e Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 14:17:56 +0300 Subject: [PATCH 12/44] chore: add Yandex site verification file --- public/yandex_dd9dad039a139021.html | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 public/yandex_dd9dad039a139021.html diff --git a/public/yandex_dd9dad039a139021.html b/public/yandex_dd9dad039a139021.html new file mode 100644 index 0000000..4db07c3 --- /dev/null +++ b/public/yandex_dd9dad039a139021.html @@ -0,0 +1,6 @@ + + + + + Verification: dd9dad039a139021 + From 71b5f68064cddb9d21501d89e7338ff1350e8bf2 Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 14:35:43 +0300 Subject: [PATCH 13/44] chore: add site verification meta tag --- src/layouts/BaseLayout.astro | 1 + 1 file changed, 1 insertion(+) diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index be61d27..d8c051a 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -20,6 +20,7 @@ const { + {title} From fe5bb4f39ce5b3a335177d20964d29b255b9fd1b Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 14:40:00 +0300 Subject: [PATCH 14/44] feat: replace ad placeholders with Yandex app referral banners Leaderboard shows horizontal layout with logo, text and CTA button. Tile/rectangle show vertical layout. All link to Yandex referral URL with rel=sponsored. Co-Authored-By: Claude Sonnet 4.6 --- src/components/AdBanner.astro | 54 ++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/src/components/AdBanner.astro b/src/components/AdBanner.astro index f516d91..1073155 100644 --- a/src/components/AdBanner.astro +++ b/src/components/AdBanner.astro @@ -5,20 +5,54 @@ interface Props { const { size } = Astro.props; +const href = 'https://redirect.appmetrica.yandex.com/serve/29835370459904648?clid=15006577&appmetrica_js_redirect=0'; + const specs = { - leaderboard: { label: '728 × 90', class: 'w-full h-[90px]' }, - rectangle: { label: '300 × 250', class: 'w-full max-w-[300px] h-[250px]' }, - tile: { label: '300 × 250', class: 'w-full h-[250px]' }, + leaderboard: { class: 'w-full h-[90px]', horizontal: true }, + rectangle: { class: 'w-full max-w-[300px] h-[250px]', horizontal: false }, + tile: { class: 'w-full h-[250px]', horizontal: false }, }; -const { label, class: sizeClass } = specs[size]; +const { class: sizeClass, horizontal } = specs[size]; --- - From af64203e17f500ec9c1d27402dc6ad6c763ade72 Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 14:43:02 +0300 Subject: [PATCH 15/44] fix: show single tile ad banner per generator page instead of three Co-Authored-By: Claude Sonnet 4.6 --- src/pages/generators/cards.astro | 4 +--- src/pages/generators/coin.astro | 4 ++-- src/pages/generators/colors.astro | 4 +--- src/pages/generators/dice.astro | 4 +--- src/pages/generators/list.astro | 4 ++-- src/pages/generators/lottery.astro | 4 +--- src/pages/generators/numbers.astro | 4 +--- src/pages/generators/password.astro | 4 +--- src/pages/generators/uuid.astro | 4 ++-- 9 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/pages/generators/cards.astro b/src/pages/generators/cards.astro index f606a11..7f35616 100644 --- a/src/pages/generators/cards.astro +++ b/src/pages/generators/cards.astro @@ -54,9 +54,7 @@ const generator = generators.find((g) => g.slug === 'cards')!; -
- - +
diff --git a/src/pages/generators/coin.astro b/src/pages/generators/coin.astro index f60389d..6a4549d 100644 --- a/src/pages/generators/coin.astro +++ b/src/pages/generators/coin.astro @@ -33,8 +33,8 @@ const generator = generators.find((g) => g.slug === 'coin')!;

{generator.description}

-
- +
+
diff --git a/src/pages/generators/colors.astro b/src/pages/generators/colors.astro index fb9f4b9..db7fe7b 100644 --- a/src/pages/generators/colors.astro +++ b/src/pages/generators/colors.astro @@ -54,9 +54,7 @@ const generator = generators.find((g) => g.slug === 'colors')!; -
- - +
diff --git a/src/pages/generators/dice.astro b/src/pages/generators/dice.astro index 0c505d3..0214f07 100644 --- a/src/pages/generators/dice.astro +++ b/src/pages/generators/dice.astro @@ -54,9 +54,7 @@ const generator = generators.find((g) => g.slug === 'dice')!; -
- - +
diff --git a/src/pages/generators/list.astro b/src/pages/generators/list.astro index 7804085..c80c31a 100644 --- a/src/pages/generators/list.astro +++ b/src/pages/generators/list.astro @@ -33,8 +33,8 @@ const generator = generators.find((g) => g.slug === 'list')!;

{generator.description}

-
- +
+
diff --git a/src/pages/generators/lottery.astro b/src/pages/generators/lottery.astro index a27974f..3d87ea8 100644 --- a/src/pages/generators/lottery.astro +++ b/src/pages/generators/lottery.astro @@ -54,9 +54,7 @@ const generator = generators.find((g) => g.slug === 'lottery')!; -
- - +
diff --git a/src/pages/generators/numbers.astro b/src/pages/generators/numbers.astro index 260d39d..cac09f2 100644 --- a/src/pages/generators/numbers.astro +++ b/src/pages/generators/numbers.astro @@ -54,9 +54,7 @@ const generator = generators.find((g) => g.slug === 'numbers')!; -
- - +
diff --git a/src/pages/generators/password.astro b/src/pages/generators/password.astro index 1613a4e..0364387 100644 --- a/src/pages/generators/password.astro +++ b/src/pages/generators/password.astro @@ -54,9 +54,7 @@ const generator = generators.find((g) => g.slug === 'password')!; -
- - +
diff --git a/src/pages/generators/uuid.astro b/src/pages/generators/uuid.astro index 49a8e87..5de87ef 100644 --- a/src/pages/generators/uuid.astro +++ b/src/pages/generators/uuid.astro @@ -33,8 +33,8 @@ const generator = generators.find((g) => g.slug === 'uuid')!;

{generator.description}

-
- +
+
From 80a39035d3f837b4678d54fec502a1d83800e3d4 Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 14:54:29 +0300 Subject: [PATCH 16/44] seo: add sitemap, robots.txt and optimised page titles/descriptions - @astrojs/sitemap generates sitemap-index.xml at build time - robots.txt points crawlers to the sitemap - generators.ts extended with seoTitle and seoDescription per generator - All generator pages now use seoTitle/seoDescription for and <meta description> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --- astro.config.mjs | 3 ++ package-lock.json | 82 +++++++++++++++++++++++++++++ package.json | 3 +- public/robots.txt | 4 ++ src/data/generators.ts | 20 +++++++ src/pages/generators/cards.astro | 4 +- src/pages/generators/coin.astro | 4 +- src/pages/generators/colors.astro | 4 +- src/pages/generators/dice.astro | 4 +- src/pages/generators/list.astro | 4 +- src/pages/generators/lottery.astro | 4 +- src/pages/generators/numbers.astro | 4 +- src/pages/generators/password.astro | 4 +- src/pages/generators/uuid.astro | 4 +- 14 files changed, 129 insertions(+), 19 deletions(-) create mode 100644 public/robots.txt diff --git a/astro.config.mjs b/astro.config.mjs index 2b45c70..518a3c2 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,7 +1,10 @@ import { defineConfig } from 'astro/config'; import tailwindcss from '@tailwindcss/vite'; +import sitemap from '@astrojs/sitemap'; export default defineConfig({ + site: 'https://randify.pro', + integrations: [sitemap()], vite: { plugins: [tailwindcss()], }, diff --git a/package-lock.json b/package-lock.json index 676afb0..c590151 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "randify", "version": "0.0.1", "dependencies": { + "@astrojs/sitemap": "^3.2.1", "@tailwindcss/vite": "^4.0.0", "astro": "^4.16.0", "tailwindcss": "^4.0.0" @@ -59,6 +60,17 @@ "node": "^18.17.1 || ^20.3.0 || >=21.0.0" } }, + "node_modules/@astrojs/sitemap": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.2.1.tgz", + "integrity": "sha512-uxMfO8f7pALq0ADL6Lk68UV6dNYjJ2xGUzyjjVj60JLBs5a6smtlkBYv3tQ0DzoqwS7c9n4FUx5lgv0yPo/fgA==", + "license": "MIT", + "dependencies": { + "sitemap": "^8.0.0", + "stream-replace-string": "^2.0.0", + "zod": "^3.23.8" + } + }, "node_modules/@astrojs/telemetry": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.1.0.tgz", @@ -1795,6 +1807,24 @@ "@types/unist": "*" } }, + "node_modules/@types/node": { + "version": "24.12.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.3.tgz", + "integrity": "sha512-8oljBDGun9cIsZRJR6fkihn0TSXJI0UDOOhncYaERq6M0JMDoPLxyscwruJcb4GKS6dvK/d8xebYBg27h/duaQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -1883,6 +1913,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -5315,6 +5351,15 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/section-matter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", @@ -5417,6 +5462,31 @@ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, + "node_modules/sitemap": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-8.0.3.tgz", + "integrity": "sha512-9Ew1tR2WYw8RGE2XLy7GjkusvYXy8Rg6y8TYuBuQMfIEdGcWoJpY2Wr5DzsEiL/TKCw56+YKTCCUHglorEYK+A==", + "license": "MIT", + "dependencies": { + "@types/node": "^17.0.5", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.4.1" + }, + "bin": { + "sitemap": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/sitemap/node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "license": "MIT" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -5450,6 +5520,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stream-replace-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", + "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==", + "license": "MIT" + }, "node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -5625,6 +5701,12 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, "node_modules/unified": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", diff --git a/package.json b/package.json index fb80cc2..8414a65 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,9 @@ "preview": "astro preview" }, "dependencies": { - "astro": "^4.16.0", + "@astrojs/sitemap": "^3.2.1", "@tailwindcss/vite": "^4.0.0", + "astro": "^4.16.0", "tailwindcss": "^4.0.0" } } diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..2096197 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://randify.pro/sitemap-index.xml diff --git a/src/data/generators.ts b/src/data/generators.ts index 257eda7..7175e3b 100644 --- a/src/data/generators.ts +++ b/src/data/generators.ts @@ -4,6 +4,8 @@ export interface Generator { description: string; icon: string; status: 'live' | 'coming-soon'; + seoTitle: string; + seoDescription: string; } export const generators: Generator[] = [ @@ -13,6 +15,8 @@ export const generators: Generator[] = [ description: 'Pick a random number within any range you choose.', icon: 'hash', status: 'live', + seoTitle: 'Random Number Generator | Randify', + seoDescription: 'Generate a random number between any two values instantly. Free online random number picker — perfect for giveaways, games, and decisions.', }, { slug: 'colors', @@ -20,6 +24,8 @@ export const generators: Generator[] = [ description: 'Generate a random color in HEX, RGB, or HSL format.', icon: 'palette', status: 'live', + seoTitle: 'Random Color Generator — HEX, RGB, HSL | Randify', + seoDescription: 'Get a random color in HEX, RGB or HSL format with one click. Free online color randomizer for designers and developers.', }, { slug: 'password', @@ -27,6 +33,8 @@ export const generators: Generator[] = [ description: 'Create a strong, random password with custom rules.', icon: 'lock', status: 'live', + seoTitle: 'Random Password Generator | Randify', + seoDescription: 'Create a strong random password with custom length, uppercase, lowercase, digits and symbols. Free and secure — runs entirely in your browser.', }, { slug: 'lottery', @@ -34,6 +42,8 @@ export const generators: Generator[] = [ description: 'Draw a set of unique numbers for lottery-style picks.', icon: 'ticket', status: 'live', + seoTitle: 'Lottery Number Generator — Random Pick | Randify', + seoDescription: 'Draw a set of unique random lottery numbers from any range. Perfect for lotteries, raffles, and lucky draws. Free online lottery picker.', }, { slug: 'dice', @@ -41,6 +51,8 @@ export const generators: Generator[] = [ description: 'Roll one or more dice with any number of sides.', icon: 'dice-6', status: 'live', + seoTitle: 'Online Dice Roller — d4, d6, d20 and more | Randify', + seoDescription: 'Roll virtual dice online. Supports d4, d6, d8, d10, d12, d20 and d100. Roll multiple dice at once — great for D&D, board games and tabletop RPGs.', }, { slug: 'cards', @@ -48,6 +60,8 @@ export const generators: Generator[] = [ description: 'Draw a random playing card from a standard deck.', icon: 'square-stack', status: 'live', + seoTitle: 'Random Card Generator — Draw from a Deck | Randify', + seoDescription: 'Draw random playing cards from a standard 52-card deck. Pick any number of cards, with or without replacement. Free online card picker.', }, { slug: 'coin', @@ -55,6 +69,8 @@ export const generators: Generator[] = [ description: 'Flip one or more coins and see heads or tails.', icon: 'circle-dollar-sign', status: 'live', + seoTitle: 'Coin Flip Online — Heads or Tails | Randify', + seoDescription: 'Flip a virtual coin online. Flip multiple coins at once and see how many heads and tails you get. Free random coin toss simulator.', }, { slug: 'list', @@ -62,6 +78,8 @@ export const generators: Generator[] = [ description: 'Paste a list of items and pick random winners.', icon: 'list', status: 'live', + seoTitle: 'Random List Picker — Pick a Random Winner | Randify', + seoDescription: 'Paste any list of names or items and pick random winners instantly. Great for giveaways, choosing who goes first, or any random selection.', }, { slug: 'uuid', @@ -69,5 +87,7 @@ export const generators: Generator[] = [ description: 'Generate UUIDs, hex tokens, or random base64 strings.', icon: 'fingerprint', status: 'live', + seoTitle: 'UUID Generator — UUID v4, Hex & Base64 Tokens | Randify', + seoDescription: 'Generate random UUID v4, hex tokens, or base64 strings online. Cryptographically secure, runs in your browser. Free UUID and token generator.', }, ]; diff --git a/src/pages/generators/cards.astro b/src/pages/generators/cards.astro index 7f35616..1940cac 100644 --- a/src/pages/generators/cards.astro +++ b/src/pages/generators/cards.astro @@ -8,8 +8,8 @@ const generator = generators.find((g) => g.slug === 'cards')!; --- <BaseLayout - title={`${generator.title} Generator — Randify`} - description={generator.description} + title={generator.seoTitle} + description={generator.seoDescription} > <div class="max-w-2xl mx-auto px-4 py-12 sm:py-20"> <nav aria-label="Breadcrumb" class="mb-10"> diff --git a/src/pages/generators/coin.astro b/src/pages/generators/coin.astro index 6a4549d..4283f34 100644 --- a/src/pages/generators/coin.astro +++ b/src/pages/generators/coin.astro @@ -8,8 +8,8 @@ const generator = generators.find((g) => g.slug === 'coin')!; --- <BaseLayout - title={`${generator.title} — Randify`} - description={generator.description} + title={generator.seoTitle} + description={generator.seoDescription} > <div class="max-w-2xl mx-auto px-4 py-12 sm:py-20"> <nav aria-label="Breadcrumb" class="mb-10"> diff --git a/src/pages/generators/colors.astro b/src/pages/generators/colors.astro index db7fe7b..4811afe 100644 --- a/src/pages/generators/colors.astro +++ b/src/pages/generators/colors.astro @@ -8,8 +8,8 @@ const generator = generators.find((g) => g.slug === 'colors')!; --- <BaseLayout - title={`${generator.title} Generator — Randify`} - description={generator.description} + title={generator.seoTitle} + description={generator.seoDescription} > <div class="max-w-2xl mx-auto px-4 py-12 sm:py-20"> <nav aria-label="Breadcrumb" class="mb-10"> diff --git a/src/pages/generators/dice.astro b/src/pages/generators/dice.astro index 0214f07..327c527 100644 --- a/src/pages/generators/dice.astro +++ b/src/pages/generators/dice.astro @@ -8,8 +8,8 @@ const generator = generators.find((g) => g.slug === 'dice')!; --- <BaseLayout - title={`${generator.title} Generator — Randify`} - description={generator.description} + title={generator.seoTitle} + description={generator.seoDescription} > <div class="max-w-2xl mx-auto px-4 py-12 sm:py-20"> <nav aria-label="Breadcrumb" class="mb-10"> diff --git a/src/pages/generators/list.astro b/src/pages/generators/list.astro index c80c31a..6aa409d 100644 --- a/src/pages/generators/list.astro +++ b/src/pages/generators/list.astro @@ -8,8 +8,8 @@ const generator = generators.find((g) => g.slug === 'list')!; --- <BaseLayout - title={`${generator.title} — Randify`} - description={generator.description} + title={generator.seoTitle} + description={generator.seoDescription} > <div class="max-w-2xl mx-auto px-4 py-12 sm:py-20"> <nav aria-label="Breadcrumb" class="mb-10"> diff --git a/src/pages/generators/lottery.astro b/src/pages/generators/lottery.astro index 3d87ea8..3b091d4 100644 --- a/src/pages/generators/lottery.astro +++ b/src/pages/generators/lottery.astro @@ -8,8 +8,8 @@ const generator = generators.find((g) => g.slug === 'lottery')!; --- <BaseLayout - title={`${generator.title} Generator — Randify`} - description={generator.description} + title={generator.seoTitle} + description={generator.seoDescription} > <div class="max-w-2xl mx-auto px-4 py-12 sm:py-20"> <nav aria-label="Breadcrumb" class="mb-10"> diff --git a/src/pages/generators/numbers.astro b/src/pages/generators/numbers.astro index cac09f2..17a2a03 100644 --- a/src/pages/generators/numbers.astro +++ b/src/pages/generators/numbers.astro @@ -8,8 +8,8 @@ const generator = generators.find((g) => g.slug === 'numbers')!; --- <BaseLayout - title={`${generator.title} Generator — Randify`} - description={generator.description} + title={generator.seoTitle} + description={generator.seoDescription} > <div class="max-w-2xl mx-auto px-4 py-12 sm:py-20"> <nav aria-label="Breadcrumb" class="mb-10"> diff --git a/src/pages/generators/password.astro b/src/pages/generators/password.astro index 0364387..2ff91bf 100644 --- a/src/pages/generators/password.astro +++ b/src/pages/generators/password.astro @@ -8,8 +8,8 @@ const generator = generators.find((g) => g.slug === 'password')!; --- <BaseLayout - title={`${generator.title} Generator — Randify`} - description={generator.description} + title={generator.seoTitle} + description={generator.seoDescription} > <div class="max-w-2xl mx-auto px-4 py-12 sm:py-20"> <nav aria-label="Breadcrumb" class="mb-10"> diff --git a/src/pages/generators/uuid.astro b/src/pages/generators/uuid.astro index 5de87ef..2c7f501 100644 --- a/src/pages/generators/uuid.astro +++ b/src/pages/generators/uuid.astro @@ -8,8 +8,8 @@ const generator = generators.find((g) => g.slug === 'uuid')!; --- <BaseLayout - title={`${generator.title} — Randify`} - description={generator.description} + title={generator.seoTitle} + description={generator.seoDescription} > <div class="max-w-2xl mx-auto px-4 py-12 sm:py-20"> <nav aria-label="Breadcrumb" class="mb-10"> From 57f1a8d705f8cacc4896e085a167f40541fa9289 Mon Sep 17 00:00:00 2001 From: emil <emil28092005@gmail.com> Date: Sat, 9 May 2026 15:03:28 +0300 Subject: [PATCH 17/44] seo: add How to use / When to use text blocks to all generator pages Each page now has a subtle text section with keyword-rich content for search engine indexing, without disrupting the tool UX. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --- src/components/SeoBlock.astro | 23 +++++++++++++++++++++++ src/pages/generators/cards.astro | 16 ++++++++++++++++ src/pages/generators/coin.astro | 16 ++++++++++++++++ src/pages/generators/colors.astro | 15 +++++++++++++++ src/pages/generators/dice.astro | 16 ++++++++++++++++ src/pages/generators/list.astro | 16 ++++++++++++++++ src/pages/generators/lottery.astro | 16 ++++++++++++++++ src/pages/generators/numbers.astro | 16 ++++++++++++++++ src/pages/generators/password.astro | 16 ++++++++++++++++ src/pages/generators/uuid.astro | 16 ++++++++++++++++ 10 files changed, 166 insertions(+) create mode 100644 src/components/SeoBlock.astro diff --git a/src/components/SeoBlock.astro b/src/components/SeoBlock.astro new file mode 100644 index 0000000..7cb73cb --- /dev/null +++ b/src/components/SeoBlock.astro @@ -0,0 +1,23 @@ +--- +interface Props { + howTo: string[]; + whenTo: string[]; +} + +const { howTo, whenTo } = Astro.props; +--- + +<section class="mt-12 border-t border-zinc-800/60 pt-8 text-sm text-zinc-500 space-y-4"> + <div> + <h2 class="text-xs font-semibold uppercase tracking-widest text-zinc-600 mb-2">How to use</h2> + <ol class="space-y-1 list-decimal list-inside"> + {howTo.map((step) => <li>{step}</li>)} + </ol> + </div> + <div> + <h2 class="text-xs font-semibold uppercase tracking-widest text-zinc-600 mb-2">When to use</h2> + <ul class="space-y-1 list-disc list-inside"> + {whenTo.map((item) => <li>{item}</li>)} + </ul> + </div> +</section> diff --git a/src/pages/generators/cards.astro b/src/pages/generators/cards.astro index 1940cac..da4fef4 100644 --- a/src/pages/generators/cards.astro +++ b/src/pages/generators/cards.astro @@ -3,6 +3,7 @@ import BaseLayout from '../../layouts/BaseLayout.astro'; import CardGenerator from '../../components/generators/CardGenerator.astro'; import AdBanner from '../../components/AdBanner.astro'; import { generators } from '../../data/generators'; +import SeoBlock from '../../components/SeoBlock.astro'; const generator = generators.find((g) => g.slug === 'cards')!; --- @@ -54,6 +55,21 @@ const generator = generators.find((g) => g.slug === 'cards')!; <CardGenerator /> </main> + <SeoBlock + howTo={[ + 'Set how many cards to draw (1–52).', + 'Toggle Allow duplicates if you want cards to repeat.', + 'Click Draw — cards are shown as mini card tiles.', + 'Click Copy to copy the result (e.g. A♠, K♥) to your clipboard.', + ]} + whenTo={[ + 'Drawing a random hand for card games.', + 'Practising card probability and statistics.', + 'Simulating a random card pull for magic tricks or puzzles.', + 'Running fair draws without a physical deck.', + ]} + /> + <div class="mt-12 flex justify-center"> <AdBanner size="tile" /> </div> diff --git a/src/pages/generators/coin.astro b/src/pages/generators/coin.astro index 4283f34..d2bc588 100644 --- a/src/pages/generators/coin.astro +++ b/src/pages/generators/coin.astro @@ -3,6 +3,7 @@ import BaseLayout from '../../layouts/BaseLayout.astro'; import CoinGenerator from '../../components/generators/CoinGenerator.astro'; import AdBanner from '../../components/AdBanner.astro'; import { generators } from '../../data/generators'; +import SeoBlock from '../../components/SeoBlock.astro'; const generator = generators.find((g) => g.slug === 'coin')!; --- @@ -33,6 +34,21 @@ const generator = generators.find((g) => g.slug === 'coin')!; <p class="mt-2 text-base text-zinc-400">{generator.description}</p> </header> <main><CoinGenerator /></main> + <SeoBlock + howTo={[ + 'Drag the slider to choose how many coins to flip (1–20).', + 'Click Flip — each coin shows H for heads or T for tails.', + 'When flipping more than one coin, the heads/tails count is shown below.', + 'Click Copy to copy all results to the clipboard.', + ]} + whenTo={[ + 'Making a quick heads-or-tails decision.', + 'Simulating a coin toss for a sports match or game.', + 'Demonstrating probability and the law of large numbers.', + 'Settling a fair 50/50 choice between two options.', + ]} + /> + <div class="mt-12 flex justify-center"> <AdBanner size="tile" /> </div> diff --git a/src/pages/generators/colors.astro b/src/pages/generators/colors.astro index 4811afe..dc361c1 100644 --- a/src/pages/generators/colors.astro +++ b/src/pages/generators/colors.astro @@ -3,6 +3,7 @@ import BaseLayout from '../../layouts/BaseLayout.astro'; import ColorGenerator from '../../components/generators/ColorGenerator.astro'; import AdBanner from '../../components/AdBanner.astro'; import { generators } from '../../data/generators'; +import SeoBlock from '../../components/SeoBlock.astro'; const generator = generators.find((g) => g.slug === 'colors')!; --- @@ -54,6 +55,20 @@ const generator = generators.find((g) => g.slug === 'colors')!; <ColorGenerator /> </main> + <SeoBlock + howTo={[ + 'Click Generate to produce a random color.', + 'Switch between HEX, RGB, and HSL tabs to see the value in your preferred format.', + 'Click the color value or the copy icon to copy it to the clipboard.', + ]} + whenTo={[ + 'Finding inspiration for a new design palette.', + 'Picking a placeholder color while prototyping.', + 'Selecting a random theme color for a project or presentation.', + 'Teaching or exploring color theory and formats.', + ]} + /> + <div class="mt-12 flex justify-center"> <AdBanner size="tile" /> </div> diff --git a/src/pages/generators/dice.astro b/src/pages/generators/dice.astro index 327c527..e95ab86 100644 --- a/src/pages/generators/dice.astro +++ b/src/pages/generators/dice.astro @@ -3,6 +3,7 @@ import BaseLayout from '../../layouts/BaseLayout.astro'; import DiceGenerator from '../../components/generators/DiceGenerator.astro'; import AdBanner from '../../components/AdBanner.astro'; import { generators } from '../../data/generators'; +import SeoBlock from '../../components/SeoBlock.astro'; const generator = generators.find((g) => g.slug === 'dice')!; --- @@ -54,6 +55,21 @@ const generator = generators.find((g) => g.slug === 'dice')!; <DiceGenerator /> </main> + <SeoBlock + howTo={[ + 'Select the die type: d4, d6, d8, d10, d12, d20, or d100.', + 'Set how many dice to roll (1–20).', + 'Click Roll — each die result appears with the total sum.', + 'Maximum rolls are highlighted in purple; ones are highlighted in red.', + ]} + whenTo={[ + 'Rolling dice for D&D, Pathfinder, or any tabletop RPG.', + 'Replacing physical dice in board games.', + 'Simulating probability outcomes in statistics or education.', + 'Settling disputes with a fair digital dice roll.', + ]} + /> + <div class="mt-12 flex justify-center"> <AdBanner size="tile" /> </div> diff --git a/src/pages/generators/list.astro b/src/pages/generators/list.astro index 6aa409d..eb27484 100644 --- a/src/pages/generators/list.astro +++ b/src/pages/generators/list.astro @@ -3,6 +3,7 @@ import BaseLayout from '../../layouts/BaseLayout.astro'; import ListGenerator from '../../components/generators/ListGenerator.astro'; import AdBanner from '../../components/AdBanner.astro'; import { generators } from '../../data/generators'; +import SeoBlock from '../../components/SeoBlock.astro'; const generator = generators.find((g) => g.slug === 'list')!; --- @@ -33,6 +34,21 @@ const generator = generators.find((g) => g.slug === 'list')!; <p class="mt-2 text-base text-zinc-400">{generator.description}</p> </header> <main><ListGenerator /></main> + <SeoBlock + howTo={[ + 'Type or paste your items into the text area — one item per line.', + 'Set how many items to pick in the Pick field.', + 'Toggle Allow duplicates if the same item can be chosen more than once.', + 'Click Pick (or press Ctrl+Enter) — the selected items appear as chips.', + ]} + whenTo={[ + 'Choosing a random winner for a giveaway or contest.', + 'Picking who presents first in a meeting or class.', + 'Organising a Secret Santa or gift exchange draw.', + 'Selecting a random movie, restaurant, or activity from a list.', + ]} + /> + <div class="mt-12 flex justify-center"> <AdBanner size="tile" /> </div> diff --git a/src/pages/generators/lottery.astro b/src/pages/generators/lottery.astro index 3b091d4..d00bffe 100644 --- a/src/pages/generators/lottery.astro +++ b/src/pages/generators/lottery.astro @@ -3,6 +3,7 @@ import BaseLayout from '../../layouts/BaseLayout.astro'; import LotteryGenerator from '../../components/generators/LotteryGenerator.astro'; import AdBanner from '../../components/AdBanner.astro'; import { generators } from '../../data/generators'; +import SeoBlock from '../../components/SeoBlock.astro'; const generator = generators.find((g) => g.slug === 'lottery')!; --- @@ -54,6 +55,21 @@ const generator = generators.find((g) => g.slug === 'lottery')!; <LotteryGenerator /> </main> + <SeoBlock + howTo={[ + 'Set the From and To fields to define your number range (e.g. 1 to 49).', + 'Enter how many numbers to pick in the Pick field.', + 'Click Draw — a sorted set of unique numbers appears.', + 'Click Copy to copy all numbers to the clipboard.', + ]} + whenTo={[ + 'Generating lottery or lotto ticket numbers.', + 'Running a fair raffle draw with unique entries.', + 'Selecting a random sample from a numbered list.', + 'Choosing lottery numbers for Powerball, EuroMillions, or local draws.', + ]} + /> + <div class="mt-12 flex justify-center"> <AdBanner size="tile" /> </div> diff --git a/src/pages/generators/numbers.astro b/src/pages/generators/numbers.astro index 17a2a03..04deb31 100644 --- a/src/pages/generators/numbers.astro +++ b/src/pages/generators/numbers.astro @@ -3,6 +3,7 @@ import BaseLayout from '../../layouts/BaseLayout.astro'; import NumberGenerator from '../../components/generators/NumberGenerator.astro'; import AdBanner from '../../components/AdBanner.astro'; import { generators } from '../../data/generators'; +import SeoBlock from '../../components/SeoBlock.astro'; const generator = generators.find((g) => g.slug === 'numbers')!; --- @@ -54,6 +55,21 @@ const generator = generators.find((g) => g.slug === 'numbers')!; <NumberGenerator /> </main> + <SeoBlock + howTo={[ + 'Enter the minimum value in the From field.', + 'Enter the maximum value in the To field.', + 'Click Generate — a random number within your range appears instantly.', + 'Click the number to copy it to the clipboard.', + ]} + whenTo={[ + 'Picking a winner in a giveaway or raffle.', + 'Choosing a random starting player in a board game.', + 'Making a quick decision between numbered options.', + 'Generating test data with values in a specific range.', + ]} + /> + <div class="mt-12 flex justify-center"> <AdBanner size="tile" /> </div> diff --git a/src/pages/generators/password.astro b/src/pages/generators/password.astro index 2ff91bf..8c83bbb 100644 --- a/src/pages/generators/password.astro +++ b/src/pages/generators/password.astro @@ -3,6 +3,7 @@ import BaseLayout from '../../layouts/BaseLayout.astro'; import PasswordGenerator from '../../components/generators/PasswordGenerator.astro'; import AdBanner from '../../components/AdBanner.astro'; import { generators } from '../../data/generators'; +import SeoBlock from '../../components/SeoBlock.astro'; const generator = generators.find((g) => g.slug === 'password')!; --- @@ -54,6 +55,21 @@ const generator = generators.find((g) => g.slug === 'password')!; <PasswordGenerator /> </main> + <SeoBlock + howTo={[ + 'Drag the length slider to set how many characters you need (4–64).', + 'Toggle uppercase, lowercase, digits, and symbols on or off.', + 'Click Generate — a new password appears instantly.', + 'Click the password to copy it to your clipboard.', + ]} + whenTo={[ + 'Creating a strong password for a new account.', + 'Generating a temporary access credential.', + 'Producing a random secret key for development or testing.', + 'Replacing a weak or reused password.', + ]} + /> + <div class="mt-12 flex justify-center"> <AdBanner size="tile" /> </div> diff --git a/src/pages/generators/uuid.astro b/src/pages/generators/uuid.astro index 2c7f501..9aa09ae 100644 --- a/src/pages/generators/uuid.astro +++ b/src/pages/generators/uuid.astro @@ -3,6 +3,7 @@ import BaseLayout from '../../layouts/BaseLayout.astro'; import UuidGenerator from '../../components/generators/UuidGenerator.astro'; import AdBanner from '../../components/AdBanner.astro'; import { generators } from '../../data/generators'; +import SeoBlock from '../../components/SeoBlock.astro'; const generator = generators.find((g) => g.slug === 'uuid')!; --- @@ -33,6 +34,21 @@ const generator = generators.find((g) => g.slug === 'uuid')!; <p class="mt-2 text-base text-zinc-400">{generator.description}</p> </header> <main><UuidGenerator /></main> + <SeoBlock + howTo={[ + 'Choose a token type: UUID v4, Hex, or Base64.', + 'For Hex and Base64, set the byte length (4–64 bytes).', + 'Enter how many tokens to generate (1–20).', + 'Click Generate, then Copy all to copy every token to the clipboard.', + ]} + whenTo={[ + 'Generating unique IDs for database records or API resources.', + 'Creating random session tokens or API keys during development.', + 'Producing test data with realistic-looking identifiers.', + 'Generating a one-time secret for environment variables or config files.', + ]} + /> + <div class="mt-12 flex justify-center"> <AdBanner size="tile" /> </div> From 70eac59eb83a6b07c666ab19c752a13422c0727b Mon Sep 17 00:00:00 2001 From: emil <emil28092005@gmail.com> Date: Sat, 9 May 2026 15:04:44 +0300 Subject: [PATCH 18/44] fix: move SEO text blocks below ad banner on generator pages Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --- src/pages/generators/cards.astro | 8 ++++---- src/pages/generators/coin.astro | 8 ++++---- src/pages/generators/colors.astro | 8 ++++---- src/pages/generators/dice.astro | 8 ++++---- src/pages/generators/list.astro | 8 ++++---- src/pages/generators/lottery.astro | 8 ++++---- src/pages/generators/numbers.astro | 8 ++++---- src/pages/generators/password.astro | 8 ++++---- src/pages/generators/uuid.astro | 8 ++++---- 9 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/pages/generators/cards.astro b/src/pages/generators/cards.astro index da4fef4..f7c7470 100644 --- a/src/pages/generators/cards.astro +++ b/src/pages/generators/cards.astro @@ -55,6 +55,10 @@ const generator = generators.find((g) => g.slug === 'cards')!; <CardGenerator /> </main> + <div class="mt-12 flex justify-center"> + <AdBanner size="tile" /> + </div> + <SeoBlock howTo={[ 'Set how many cards to draw (1–52).', @@ -69,9 +73,5 @@ const generator = generators.find((g) => g.slug === 'cards')!; 'Running fair draws without a physical deck.', ]} /> - - <div class="mt-12 flex justify-center"> - <AdBanner size="tile" /> - </div> </div> </BaseLayout> diff --git a/src/pages/generators/coin.astro b/src/pages/generators/coin.astro index d2bc588..ffa7b9d 100644 --- a/src/pages/generators/coin.astro +++ b/src/pages/generators/coin.astro @@ -34,6 +34,10 @@ const generator = generators.find((g) => g.slug === 'coin')!; <p class="mt-2 text-base text-zinc-400">{generator.description}</p> </header> <main><CoinGenerator /></main> + <div class="mt-12 flex justify-center"> + <AdBanner size="tile" /> + </div> + <SeoBlock howTo={[ 'Drag the slider to choose how many coins to flip (1–20).', @@ -48,9 +52,5 @@ const generator = generators.find((g) => g.slug === 'coin')!; 'Settling a fair 50/50 choice between two options.', ]} /> - - <div class="mt-12 flex justify-center"> - <AdBanner size="tile" /> - </div> </div> </BaseLayout> diff --git a/src/pages/generators/colors.astro b/src/pages/generators/colors.astro index dc361c1..3941e2c 100644 --- a/src/pages/generators/colors.astro +++ b/src/pages/generators/colors.astro @@ -55,6 +55,10 @@ const generator = generators.find((g) => g.slug === 'colors')!; <ColorGenerator /> </main> + <div class="mt-12 flex justify-center"> + <AdBanner size="tile" /> + </div> + <SeoBlock howTo={[ 'Click Generate to produce a random color.', @@ -68,9 +72,5 @@ const generator = generators.find((g) => g.slug === 'colors')!; 'Teaching or exploring color theory and formats.', ]} /> - - <div class="mt-12 flex justify-center"> - <AdBanner size="tile" /> - </div> </div> </BaseLayout> diff --git a/src/pages/generators/dice.astro b/src/pages/generators/dice.astro index e95ab86..b62d27b 100644 --- a/src/pages/generators/dice.astro +++ b/src/pages/generators/dice.astro @@ -55,6 +55,10 @@ const generator = generators.find((g) => g.slug === 'dice')!; <DiceGenerator /> </main> + <div class="mt-12 flex justify-center"> + <AdBanner size="tile" /> + </div> + <SeoBlock howTo={[ 'Select the die type: d4, d6, d8, d10, d12, d20, or d100.', @@ -69,9 +73,5 @@ const generator = generators.find((g) => g.slug === 'dice')!; 'Settling disputes with a fair digital dice roll.', ]} /> - - <div class="mt-12 flex justify-center"> - <AdBanner size="tile" /> - </div> </div> </BaseLayout> diff --git a/src/pages/generators/list.astro b/src/pages/generators/list.astro index eb27484..85ba1d4 100644 --- a/src/pages/generators/list.astro +++ b/src/pages/generators/list.astro @@ -34,6 +34,10 @@ const generator = generators.find((g) => g.slug === 'list')!; <p class="mt-2 text-base text-zinc-400">{generator.description}</p> </header> <main><ListGenerator /></main> + <div class="mt-12 flex justify-center"> + <AdBanner size="tile" /> + </div> + <SeoBlock howTo={[ 'Type or paste your items into the text area — one item per line.', @@ -48,9 +52,5 @@ const generator = generators.find((g) => g.slug === 'list')!; 'Selecting a random movie, restaurant, or activity from a list.', ]} /> - - <div class="mt-12 flex justify-center"> - <AdBanner size="tile" /> - </div> </div> </BaseLayout> diff --git a/src/pages/generators/lottery.astro b/src/pages/generators/lottery.astro index d00bffe..8933ffc 100644 --- a/src/pages/generators/lottery.astro +++ b/src/pages/generators/lottery.astro @@ -55,6 +55,10 @@ const generator = generators.find((g) => g.slug === 'lottery')!; <LotteryGenerator /> </main> + <div class="mt-12 flex justify-center"> + <AdBanner size="tile" /> + </div> + <SeoBlock howTo={[ 'Set the From and To fields to define your number range (e.g. 1 to 49).', @@ -69,9 +73,5 @@ const generator = generators.find((g) => g.slug === 'lottery')!; 'Choosing lottery numbers for Powerball, EuroMillions, or local draws.', ]} /> - - <div class="mt-12 flex justify-center"> - <AdBanner size="tile" /> - </div> </div> </BaseLayout> diff --git a/src/pages/generators/numbers.astro b/src/pages/generators/numbers.astro index 04deb31..35deaea 100644 --- a/src/pages/generators/numbers.astro +++ b/src/pages/generators/numbers.astro @@ -55,6 +55,10 @@ const generator = generators.find((g) => g.slug === 'numbers')!; <NumberGenerator /> </main> + <div class="mt-12 flex justify-center"> + <AdBanner size="tile" /> + </div> + <SeoBlock howTo={[ 'Enter the minimum value in the From field.', @@ -69,9 +73,5 @@ const generator = generators.find((g) => g.slug === 'numbers')!; 'Generating test data with values in a specific range.', ]} /> - - <div class="mt-12 flex justify-center"> - <AdBanner size="tile" /> - </div> </div> </BaseLayout> diff --git a/src/pages/generators/password.astro b/src/pages/generators/password.astro index 8c83bbb..a1b1e6f 100644 --- a/src/pages/generators/password.astro +++ b/src/pages/generators/password.astro @@ -55,6 +55,10 @@ const generator = generators.find((g) => g.slug === 'password')!; <PasswordGenerator /> </main> + <div class="mt-12 flex justify-center"> + <AdBanner size="tile" /> + </div> + <SeoBlock howTo={[ 'Drag the length slider to set how many characters you need (4–64).', @@ -69,9 +73,5 @@ const generator = generators.find((g) => g.slug === 'password')!; 'Replacing a weak or reused password.', ]} /> - - <div class="mt-12 flex justify-center"> - <AdBanner size="tile" /> - </div> </div> </BaseLayout> diff --git a/src/pages/generators/uuid.astro b/src/pages/generators/uuid.astro index 9aa09ae..57f0240 100644 --- a/src/pages/generators/uuid.astro +++ b/src/pages/generators/uuid.astro @@ -34,6 +34,10 @@ const generator = generators.find((g) => g.slug === 'uuid')!; <p class="mt-2 text-base text-zinc-400">{generator.description}</p> </header> <main><UuidGenerator /></main> + <div class="mt-12 flex justify-center"> + <AdBanner size="tile" /> + </div> + <SeoBlock howTo={[ 'Choose a token type: UUID v4, Hex, or Base64.', @@ -48,9 +52,5 @@ const generator = generators.find((g) => g.slug === 'uuid')!; 'Generating a one-time secret for environment variables or config files.', ]} /> - - <div class="mt-12 flex justify-center"> - <AdBanner size="tile" /> - </div> </div> </BaseLayout> From 2ba1b53ba8fb0861e9a6de9d754fb1c892010b10 Mon Sep 17 00:00:00 2001 From: emil <emil28092005@gmail.com> Date: Sat, 9 May 2026 15:10:51 +0300 Subject: [PATCH 19/44] feat: randomise ad banner between Yandex app and Yandex Browser offers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --- src/components/AdBanner.astro | 38 +++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/components/AdBanner.astro b/src/components/AdBanner.astro index 1073155..1f40538 100644 --- a/src/components/AdBanner.astro +++ b/src/components/AdBanner.astro @@ -5,12 +5,29 @@ interface Props { const { size } = Astro.props; -const href = 'https://redirect.appmetrica.yandex.com/serve/29835370459904648?clid=15006577&appmetrica_js_redirect=0'; +const ads = [ + { + href: 'https://redirect.appmetrica.yandex.com/serve/29835370459904648?clid=15006577&appmetrica_js_redirect=0', + label: 'Яндекс с Алисой — скачать бесплатно', + sublabel: 'Голосовой ИИ-помощник для Android и iOS', + cta: 'Установить', + ctaMobile: 'Установить бесплатно', + }, + { + href: 'https://yandex.ru/project/browser/bonus/multioffer/affiliate_4prod?source=pWRP8eS1VsC2X59560&partner_string=P89XvN11U6RuE47077&cliddbro=15006579&clidmbro=15006578&cliddefault=15006570&clidpp=15006567', + label: 'Яндекс Браузер — быстрый и безопасный', + sublabel: 'Браузер с Алисой и защитой от угроз', + cta: 'Скачать', + ctaMobile: 'Скачать бесплатно', + }, +]; + +const ad = ads[Math.floor(Math.random() * ads.length)]; const specs = { - leaderboard: { class: 'w-full h-[90px]', horizontal: true }, + leaderboard: { class: 'w-full h-[90px]', horizontal: true }, rectangle: { class: 'w-full max-w-[300px] h-[250px]', horizontal: false }, - tile: { class: 'w-full h-[250px]', horizontal: false }, + tile: { class: 'w-full h-[250px]', horizontal: false }, }; const { class: sizeClass, horizontal } = specs[size]; @@ -18,12 +35,11 @@ const { class: sizeClass, horizontal } = specs[size]; <aside aria-label="Реклама"> <a - href={href} + href={ad.href} target="_blank" rel="noopener sponsored" class={`${sizeClass} flex ${horizontal ? 'flex-row items-center gap-4 px-6' : 'flex-col items-center justify-center gap-3 px-4'} rounded-xl border border-zinc-800 bg-zinc-900/50 hover:border-[#534AB7]/50 hover:bg-zinc-900 transition-colors duration-200 group`} > - <!-- Yandex logo --> <svg class="shrink-0 text-[#FC3F1D]" width={horizontal ? '32' : '40'} height={horizontal ? '32' : '40'} viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> <circle cx="20" cy="20" r="20" fill="currentColor"/> <path d="M22.44 10H19.1c-3.78 0-6.1 2.02-6.1 5.32 0 2.66 1.26 4.24 3.64 5.8L13.5 30h3.38l3.4-8.38-.96-.58c-1.84-1.12-2.72-2.08-2.72-3.92 0-1.94 1.3-3.08 3.44-3.08h1.4V30h3V10z" fill="white"/> @@ -33,24 +49,24 @@ const { class: sizeClass, horizontal } = specs[size]; <div class="flex-1 min-w-0"> <div class="text-xs font-medium text-zinc-500 uppercase tracking-widest mb-0.5">Реклама</div> <div class="text-sm font-semibold text-zinc-100 group-hover:text-[#534AB7] transition-colors leading-tight"> - Яндекс с Алисой — скачать бесплатно + {ad.label} </div> - <div class="text-xs text-zinc-500 mt-0.5">Голосовой ИИ-помощник для Android и iOS</div> + <div class="text-xs text-zinc-500 mt-0.5">{ad.sublabel}</div> </div> <span class="shrink-0 text-xs font-semibold text-white bg-[#FC3F1D] px-3 py-1.5 rounded-lg group-hover:bg-[#e03518] transition-colors whitespace-nowrap"> - Установить + {ad.cta} </span> ) : ( <> <div class="text-center"> <div class="text-xs font-medium text-zinc-500 uppercase tracking-widest mb-2">Реклама</div> <div class="text-base font-semibold text-zinc-100 group-hover:text-[#534AB7] transition-colors leading-snug"> - Яндекс с Алисой + {ad.label} </div> - <div class="text-xs text-zinc-500 mt-1">Голосовой ИИ-помощник</div> + <div class="text-xs text-zinc-500 mt-1">{ad.sublabel}</div> </div> <span class="text-xs font-semibold text-white bg-[#FC3F1D] px-4 py-2 rounded-lg group-hover:bg-[#e03518] transition-colors"> - Установить бесплатно + {ad.ctaMobile} </span> </> )} From f0ed8304b608ea4e548eeac67940e45df83a581c Mon Sep 17 00:00:00 2001 From: emil <emil28092005@gmail.com> Date: Sat, 9 May 2026 15:30:52 +0300 Subject: [PATCH 20/44] feat: add Yandex Metrika counter to all pages Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --- src/layouts/BaseLayout.astro | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index d8c051a..b7e1d68 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -22,6 +22,18 @@ const { <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&display=swap" rel="stylesheet" /> <meta name="verification" content="er9ndnv9ih7agmh8" /> <title>{title} + + + + From 3863eb214e402d33d33646cbbd730355992aca11 Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 16:03:30 +0300 Subject: [PATCH 21/44] feat: add Spin the Wheel generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Canvas-based wheel with 8-color palette, ease-out cubic spin animation, deterministic winner calculation, confetti burst on result, copy to clipboard. Supports 2–24 custom items with live textarea update. Co-Authored-By: Claude Sonnet 4.6 --- src/components/GeneratorCard.astro | 1 + src/components/generators/WheelSpinner.astro | 338 +++++++++++++++++++ src/data/generators.ts | 9 + src/pages/generators/wheel.astro | 77 +++++ 4 files changed, 425 insertions(+) create mode 100644 src/components/generators/WheelSpinner.astro create mode 100644 src/pages/generators/wheel.astro diff --git a/src/components/GeneratorCard.astro b/src/components/GeneratorCard.astro index b301bdb..9f54261 100644 --- a/src/components/GeneratorCard.astro +++ b/src/components/GeneratorCard.astro @@ -20,6 +20,7 @@ const icons: Record = { 'circle-dollar-sign': ``, 'list': ``, 'fingerprint': ``, + 'pie-chart': ``, }; --- diff --git a/src/components/generators/WheelSpinner.astro b/src/components/generators/WheelSpinner.astro new file mode 100644 index 0000000..a09e087 --- /dev/null +++ b/src/components/generators/WheelSpinner.astro @@ -0,0 +1,338 @@ +--- +// Wheel of Fortune spinner — canvas-based, vanilla JS, no libraries +--- + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + +
+ + +
+ + + + +
+ + + + diff --git a/src/data/generators.ts b/src/data/generators.ts index 7175e3b..f4b23d2 100644 --- a/src/data/generators.ts +++ b/src/data/generators.ts @@ -90,4 +90,13 @@ export const generators: Generator[] = [ seoTitle: 'UUID Generator — UUID v4, Hex & Base64 Tokens | Randify', seoDescription: 'Generate random UUID v4, hex tokens, or base64 strings online. Cryptographically secure, runs in your browser. Free UUID and token generator.', }, + { + slug: 'wheel', + title: 'Spin the Wheel', + description: 'Add your items, spin the wheel, and let chance decide.', + icon: 'pie-chart', + status: 'live', + seoTitle: 'Spin the Wheel — Random Picker | Randify', + seoDescription: 'Spin a customizable wheel of fortune to pick a random winner, choice, or option. Add your own items and let the wheel decide.', + }, ]; diff --git a/src/pages/generators/wheel.astro b/src/pages/generators/wheel.astro new file mode 100644 index 0000000..55f057b --- /dev/null +++ b/src/pages/generators/wheel.astro @@ -0,0 +1,77 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import WheelSpinner from '../../components/generators/WheelSpinner.astro'; +import AdBanner from '../../components/AdBanner.astro'; +import SeoBlock from '../../components/SeoBlock.astro'; +import { generators } from '../../data/generators'; + +const generator = generators.find((g) => g.slug === 'wheel')!; +--- + + +
+ + +
+
+ + randify +
+

+ {generator.title} +

+

{generator.description}

+
+ +
+ +
+ +
+ +
+ + +
+
From e72312f23ff649c8dc5812402ee4d094bfd4a212 Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 16:06:37 +0300 Subject: [PATCH 22/44] fix: confetti uses position:fixed to prevent page layout expansion Co-Authored-By: Claude Sonnet 4.6 --- src/components/generators/WheelSpinner.astro | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/generators/WheelSpinner.astro b/src/components/generators/WheelSpinner.astro index a09e087..3f1885d 100644 --- a/src/components/generators/WheelSpinner.astro +++ b/src/components/generators/WheelSpinner.astro @@ -290,10 +290,10 @@ Frank // ── Confetti ───────────────────────────────────────────────────── function spawnConfetti() { + // Use viewport coordinates + position:fixed so confetti never affects page layout const cr = canvas.getBoundingClientRect(); - const wr = wheelWrap.getBoundingClientRect(); - const cx = cr.left - wr.left + cr.width / 2; - const cy = cr.top - wr.top + cr.height / 2; + const cx = cr.left + cr.width / 2; + const cy = cr.top + cr.height / 2; type Particle = { el: HTMLDivElement; x: number; y: number; vx: number; vy: number }; const particles: Particle[] = []; @@ -302,10 +302,10 @@ Frank const el = document.createElement('div'); const w = 4 + Math.random() * 6; const h = 4 + Math.random() * 6; - el.style.cssText = `position:absolute;width:${w}px;height:${h}px;` + + el.style.cssText = `position:fixed;width:${w}px;height:${h}px;` + `background:${COLORS[i % COLORS.length]};border-radius:1px;` + - `pointer-events:none;left:${cx}px;top:${cy}px;will-change:transform,opacity;`; - wheelWrap.appendChild(el); + `pointer-events:none;left:${cx}px;top:${cy}px;will-change:transform,opacity;z-index:9999;`; + document.body.appendChild(el); const angle = Math.random() * TAU; const speed = 4 + Math.random() * 7; From 6e540fdb124e568c96c5e8d0239120e943c30062 Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 16:08:41 +0300 Subject: [PATCH 23/44] feat: wheel auto-updates on textarea input with 300ms debounce Co-Authored-By: Claude Sonnet 4.6 --- src/components/generators/WheelSpinner.astro | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/generators/WheelSpinner.astro b/src/components/generators/WheelSpinner.astro index 3f1885d..736ee57 100644 --- a/src/components/generators/WheelSpinner.astro +++ b/src/components/generators/WheelSpinner.astro @@ -205,9 +205,17 @@ Frank function showError(msg: string) { errorEl.textContent = msg; errorEl.classList.remove('hidden'); } function clearError() { errorEl.textContent = ''; errorEl.classList.add('hidden'); } - updateBtn.addEventListener('click', () => { + function applyItems() { const parsed = parseItems(); if (parsed) { items = parsed; draw(currentRotation); } + } + + updateBtn.addEventListener('click', applyItems); + + let debounceTimer: ReturnType | null = null; + textarea.addEventListener('input', () => { + if (debounceTimer) clearTimeout(debounceTimer); + debounceTimer = setTimeout(applyItems, 300); }); // ── Spin ───────────────────────────────────────────────────────── From 76d9233faa7bb26fdd8e4d975e8dbb7a8b02c700 Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 16:10:20 +0300 Subject: [PATCH 24/44] feat: remove Update wheel button, wheel updates automatically on input Co-Authored-By: Claude Sonnet 4.6 --- src/components/generators/WheelSpinner.astro | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/components/generators/WheelSpinner.astro b/src/components/generators/WheelSpinner.astro index 736ee57..73f7c27 100644 --- a/src/components/generators/WheelSpinner.astro +++ b/src/components/generators/WheelSpinner.astro @@ -21,16 +21,7 @@ Dave Eve Frank
-
- - -
+
@@ -98,7 +89,6 @@ Frank const TAU = 2 * Math.PI; const textarea = document.getElementById('ws-items') as HTMLTextAreaElement; - const updateBtn = document.getElementById('ws-update-btn') as HTMLButtonElement; const errorEl = document.getElementById('ws-error') as HTMLParagraphElement; const canvas = document.getElementById('ws-canvas') as HTMLCanvasElement; const spinBtn = document.getElementById('ws-spin-btn') as HTMLButtonElement; @@ -210,8 +200,6 @@ Frank if (parsed) { items = parsed; draw(currentRotation); } } - updateBtn.addEventListener('click', applyItems); - let debounceTimer: ReturnType | null = null; textarea.addEventListener('input', () => { if (debounceTimer) clearTimeout(debounceTimer); From 65e42e7d44f143fe88cc0438ad57bd0048e789b7 Mon Sep 17 00:00:00 2001 From: emil Date: Sat, 9 May 2026 16:49:57 +0300 Subject: [PATCH 25/44] Add Russian language version of the site - New i18n system: src/i18n/translations.ts with en/ru translation objects - LanguageSwitcher component (fixed top-right EN/RU toggle) - BaseLayout updated with lang prop, hreflang links, auto-redirect for Russian browsers - All 10 generator components updated to use T.* translations - GeneratorCard updated to show ruTitle/ruDescription and link to /ru/ routes - SeoBlock updated with translated section headers - New /ru/ home page and /ru/generators/[slug] dynamic route (all 10 generators) - Russian SEO content (howTo/whenTo) for all generator pages - English pages explicitly pass lang="en" Co-Authored-By: Claude Sonnet 4.6 --- src/components/GeneratorCard.astro | 19 +- src/components/LanguageSwitcher.astro | 41 ++++ src/components/SeoBlock.astro | 11 +- src/components/generators/CardGenerator.astro | 79 +++--- src/components/generators/CoinGenerator.astro | 70 +++--- .../generators/ColorGenerator.astro | 5 +- src/components/generators/DiceGenerator.astro | 45 ++-- src/components/generators/ListGenerator.astro | 91 +++---- .../generators/LotteryGenerator.astro | 159 ++++-------- .../generators/NumberGenerator.astro | 127 +++------- .../generators/PasswordGenerator.astro | 170 ++++--------- src/components/generators/UuidGenerator.astro | 30 ++- src/components/generators/WheelSpinner.astro | 44 ++-- src/data/generators.ts | 44 ++++ src/i18n/translations.ts | 138 +++++++++++ src/layouts/BaseLayout.astro | 32 ++- src/pages/generators/cards.astro | 2 +- src/pages/generators/coin.astro | 2 +- src/pages/generators/colors.astro | 2 +- src/pages/generators/dice.astro | 2 +- src/pages/generators/list.astro | 2 +- src/pages/generators/lottery.astro | 2 +- src/pages/generators/numbers.astro | 2 +- src/pages/generators/password.astro | 2 +- src/pages/generators/uuid.astro | 2 +- src/pages/generators/wheel.astro | 2 +- src/pages/index.astro | 9 +- src/pages/ru/generators/[slug].astro | 227 ++++++++++++++++++ src/pages/ru/index.astro | 50 ++++ 29 files changed, 893 insertions(+), 518 deletions(-) create mode 100644 src/components/LanguageSwitcher.astro create mode 100644 src/i18n/translations.ts create mode 100644 src/pages/ru/generators/[slug].astro create mode 100644 src/pages/ru/index.astro diff --git a/src/components/GeneratorCard.astro b/src/components/GeneratorCard.astro index 9f54261..c43ae90 100644 --- a/src/components/GeneratorCard.astro +++ b/src/components/GeneratorCard.astro @@ -1,14 +1,21 @@ --- import type { Generator } from '../data/generators'; +import { useT } from '../i18n/translations'; +import type { Lang } from '../i18n/translations'; interface Props { generator: Generator; + lang?: Lang; } -const { generator } = Astro.props; -const { slug, title, description, icon, status } = generator; +const { generator, lang = 'en' } = Astro.props; +const T = useT(lang); +const { slug, icon, status } = generator; const isLive = status === 'live'; -const href = `/generators/${slug}/`; +const isRu = lang === 'ru'; +const title = isRu ? generator.ruTitle : generator.title; +const description = isRu ? generator.ruDescription : generator.description; +const href = isRu ? `/ru/generators/${slug}/` : `/generators/${slug}/`; const icons: Record = { hash: ``, @@ -32,7 +39,7 @@ const icons: Record = {

@@ -43,12 +50,12 @@ const icons: Record = { ) : (

{title}

diff --git a/src/components/LanguageSwitcher.astro b/src/components/LanguageSwitcher.astro new file mode 100644 index 0000000..a352d9b --- /dev/null +++ b/src/components/LanguageSwitcher.astro @@ -0,0 +1,41 @@ +--- +interface Props { + lang: 'en' | 'ru'; + alternatePath: string; +} + +const { lang, alternatePath } = Astro.props; +const targetLang = lang === 'en' ? 'ru' : 'en'; +--- + +
+ EN + RU +
+ + diff --git a/src/components/SeoBlock.astro b/src/components/SeoBlock.astro index 7cb73cb..270640d 100644 --- a/src/components/SeoBlock.astro +++ b/src/components/SeoBlock.astro @@ -1,21 +1,26 @@ --- +import { useT } from '../i18n/translations'; +import type { Lang } from '../i18n/translations'; + interface Props { howTo: string[]; whenTo: string[]; + lang?: Lang; } -const { howTo, whenTo } = Astro.props; +const { howTo, whenTo, lang = 'en' } = Astro.props; +const T = useT(lang); ---
-

How to use

+

{T.howToUse}

    {howTo.map((step) =>
  1. {step}
  2. )}
-

When to use

+

{T.whenToUse}

    {whenTo.map((item) =>
  • {item}
  • )}
diff --git a/src/components/generators/CardGenerator.astro b/src/components/generators/CardGenerator.astro index 3217ba8..42c340e 100644 --- a/src/components/generators/CardGenerator.astro +++ b/src/components/generators/CardGenerator.astro @@ -1,33 +1,35 @@ --- -// Interactive card generator — runs on the client via inline script +import { useT } from '../../i18n/translations'; +const isRu = Astro.url.pathname.startsWith('/ru'); +const T = useT(isRu ? 'ru' : 'en'); ---
- +
diff --git a/src/components/generators/PasswordGenerator.astro b/src/components/generators/PasswordGenerator.astro index 92afef6..129acfd 100644 --- a/src/components/generators/PasswordGenerator.astro +++ b/src/components/generators/PasswordGenerator.astro @@ -1,193 +1,115 @@ --- -// Interactive password generator — runs on the client via inline script +import { useT } from '../../i18n/translations'; +const isRu = Astro.url.pathname.startsWith('/ru'); +const T = useT(isRu ? 'ru' : 'en'); ---
-
- + 16
- -
- 464 -
+ +
464
-
{[ - { id: 'pg-upper', label: 'Uppercase (A–Z)' }, - { id: 'pg-lower', label: 'Lowercase (a–z)' }, - { id: 'pg-digits', label: 'Digits (0–9)' }, - { id: 'pg-symbols', label: 'Symbols (!@#…)' }, + { id: 'pg-upper', label: T.uppercase }, + { id: 'pg-lower', label: T.lowercase }, + { id: 'pg-digits', label: T.digits }, + { id: 'pg-symbols', label: T.symbols }, ].map(({ id, label }) => ( ))}
- +
- - - Copied + {T.copied}
-
+ diff --git a/src/layouts/GeneratorLayout.astro b/src/layouts/GeneratorLayout.astro new file mode 100644 index 0000000..89e637e --- /dev/null +++ b/src/layouts/GeneratorLayout.astro @@ -0,0 +1,76 @@ +--- +import BaseLayout from './BaseLayout.astro'; +import AdBanner from '../components/AdBanner.astro'; +import SeoBlock from '../components/SeoBlock.astro'; +import { useT } from '../i18n/translations'; +import type { Lang } from '../i18n/translations'; +import type { Generator } from '../data/generators'; + +interface Props { + generator: Generator; +} + +const { generator } = Astro.props; +const lang = (Astro.currentLocale as Lang) || 'en'; +const T = useT(lang); +const isRu = lang === 'ru'; +--- + + +
+ + +
+
+ + randify +
+

+ {isRu ? generator.ruPageTitle : generator.pageTitle} +

+

{isRu ? generator.ruDescription : generator.description}

+
+ +
+ +
+ +
+ +
+ + +
+
diff --git a/src/lib/generator-schema.ts b/src/lib/generator-schema.ts new file mode 100644 index 0000000..943d2e9 --- /dev/null +++ b/src/lib/generator-schema.ts @@ -0,0 +1,25 @@ +import { z } from "zod"; + +export const generatorSchema = z + .object({ + slug: z.string(), + title: z.string(), + description: z.string(), + icon: z.string(), + status: z.enum(["live", "coming-soon"]), + seoTitle: z.string(), + seoDescription: z.string(), + ruTitle: z.string(), + ruDescription: z.string(), + ruSeoTitle: z.string(), + ruSeoDescription: z.string(), + pageTitle: z.string(), + ruPageTitle: z.string(), + howTo: z.array(z.string()), + whenTo: z.array(z.string()), + ruHowTo: z.array(z.string()), + ruWhenTo: z.array(z.string()), + }) + .strict(); + +export type Generator = z.infer; diff --git a/src/pages/generators/cards.astro b/src/pages/generators/cards.astro index 9f19a5d..601a1ff 100644 --- a/src/pages/generators/cards.astro +++ b/src/pages/generators/cards.astro @@ -1,77 +1,11 @@ --- -import BaseLayout from '../../layouts/BaseLayout.astro'; -import CardGenerator from '../../components/generators/CardGenerator.astro'; -import AdBanner from '../../components/AdBanner.astro'; -import { generators } from '../../data/generators'; -import SeoBlock from '../../components/SeoBlock.astro'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import CardGenerator from "@/components/generators/CardGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'cards')!; +const generator = generators.find((g) => g.slug === "cards")!; --- - -
- - -
-
- - randify -
-

- {generator.title} Generator -

-

{generator.description}

-
- -
- -
- -
- -
- - -
-
+ + + diff --git a/src/pages/generators/coin.astro b/src/pages/generators/coin.astro index 493392e..3affc26 100644 --- a/src/pages/generators/coin.astro +++ b/src/pages/generators/coin.astro @@ -1,56 +1,11 @@ --- -import BaseLayout from '../../layouts/BaseLayout.astro'; -import CoinGenerator from '../../components/generators/CoinGenerator.astro'; -import AdBanner from '../../components/AdBanner.astro'; -import { generators } from '../../data/generators'; -import SeoBlock from '../../components/SeoBlock.astro'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import CoinGenerator from "@/components/generators/CoinGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'coin')!; +const generator = generators.find((g) => g.slug === "coin")!; --- - -
- -
-
- - randify -
-

{generator.title}

-

{generator.description}

-
-
-
- -
- - -
-
+ + + diff --git a/src/pages/generators/colors.astro b/src/pages/generators/colors.astro index 28b41a9..5ab6aac 100644 --- a/src/pages/generators/colors.astro +++ b/src/pages/generators/colors.astro @@ -1,76 +1,11 @@ --- -import BaseLayout from '../../layouts/BaseLayout.astro'; -import ColorGenerator from '../../components/generators/ColorGenerator.astro'; -import AdBanner from '../../components/AdBanner.astro'; -import { generators } from '../../data/generators'; -import SeoBlock from '../../components/SeoBlock.astro'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import ColorGenerator from "@/components/generators/ColorGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'colors')!; +const generator = generators.find((g) => g.slug === "colors")!; --- - -
- - -
-
- - randify -
-

- {generator.title} Generator -

-

{generator.description}

-
- -
- -
- -
- -
- - -
-
+ + + diff --git a/src/pages/generators/dice.astro b/src/pages/generators/dice.astro index 19a90c3..96e2fd5 100644 --- a/src/pages/generators/dice.astro +++ b/src/pages/generators/dice.astro @@ -1,79 +1,11 @@ --- -import BaseLayout from '../../layouts/BaseLayout.astro'; -import DiceGenerator from '../../components/generators/DiceGenerator.astro'; -import AdBanner from '../../components/AdBanner.astro'; -import { generators } from '../../data/generators'; -import SeoBlock from '../../components/SeoBlock.astro'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import DiceGenerator from "@/components/generators/DiceGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'dice')!; +const generator = generators.find((g) => g.slug === "dice")!; --- - -
- - -
-
- - randify -
-

- {generator.title} Generator -

-

{generator.description}

-
- -
- -
- -
- -
- - -
-
+ + + diff --git a/src/pages/generators/list.astro b/src/pages/generators/list.astro index 4b4d2f4..031a112 100644 --- a/src/pages/generators/list.astro +++ b/src/pages/generators/list.astro @@ -1,56 +1,11 @@ --- -import BaseLayout from '../../layouts/BaseLayout.astro'; -import ListGenerator from '../../components/generators/ListGenerator.astro'; -import AdBanner from '../../components/AdBanner.astro'; -import { generators } from '../../data/generators'; -import SeoBlock from '../../components/SeoBlock.astro'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import ListGenerator from "@/components/generators/ListGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'list')!; +const generator = generators.find((g) => g.slug === "list")!; --- - -
- -
-
- - randify -
-

{generator.title}

-

{generator.description}

-
-
-
- -
- - -
-
+ + + diff --git a/src/pages/generators/lottery.astro b/src/pages/generators/lottery.astro index f295e41..47c78e5 100644 --- a/src/pages/generators/lottery.astro +++ b/src/pages/generators/lottery.astro @@ -1,77 +1,11 @@ --- -import BaseLayout from '../../layouts/BaseLayout.astro'; -import LotteryGenerator from '../../components/generators/LotteryGenerator.astro'; -import AdBanner from '../../components/AdBanner.astro'; -import { generators } from '../../data/generators'; -import SeoBlock from '../../components/SeoBlock.astro'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import LotteryGenerator from "@/components/generators/LotteryGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'lottery')!; +const generator = generators.find((g) => g.slug === "lottery")!; --- - -
- - -
-
- - randify -
-

- {generator.title} Generator -

-

{generator.description}

-
- -
- -
- -
- -
- - -
-
+ + + diff --git a/src/pages/generators/numbers.astro b/src/pages/generators/numbers.astro index 2ecc23e..d1c9fd2 100644 --- a/src/pages/generators/numbers.astro +++ b/src/pages/generators/numbers.astro @@ -1,77 +1,11 @@ --- -import BaseLayout from '../../layouts/BaseLayout.astro'; -import NumberGenerator from '../../components/generators/NumberGenerator.astro'; -import AdBanner from '../../components/AdBanner.astro'; -import { generators } from '../../data/generators'; -import SeoBlock from '../../components/SeoBlock.astro'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import NumberGenerator from "@/components/generators/NumberGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'numbers')!; +const generator = generators.find((g) => g.slug === "numbers")!; --- - -
- - -
-
- - randify -
-

- {generator.title} Generator -

-

{generator.description}

-
- -
- -
- -
- -
- - -
-
+ + + diff --git a/src/pages/generators/password.astro b/src/pages/generators/password.astro index 6e33fdb..43233ad 100644 --- a/src/pages/generators/password.astro +++ b/src/pages/generators/password.astro @@ -1,77 +1,11 @@ --- -import BaseLayout from '../../layouts/BaseLayout.astro'; -import PasswordGenerator from '../../components/generators/PasswordGenerator.astro'; -import AdBanner from '../../components/AdBanner.astro'; -import { generators } from '../../data/generators'; -import SeoBlock from '../../components/SeoBlock.astro'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import PasswordGenerator from "@/components/generators/PasswordGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'password')!; +const generator = generators.find((g) => g.slug === "password")!; --- - -
- - -
-
- - randify -
-

- {generator.title} Generator -

-

{generator.description}

-
- -
- -
- -
- -
- - -
-
+ + + diff --git a/src/pages/generators/uuid.astro b/src/pages/generators/uuid.astro index fd56329..b28faaa 100644 --- a/src/pages/generators/uuid.astro +++ b/src/pages/generators/uuid.astro @@ -1,56 +1,11 @@ --- -import BaseLayout from '../../layouts/BaseLayout.astro'; -import UuidGenerator from '../../components/generators/UuidGenerator.astro'; -import AdBanner from '../../components/AdBanner.astro'; -import { generators } from '../../data/generators'; -import SeoBlock from '../../components/SeoBlock.astro'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import UuidGenerator from "@/components/generators/UuidGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'uuid')!; +const generator = generators.find((g) => g.slug === "uuid")!; --- - -
- -
-
- - randify -
-

{generator.title}

-

{generator.description}

-
-
-
- -
- - -
-
+ + + diff --git a/src/pages/generators/wheel.astro b/src/pages/generators/wheel.astro index 5dd9b3c..921d2eb 100644 --- a/src/pages/generators/wheel.astro +++ b/src/pages/generators/wheel.astro @@ -1,77 +1,11 @@ --- -import BaseLayout from '../../layouts/BaseLayout.astro'; -import WheelSpinner from '../../components/generators/WheelSpinner.astro'; -import AdBanner from '../../components/AdBanner.astro'; -import SeoBlock from '../../components/SeoBlock.astro'; -import { generators } from '../../data/generators'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import WheelSpinner from "@/components/generators/WheelSpinner.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'wheel')!; +const generator = generators.find((g) => g.slug === "wheel")!; --- - -
- - -
-
- - randify -
-

- {generator.title} -

-

{generator.description}

-
- -
- -
- -
- -
- - -
-
+ + + diff --git a/src/pages/index.astro b/src/pages/index.astro index 71ef7cf..7ce753a 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,46 +1,53 @@ --- -import BaseLayout from '../layouts/BaseLayout.astro'; -import GeneratorCard from '../components/GeneratorCard.astro'; -import AdBanner from '../components/AdBanner.astro'; -import { generators } from '../data/generators'; -import { useT } from '../i18n/translations'; +import BaseLayout from "@/layouts/BaseLayout.astro"; +import GeneratorCard from "@/components/GeneratorCard.astro"; +import AdBanner from "@/components/AdBanner.astro"; +import { generators } from "@/data/generators"; +import { useT } from "@/i18n/translations"; +import type { Lang } from "@/i18n/translations"; -const T = useT('en'); +const lang = (Astro.currentLocale as Lang) || "en"; +const T = useT(lang); --- - -
-
-
- - randify -
-

- {T.homeTitle} -

-

- {T.homeSubtitle} -

-
+ +
+
+
+ + {T.brandLabel} +
+

+ {T.homeTitle} +

+

+ {T.homeSubtitle} +

+
- + -
-
- {generators.map((generator) => ( -
- -
- ))} -
-
-
+
+
+ { + generators.map((generator) => ( +
+ +
+ )) + } +
+
+
diff --git a/src/pages/ru/generators/cards.astro b/src/pages/ru/generators/cards.astro index 3f5d976..601a1ff 100644 --- a/src/pages/ru/generators/cards.astro +++ b/src/pages/ru/generators/cards.astro @@ -1,40 +1,11 @@ --- -import BaseLayout from '../../../layouts/BaseLayout.astro'; -import CardGenerator from '../../../components/generators/CardGenerator.astro'; -import AdBanner from '../../../components/AdBanner.astro'; -import SeoBlock from '../../../components/SeoBlock.astro'; -import { generators } from '../../../data/generators'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import CardGenerator from "@/components/generators/CardGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'cards')!; +const generator = generators.find((g) => g.slug === "cards")!; --- - -
- -
-
- - randify -
-

Генератор карт

-

{generator.ruDescription}

-
-
-
- -
-
+ + + diff --git a/src/pages/ru/generators/coin.astro b/src/pages/ru/generators/coin.astro index 9399ebc..3affc26 100644 --- a/src/pages/ru/generators/coin.astro +++ b/src/pages/ru/generators/coin.astro @@ -1,40 +1,11 @@ --- -import BaseLayout from '../../../layouts/BaseLayout.astro'; -import CoinGenerator from '../../../components/generators/CoinGenerator.astro'; -import AdBanner from '../../../components/AdBanner.astro'; -import SeoBlock from '../../../components/SeoBlock.astro'; -import { generators } from '../../../data/generators'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import CoinGenerator from "@/components/generators/CoinGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'coin')!; +const generator = generators.find((g) => g.slug === "coin")!; --- - -
- -
-
- - randify -
-

Подбросить монетку

-

{generator.ruDescription}

-
-
-
- -
-
+ + + diff --git a/src/pages/ru/generators/colors.astro b/src/pages/ru/generators/colors.astro index 039f4ca..5ab6aac 100644 --- a/src/pages/ru/generators/colors.astro +++ b/src/pages/ru/generators/colors.astro @@ -1,39 +1,11 @@ --- -import BaseLayout from '../../../layouts/BaseLayout.astro'; -import ColorGenerator from '../../../components/generators/ColorGenerator.astro'; -import AdBanner from '../../../components/AdBanner.astro'; -import SeoBlock from '../../../components/SeoBlock.astro'; -import { generators } from '../../../data/generators'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import ColorGenerator from "@/components/generators/ColorGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'colors')!; +const generator = generators.find((g) => g.slug === "colors")!; --- - -
- -
-
- - randify -
-

Генератор цветов

-

{generator.ruDescription}

-
-
-
- -
-
+ + + diff --git a/src/pages/ru/generators/dice.astro b/src/pages/ru/generators/dice.astro index 9787670..96e2fd5 100644 --- a/src/pages/ru/generators/dice.astro +++ b/src/pages/ru/generators/dice.astro @@ -1,43 +1,11 @@ --- -import BaseLayout from '../../../layouts/BaseLayout.astro'; -import DiceGenerator from '../../../components/generators/DiceGenerator.astro'; -import AdBanner from '../../../components/AdBanner.astro'; -import SeoBlock from '../../../components/SeoBlock.astro'; -import { generators } from '../../../data/generators'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import DiceGenerator from "@/components/generators/DiceGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'dice')!; +const generator = generators.find((g) => g.slug === "dice")!; --- - -
- -
-
- - randify -
-

Бросить кубик онлайн

-

{generator.ruDescription}

-
-
-
- -
-
+ + + diff --git a/src/pages/ru/generators/list.astro b/src/pages/ru/generators/list.astro index 00ef839..031a112 100644 --- a/src/pages/ru/generators/list.astro +++ b/src/pages/ru/generators/list.astro @@ -1,40 +1,11 @@ --- -import BaseLayout from '../../../layouts/BaseLayout.astro'; -import ListGenerator from '../../../components/generators/ListGenerator.astro'; -import AdBanner from '../../../components/AdBanner.astro'; -import SeoBlock from '../../../components/SeoBlock.astro'; -import { generators } from '../../../data/generators'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import ListGenerator from "@/components/generators/ListGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'list')!; +const generator = generators.find((g) => g.slug === "list")!; --- - -
- -
-
- - randify -
-

Случайный выбор из списка

-

{generator.ruDescription}

-
-
-
- -
-
+ + + diff --git a/src/pages/ru/generators/lottery.astro b/src/pages/ru/generators/lottery.astro index 6419aa5..47c78e5 100644 --- a/src/pages/ru/generators/lottery.astro +++ b/src/pages/ru/generators/lottery.astro @@ -1,40 +1,11 @@ --- -import BaseLayout from '../../../layouts/BaseLayout.astro'; -import LotteryGenerator from '../../../components/generators/LotteryGenerator.astro'; -import AdBanner from '../../../components/AdBanner.astro'; -import SeoBlock from '../../../components/SeoBlock.astro'; -import { generators } from '../../../data/generators'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import LotteryGenerator from "@/components/generators/LotteryGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'lottery')!; +const generator = generators.find((g) => g.slug === "lottery")!; --- - -
- -
-
- - randify -
-

Генератор лотереи

-

{generator.ruDescription}

-
-
-
- -
-
+ + + diff --git a/src/pages/ru/generators/numbers.astro b/src/pages/ru/generators/numbers.astro index 3a1871f..d1c9fd2 100644 --- a/src/pages/ru/generators/numbers.astro +++ b/src/pages/ru/generators/numbers.astro @@ -1,41 +1,11 @@ --- -import BaseLayout from '../../../layouts/BaseLayout.astro'; -import NumberGenerator from '../../../components/generators/NumberGenerator.astro'; -import AdBanner from '../../../components/AdBanner.astro'; -import SeoBlock from '../../../components/SeoBlock.astro'; -import { generators } from '../../../data/generators'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import NumberGenerator from "@/components/generators/NumberGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'numbers')!; +const generator = generators.find((g) => g.slug === "numbers")!; --- - -
- -
-
- - randify -
-

Генератор чисел

-

{generator.ruDescription}

-
-
-
- -
-
+ + + diff --git a/src/pages/ru/generators/password.astro b/src/pages/ru/generators/password.astro index e41ff34..43233ad 100644 --- a/src/pages/ru/generators/password.astro +++ b/src/pages/ru/generators/password.astro @@ -1,40 +1,11 @@ --- -import BaseLayout from '../../../layouts/BaseLayout.astro'; -import PasswordGenerator from '../../../components/generators/PasswordGenerator.astro'; -import AdBanner from '../../../components/AdBanner.astro'; -import SeoBlock from '../../../components/SeoBlock.astro'; -import { generators } from '../../../data/generators'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import PasswordGenerator from "@/components/generators/PasswordGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'password')!; +const generator = generators.find((g) => g.slug === "password")!; --- - -
- -
-
- - randify -
-

Генератор паролей

-

{generator.ruDescription}

-
-
-
- -
-
+ + + diff --git a/src/pages/ru/generators/uuid.astro b/src/pages/ru/generators/uuid.astro index d5c61b3..b28faaa 100644 --- a/src/pages/ru/generators/uuid.astro +++ b/src/pages/ru/generators/uuid.astro @@ -1,40 +1,11 @@ --- -import BaseLayout from '../../../layouts/BaseLayout.astro'; -import UuidGenerator from '../../../components/generators/UuidGenerator.astro'; -import AdBanner from '../../../components/AdBanner.astro'; -import SeoBlock from '../../../components/SeoBlock.astro'; -import { generators } from '../../../data/generators'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import UuidGenerator from "@/components/generators/UuidGenerator.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'uuid')!; +const generator = generators.find((g) => g.slug === "uuid")!; --- - -
- -
-
- - randify -
-

Генератор UUID и токенов

-

{generator.ruDescription}

-
-
-
- -
-
+ + + diff --git a/src/pages/ru/generators/wheel.astro b/src/pages/ru/generators/wheel.astro index bafb5f3..921d2eb 100644 --- a/src/pages/ru/generators/wheel.astro +++ b/src/pages/ru/generators/wheel.astro @@ -1,40 +1,11 @@ --- -import BaseLayout from '../../../layouts/BaseLayout.astro'; -import WheelSpinner from '../../../components/generators/WheelSpinner.astro'; -import AdBanner from '../../../components/AdBanner.astro'; -import SeoBlock from '../../../components/SeoBlock.astro'; -import { generators } from '../../../data/generators'; +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import WheelSpinner from "@/components/generators/WheelSpinner.astro"; +import { generators } from "@/data/generators"; -const generator = generators.find((g) => g.slug === 'wheel')!; +const generator = generators.find((g) => g.slug === "wheel")!; --- - -
- -
-
- - randify -
-

Колесо фортуны онлайн

-

{generator.ruDescription}

-
-
-
- -
-
+ + + diff --git a/src/pages/ru/index.astro b/src/pages/ru/index.astro index 8b79dfc..7ce753a 100644 --- a/src/pages/ru/index.astro +++ b/src/pages/ru/index.astro @@ -1,50 +1,53 @@ --- -import BaseLayout from '../../layouts/BaseLayout.astro'; -import GeneratorCard from '../../components/GeneratorCard.astro'; -import AdBanner from '../../components/AdBanner.astro'; -import { generators } from '../../data/generators'; -import { useT } from '../../i18n/translations'; +import BaseLayout from "@/layouts/BaseLayout.astro"; +import GeneratorCard from "@/components/GeneratorCard.astro"; +import AdBanner from "@/components/AdBanner.astro"; +import { generators } from "@/data/generators"; +import { useT } from "@/i18n/translations"; +import type { Lang } from "@/i18n/translations"; -const T = useT('ru'); +const lang = (Astro.currentLocale as Lang) || "en"; +const T = useT(lang); --- - -
-
-
- - {T.brandLabel} -
-

- {T.homeTitle} -

-

- {T.homeSubtitle} -

-
+ +
+
+
+ + {T.brandLabel} +
+

+ {T.homeTitle} +

+

+ {T.homeSubtitle} +

+
- + -
-
- {generators.map((generator) => ( -
- -
- ))} -
-
-
+
+
+ { + generators.map((generator) => ( +
+ +
+ )) + } +
+
+
From 56ab3f6952f30d03999d25a81dd4057065c9c04a Mon Sep 17 00:00:00 2001 From: emil Date: Sun, 10 May 2026 20:56:33 +0300 Subject: [PATCH 33/44] refactor: extract shared client utilities - Add src/lib/client/clipboard.ts with CopyFeedback class - Add src/lib/client/validation.ts with createErrorDisplay and isInteger - Add src/lib/client/animations.ts with popElement - Refactor NumberGenerator, LotteryGenerator, PasswordGenerator, ListGenerator, CardGenerator, CoinGenerator, UuidGenerator, WheelSpinner to use shared utilities - Leave DiceGenerator and ColorGenerator untouched (complex patterns) --- src/components/generators/CardGenerator.astro | 402 ++++++---- src/components/generators/CoinGenerator.astro | 302 ++++---- src/components/generators/ListGenerator.astro | 384 ++++++---- .../generators/LotteryGenerator.astro | 306 +++++--- .../generators/NumberGenerator.astro | 276 ++++--- .../generators/PasswordGenerator.astro | 312 +++++--- src/components/generators/UuidGenerator.astro | 408 +++++----- src/components/generators/WheelSpinner.astro | 698 ++++++++++-------- src/lib/client/animations.ts | 10 + src/lib/client/clipboard.ts | 25 + src/lib/client/validation.ts | 16 + 11 files changed, 1921 insertions(+), 1218 deletions(-) create mode 100644 src/lib/client/animations.ts create mode 100644 src/lib/client/clipboard.ts create mode 100644 src/lib/client/validation.ts diff --git a/src/components/generators/CardGenerator.astro b/src/components/generators/CardGenerator.astro index 42c340e..e88277e 100644 --- a/src/components/generators/CardGenerator.astro +++ b/src/components/generators/CardGenerator.astro @@ -1,188 +1,272 @@ --- -import { useT } from '../../i18n/translations'; -const isRu = Astro.url.pathname.startsWith('/ru'); -const T = useT(isRu ? 'ru' : 'en'); +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); ---
-
-
- - +
+
+ + +
+
- -
- - -
-
- - -
+

-
- -
+
+
+
+ + +
+ +
+ +
diff --git a/src/components/generators/CoinGenerator.astro b/src/components/generators/CoinGenerator.astro index be3a60b..dc79e6a 100644 --- a/src/components/generators/CoinGenerator.astro +++ b/src/components/generators/CoinGenerator.astro @@ -1,148 +1,200 @@ --- -import { useT } from '../../i18n/translations'; -const isRu = Astro.url.pathname.startsWith('/ru'); -const T = useT(isRu ? 'ru' : 'en'); +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); ---
-
-
- - 1 +
+
+ + 1 +
+ +
+ 120 +
- -
- 120 + +
+
+
+ + + +
-
-
-
- - - - -
- -
- -
+
+ +
diff --git a/src/components/generators/ListGenerator.astro b/src/components/generators/ListGenerator.astro index b36bfa9..c724dcb 100644 --- a/src/components/generators/ListGenerator.astro +++ b/src/components/generators/ListGenerator.astro @@ -1,179 +1,251 @@ --- -import { useT } from '../../i18n/translations'; -const isRu = Astro.url.pathname.startsWith('/ru'); -const T = useT(isRu ? 'ru' : 'en'); +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); ---
-
-
- - +
+
+ + +
+ +
+
+ + +
+ +
-
-
- - -
- + + +
+
+
+ +
-
- - -
-
- - -
- -
- -
+
+ +
diff --git a/src/components/generators/LotteryGenerator.astro b/src/components/generators/LotteryGenerator.astro index 6952ccf..dba76b1 100644 --- a/src/components/generators/LotteryGenerator.astro +++ b/src/components/generators/LotteryGenerator.astro @@ -1,116 +1,224 @@ --- -import { useT } from '../../i18n/translations'; -const isRu = Astro.url.pathname.startsWith('/ru'); -const T = useT(isRu ? 'ru' : 'en'); +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); ---
-
-
- - +
+
+ + +
+
+ + +
+
+ + +
-
- - + + + +
+
+
+
-
- - + +
+
-
- - - -
-
- -
- -
- -
diff --git a/src/components/generators/NumberGenerator.astro b/src/components/generators/NumberGenerator.astro index 4200483..5ac6594 100644 --- a/src/components/generators/NumberGenerator.astro +++ b/src/components/generators/NumberGenerator.astro @@ -1,126 +1,182 @@ --- -import { useT } from '../../i18n/translations'; -const isRu = Astro.url.pathname.startsWith('/ru'); -const T = useT(isRu ? 'ru' : 'en'); +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); --- -
-
-
- - +
+
+
+ + +
+
+ + +
-
- - + + + +
+ + {T.copied}
-
- - -
- - {T.copied} -
- -
- -
+
+ +
diff --git a/src/components/generators/PasswordGenerator.astro b/src/components/generators/PasswordGenerator.astro index 129acfd..3fd261a 100644 --- a/src/components/generators/PasswordGenerator.astro +++ b/src/components/generators/PasswordGenerator.astro @@ -1,123 +1,227 @@ --- -import { useT } from '../../i18n/translations'; -const isRu = Astro.url.pathname.startsWith('/ru'); -const T = useT(isRu ? 'ru' : 'en'); +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); ---
-
-
-
- - 16 -
- -
464
+
+
+
+ + 16 +
+ +
+ 464 +
+
+
+ { + [ + { id: "pg-upper", label: T.uppercase }, + { id: "pg-lower", label: T.lowercase }, + { id: "pg-digits", label: T.digits }, + { id: "pg-symbols", label: T.symbols }, + ].map(({ id, label }) => ( + + )) + } +
-
- {[ - { id: 'pg-upper', label: T.uppercase }, - { id: 'pg-lower', label: T.lowercase }, - { id: 'pg-digits', label: T.digits }, - { id: 'pg-symbols', label: T.symbols }, - ].map(({ id, label }) => ( - - ))} + + + +
+ + {T.copied}
-
- - -
- - {T.copied} -
- -
- -
+
+ +
diff --git a/src/components/generators/UuidGenerator.astro b/src/components/generators/UuidGenerator.astro index 1948870..b3b56e4 100644 --- a/src/components/generators/UuidGenerator.astro +++ b/src/components/generators/UuidGenerator.astro @@ -1,191 +1,263 @@ --- -import { useT } from '../../i18n/translations'; -const isRu = Astro.url.pathname.startsWith('/ru'); -const T = useT(isRu ? 'ru' : 'en'); +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); ---
-
-
- -
- {[ - { value: 'uuid', label: 'UUID v4' }, - { value: 'hex', label: 'Hex' }, - { value: 'base64', label: 'Base64' }, - ].map(({ value, label }) => ( -
-
- -
- - -
+
+
-
- -
-
- - -
- -
- -
diff --git a/src/components/generators/WheelSpinner.astro b/src/components/generators/WheelSpinner.astro index 166a2ac..b255ca6 100644 --- a/src/components/generators/WheelSpinner.astro +++ b/src/components/generators/WheelSpinner.astro @@ -1,332 +1,436 @@ --- -import { useT } from '../../i18n/translations'; -const isRu = Astro.url.pathname.startsWith('/ru'); -const T = useT(isRu ? 'ru' : 'en'); +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); ---
- - -
-
- - -
- -
- - -
-
- - - + +
+
+ + +
+
- -
+ +
+
+ + + +
- - + +
+ +
diff --git a/src/lib/client/animations.ts b/src/lib/client/animations.ts new file mode 100644 index 0000000..123714c --- /dev/null +++ b/src/lib/client/animations.ts @@ -0,0 +1,10 @@ +export function popElement(el: HTMLElement) { + el.style.transform = 'scale(1.18)'; + el.style.opacity = '0.7'; + requestAnimationFrame(() => + requestAnimationFrame(() => { + el.style.transform = 'scale(1)'; + el.style.opacity = '1'; + }) + ); +} diff --git a/src/lib/client/clipboard.ts b/src/lib/client/clipboard.ts new file mode 100644 index 0000000..d4fc76a --- /dev/null +++ b/src/lib/client/clipboard.ts @@ -0,0 +1,25 @@ +export class CopyFeedback { + private timer: ReturnType | null = null; + + constructor( + private copyIcon: HTMLElement, + private checkIcon: HTMLElement, + private duration = 1500 + ) {} + + showCopied() { + this.copyIcon.classList.add('hidden'); + this.checkIcon.classList.remove('hidden'); + if (this.timer) clearTimeout(this.timer); + this.timer = setTimeout(() => this.revert(), this.duration); + } + + revert() { + this.checkIcon.classList.add('hidden'); + this.copyIcon.classList.remove('hidden'); + } + + cleanup() { + if (this.timer) clearTimeout(this.timer); + } +} diff --git a/src/lib/client/validation.ts b/src/lib/client/validation.ts new file mode 100644 index 0000000..4f873e6 --- /dev/null +++ b/src/lib/client/validation.ts @@ -0,0 +1,16 @@ +export function createErrorDisplay(errorEl: HTMLElement) { + return { + show(msg: string) { + errorEl.textContent = msg; + errorEl.classList.remove('hidden'); + }, + clear() { + errorEl.textContent = ''; + errorEl.classList.add('hidden'); + }, + }; +} + +export function isInteger(val: string): boolean { + return /^-?\d+$/.test(val.trim()); +} From 3750cd2cc686c31fe08000fce490dc6107e33909 Mon Sep 17 00:00:00 2001 From: emil Date: Sun, 10 May 2026 21:06:19 +0300 Subject: [PATCH 34/44] feat: add PWA support with offline caching - Generate 192x192, 512x512 and Apple touch icons from favicon.svg - Add web app manifest (manifest.json) - Add service worker with network-first for HTML, cache-first for assets - Register SW in BaseLayout - Add theme-color meta and apple-touch-icon link --- public/apple-touch-icon.png | Bin 0 -> 3299 bytes public/icon-192.png | Bin 0 -> 3629 bytes public/icon-512.png | Bin 0 -> 10821 bytes public/manifest.json | 24 ++++++++++++++++ public/sw.js | 52 +++++++++++++++++++++++++++++++++++ src/layouts/BaseLayout.astro | 8 ++++++ 6 files changed, 84 insertions(+) create mode 100644 public/apple-touch-icon.png create mode 100644 public/icon-192.png create mode 100644 public/icon-512.png create mode 100644 public/manifest.json create mode 100644 public/sw.js diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7b1ceaaaf3307d6c370e250d40f2a524f6483dfd GIT binary patch literal 3299 zcmai1c~p{H_Xb12#94C|8bHlT%2IPi^QxG`HI?eSN#X7R5>Ha0j+Y1kfV9%y00MHJssRA7Os8`g�&0r|zVA zc4OER&nPH9 z_j=?>n_0e0xOjkR*>~vu@rMr$mL5);#P!u2>n&iGPsh-}b3!I1q;Zk( z4>-bC=NR6KXMlN(RS_VN0@Sps{CP8}=Ag0wEtR6?-uRprXItDmJo?oWM4kF6#xL?k zHI6w4U%P?7p^yXj(r;W$dwf(`pt_NmU&Q58Znyng)lHbGIVjMW`;h4}uV0XLr&TRY z)%lKhzjJox)9{w`%_-$yK<~5ag_1kXR-$5R(Rg}XyWNR}@5R@?ez1}w|&@pWIP)Zuq3pKH*8EFb5Mqcel`_WDcDR9EJEO z<#T8{PCMB=3`SIv@Psz@2DTj_F`LIdsuHdrTd#hjTGS56{x%2P-6`yeMFnb5JaG2UHg2Z(#!%t;amN8W1 z@{~FNYtuwGguRgCIAQz&dJn=sV84;?MuK7#+cGq_32Ve@Z6N&!)H1X2@^L;(0id3* zNCHONwzA(I2G9V=0VYtY_OD0>fF2Ob5pEa1n)>@ad+J>od}t7ytCSR{Oq6ItNydV6 zb)l!nEHL!;IVlMlV>kJLS9juU;s=yK8n>Py7*|(8|HDLgw#K?~Ox>+gHoT7`SrBBg zajv2^#W&u7)q~Yfw9zf{6ZtN_IN5l-d}CALG&3Une(N)l%rv`*W=B^Fo%*e55KXw~ z;6WHLHeIT^Jo14{c}WX{5+%5;TW9>$BAZ3N0h&~|UvD@3{G|TRt&93!N%c#vt`#`=<*}Ot)tY`%$ynp*S!vq&>oht&Yy=7 zaJVe(svDnfv47;Mnlqys!>iY7JirLX$&|Ni;o@AmrfL)2c(~7(TYj#?2Y7zPFdMpG z&L-8^Ukx8E++i5|L`78ArUZ&^ty+2a0zhV{-C5#Dw8rTDhLvwKd4tBNZQrJULRJPd z7h&vK2bOG*-{NCef6dnIu02TAV{CfRbpcE5gN1`=epbunpyg|YRBGZ-=v~2=o{{Lj zwJ804@*K6`cc=+i&xo+z-!yn@#MWQ)<}`C&jFAGA2vgz`hC-Ld4p?=raPnDU4J*A* zEB8Xj+Vr#;$yJep0@q%|HEB>u+!yyg-0?r3XOtInE$-3Wh8CDD4~ z-+MhP6fQG_!Z@z2&CP#z2YmRY%&rsD>!vp^`D6Z*^&<{+>#k&8t~#U)R%1rj>ky{{%!yoTNLek4()5zSjXgSWor-lcPv@5EWW;sB31 zixfM^KK?RwtllSjl_ObW0{-t>ZWVmN%Is4;WVv7^#Ef~d;oqfu=XQ|M97;S_C3F>L}P+XWR?Ev|GmX zUpmN`OSlK;lts0-RQN<#lppPNDjaf+9unu)DbM*WN5iDYr&HnUh21YJAtboLbu|g? zzj`14xHxa>Fkalzl`NuAGf%-{Qv-O424M{vv?34JWfQ^$?X0xj4ThGic^=> zRF%-i(EM*xn}VDMKCLb}Od4ihJ%piaKetHEP4>5)dD_|T5Wk^g@D(8Oy{!>g^=pW= z@&~slaZ)liuv0 z{}OC^!D(vYtr0*u39VfSIc9zM^PvI*mN^LF7x*%ynBnQ2e79X>1e)w(bBS^g%kp(^Cao=*qoito z%z^+BRF~n*#hlh*whhj@*uFSbtg$TBI6PvOVu}!>c>lh$4Qs(6zUX0N;G@&dJ97Is z%k6a=a7eE`5qLss>Tz5%&Xpy* zsIvd?p!;|ajM)%=W~g?}!_z+IQsgxF*bqY%gDf!R(l#E*Essq{oxi!%G-rxe&SW^h z!-L*SA_doczN@dl>@&tw zb@309TMW&zi|i&{JI?E?3a(?K4XjT7uEBk-Q<)I>Szl{91e(nSzs^Vqo@4fk#dw=j zY0QuL8F--S=OZZ|9g|(ug_-(S+cx%!H?&1U_V_GC&D&QNF8bpNjv|-Zta@Q1@WQsv j3b~8-gg1G&VHYC0YT@O1c(hXd*8*^|cRg2Sdo}JqJZ($O literal 0 HcmV?d00001 diff --git a/public/icon-192.png b/public/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..82622ce65a128f0ff2ad17d9f9ff906a5e833c07 GIT binary patch literal 3629 zcmbVPc|6o@*Z<9u8AFI7ikZ@6Yot(77)vTjqRmcjjC~)vCkzoqZpnNUuv@g4ea^!4a&*BWYxbdhw=!&L`ECAd ztq*P1R>CCrilgn6x0F*XT3tW>hw5=z+cd``dC?a4w<+mmDI}|DDjj*C1-;xD*LtWf z;a-7OXLOz)ldqAbsadWR!RJB$z-Ih4E6ixMzzXBLO$dYfQW70p zhqJ4etY%E34zDMJXCIXqWRz3~$mUN7h_KGM!u{)qx z7HAW<&F`r9S#e4E&c95@$}S1_V%}}a?p7xv_0hhb?#mdb+WUkIz|iK=)dGE>R8wa~ zS{B=5E}r*k?NxB`om3JDUSW7_6VFFJ#jer93;$Ml<$t z@s=U(-dZGGXt(CgtCp+IPLJd(jZatfOM+z{ug?j;HiQHtTTavzCq+(fH8d71`PcEo zN4&&9DIZ1#lFbx?obp|oBjdLM7#mk`CR|~+k(@+R1rT!a*r-g3q)eU*KHiIRoYj25T!*{`tRc%7A4a_V!eSIW`YIjg=pxjlr&2 z{6uXZLPJpp@gA*ud$K68t}&sH?MXTgpeV6ag~bTR=Eun}q-(xO@EIIZEj?=E_|+S* zvkIb6ba4Zd%@uGGP3Eq2r$1g;8TOI{rFg=gVQ#^2D^G9#9iu$PR$|aT zO5wrV^O}`lyF-UbAKg%diqkVx4=_Nv|mMYVE-|}hhZB~bc~hA7f9U3 zSXr@8Pql~0O;0$Mc=uw(vI1R!^(73?K1PT`4$&AFsXv5)PRq+G)3vO)Q9CZ}a`tX0 zC3!mJZP_?BJnrd>*C+ra16ZAcq5v@VZZZJH0_w{$L;wz;RlqU;i1OMT0Hc7cXgC&t z5@n@9aGm+eN&&~Ex1jLEhsjkz&~+2-GpL0S`m-7hihWliQDSLT_Z96yTb`c=FnmA?WMl8FK zXCNvfGZe6+W?0#oF%jMf^{`F3e~C z_A4H+`23GXv1MJz^t%A5#aEy9Z|~O^Rf!rDEEB(NN}?2y>j@>o-wD1wXcuI8g1TmElRtHIC zhhyFfa_4040IS3;(wS`et7QJ7fhbcfg3r>#A6S+Mz(QIIL3JHm*`bSr&to36T@V`z zgkz;|z1YX^)miv)D{1X(7`bm`ZL+A^1_hu=cwnpu5Yu)=8i-VziHvfC9GtIaB0iBM~*1+SRHL2Vd-}sjIdYTk~Yg;eEL=9vo4nm;ngAAH_iu>!SM%!mB0ir{=|2*3b~Y6Ojn) z#C@+bve{MOUuRSe`DS+!{tN}j#fQVRuBc<&~xtWDO3c zUucpH$0VsZzY>m@Dn}6yy2H-)!a*}nYo(h>W^tiE?{vm4%-UpoUrY@XMQ=A4h|Svv zd@fb|Png_N=s)4oVC)KRB})%x<&FYkYc))Hr!Y9!Iu_7_px@zn3ht2q<$Wtl6CK_f z$p8ai&08h(TA^a#&$>At#h@NiCl=s8l3PtFJ+R#q2`3&hM$d^6S^`+ZObCcB88*J@ z`Y9u=<*fB!myzfpw6LN#kw`1rs{!>Brc4N!e!Ap<{!O+%1_xdz0uehjOfMEmVI#?! zEifo0^{iLgDP^Mbzdi(V%-f>X9_bT4>_O0p$UO}?aqhrQ6A$?<_7|E7+-CsCjR*DZ z@5_0>mgp>-pvZH(Kz$qPzK-S3r(A!&1ilFoaNUOlB4B7~MPJoch%*KhRa;P|68>BnPm{)JM&f{r`((LbYftQ#wUR5= zZ@=6>LibYZ@Gfst&y!u16ea@CY7|`mWLT~{MuHHycx^eWobN5n3S{)9#^>!3Ntq4V zgfPG`WBg$`Zlguv%-|!gCT0dMJOG;EL6ys@C~ncAjeeI1g_Y&+jU9lSmVzklqf=tl zy9ZP>so8%}tP@>Qt%jtQ?u|Db@M2d@7M&`|mmT6nyw$Cm*oU6gSx*LLOG*r$*A0z+ z4QOnC6!UXRpo8*C951Y1bU;+j-FZ>0;bDA3Xp6#J{kA#mHcPUbXSFWh!r%_6q?`8k zM5uclJS{+9%fh&M?|{u{>qiYylZgoapsVmPW~dE z*V-_#P(Pz7yn2IG-4+(~D|GG9EAu52(Jssl8ua)=B?J?*`ogK|*niuv0)T&z!pC z)WV3JC=i;Oi29^F$S>6zroyrBSrj}0W8*64NSL$eOnW$55mdtxKegqbh8`2ZI4&3_ Uk-?P^^?$&*(>4}WX6_090|1@Rg#Z8m literal 0 HcmV?d00001 diff --git a/public/icon-512.png b/public/icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..2e07c690039c3744240f78055479479cb69af64d GIT binary patch literal 10821 zcmd^lc_7r^_xH6`QdE>m5h+`xQmHJXM3%B7q>)h+$`&DFW?EI&WJ%TXn5^SWodXJT}8`O@`E5kkxL^^TlC zh!_6lMN1aJ*YwpN^YA6;u4mpm);3e?y<8zG;@4i&sy-KEgjac~8CAvDxlVxtmKi4^Qh-c;c zTUuW;ytWFiTeWJ5w6yA~B`bJuwSNt1m&Kll9nr;8Fm+g~|3!Y@&{9I3&c+ z#KsoQzB;LgPqUGtzw`SUkgv^roLTTaWHL#$l5jq!utL!%>nHJ!`KVwzE4xIV`X-$f2;lki%(T>zENeISfH~>sr@g33Z1yp|s_&*=W2)vp?|u?_#c4bxoM3N!sZO8;!}#YV zI{V%rPE?nf+zc>eA-iR(Hu>=lo!WoilkTO z#PxBSXhZS%nTwG_f>fQ2n*Ni8Db#*J-!p%5M>jLMp{Xq4LDDv4Vq2ukxcsay?B&Vq zLk%=`O3I6&dtPqPd@q6nulAr*_DR|m^oB8WzHIw;l3g=k4M$<>ICzwuxoFpiw}DwIoB!W^?FV9 z;_?wW+JvZ|uWUHiZR8gtec)K2>&cK=q0hMIh%7%ZWpdK1TyIQOR&H0WK51i3lmTfc z54tC>`rgJ%D!P+&*W}J}32x}PR-(I|;DpF#q}4T-T=ZBYhyhgn=eUwj^sW2(EA8947?S0l1HX{|;nWh&cT65!0n!$v;4J!Hg zXk1;qSNTO#IXiT^Sm*R?vSjECn44V=DZ}nASA38iv}|Yh2M4Y(yK*do17x*^v`zhS z0g=zCv??z-TA51M zSmoM__j*iV@SzhN{gUUex*M2;dk>dq!!gTKjps`dl9y-_ca^IZT_nKibjwZJ#N|OK zGS+l7@wJ@(SWv27`5R7v?)w7`LBfcKfB2CA9Hc23IYe<@qaUIV#E8RY`3& zz2twyFKL^-^Ur{GM|$?O3?H%;Il|b!vA{H{Q(0&=W&P$)Z{y0u#Ssc{@1C8~^-9=O z zO2?@|c24HGDCXnor##3Oe~4k2O8@!pZvE)Fi85ZKl^1Ht32Ie zJpa<4LT|DD;z+uf?|9hnIzq2a^NZcHkNZ)pX$MHJW*4K#?z_pqG_Hy62_wWzu z@Su{dD_or&E^?k5#F}Llh~Rrw^_2l-_eoROeFcB!0}ZT_{*>PgK&ZDjT4dwFUX7^T z=f8#vQXL|bws8CSP*>Tl^0qMS)S`CV4>!MY%WgFeCw}KauNgZvheM*CmOJ`b33oap zG^x$VI(dIX`g-mCql*!ll<->h@yqW~GGBgdW$(3Q!BQe$<*Api{tQSyk#b9DLxx9C z3e6hkeRHI4vX&<(7zEGaVu)ru$Z@f5VVt7Q-Jh;0({X}GOKmXYy{OVi{Mqt7oIR}L z+Vdg&C^=J}S}|~sWaCtROC?12z2t=IN<`jg9~ox@=I5E(aW03OJsr0gW~;OYJZUg` z(Nqw&%vddg*n^Ox!`9(cb9!x*_t&nE8b?p{_8Cx}5E8rLF;#oeqXw_&8)nWi{N=U= zA%Sx_)aMcX%Ph~3>>_E>Y+W8?BCE^TI0KksUg3~^$b!jCrO6_6|EqkH=xnP&=Q;Bx z$3az)fXvXzknf7Vm9NkC(9UAU`2`^pXIB^Y^9qtlg<-}dEsA6Y>dVvnD&x2G1-%~a zc*bH>B6l{Dwt7cFZRtjC{B#@-pqRW*)^HzT>gYYHAZT{@3x?h8cU)(ae67s@BR8R|jJo&TI&k$3tjoPGvCfxSyJ+2rcoo$IAzQfU z2y17KXa&yh9TW#rSe%!mj~oR`6jjYx0=$Skth#QbkmAhQnET~uhIc@N@(P6J_+A3M z{+Q&o%`H4We;Z7bWTMNA!xu1`WJ1=ZQE zKHr}hO*{m@RqdP%L-|`@GOr5TEJLWSdY9iX(gq?=9Y=Uj9J~kr@4=IKl8t>h|GD2j zRwLI^2}=+fKd#a`vcWWNu7*b(QPw9#6Ia4gQoGGO9hT>#Q@v^WRPZ#qL}KfSmE?Wg zP&+m=coL?2!;v$!Y9|d3(xa~th7dh{4);C~9C@Z4k7q>Ot6Zee=aVgZD#hVtEtCfraCVH-{!41fE% z9Ksx4mFr%J}>NiC~(uTbCL1$qZ#hNb&o=7hN(;XR${eYu7S{Z3>S|i1 zNIm8c8-#D6w3~J7la)SWn_DUBzVG*40nZDpgD2BVca_BaC2AcwW$v4kH#Xb^eM<_R zY}4jxR?>uBzn&1hM%p5>@K_9FhR$e;!RHl*34GS|YQv&hF(Uu}1M-XZnh8qg91WB#Ag>@gbrFztd+UuKwV>yN0*eNvZ65e#MKB<0s7f#!XO zd635rXJ>`oq|t)?Qv`Lj3YIuwzoCC7YSWcRS~cwHJ8}`u$@CC{#Le*W{bZ92tQKbD&I z5k5F-Pq&EI5R{9J$Sx{kXjA#YOPX!PF2>W#bF!4HG)^zLZJS~&PFl{x2lT= z%x=ef2W5%cwyh9AU*Dv~6vwB2eN?M1cAj&4s_|%2Kt+#4S(D&vm%Fg@9DhmZMkgZ_ zoO9U-Ik(qI1aRDwc(BDKp2Ff@vrZ!R3fbP(p+NVEBoLf?+M)kD%4j|STOW8!xWAi} zuS&Xq3xcMs)qgjBfp;_TrJAsIgX zodXh%1Oj2-r(^dg*MAzhZP(8kUA-W5@OL~0;=iYzSIE9FGYiB!%gsnB2Pe3v9a)eZ z#@v;%RL`EVM62lP#N2(b$CtwjlOb5^yb>>p!fH-S1kAb{ z9QI2j@Rg%J^g$&u06Bw2Oh_k+O3Be6J^m9);QeCJn~l>d&FRU2<<(IWYl ztC;pt`zjgweK@!2D-+oHQa}V#U3}?a88mhyOMZ&x%T%d%pcP#sc(Y855SrbIslV@N z%XX{#*6yRh@>8{s-*25C?$(H~kt=z30I1vCoz^=3-DP;Vqb(+3e}-D+rK^|A-VIFE zK6+<>nO`6985-}?IK55B;#KpS^PHZrOZM03pK(0y<(bAon67L9C4G=y`~dX7R0Y%j zZ!IEQW4UcHwW62|{_Y~zCbo_qv>$gUFyw&F;raz7Sr}1C%QDKoZo{sIG45DxlIzfr zIKs<-iX64M>UycM2Bri}opUG?!ecrwpaps%W383vV0=RqU7ODq4Go$Sot~=7%Ndyw zB5z*@Jc;&`6srZUC?(R?k4Wn(gzmf}v0NW06(WO99C;};AnE7s70D>g^fi-hzj(?o z6X6l;C&!_`+arfQ-S!({t)Ps*md3Pz2b16uTx{Ody7@L&oAm*+#l^+LDcuh-CHoIC z9f>miq}^Z!PG*+6NdYsJBfp0Dn{On;@r3_bQ_#&_$sz2rDb04+cQlDKn~{s@_rmCS zi{}My5&g}2vmEw8l6;`=6irR5rJhTbcWEx)bxeKr(bPLiqLdNfejPrvM+~YcX0>2#K z8UOJx?rANzIo=B+pBI4ZlctX=7)Mo7h$ru+W(+|Ya_vRd7zLXkB0QC!7jXaB*ga3) zt_(B3h;O8h+@J-a9p=$exJ|uHm``naKVA|!N5YBtc5$LWy%}=GvS0(G6Is=kzrSd^nitHCfa!O0J} zN$pT~yAa7p3b^1o-IC|hE9X|C3xUz9I8~oB*p-;2EK$GfMekdm#`RWiW1Rz2$~HNN z{1Xt~9klF?m-0w(@%2|$MH-dE{qdUH4Ig~@+`nL_M`4YFEk>2|^S|7ZJtch=KuZa` zJ&AwtKch#CHgqP8w25<< zl(5QwvG@a8%HF;edq3K$gAs?Ve$RijeH{>YkFGBMk6)r}v!Z`q0CBv4I2jCIz!f@_o&uok4T|{2ZFwt&-L=t6 z1kl~#Ny9#H1l>3lq5voKSG{l>7N#kyb?3WcH|9M!^RKQ&gB2k2UEW52iSNJN|L#6U z0Qhl7Fkj5-emsCJxQc}rj!w5Y9K&w`R}s5dR5aIYdJ^8+sk@RT_7@jLxE;+c;9)RZW29E&DaN6HR&qb^S|9;*`-(w; z!E{KF*k)XjX3d0tNgrIsw=pz?jqoSLkQj!B5`c#KfQEX2hR%S7TXbL>fX}x8pV1JM z{O_5N;4F6@Vi03jc0q#vzRXl(XYhN+YKmMmxLVeXnexxLEb?mbFitx*e%lLYWM=$m z)p?E!-UnpNQr6)FUqOA-=C*TQzvxhC{+$qaP<#UzuuWq!(b0UA=86Zr~B?2 zy?8)k;FpdB=;;D(#B!VcKnJ&h5v0ef`uRmHzY;-`u4Jj6p~_aUM8|xM;Nlcois}?C ztq-bJESi5SRsQ~>hFFjoqn<7)4jK-|nNyjmssWJ%`%Ciy(>~Ic%sd?WRQHN>NV zf*4Umt`-EUc?k^GW{}ur>eYjltcTW5AvEacN4B0A)mr;oqh*?`58x_U#_EX!{3SZ^ z2ss;pHLiK&PM?HDb^5EF+lIu?X_g`~&Rz^93-r|x73TX|nqp>fy{~`1O}gv>+{Eqk z;6<+w01}+Gt^PAouUhA&T;_YXX3ISzwCy)P3hxC*_aBoJ9;=xh83OpLNh;g{GUn{8 zhad&pG!!;1z4GzBO6&ZRGd`BEW)qi;F#&O;`=eK!z7Igna_-yx5Kc^d0^iI4>=tpM zzUBUCG=F9gMz;{+H=gHgkMr3Q%l#ob21kQ9d&N-K&f|c5_Iw*PJT6u_;5OH9T&C*M z3b=q+L)fM{!?D#s@WwW}eL`+yt>&zd)2MB}inYl>4q4} zH||5J9W^IDAHQk?nlaY|*c#XkQ7Cx(BFB7pRkA|v)#l9z1Vicyj;_?bB}VEvz_#~P z1KcGxk3h<6Aq3dx07oaGu1l}X!h!c*t(paY{lEfBVG3q9z*1fM(PI$}~=foDtS6M!9arM#oD8rF|u zWV-WY8;1Bx#((Ut{sA~tl-ibomiZ8JS=@q8+`FDI>ge824Z~FAHv+ZPgdKL zG}Ir->3q6*C#6Y{jN7yn?bH&fJ@wFDRAc6r0*tm}qapv#sIq$A-uXrAU4MM90Y^Xc z4qUZ==b;(t5NG;m0C2P=%q@5~>FR#t`?mtIumO>@#E>o04Cu>&!?))ihHIk-A_sL> zEOMCR17YxuR~zncOisZdrxMX1Rb84^sm`75bEOuwM7D@z?TM5^rJ5Nb z3?vr%6;D0~O`R{>SpdmLsBL6Q(}sI?efWDMix ziH~VE$2|U>p5ym(7T~cU##=YGXsGju93p-eL<_gqetuB6f2^0}91zJ6mp#bit{{iV z+r}`$=Y|ic0O8#k#9)T1s~&_G-yZzC@c2koKNc?gpBa*(Tr_svL`wHp+LdlR!x^~UHgWKj=RYMAe8RbC?n zNmTm^`8;`+pzWPGn6=4Jm*KP;>fZk>*nXzl7^xcklY5o;w5!DJ?;3|kC*9>rYOAmz zXBpkSn&PAUSMU61>l|F}xtB(UBE09kF1muxIE3{AVCh>Z&t?8dA1(cxM?J2(*2Le0Um7(*p9A?w?x8V#0+@bjXQn42C-O4R$N#OOIKpf0FdUUe!L$`e#=KcKdY zqSDG`afy7%9jYmdfey`sl#cUMjs*GP-=76FkF%&2=-|Z;lJY{rx;|DqM5M?9Vde5q z7imfwkgFZ9UpwO6IMmb0I?Z!+Mz%zK@FNfs_C;Q`9}?tB;=J>5sf+nw@%5CBdlJIFD@Rhb7FxZ8!IoVNnQ=CjGq;1y#4#&6m&Hpi{AC01| zp>*_2-h?@Aij~#e%s3K>$AqIe0bztz>OdpxjO-p4Yv<WFea97^ z=S`zbQ3^|Q;tA}q&*ZG~*#1Up)*x>k7mr4yA|*G#lGn{#FZP;XWzSzX`4HK;B$E3> zTAW;vIA=p)*-Ui9Oz-EPBqc}3@$2-0Jv17W5zOLof4C@zK^I3xAk^^O&6JLHd^=ES zpfNF~-ZgDH2;M1nf~wu-i=wmxT?-rby2TKsvBFZdBgwPZ{ri&wj^X@mc#-zuF?Zms zIN4x}gniUC16CQFT!x@wX=ld`-W9G(e;0?}6bBujmbff#aXRXWcG`z>$2>d-a*ycR z3SGt-K20X+_1a#yDB@YH*c9)9%gj0W?hmK_Vv`)_7a)iCCageq+6K)Vl06=#5`gV- zrdf^Uxm0b{m%o&uP+-q|Q4nT%?u()-fu(~L)-0hee%gC_ELkAAPgfr+#&r?wPTC#W z;{9jlvfDnagm><539smVgiM0DkB6`g97Jl9)zpl^%ixV_v&Xx~mNzJ`N8}S`uBOco zl@y?BBjJ}al-fNcEcBC{!%c}c!ZzwK$1XBXlwHZaxh4KUBoV|ZPZ9pP|Fr!SDU;t3 zcWC}NR0)m+d-#~AsC@XNX`4G1MO1;8szkZRTp9?LlPf4J^9DDlV)SwP)HLR}?DmfMnIr9L9f>A>hf*(Hg^P(>aYQVg6_Y~Sk5Fr@DQ7Cm+wiLT+5B^+^VLvY+OB}h zgwCEm1GQ&ZEib2S6K5+gsxykDos`CI5}H&HkoJc)ZqR9UE1 zGbI#tDxQDTaLcW%j>78KUzrc6egW-miaPh}MY-*Yc{cOa0&r8AU9}K4Q{)^**Y3L$vk+4a$3P7*xN3Z=7>3*YkR4u6#PE1d4fIYdejz#nyuZb-$u+h zrxV<--~7or%TV2jkn$Ht?!Z^DYa0bK%%Uxr4kYpOcHT~q%26$zB8>fdKo zi^yV-9lAkQcW*|$>&#3O zp(S~I(;`>tWoZ8uSL3U}%9f10%g#U9Z_Qm_u>u8AopA7MnD>wMQV%+*dP2Ley4S8` zQz5C^mTMAua)n3>p z{z7{D`azKmP~bErdZqn2sN?4#Bd9VtFq#)Aq#o?8mWm@Ia!#;!VTealA{euBk<`kM zN0&+Tmg|$&@*t+&_E-pO! zfOC`|jeL@)W^r$5_SR-WUs@D&xY?H+7|quwu_|YHIj-rfHG?E;6t1evtk!*B2pwD< zmorF7Siju%TC9d1R3F04X$=Ts3L*Kuy?qkBS+Ak5XS_om>$+>WC1+YRxq+9YKc^;c zEXQwh3~S;uy2?w)-|klQhod*oj0v|s|D!80Ze;X!!Y+HoN=&M6nag)OpCEB3v4 zci~q@t|xdz7R;VfAM?4I2u`PLiWqg*PVMzl*)I*fpji%D5t-a)&sfG8vppdm-#Vm@_6kQcfqu$aAYXV{1l?N9sZ@&eaIg hG#Y566pvF3?)#k&%FAMcp(_O;eI27CuMXSZ_&*zMU|9eF literal 0 HcmV?d00001 diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..a85da6a --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,24 @@ +{ + "name": "Randify — Random Value Generators", + "short_name": "Randify", + "description": "Simple tools for raffles, games, and everything that needs randomness.", + "start_url": "/", + "display": "standalone", + "background_color": "#09090b", + "theme_color": "#534AB7", + "orientation": "portrait-primary", + "scope": "/", + "lang": "en", + "icons": [ + { + "src": "/icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..f71d5ee --- /dev/null +++ b/public/sw.js @@ -0,0 +1,52 @@ +const CACHE_NAME = 'randify-v1'; + +self.addEventListener('install', (event) => { + self.skipWaiting(); +}); + +self.addEventListener('activate', (event) => { + event.waitUntil(self.clients.claim()); +}); + +self.addEventListener('fetch', (event) => { + const { request } = event; + const isNavigate = request.mode === 'navigate'; + const isSameOrigin = new URL(request.url).origin === self.location.origin; + + if (!isSameOrigin) { + return; + } + + if (isNavigate) { + // Network-first for HTML pages: fresh content when online, + // fallback to cache when offline. + event.respondWith( + fetch(request) + .then((response) => { + if (response && response.status === 200) { + const clone = response.clone(); + caches.open(CACHE_NAME).then((cache) => cache.put(request, clone)); + } + return response; + }) + .catch(() => caches.match(request)) + ); + } else { + // Cache-first for static assets (JS, CSS, SVG, fonts, etc.) + event.respondWith( + caches.match(request).then((cached) => { + if (cached) { + return cached; + } + return fetch(request).then((response) => { + if (!response || response.status !== 200 || response.type !== 'basic') { + return response; + } + const clone = response.clone(); + caches.open(CACHE_NAME).then((cache) => cache.put(request, clone)); + return response; + }); + }) + ); + } +}); diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index f7cde42..1349385 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -36,8 +36,16 @@ const ruUrl = `https://randify.pro${getRelativeLocaleUrl("ru", pathWithoutLocale + + + {title} + From bb62d7310e2eb388ed0d0a2d2980f15a07e84d15 Mon Sep 17 00:00:00 2001 From: emil Date: Sun, 10 May 2026 21:12:08 +0300 Subject: [PATCH 35/44] test: add Vitest + 30 dice-engine tests - Install vitest and @vitest/coverage-v8 - Add test scripts to package.json - Create vitest.config.ts - Add 30 comprehensive tests for parseDiceNotation, rollDice, rollAdvantage, buildNotation, isAdvancedNotation - Fix isAdvancedNotation regex to detect ro (reroll once) --- package-lock.json | 532 ++++++++++++++++++++++++++++++++++-- package.json | 9 +- src/lib/dice-engine.test.ts | 286 +++++++++++++++++++ src/lib/dice-engine.ts | 124 ++++++--- vitest.config.ts | 9 + 5 files changed, 903 insertions(+), 57 deletions(-) create mode 100644 src/lib/dice-engine.test.ts create mode 100644 vitest.config.ts diff --git a/package-lock.json b/package-lock.json index c590151..5afcfd5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,10 @@ "@tailwindcss/vite": "^4.0.0", "astro": "^4.16.0", "tailwindcss": "^4.0.0" + }, + "devDependencies": { + "@vitest/coverage-v8": "^4.1.5", + "vitest": "^4.1.5" } }, "node_modules/@astrojs/compiler": { @@ -363,6 +367,16 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -831,7 +845,6 @@ "version": "0.128.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.128.0.tgz", "integrity": "sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==", - "peer": true, "funding": { "url": "https://github.com/sponsors/Boshen" } @@ -847,7 +860,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -863,7 +875,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -879,7 +890,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -895,7 +905,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -911,7 +920,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -927,7 +935,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -943,7 +950,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -959,7 +965,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -975,7 +980,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -991,7 +995,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1007,7 +1010,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1023,7 +1025,6 @@ "os": [ "openharmony" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1036,7 +1037,6 @@ "wasm32" ], "optional": true, - "peer": true, "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", @@ -1057,7 +1057,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1073,7 +1072,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1081,8 +1079,7 @@ "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.18", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.18.tgz", - "integrity": "sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==", - "peer": true + "integrity": "sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==" }, "node_modules/@rollup/pluginutils": { "version": "5.3.0", @@ -1472,6 +1469,13 @@ "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==" }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@tailwindcss/node": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", @@ -1760,6 +1764,17 @@ "@babel/types": "^7.28.2" } }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -1773,6 +1788,13 @@ "@types/ms": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -1835,6 +1857,162 @@ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==" }, + "node_modules/@vitest/coverage-v8": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.5.tgz", + "integrity": "sha512-38C0/Ddb7HcRG0Z4/DUem8x57d2p9jYgp18mkaYswEOQBGsI1CG4f/hjm0ZCeaJfWhSZ4k7jgs29V1Zom7Ki9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.5", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.5", + "vitest": "4.1.5" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/coverage-v8/node_modules/magicast": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", + "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz", + "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz", + "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.5", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", + "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz", + "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.5", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz", + "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.5", + "@vitest/utils": "4.1.5", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz", + "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", + "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.5", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -1941,6 +2119,35 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.0.tgz", + "integrity": "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/astro": { "version": "4.16.19", "resolved": "https://registry.npmjs.org/astro/-/astro-4.16.19.tgz", @@ -2597,6 +2804,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", @@ -2952,6 +3169,16 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==" }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -2995,7 +3222,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "peer": true, "engines": { "node": ">=12.0.0" }, @@ -3146,6 +3372,16 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/hast-util-from-html": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", @@ -3468,6 +3704,52 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports/node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/jiti": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", @@ -3873,6 +4155,22 @@ "source-map-js": "^1.2.0" } }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/markdown-table": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", @@ -4726,6 +5024,17 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==" }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/onetime": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", @@ -4881,6 +5190,13 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5252,7 +5568,6 @@ "version": "1.0.0-rc.18", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.18.tgz", "integrity": "sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg==", - "peer": true, "dependencies": { "@oxc-project/types": "=0.128.0", "@rolldown/pluginutils": "1.0.0-rc.18" @@ -5437,6 +5752,13 @@ "@types/hast": "^3.0.4" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -5509,6 +5831,20 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/stdin-discarder": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", @@ -5585,6 +5921,19 @@ "node": ">=0.10.0" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tailwindcss": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", @@ -5602,6 +5951,13 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyexec": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", @@ -5611,7 +5967,6 @@ "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", - "peer": true, "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" @@ -5623,6 +5978,16 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5911,7 +6276,6 @@ "version": "8.0.11", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.11.tgz", "integrity": "sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow==", - "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -5997,6 +6361,113 @@ } } }, + "node_modules/vitest": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz", + "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.5", + "@vitest/mocker": "4.1.5", + "@vitest/pretty-format": "4.1.5", + "@vitest/runner": "4.1.5", + "@vitest/snapshot": "4.1.5", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.5", + "@vitest/browser-preview": "4.1.5", + "@vitest/browser-webdriverio": "4.1.5", + "@vitest/coverage-istanbul": "4.1.5", + "@vitest/coverage-v8": "4.1.5", + "@vitest/ui": "4.1.5", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vitest/node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vitest/node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", @@ -6025,6 +6496,23 @@ "node": ">=4" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/widest-line": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", diff --git a/package.json b/package.json index 8414a65..c1293fb 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,19 @@ "scripts": { "dev": "astro dev", "build": "astro build", - "preview": "astro preview" + "preview": "astro preview", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage" }, "dependencies": { "@astrojs/sitemap": "^3.2.1", "@tailwindcss/vite": "^4.0.0", "astro": "^4.16.0", "tailwindcss": "^4.0.0" + }, + "devDependencies": { + "@vitest/coverage-v8": "^4.1.5", + "vitest": "^4.1.5" } } diff --git a/src/lib/dice-engine.test.ts b/src/lib/dice-engine.test.ts new file mode 100644 index 0000000..d78a886 --- /dev/null +++ b/src/lib/dice-engine.test.ts @@ -0,0 +1,286 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { + parseDiceNotation, + rollDice, + rollAdvantage, + buildNotation, + isAdvancedNotation, +} from "./dice-engine"; + +describe("parseDiceNotation", () => { + it('parses basic notation "d6"', () => { + const parsed = parseDiceNotation("d6"); + expect(parsed).not.toBeNull(); + expect(parsed!.count).toBe(1); + expect(parsed!.sides).toBe(6); + expect(parsed!.modifier).toBe(0); + expect(parsed!.keepDrop).toBeNull(); + expect(parsed!.explode.active).toBe(false); + expect(parsed!.reroll.active).toBe(false); + }); + + it('parses "2d20+3"', () => { + const parsed = parseDiceNotation("2d20+3"); + expect(parsed).not.toBeNull(); + expect(parsed!.count).toBe(2); + expect(parsed!.sides).toBe(20); + expect(parsed!.modifier).toBe(3); + }); + + it('parses "3d8-1"', () => { + const parsed = parseDiceNotation("3d8-1"); + expect(parsed!.count).toBe(3); + expect(parsed!.sides).toBe(8); + expect(parsed!.modifier).toBe(-1); + }); + + it('parses "4d6kh3"', () => { + const parsed = parseDiceNotation("4d6kh3"); + expect(parsed!.keepDrop).toEqual({ type: "kh", count: 3 }); + }); + + it('parses "5d6dl2"', () => { + const parsed = parseDiceNotation("5d6dl2"); + expect(parsed!.keepDrop).toEqual({ type: "dl", count: 2 }); + }); + + it('parses exploding "3d6!"', () => { + const parsed = parseDiceNotation("3d6!"); + expect(parsed!.explode.active).toBe(true); + expect(parsed!.explode.penetrating).toBe(false); + expect(parsed!.explode.threshold).toBe(6); + }); + + it('parses penetrating explode "2d10!p"', () => { + const parsed = parseDiceNotation("2d10!p"); + expect(parsed!.explode.active).toBe(true); + expect(parsed!.explode.penetrating).toBe(true); + }); + + it('parses explode with custom threshold "2d6!>4"', () => { + const parsed = parseDiceNotation("2d6!>4"); + expect(parsed!.explode.active).toBe(true); + expect(parsed!.explode.threshold).toBe(4); + }); + + it('parses reroll "2d6r1"', () => { + const parsed = parseDiceNotation("2d6r1"); + expect(parsed!.reroll.active).toBe(true); + expect(parsed!.reroll.once).toBe(false); + expect(parsed!.reroll.values).toContain(1); + }); + + it('parses reroll once "2d6ro1"', () => { + const parsed = parseDiceNotation("2d6ro1"); + expect(parsed!.reroll.active).toBe(true); + expect(parsed!.reroll.once).toBe(true); + }); + + it('parses reroll less-than "2d6r<3"', () => { + const parsed = parseDiceNotation("2d6r<3"); + expect(parsed!.reroll.active).toBe(true); + expect(parsed!.reroll.values).toContain(1); + expect(parsed!.reroll.values).toContain(2); + expect(parsed!.reroll.values).not.toContain(3); + }); + + it("ignores whitespace", () => { + const parsed = parseDiceNotation(" 2 d 20 + 5 "); + expect(parsed!.count).toBe(2); + expect(parsed!.sides).toBe(20); + expect(parsed!.modifier).toBe(5); + }); + + it("returns null for invalid notation", () => { + expect(parseDiceNotation("")).toBeNull(); + expect(parseDiceNotation("abc")).toBeNull(); + expect(parseDiceNotation("2d6+1+2")).toBeNull(); + expect(parseDiceNotation("0d6")).toBeNull(); + expect(parseDiceNotation("21d6")).toBeNull(); + expect(parseDiceNotation("2d0")).toBeNull(); + expect(parseDiceNotation("2d6kh0")).toBeNull(); + expect(parseDiceNotation("2d6kh2")).toBeNull(); + }); +}); + +describe("rollDice", () => { + let mathRandomSpy: ReturnType; + + afterEach(() => { + mathRandomSpy?.mockRestore(); + }); + + it("rolls basic dice", () => { + mathRandomSpy = vi.spyOn(Math, "random").mockReturnValue(0.5); + const parsed = parseDiceNotation("3d6")!; + const result = rollDice(parsed); + + expect(result.dice).toHaveLength(3); + expect(result.kept).toHaveLength(3); + expect(result.dropped).toHaveLength(0); + expect(result.total).toBe(12); // each die: floor(0.5*6)+1 = 4, 3*4 = 12 + }); + + it("applies modifier", () => { + mathRandomSpy = vi.spyOn(Math, "random").mockReturnValue(0.5); + const parsed = parseDiceNotation("1d20+5")!; + const result = rollDice(parsed); + + expect(result.total).toBe(16); // 11 + 5 + }); + + it("keeps highest rolls (kh)", () => { + mathRandomSpy = vi + .spyOn(Math, "random") + .mockReturnValueOnce(0.1) // 1 + .mockReturnValueOnce(0.9) // 6 + .mockReturnValueOnce(0.5); // 4 + + const parsed = parseDiceNotation("3d6kh2")!; + const result = rollDice(parsed); + + expect(result.kept).toHaveLength(2); + expect(result.dropped).toHaveLength(1); + expect(result.dropped[0].dropped).toBe(true); + expect(result.total).toBe(10); // 6 + 4 + }); + + it("drops highest rolls (dh)", () => { + mathRandomSpy = vi + .spyOn(Math, "random") + .mockReturnValueOnce(0.1) // 1 + .mockReturnValueOnce(0.9) // 6 + .mockReturnValueOnce(0.5); // 4 + + const parsed = parseDiceNotation("3d6dh1")!; + const result = rollDice(parsed); + + expect(result.kept).toHaveLength(2); + expect(result.dropped).toHaveLength(1); + expect(result.dropped[0].value).toBe(6); + expect(result.total).toBe(5); // 1 + 4 + }); + + it("handles exploding dice", () => { + mathRandomSpy = vi + .spyOn(Math, "random") + .mockReturnValueOnce(0.99) // first roll: 6 (max, triggers explode) + .mockReturnValueOnce(0.99) // explosion 1: 6 (triggers again) + .mockReturnValueOnce(0.1); // explosion 2: 1 (stops) + + const parsed = parseDiceNotation("1d6!")!; + const result = rollDice(parsed); + + expect(result.dice[0].exploded).toBe(true); + expect(result.dice[0].explosions).toEqual([6, 1]); + expect(result.total).toBe(13); // 6 + 6 + 1 + }); + + it("handles penetrating explode", () => { + mathRandomSpy = vi + .spyOn(Math, "random") + .mockReturnValueOnce(0.99) // first roll: 6 (explode) + .mockReturnValueOnce(0.99); // explosion: 6 -> penetrating -> 5, then 5 < 6 stops + + const parsed = parseDiceNotation("1d6!p")!; + const result = rollDice(parsed); + + expect(result.dice[0].explosions).toEqual([5]); + expect(result.total).toBe(11); // 6 + 5 + }); + + it("handles reroll", () => { + mathRandomSpy = vi + .spyOn(Math, "random") + .mockReturnValueOnce(0.0) // first roll: 1 (reroll) + .mockReturnValueOnce(0.5); // reroll: 4 + + const parsed = parseDiceNotation("1d6r1")!; + const result = rollDice(parsed); + + expect(result.dice[0].rerolledFrom).toBe(1); + expect(result.dice[0].value).toBe(4); + }); + + it("handles reroll once (ro)", () => { + mathRandomSpy = vi + .spyOn(Math, "random") + .mockReturnValueOnce(0.0) // first: 1 (reroll once) + .mockReturnValueOnce(0.0); // second: 1 (stays, no more rerolls) + + const parsed = parseDiceNotation("1d6ro1")!; + const result = rollDice(parsed); + + expect(result.dice[0].rerolledFrom).toBe(1); + expect(result.dice[0].value).toBe(1); + }); +}); + +describe("rollAdvantage", () => { + let mathRandomSpy: ReturnType; + + afterEach(() => { + mathRandomSpy?.mockRestore(); + }); + + it("returns highest for advantage", () => { + mathRandomSpy = vi + .spyOn(Math, "random") + .mockReturnValueOnce(0.1) // 3 + .mockReturnValueOnce(0.9); // 19 + + const result = rollAdvantage(20, 0, true); + expect(result.total).toBe(19); + expect(result.advantageRolls).toEqual([3, 19]); + }); + + it("returns lowest for disadvantage", () => { + mathRandomSpy = vi + .spyOn(Math, "random") + .mockReturnValueOnce(0.9) // 19 + .mockReturnValueOnce(0.1); // 3 + + const result = rollAdvantage(20, 2, false); + expect(result.total).toBe(5); // 3 + 2 + expect(result.advantageRolls).toEqual([19, 3]); + }); +}); + +describe("buildNotation", () => { + it("builds basic notation", () => { + expect(buildNotation(1, 6, 0)).toBe("1d6"); + expect(buildNotation(2, 20, 0)).toBe("2d20"); + }); + + it("adds positive modifier", () => { + expect(buildNotation(2, 6, 3)).toBe("2d6+3"); + }); + + it("adds negative modifier", () => { + expect(buildNotation(2, 6, -2)).toBe("2d6-2"); + }); +}); + +describe("isAdvancedNotation", () => { + it("returns false for basic notation", () => { + expect(isAdvancedNotation("2d6")).toBe(false); + expect(isAdvancedNotation("1d20+5")).toBe(false); + expect(isAdvancedNotation("3d8-1")).toBe(false); + }); + + it("returns true for keep/drop", () => { + expect(isAdvancedNotation("4d6kh3")).toBe(true); + expect(isAdvancedNotation("5d6dl2")).toBe(true); + }); + + it("returns true for explode", () => { + expect(isAdvancedNotation("3d6!")).toBe(true); + expect(isAdvancedNotation("2d10!p")).toBe(true); + }); + + it("returns true for reroll", () => { + expect(isAdvancedNotation("2d6r1")).toBe(true); + expect(isAdvancedNotation("2d6ro1")).toBe(true); + expect(isAdvancedNotation("2d6r<3")).toBe(true); + }); +}); diff --git a/src/lib/dice-engine.ts b/src/lib/dice-engine.ts index ade5f30..72d4076 100644 --- a/src/lib/dice-engine.ts +++ b/src/lib/dice-engine.ts @@ -2,7 +2,7 @@ // Supports: XdY±Z, kh/kl/dh/dl, ! exploding, r reroll, advantage/disadvantage // Caps: explode chain ≤100, reroll recursion ≤1000 -type KeepDrop = { type: 'kh' | 'kl' | 'dh' | 'dl'; count: number } | null; +type KeepDrop = { type: "kh" | "kl" | "dh" | "dl"; count: number } | null; type Explode = { active: boolean; @@ -14,7 +14,7 @@ type Reroll = { active: boolean; values: Set; once: boolean; // "ro" = once only; "r" = recursive - operator: 'eq' | 'lt' | 'gt'; + operator: "eq" | "lt" | "gt"; }; export interface Parsed { @@ -32,7 +32,7 @@ export interface DieResult { value: number; original: number; exploded: boolean; - explosions: number[]; // chained explosion rolls + explosions: number[]; // chained explosion rolls rerolledFrom: number | null; dropped: boolean; } @@ -52,7 +52,7 @@ const REROLL_CAP = 1000; /** Remove all whitespace and lowercase for parsing */ function normalize(raw: string): string { - return raw.replace(/\s+/g, '').toLowerCase(); + return raw.replace(/\s+/g, "").toLowerCase(); } /** @@ -67,7 +67,7 @@ export function parseDiceNotation(raw: string): Parsed | null { const main = s.match(/^(\d*)d(\d+)(.*)$/); if (!main) return null; - const count = main[1] === '' ? 1 : parseInt(main[1], 10); + const count = main[1] === "" ? 1 : parseInt(main[1], 10); const sides = parseInt(main[2], 10); const rest = main[3]; @@ -81,7 +81,10 @@ export function parseDiceNotation(raw: string): Parsed | null { if (kdMatch) { const countKD = parseInt(kdMatch[2], 10); if (countKD < 1 || countKD >= count) return null; - keepDrop = { type: kdMatch[1] as 'kh' | 'kl' | 'dh' | 'dl', count: countKD }; + keepDrop = { + type: kdMatch[1] as "kh" | "kl" | "dh" | "dl", + count: countKD, + }; remaining = remaining.slice(kdMatch[0].length); } @@ -90,8 +93,8 @@ export function parseDiceNotation(raw: string): Parsed | null { const expMatch = remaining.match(/^(!p|!)(>?)(\d*)/); if (expMatch) { explode.active = true; - explode.penetrating = expMatch[1] === '!p'; - if (expMatch[2] === '>' && expMatch[3]) { + explode.penetrating = expMatch[1] === "!p"; + if (expMatch[2] === ">" && expMatch[3]) { explode.threshold = parseInt(expMatch[3], 10); if (explode.threshold < 2 || explode.threshold > sides) return null; } else { @@ -101,21 +104,26 @@ export function parseDiceNotation(raw: string): Parsed | null { } // Reroll: rN, roN, r]?)(\d+)/); if (rrMatch) { reroll.active = true; - reroll.once = rrMatch[1] === 'ro'; - const op = rrMatch[2] as '' | '<' | '>'; + reroll.once = rrMatch[1] === "ro"; + const op = rrMatch[2] as "" | "<" | ">"; const val = parseInt(rrMatch[3], 10); - if (op === '<') { - reroll.operator = 'lt'; + if (op === "<") { + reroll.operator = "lt"; for (let i = 1; i < val && i < sides; i++) reroll.values.add(i); - } else if (op === '>') { - reroll.operator = 'gt'; + } else if (op === ">") { + reroll.operator = "gt"; for (let i = val + 1; i <= sides; i++) reroll.values.add(i); } else { - reroll.operator = 'eq'; + reroll.operator = "eq"; reroll.values.add(val); } remaining = remaining.slice(rrMatch[0].length); @@ -133,7 +141,16 @@ export function parseDiceNotation(raw: string): Parsed | null { // If anything remains unparsed, it's invalid if (remaining.length > 0) return null; - return { count, sides, modifier, keepDrop, explode, reroll, advantage: false, disadvantage: false }; + return { + count, + sides, + modifier, + keepDrop, + explode, + reroll, + advantage: false, + disadvantage: false, + }; } /** Roll a single die with optional reroll and explode logic */ @@ -194,34 +211,47 @@ export function rollDice(parsed: Parsed): RollResult { if (parsed.keepDrop) { const sorted = [...dice].map((d, i) => ({ die: d, idx: i })); - if (parsed.keepDrop.type === 'kh') { + if (parsed.keepDrop.type === "kh") { sorted.sort((a, b) => b.die.value - a.die.value); - const keepIndices = new Set(sorted.slice(0, parsed.keepDrop.count).map(x => x.idx)); + const keepIndices = new Set( + sorted.slice(0, parsed.keepDrop.count).map((x) => x.idx), + ); kept = dice.filter((_, i) => keepIndices.has(i)); dropped = dice.filter((_, i) => !keepIndices.has(i)); - } else if (parsed.keepDrop.type === 'kl') { + } else if (parsed.keepDrop.type === "kl") { sorted.sort((a, b) => a.die.value - b.die.value); - const keepIndices = new Set(sorted.slice(0, parsed.keepDrop.count).map(x => x.idx)); + const keepIndices = new Set( + sorted.slice(0, parsed.keepDrop.count).map((x) => x.idx), + ); kept = dice.filter((_, i) => keepIndices.has(i)); dropped = dice.filter((_, i) => !keepIndices.has(i)); - } else if (parsed.keepDrop.type === 'dh') { + } else if (parsed.keepDrop.type === "dh") { sorted.sort((a, b) => b.die.value - a.die.value); - const dropIndices = new Set(sorted.slice(0, parsed.keepDrop.count).map(x => x.idx)); + const dropIndices = new Set( + sorted.slice(0, parsed.keepDrop.count).map((x) => x.idx), + ); kept = dice.filter((_, i) => !dropIndices.has(i)); dropped = dice.filter((_, i) => dropIndices.has(i)); - } else if (parsed.keepDrop.type === 'dl') { + } else if (parsed.keepDrop.type === "dl") { sorted.sort((a, b) => a.die.value - b.die.value); - const dropIndices = new Set(sorted.slice(0, parsed.keepDrop.count).map(x => x.idx)); + const dropIndices = new Set( + sorted.slice(0, parsed.keepDrop.count).map((x) => x.idx), + ); kept = dice.filter((_, i) => !dropIndices.has(i)); dropped = dice.filter((_, i) => dropIndices.has(i)); } } - dropped.forEach(d => { d.dropped = true; }); + dropped.forEach((d) => { + d.dropped = true; + }); // Calculate total let total = kept.reduce((sum, d) => sum + d.value, 0); - total += kept.reduce((sum, d) => sum + d.explosions.reduce((s, v) => s + v, 0), 0); + total += kept.reduce( + (sum, d) => sum + d.explosions.reduce((s, v) => s + v, 0), + 0, + ); total += parsed.modifier; return { @@ -230,30 +260,56 @@ export function rollDice(parsed: Parsed): RollResult { dropped, modifier: parsed.modifier, total, - notation: '', // filled by caller + notation: "", // filled by caller advantageRolls: null, }; } /** Roll with advantage or disadvantage (2dX keep highest/lowest) */ -export function rollAdvantage(sides: number, modifier: number, advantage: boolean): RollResult { +export function rollAdvantage( + sides: number, + modifier: number, + advantage: boolean, +): RollResult { const r1 = Math.floor(Math.random() * sides) + 1; const r2 = Math.floor(Math.random() * sides) + 1; const keptVal = advantage ? Math.max(r1, r2) : Math.min(r1, r2); return { - dice: [{ value: keptVal, original: keptVal, exploded: false, explosions: [], rerolledFrom: null, dropped: false }], - kept: [{ value: keptVal, original: keptVal, exploded: false, explosions: [], rerolledFrom: null, dropped: false }], + dice: [ + { + value: keptVal, + original: keptVal, + exploded: false, + explosions: [], + rerolledFrom: null, + dropped: false, + }, + ], + kept: [ + { + value: keptVal, + original: keptVal, + exploded: false, + explosions: [], + rerolledFrom: null, + dropped: false, + }, + ], dropped: [], modifier, total: keptVal + modifier, - notation: '', + notation: "", advantageRolls: [r1, r2], }; } /** Build notation string from parsed object (basic only, for simple sync) */ -export function buildNotation(count: number, sides: number, modifier: number): string { +export function buildNotation( + count: number, + sides: number, + modifier: number, +): string { let s = `${count}d${sides}`; if (modifier > 0) s += `+${modifier}`; else if (modifier < 0) s += modifier; @@ -263,5 +319,5 @@ export function buildNotation(count: number, sides: number, modifier: number): s /** Check if notation contains advanced features beyond basic XdY±Z */ export function isAdvancedNotation(raw: string): boolean { const s = normalize(raw); - return /(kh|kl|dh|dl|!|[<>]?r\d)/.test(s); + return /(kh|kl|dh|dl|!|ro?[<>]?\d)/.test(s); } diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..7dd1325 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + include: ['src/**/*.test.ts'], + }, +}); From 8421441664a5e3cf4065594217a7e34027ddbf50 Mon Sep 17 00:00:00 2001 From: emil Date: Sun, 10 May 2026 21:17:34 +0300 Subject: [PATCH 36/44] chore: add ESLint + Prettier, fix all lint errors - Install eslint, typescript-eslint, eslint-plugin-astro, prettier, prettier-plugin-astro, globals - Add flat eslint.config.mjs with recommended rules for JS, TS, Astro - Add .prettierrc with astro plugin - Add lint, lint:fix, format, format:check scripts - Fix prefer-const, no-unused-vars, no-useless-assignment across codebase - Fix AdBanner.astro JSX fragment for Prettier compatibility - Add eslint-disable around Yandex/Top.Mail.Ru inline scripts - Auto-format entire codebase with Prettier --- .prettierignore | 6 + .prettierrc | 11 + eslint.config.mjs | 22 + package-lock.json | 1391 +++++++++++++++++ package.json | 15 +- public/sw.js | 20 +- public/yandex_dd9dad039a139021.html | 10 +- src/components/AdBanner.astro | 102 +- src/components/Analytics.astro | 144 +- src/components/GeneratorCard.astro | 94 +- src/components/LanguageSwitcher.astro | 50 +- src/components/SeoBlock.astro | 22 +- src/components/generators/CardGenerator.astro | 464 +++--- src/components/generators/CoinGenerator.astro | 329 ++-- .../generators/ColorGenerator.astro | 160 +- src/components/generators/DiceGenerator.astro | 1374 +++++++++------- src/components/generators/ListGenerator.astro | 427 +++-- .../generators/LotteryGenerator.astro | 387 +++-- .../generators/NumberGenerator.astro | 310 ++-- .../generators/PasswordGenerator.astro | 388 +++-- src/components/generators/UuidGenerator.astro | 455 +++--- src/components/generators/WheelSpinner.astro | 773 +++++---- src/data/config.ts | 6 +- src/env.d.ts | 2 +- src/i18n/translations.ts | 234 +-- src/layouts/BaseLayout.astro | 127 +- src/layouts/GeneratorLayout.astro | 32 +- src/lib/client/animations.ts | 10 +- src/lib/client/clipboard.ts | 10 +- src/lib/client/validation.ts | 6 +- src/lib/dice-engine.test.ts | 2 +- src/lib/dice-engine.ts | 10 +- src/pages/generators/cards.astro | 2 +- src/pages/generators/coin.astro | 2 +- src/pages/generators/colors.astro | 2 +- src/pages/generators/dice.astro | 2 +- src/pages/generators/list.astro | 2 +- src/pages/generators/lottery.astro | 2 +- src/pages/generators/numbers.astro | 2 +- src/pages/generators/password.astro | 2 +- src/pages/generators/uuid.astro | 2 +- src/pages/generators/wheel.astro | 2 +- src/pages/index.astro | 70 +- src/pages/ru/generators/cards.astro | 2 +- src/pages/ru/generators/coin.astro | 2 +- src/pages/ru/generators/colors.astro | 2 +- src/pages/ru/generators/dice.astro | 2 +- src/pages/ru/generators/list.astro | 2 +- src/pages/ru/generators/lottery.astro | 2 +- src/pages/ru/generators/numbers.astro | 2 +- src/pages/ru/generators/password.astro | 2 +- src/pages/ru/generators/uuid.astro | 2 +- src/pages/ru/generators/wheel.astro | 2 +- src/pages/ru/index.astro | 70 +- vitest.config.ts | 6 +- 55 files changed, 4625 insertions(+), 2954 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 eslint.config.mjs diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..ad52979 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +dist/ +node_modules/ +.astro/ +package-lock.json +CLAUDE.md +.prettierignore diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..d87faa5 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,11 @@ +{ + "plugins": ["prettier-plugin-astro"], + "overrides": [ + { + "files": "*.astro", + "options": { + "parser": "astro" + } + } + ] +} diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..85eb659 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,22 @@ +import js from "@eslint/js"; +import tseslint from "typescript-eslint"; +import astro from "eslint-plugin-astro"; +import globals from "globals"; + +export default [ + { ignores: ["dist/**", ".astro/**", "node_modules/**", "src/env.d.ts"] }, + js.configs.recommended, + ...tseslint.configs.recommended, + ...astro.configs.recommended, + { + files: ["public/sw.js"], + languageOptions: { + globals: { + ...globals.serviceworker, + }, + }, + rules: { + "@typescript-eslint/no-unused-vars": "off", + }, + }, +]; diff --git a/package-lock.json b/package-lock.json index 5afcfd5..6b6f7a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,16 @@ "tailwindcss": "^4.0.0" }, "devDependencies": { + "@eslint/js": "^10.0.1", + "@typescript-eslint/eslint-plugin": "^8.59.2", + "@typescript-eslint/parser": "^8.59.2", "@vitest/coverage-v8": "^4.1.5", + "eslint": "^10.3.0", + "eslint-plugin-astro": "^1.7.0", + "globals": "^17.6.0", + "prettier": "^3.8.3", + "prettier-plugin-astro": "^0.14.1", + "typescript-eslint": "^8.59.2", "vitest": "^4.1.5" } }, @@ -405,6 +414,187 @@ "tslib": "^2.4.0" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", + "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", @@ -849,6 +1039,19 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.0-rc.18", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.18.tgz", @@ -1795,6 +1998,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -1808,6 +2018,13 @@ "@types/unist": "*" } }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -1852,6 +2069,239 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", + "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/type-utils": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz", + "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", + "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.2", + "@typescript-eslint/types": "^8.59.2", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", + "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", + "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz", + "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", + "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", + "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.2", + "@typescript-eslint/tsconfig-utils": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", + "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", + "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", @@ -2024,6 +2474,33 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -2228,6 +2705,94 @@ "sharp": "^0.33.3" } }, + "node_modules/astro-eslint-parser": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/astro-eslint-parser/-/astro-eslint-parser-1.4.0.tgz", + "integrity": "sha512-+QDcgc7e+au6EZ0YjMmRRjNoQo5bDMlaR45aWDoFsuxQTCM9qmCHRoiKJPELgckJ8Wmr7vcfpa9eCDHBFh6G4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@astrojs/compiler": "^2.0.0 || ^3.0.0", + "@typescript-eslint/scope-manager": "^7.0.0 || ^8.0.0", + "@typescript-eslint/types": "^7.0.0 || ^8.0.0", + "astrojs-compiler-sync": "^1.0.0", + "debug": "^4.3.4", + "entities": "^7.0.0", + "eslint-scope": "^8.0.1", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.0", + "fast-glob": "^3.3.3", + "is-glob": "^4.0.3", + "semver": "^7.3.8" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + } + }, + "node_modules/astro-eslint-parser/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/astro-eslint-parser/node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/astro-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/astro-eslint-parser/node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/astro/node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -2668,6 +3233,25 @@ } } }, + "node_modules/astrojs-compiler-sync": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/astrojs-compiler-sync/-/astrojs-compiler-sync-1.1.1.tgz", + "integrity": "sha512-0mKvB9sDQRIZPsEJadw6OaFbGJ92cJPPR++ICca9XEyiUAZqgVuk25jNmzHPT0KF80rI94trSZrUR5iHFXGGOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "synckit": "^0.11.0" + }, + "engines": { + "node": "^18.18.0 || >=20.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "@astrojs/compiler": ">=0.27.0" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -2685,6 +3269,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/base-64": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", @@ -2722,6 +3316,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -2978,6 +3585,21 @@ "node": ">= 0.6" } }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -3017,6 +3639,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3144,6 +3773,307 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", + "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.5.5", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.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", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.6.5.tgz", + "integrity": "sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-plugin-astro": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-astro/-/eslint-plugin-astro-1.7.0.tgz", + "integrity": "sha512-89xpAn528UKCdmyysbg0AHHqi6sqcK89wXnJIpu4F0mFBN03zATEBNK7pRtMfl6gwtMOm5ECXs+Wz5qDHhwTFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@jridgewell/sourcemap-codec": "^1.4.14", + "@typescript-eslint/types": "^7.7.1 || ^8", + "astro-eslint-parser": "^1.3.0", + "eslint-compat-utils": "^0.6.0", + "globals": "^16.0.0", + "postcss": "^8.4.14", + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": ">=8.57.0" + } + }, + "node_modules/eslint-plugin-astro/node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -3156,6 +4086,42 @@ "node": ">=4" } }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -3164,6 +4130,16 @@ "@types/estree": "^1.0.0" } }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eventemitter3": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", @@ -3195,6 +4171,13 @@ "node": ">=0.10.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -3210,6 +4193,20 @@ "node": ">=8.6.0" } }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -3234,6 +4231,19 @@ } } }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -3277,6 +4287,27 @@ "pkg-dir": "^4.2.0" } }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, "node_modules/flattie": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", @@ -3333,6 +4364,19 @@ "node": ">= 6" } }, + "node_modules/globals": { + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", + "integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -3568,6 +4612,16 @@ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==" }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/import-meta-resolve": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", @@ -3577,6 +4631,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/is-arrayish": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", @@ -3704,6 +4768,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -3785,6 +4856,27 @@ "node": ">=6" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -3796,6 +4888,16 @@ "node": ">=6" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -3812,6 +4914,20 @@ "node": ">=6" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", @@ -4969,6 +6085,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", @@ -4999,6 +6131,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/neotraverse": { "version": "0.6.18", "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", @@ -5059,6 +6198,24 @@ "regex-recursion": "^5.1.1" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/ora": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", @@ -5190,6 +6347,16 @@ "node": ">=8" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -5259,6 +6426,20 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/preferred-pm": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/preferred-pm/-/preferred-pm-4.1.1.tgz", @@ -5272,6 +6453,47 @@ "node": ">=18.12" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-astro": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-astro/-/prettier-plugin-astro-0.14.1.tgz", + "integrity": "sha512-RiBETaaP9veVstE4vUwSIcdATj6dKmXljouXc/DDNwBSPTp8FRkLGDSGFClKsAFeeg+13SB0Z1JZvbD76bigJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@astrojs/compiler": "^2.9.1", + "prettier": "^3.0.0", + "sass-formatter": "^0.7.6" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, "node_modules/prismjs": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", @@ -5309,6 +6531,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5666,6 +6898,23 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/s.color": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/s.color/-/s.color-0.0.15.tgz", + "integrity": "sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass-formatter": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/sass-formatter/-/sass-formatter-0.7.9.tgz", + "integrity": "sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "suf-log": "^2.5.3" + } + }, "node_modules/sax": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", @@ -5737,6 +6986,29 @@ "@img/sharp-win32-x64": "0.33.5" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/shiki": { "version": "1.29.2", "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.29.2.tgz", @@ -5921,6 +7193,16 @@ "node": ">=0.10.0" } }, + "node_modules/suf-log": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/suf-log/-/suf-log-2.5.3.tgz", + "integrity": "sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==", + "dev": true, + "license": "MIT", + "dependencies": { + "s.color": "0.0.15" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5934,6 +7216,22 @@ "node": ">=8" } }, + "node_modules/synckit": { + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, "node_modules/tailwindcss": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", @@ -6017,6 +7315,19 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/tsconfck": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", @@ -6042,6 +7353,19 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "optional": true }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-fest": { "version": "4.41.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", @@ -6066,6 +7390,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.2.tgz", + "integrity": "sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.2", + "@typescript-eslint/parser": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -6233,6 +7581,23 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -6477,6 +7842,22 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/which-pm": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-3.0.1.tgz", @@ -6527,6 +7908,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", diff --git a/package.json b/package.json index c1293fb..3c23485 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,11 @@ "preview": "astro preview", "test": "vitest run", "test:watch": "vitest", - "test:coverage": "vitest run --coverage" + "test:coverage": "vitest run --coverage", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write .", + "format:check": "prettier --check ." }, "dependencies": { "@astrojs/sitemap": "^3.2.1", @@ -17,7 +21,16 @@ "tailwindcss": "^4.0.0" }, "devDependencies": { + "@eslint/js": "^10.0.1", + "@typescript-eslint/eslint-plugin": "^8.59.2", + "@typescript-eslint/parser": "^8.59.2", "@vitest/coverage-v8": "^4.1.5", + "eslint": "^10.3.0", + "eslint-plugin-astro": "^1.7.0", + "globals": "^17.6.0", + "prettier": "^3.8.3", + "prettier-plugin-astro": "^0.14.1", + "typescript-eslint": "^8.59.2", "vitest": "^4.1.5" } } diff --git a/public/sw.js b/public/sw.js index f71d5ee..016b8f2 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,16 +1,16 @@ -const CACHE_NAME = 'randify-v1'; +const CACHE_NAME = "randify-v1"; -self.addEventListener('install', (event) => { +self.addEventListener("install", (event) => { self.skipWaiting(); }); -self.addEventListener('activate', (event) => { +self.addEventListener("activate", (event) => { event.waitUntil(self.clients.claim()); }); -self.addEventListener('fetch', (event) => { +self.addEventListener("fetch", (event) => { const { request } = event; - const isNavigate = request.mode === 'navigate'; + const isNavigate = request.mode === "navigate"; const isSameOrigin = new URL(request.url).origin === self.location.origin; if (!isSameOrigin) { @@ -29,7 +29,7 @@ self.addEventListener('fetch', (event) => { } return response; }) - .catch(() => caches.match(request)) + .catch(() => caches.match(request)), ); } else { // Cache-first for static assets (JS, CSS, SVG, fonts, etc.) @@ -39,14 +39,18 @@ self.addEventListener('fetch', (event) => { return cached; } return fetch(request).then((response) => { - if (!response || response.status !== 200 || response.type !== 'basic') { + if ( + !response || + response.status !== 200 || + response.type !== "basic" + ) { return response; } const clone = response.clone(); caches.open(CACHE_NAME).then((cache) => cache.put(request, clone)); return response; }); - }) + }), ); } }); diff --git a/public/yandex_dd9dad039a139021.html b/public/yandex_dd9dad039a139021.html index 4db07c3..af97c4d 100644 --- a/public/yandex_dd9dad039a139021.html +++ b/public/yandex_dd9dad039a139021.html @@ -1,6 +1,8 @@ - - - - Verification: dd9dad039a139021 + + + + + Verification: dd9dad039a139021 + diff --git a/src/components/AdBanner.astro b/src/components/AdBanner.astro index 1f40538..2e0556a 100644 --- a/src/components/AdBanner.astro +++ b/src/components/AdBanner.astro @@ -1,33 +1,33 @@ --- interface Props { - size: 'leaderboard' | 'rectangle' | 'tile'; + size: "leaderboard" | "rectangle" | "tile"; } const { size } = Astro.props; const ads = [ { - href: 'https://redirect.appmetrica.yandex.com/serve/29835370459904648?clid=15006577&appmetrica_js_redirect=0', - label: 'Яндекс с Алисой — скачать бесплатно', - sublabel: 'Голосовой ИИ-помощник для Android и iOS', - cta: 'Установить', - ctaMobile: 'Установить бесплатно', + href: "https://redirect.appmetrica.yandex.com/serve/29835370459904648?clid=15006577&appmetrica_js_redirect=0", + label: "Яндекс с Алисой — скачать бесплатно", + sublabel: "Голосовой ИИ-помощник для Android и iOS", + cta: "Установить", + ctaMobile: "Установить бесплатно", }, { - href: 'https://yandex.ru/project/browser/bonus/multioffer/affiliate_4prod?source=pWRP8eS1VsC2X59560&partner_string=P89XvN11U6RuE47077&cliddbro=15006579&clidmbro=15006578&cliddefault=15006570&clidpp=15006567', - label: 'Яндекс Браузер — быстрый и безопасный', - sublabel: 'Браузер с Алисой и защитой от угроз', - cta: 'Скачать', - ctaMobile: 'Скачать бесплатно', + href: "https://yandex.ru/project/browser/bonus/multioffer/affiliate_4prod?source=pWRP8eS1VsC2X59560&partner_string=P89XvN11U6RuE47077&cliddbro=15006579&clidmbro=15006578&cliddefault=15006570&clidpp=15006567", + label: "Яндекс Браузер — быстрый и безопасный", + sublabel: "Браузер с Алисой и защитой от угроз", + cta: "Скачать", + ctaMobile: "Скачать бесплатно", }, ]; const ad = ads[Math.floor(Math.random() * ads.length)]; const specs = { - leaderboard: { class: 'w-full h-[90px]', horizontal: true }, - rectangle: { class: 'w-full max-w-[300px] h-[250px]', horizontal: false }, - tile: { class: 'w-full h-[250px]', horizontal: false }, + leaderboard: { class: "w-full h-[90px]", horizontal: true }, + rectangle: { class: "w-full max-w-[300px] h-[250px]", horizontal: false }, + tile: { class: "w-full h-[250px]", horizontal: false }, }; const { class: sizeClass, horizontal } = specs[size]; @@ -38,37 +38,55 @@ const { class: sizeClass, horizontal } = specs[size]; href={ad.href} target="_blank" rel="noopener sponsored" - class={`${sizeClass} flex ${horizontal ? 'flex-row items-center gap-4 px-6' : 'flex-col items-center justify-center gap-3 px-4'} rounded-xl border border-zinc-800 bg-zinc-900/50 hover:border-[#534AB7]/50 hover:bg-zinc-900 transition-colors duration-200 group`} + class={`${sizeClass} flex ${horizontal ? "flex-row items-center gap-4 px-6" : "flex-col items-center justify-center gap-3 px-4"} rounded-xl border border-zinc-800 bg-zinc-900/50 hover:border-[#534AB7]/50 hover:bg-zinc-900 transition-colors duration-200 group`} > -
-
Реклама
-
- {ad.label} -
-
{ad.sublabel}
-
- - {ad.cta} - - ) : ( - <> -
-
Реклама
-
- {ad.label} + { + horizontal ? ( + <> +
+
+ Реклама +
+
+ {ad.label} +
+
{ad.sublabel}
-
{ad.sublabel}
-
- - {ad.ctaMobile} - - - )} + + {ad.cta} + + + ) : ( + <> +
+
+ Реклама +
+
+ {ad.label} +
+
{ad.sublabel}
+
+ + {ad.ctaMobile} + + + ) + } diff --git a/src/components/Analytics.astro b/src/components/Analytics.astro index 3d2109f..dd2d258 100644 --- a/src/components/Analytics.astro +++ b/src/components/Analytics.astro @@ -7,85 +7,87 @@ const mailRuId = analytics.topMailRuId;
+ +
+ Top.Mail.Ru +
diff --git a/src/components/GeneratorCard.astro b/src/components/GeneratorCard.astro index 0e88051..d151ba8 100644 --- a/src/components/GeneratorCard.astro +++ b/src/components/GeneratorCard.astro @@ -1,7 +1,7 @@ --- -import type { Generator } from '../data/generators'; -import { useT } from '../i18n/translations'; -import type { Lang } from '../i18n/translations'; +import type { Generator } from "../data/generators"; +import { useT } from "../i18n/translations"; +import type { Lang } from "../i18n/translations"; interface Props { generator: Generator; @@ -9,11 +9,11 @@ interface Props { } const { generator, lang: langProp } = Astro.props; -const lang = (langProp || (Astro.currentLocale as Lang) || 'en'); +const lang = langProp || (Astro.currentLocale as Lang) || "en"; const T = useT(lang); const { slug, icon, status } = generator; -const isLive = status === 'live'; -const isRu = lang === 'ru'; +const isLive = status === "live"; +const isRu = lang === "ru"; const title = isRu ? generator.ruTitle : generator.title; const description = isRu ? generator.ruDescription : generator.description; const href = isRu ? `/ru/generators/${slug}/` : `/generators/${slug}/`; @@ -23,43 +23,53 @@ const icons: Record = { palette: ``, lock: ``, ticket: ``, - 'dice-6': ``, - 'square-stack': ``, - 'circle-dollar-sign': ``, - 'list': ``, - 'fingerprint': ``, - 'pie-chart': ``, + "dice-6": ``, + "square-stack": ``, + "circle-dollar-sign": ``, + list: ``, + fingerprint: ``, + "pie-chart": ``, }; --- -{isLive ? ( - -
-

+

{isRu ? generator.ruDescription : generator.description}

+
+
diff --git a/src/pages/index.astro b/src/pages/index.astro index 7de64c3..01663dd 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -27,9 +27,13 @@ const webSiteSchema = { />
-
+
+ {T.brandLabel} {T.homeTitle} -

+

{T.homeSubtitle}

@@ -48,7 +52,7 @@ const webSiteSchema = {
diff --git a/src/pages/ru/index.astro b/src/pages/ru/index.astro index 7de64c3..79ab89a 100644 --- a/src/pages/ru/index.astro +++ b/src/pages/ru/index.astro @@ -29,7 +29,7 @@ const webSiteSchema = {
{T.brandLabel}
From 7e36ec52ec8116cd4501065c792fd7ca015e8d8b Mon Sep 17 00:00:00 2001 From: emil Date: Mon, 11 May 2026 01:40:14 +0300 Subject: [PATCH 39/44] fix(wheel): default items separated by newlines for correct parsing --- src/components/generators/WheelSpinner.astro | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/generators/WheelSpinner.astro b/src/components/generators/WheelSpinner.astro index 131831e..8fb6815 100644 --- a/src/components/generators/WheelSpinner.astro +++ b/src/components/generators/WheelSpinner.astro @@ -20,7 +20,12 @@ const T = useT(isRu ? "ru" : "en"); id="ws-items" rows="6" class="w-full bg-zinc-900 border border-zinc-700 rounded-lg px-3 py-2 text-zinc-100 text-sm font-mono focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent transition-colors resize-y" - >Alice Bob Carol Dave Eve FrankAlice +Bob +Carol +Dave +Eve +Frank

Date: Mon, 11 May 2026 20:52:27 +0300 Subject: [PATCH 40/44] feat: integrate Yandex RTB ad block R-A-19259130-1 --- src/components/YandexRTB.astro | 10 ++++++++++ src/layouts/BaseLayout.astro | 3 +++ src/layouts/GeneratorLayout.astro | 4 ++-- src/pages/index.astro | 4 ++-- src/pages/ru/index.astro | 4 ++-- 5 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 src/components/YandexRTB.astro diff --git a/src/components/YandexRTB.astro b/src/components/YandexRTB.astro new file mode 100644 index 0000000..3ea5664 --- /dev/null +++ b/src/components/YandexRTB.astro @@ -0,0 +1,10 @@ + +

+ diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index d39ecde..0b88247 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -55,6 +55,9 @@ const webPageSchema = { + + + {title} diff --git a/src/layouts/GeneratorLayout.astro b/src/layouts/GeneratorLayout.astro index 9593c4e..775d2be 100644 --- a/src/layouts/GeneratorLayout.astro +++ b/src/layouts/GeneratorLayout.astro @@ -1,6 +1,6 @@ --- import BaseLayout from "./BaseLayout.astro"; -import AdBanner from "../components/AdBanner.astro"; +import YandexRTB from "../components/YandexRTB.astro"; import SeoBlock from "../components/SeoBlock.astro"; import { useT } from "../i18n/translations"; import type { Lang } from "../i18n/translations"; @@ -115,7 +115,7 @@ const breadcrumbSchema = {
- +
- +
- +
Date: Mon, 11 May 2026 21:59:37 +0300 Subject: [PATCH 41/44] feat: prepare site for Yandex Ads (RSYA) moderation - Add About page (/about, /ru/about) with mission, history, contacts - Add FaqBlock component for generator FAQ sections - Add FAQ to all 10 generators (3 Q&A each, EN+RU) - Expand howTo/whenTo content on numbers, password, coin generators - Add About link to LanguageSwitcher navigation - Improve Yandex RTB ad block: min-height placeholder, no layout shift - Extend generator schema with optional faq/ruFaq fields - Update translations.ts with about and faq i18n keys --- src/components/FaqBlock.astro | 33 ++++++++++ src/components/LanguageSwitcher.astro | 7 ++ src/components/YandexRTB.astro | 15 +++-- src/content/generators/cards.json | 10 +++ src/content/generators/coin.json | 16 ++++- src/content/generators/colors.json | 20 ++++++ src/content/generators/dice.json | 20 ++++++ src/content/generators/list.json | 20 ++++++ src/content/generators/lottery.json | 10 +++ src/content/generators/numbers.json | 16 ++++- src/content/generators/password.json | 16 ++++- src/content/generators/uuid.json | 10 +++ src/content/generators/wheel.json | 10 +++ src/i18n/translations.ts | 28 ++++++++ src/layouts/GeneratorLayout.astro | 10 +++ src/lib/generator-schema.ts | 2 + src/pages/about.astro | 95 +++++++++++++++++++++++++++ src/pages/ru/about.astro | 95 +++++++++++++++++++++++++++ 18 files changed, 423 insertions(+), 10 deletions(-) create mode 100644 src/components/FaqBlock.astro create mode 100644 src/pages/about.astro create mode 100644 src/pages/ru/about.astro diff --git a/src/components/FaqBlock.astro b/src/components/FaqBlock.astro new file mode 100644 index 0000000..5f71e0a --- /dev/null +++ b/src/components/FaqBlock.astro @@ -0,0 +1,33 @@ +--- +import { useT } from "../i18n/translations"; +import type { Lang } from "../i18n/translations"; + +interface Props { + questions: Array<{ q: string; a: string }>; + lang?: Lang; +} + +const { questions, lang = "en" } = Astro.props; +const T = useT(lang); +--- + +
+
+

+ + {T.faq} +

+
+ { + questions.map(({ q, a }) => ( +
+

{q}

+

{a}

+
+ )) + } +
+
+
diff --git a/src/components/LanguageSwitcher.astro b/src/components/LanguageSwitcher.astro index c75d901..8d464af 100644 --- a/src/components/LanguageSwitcher.astro +++ b/src/components/LanguageSwitcher.astro @@ -25,6 +25,13 @@ const alternatePath = isRu data-target-lang="ru" data-alternate={isRu ? "" : alternatePath}>RU + +
+ {isRu ? "О проекте" : "About"} +
diff --git a/src/content/generators/cards.json b/src/content/generators/cards.json index bca1181..8d4ad28 100644 --- a/src/content/generators/cards.json +++ b/src/content/generators/cards.json @@ -34,5 +34,15 @@ "Случайная раздача карт для карточных игр.", "Выбор случайных карт для гаданий.", "Генерация случайных комбинаций карт для тестирования." + ], + "faq": [ + { "q": "What deck is used?", "a": "A standard 52-card deck with four suits: spades ♠, hearts ♥, diamonds ♦, and clubs ♣." }, + { "q": "Can I draw the same card twice?", "a": "Only if Allow duplicates is enabled. By default, each card is drawn at most once." }, + { "q": "Are jokers included?", "a": "No. Only the standard 52 playing cards are used." } + ], + "ruFaq": [ + { "q": "Какая колода используется?", "a": "Стандартная колода из 52 карт четырёх мастей: пики ♠, червы ♥, бубны ♦, трефы ♣." }, + { "q": "Можно вытащить одну карту дважды?", "a": "Только если включены повторения. По умолчанию каждая карта вытаскивается не более одного раза." }, + { "q": "Есть ли джокеры?", "a": "Нет. Используется только стандартная колода из 52 карт." } ] } diff --git a/src/content/generators/coin.json b/src/content/generators/coin.json index b551e37..d0ff7d0 100644 --- a/src/content/generators/coin.json +++ b/src/content/generators/coin.json @@ -16,7 +16,8 @@ "Drag the slider to choose how many coins to flip (1–20).", "Click Flip — each coin shows H for heads or T for tails.", "When flipping more than one coin, the heads/tails count is shown below.", - "Click Copy to copy all results to the clipboard." + "Click Copy to copy all results to the clipboard.", + "Use a single coin for a classic 50/50 decision." ], "whenTo": [ "Making a quick heads-or-tails decision.", @@ -28,7 +29,18 @@ "Выберите количество монет с помощью ползунка.", "Нажмите «Подбросить» — орёл или решка.", "При нескольких монетах отображается итоговый счёт.", - "Нажмите «Копировать», чтобы сохранить результат." + "Нажмите «Копировать», чтобы сохранить результат.", + "Одна монетка — классическое решение «орёл или решка»." + ], + "faq": [ + { "q": "Is the coin flip fair?", "a": "Yes. Each flip uses your browser's random function, giving a true 50/50 chance for heads or tails." }, + { "q": "Can I flip multiple coins at once?", "a": "Yes. Use the slider to choose up to 20 coins, and see the total heads and tails count." }, + { "q": "What do H and T stand for?", "a": "H means Heads and T means Tails — the two sides of a coin." } + ], + "ruFaq": [ + { "q": "Подбрасывание честное?", "a": "Да. Каждый бросок использует функцию случайности браузера, давая истинный шанс 50/50 на орла или решку." }, + { "q": "Можно подбросить несколько монет сразу?", "a": "Да. С помощью ползунка выберите до 20 монет и увидите общий счёт орлов и решек." }, + { "q": "Что означают О и Р?", "a": "О — орёл, Р — решка. Это две стороны монеты." } ], "ruWhenTo": [ "Принятие решений по принципу «орёл или решка».", diff --git a/src/content/generators/colors.json b/src/content/generators/colors.json index 77080ef..0812034 100644 --- a/src/content/generators/colors.json +++ b/src/content/generators/colors.json @@ -23,6 +23,16 @@ "Selecting a random theme color for a project or presentation.", "Teaching or exploring color theory and formats." ], + "faq": [ + { "q": "What color formats are supported?", "a": "HEX, RGB, and HSL. All three are shown at once, so you can copy the one you need." }, + { "q": "Can I use the generated color in CSS?", "a": "Yes. Copy the HEX or RGB value and paste it directly into your stylesheet or design tool." }, + { "q": "Are the colors truly random?", "a": "Yes. Each color is generated using a cryptographically secure random number generator, covering the entire color space." } + ], + "ruFaq": [ + { "q": "Какие форматы цветов поддерживаются?", "a": "HEX, RGB и HSL. Все три формата отображаются одновременно — скопируйте тот, который нужен." }, + { "q": "Можно ли использовать сгенерированный цвет в CSS?", "a": "Да. Скопируйте значение HEX или RGB и вставьте его прямо в стили или графический редактор." }, + { "q": "Цвета действительно случайные?", "a": "Да. Каждый цвет генерируется с помощью криптографически стойкого генератора случайных чисел, покрывая всё цветовое пространство." } + ], "ruHowTo": [ "Нажмите «Сгенерировать», чтобы получить случайный цвет.", "Цвет отображается в форматах HEX, RGB и HSL одновременно.", @@ -32,5 +42,15 @@ "Поиск вдохновения для цветовой палитры.", "Случайный выбор цвета для дизайна или иллюстрации.", "Генерация тестовых данных с цветовыми значениями." + ], + "faq": [ + { "q": "What color formats are supported?", "a": "HEX, RGB, and HSL. All three are shown simultaneously for every generated color." }, + { "q": "Can I copy just one format?", "a": "Yes. Click the copy icon next to any format to copy only that value." }, + { "q": "Are the colors truly random?", "a": "Yes. Each color is generated using random values for hue, saturation, and lightness." } + ], + "ruFaq": [ + { "q": "Какие форматы цветов поддерживаются?", "a": "HEX, RGB и HSL. Все три отображаются одновременно для каждого сгенерированного цвета." }, + { "q": "Можно скопировать только один формат?", "a": "Да. Нажмите иконку копирования рядом с нужным форматом." }, + { "q": "Цвета действительно случайные?", "a": "Да. Каждый цвет генерируется из случайных значений оттенка, насыщенности и освещённости." } ] } diff --git a/src/content/generators/dice.json b/src/content/generators/dice.json index 098d4ce..1a77993 100644 --- a/src/content/generators/dice.json +++ b/src/content/generators/dice.json @@ -26,6 +26,16 @@ "Rolling exploding dice for Savage Worlds or house rules (e.g. 3d6!).", "Using keep/drop modifiers like 4d6kh3 to keep highest rolls." ], + "faq": [ + { "q": "What dice notation can I use?", "a": "You can use standard XdY+Z notation, plus modifiers like advantage, disadvantage, keep/drop (kh/kl/dl/dh), and exploding dice (!)." }, + { "q": "Is this fair for D&D and other RPGs?", "a": "Yes. Randify uses the browser's cryptographically secure random number generator, so every roll is unbiased." }, + { "q": "Can I roll multiple dice at once?", "a": "Absolutely. Enter something like 5d6 or click the die buttons and set the count to roll up to 20 dice in a single throw." } + ], + "ruFaq": [ + { "q": "Какую нотацию кубиков поддерживает генератор?", "a": "Стандартную нотацию XdY+Z, а также модификаторы: преимущество, помеха, keep/drop (kh/kl/dl/dh) и взрывающиеся кубики (!)." }, + { "q": "Подходит ли генератор для D&D и других НРИ?", "a": "Да. Randify использует криптографически стойкий генератор случайных чисел браузера, поэтому каждый бросок честный и непредвзятый." }, + { "q": "Можно ли бросать несколько кубиков одновременно?", "a": "Конечно. Введите что-то вроде 5d6 или настройте кубики вручную — можно бросить до 20 кубиков за раз." } + ], "ruHowTo": [ "Введите нотацию, например 2d6+3 или 1d20 — или настройте кубики вручную.", "Выберите режим: Обычный, Преимущество или Помеха (только для одного кубика).", @@ -39,5 +49,15 @@ "Бросок характеристик: 4d6, отбросить наименьший (4d6dl1).", "Взрывающиеся кубики для Savage Worlds или хаус-рулов (например, 3d6!).", "Использование keep/drop модификаторов типа 4d6kh3 (оставить три лучших)." + ], + "faq": [ + { "q": "What dice notation is supported?", "a": "Standard XdY+Z, advantage/disadvantage, keep/drop (kh/kl/dh/dl), reroll (r), and exploding dice (!)." }, + { "q": "Can I roll more than one type of die at once?", "a": "Yes. Enter multiple notations separated by spaces, like 2d6 1d20." }, + { "q": "Is the roll history saved?", "a": "Yes. Your last rolls are shown below the results until you refresh the page." } + ], + "ruFaq": [ + { "q": "Какая нотация кубиков поддерживается?", "a": "Стандартная XdY+Z, преимущество/помеха, keep/drop (kh/kl/dh/dl), переброс (r) и взрывающиеся кубики (!)." }, + { "q": "Можно ли бросить несколько типов кубиков сразу?", "a": "Да. Введите несколько нотаций через пробел, например 2d6 1d20." }, + { "q": "История бросков сохраняется?", "a": "Да. Последние броски отображаются под результатами до обновления страницы." } ] } diff --git a/src/content/generators/list.json b/src/content/generators/list.json index ffa448a..5d11007 100644 --- a/src/content/generators/list.json +++ b/src/content/generators/list.json @@ -24,6 +24,16 @@ "Organising a Secret Santa or gift exchange draw.", "Selecting a random movie, restaurant, or activity from a list." ], + "faq": [ + { "q": "Can the same item be picked more than once?", "a": "Only if you enable the 'Allow duplicates' toggle. By default, each item can be chosen only once per draw." }, + { "q": "Is there a limit to how many items I can add?", "a": "You can add up to 1,000 items. For most use cases — giveaways, raffles, or team assignments — this is more than enough." }, + { "q": "Can I pick multiple winners at once?", "a": "Yes. Set the 'Pick' field to any number up to the total count of items, and Randify will select that many winners in one go." } + ], + "ruFaq": [ + { "q": "Может ли один и тот же элемент выпасть несколько раз?", "a": "Только если включён переключатель «Разрешить повторения». По умолчанию каждый элемент выбирается не более одного раза." }, + { "q": "Есть ли ограничение на количество элементов в списке?", "a": "Можно добавить до 1 000 элементов. Для розыгрышей, жеребьёвок и распределения задач этого более чем достаточно." }, + { "q": "Можно ли выбрать несколько победителей сразу?", "a": "Да. Укажите в поле «Выбрать» любое число до общего количества элементов — Randify выберет нужное количество победителей за один раз." } + ], "ruHowTo": [ "Введите список элементов — по одному на строку.", "Укажите, сколько элементов выбрать.", @@ -34,5 +44,15 @@ "Розыгрыш победителей конкурса.", "Случайное распределение задач между участниками команды.", "Выбор случайного варианта из списка." + ], + "faq": [ + { "q": "Can the same item be picked more than once?", "a": "Only if you toggle Allow duplicates. By default, each item is picked at most once." }, + { "q": "Is there a limit to how many items I can add?", "a": "You can add up to 500 items, and pick up to the total number of unique items." }, + { "q": "Does the order of items matter?", "a": "No. The selection is completely random regardless of the order you enter items." } + ], + "ruFaq": [ + { "q": "Может ли один элемент выпасть несколько раз?", "a": "Только если включена опция «Разрешить повторения». По умолчанию каждый элемент выбирается не более одного раза." }, + { "q": "Есть ли ограничение на количество элементов?", "a": "Можно добавить до 500 элементов и выбрать до общего числа уникальных элементов." }, + { "q": "Имеет ли значение порядок элементов?", "a": "Нет. Выбор полностью случайный независимо от порядка ввода." } ] } diff --git a/src/content/generators/lottery.json b/src/content/generators/lottery.json index 044461a..bc63d82 100644 --- a/src/content/generators/lottery.json +++ b/src/content/generators/lottery.json @@ -34,5 +34,15 @@ "Розыгрыш лотерейных номеров.", "Жеребьёвка участников конкурса.", "Случайный выбор нескольких уникальных чисел." + ], + "faq": [ + { "q": "Are the numbers guaranteed to be unique?", "a": "Yes. The lottery generator always picks unique numbers within your chosen range." }, + { "q": "Can I use this for real lottery tickets?", "a": "Yes, but remember that no generator can improve your odds — each combination has the same chance." }, + { "q": "What is the maximum range?", "a": "You can set any range where the difference between From and To is at least as large as the number of picks." } + ], + "ruFaq": [ + { "q": "Числа гарантированно уникальны?", "a": "Да. Генератор лотереи всегда выбирает уникальные числа в заданном диапазоне." }, + { "q": "Можно ли использовать для реальных лотерей?", "a": "Да, но помните: генератор не улучшает шансы — каждая комбинация имеет равную вероятность." }, + { "q": "Какой максимальный диапазон?", "a": "Любой диапазон, где разница между «От» и «До» не меньше количества выбираемых чисел." } ] } diff --git a/src/content/generators/numbers.json b/src/content/generators/numbers.json index 0ec7fe3..310b4cc 100644 --- a/src/content/generators/numbers.json +++ b/src/content/generators/numbers.json @@ -22,7 +22,8 @@ "Picking a winner in a giveaway or raffle.", "Choosing a random starting player in a board game.", "Making a quick decision between numbered options.", - "Generating test data with values in a specific range." + "Generating test data with values in a specific range.", + "Assigning random IDs or order numbers to items." ], "ruHowTo": [ "Введите минимальное значение в поле «От».", @@ -34,6 +35,17 @@ "Выбор победителя в розыгрыше или конкурсе.", "Определение случайного первого игрока в настольной игре.", "Быстрое принятие решения между пронумерованными вариантами.", - "Генерация тестовых данных с числами в заданном диапазоне." + "Генерация тестовых данных с числами в заданном диапазоне.", + "Назначение случайных номеров или порядковых идентификаторов." + ], + "faq": [ + { "q": "Is the number truly random?", "a": "Yes. Randify uses your browser's built-in random function, which produces unpredictable results every time." }, + { "q": "Can I generate negative numbers?", "a": "Absolutely. Just enter a negative value in the From field and a positive or negative value in the To field." }, + { "q": "Is there a limit to the range?", "a": "You can use any whole numbers that JavaScript supports — safely up to about 9 quadrillion." } + ], + "ruFaq": [ + { "q": "Число действительно случайное?", "a": "Да. Randify использует встроенную функцию случайности браузера, которая каждый раз выдаёт непредсказуемый результат." }, + { "q": "Можно ли генерировать отрицательные числа?", "a": "Конечно. Введите отрицательное значение в поле «От» и любое значение в поле «До»." }, + { "q": "Есть ли ограничение на диапазон?", "a": "Можно использовать любые целые числа, которые поддерживает JavaScript — безопасно до примерно 9 квадриллионов." } ] } diff --git a/src/content/generators/password.json b/src/content/generators/password.json index 3726b70..fbda23f 100644 --- a/src/content/generators/password.json +++ b/src/content/generators/password.json @@ -22,7 +22,8 @@ "Creating a strong password for a new account.", "Generating a temporary access credential.", "Producing a random secret key for development or testing.", - "Replacing a weak or reused password." + "Replacing a weak or reused password.", + "Setting up a secure Wi-Fi passphrase." ], "ruHowTo": [ "Задайте длину пароля с помощью ползунка.", @@ -33,6 +34,17 @@ "ruWhenTo": [ "Создание надёжного пароля для нового аккаунта.", "Генерация секретного ключа или токена.", - "Регулярное обновление паролей для повышения безопасности." + "Регулярное обновление паролей для повышения безопасности.", + "Создание сложного пароля для Wi-Fi сети." + ], + "faq": [ + { "q": "Are the passwords stored anywhere?", "a": "No. Passwords are generated locally in your browser and never leave your device." }, + { "q": "How strong are the generated passwords?", "a": "Strength depends on length and character types. A 16-character password with all types enabled is considered very strong." }, + { "q": "Can I use these passwords for banking?", "a": "Yes, but always follow your bank's specific requirements and consider using a dedicated password manager." } + ], + "ruFaq": [ + { "q": "Пароли где-то сохраняются?", "a": "Нет. Пароли генерируются локально в браузере и никогда не покидают ваше устройство." }, + { "q": "Насколько надёжны сгенерированные пароли?", "a": "Надёжность зависит от длины и наборов символов. Пароль из 16 символов со всеми типами считается очень сильным." }, + { "q": "Можно ли использовать эти пароли для банкинга?", "a": "Да, но всегда учитывайте требования вашего банка и рассмотрите использование специального менеджера паролей." } ] } diff --git a/src/content/generators/uuid.json b/src/content/generators/uuid.json index 022c57d..c6ee06b 100644 --- a/src/content/generators/uuid.json +++ b/src/content/generators/uuid.json @@ -34,5 +34,15 @@ "Генерация уникальных идентификаторов для баз данных.", "Создание токенов для API и сессий.", "Генерация случайных ключей шифрования." + ], + "faq": [ + { "q": "Are these UUIDs cryptographically secure?", "a": "Yes. UUID v4 is generated using the browser's crypto.getRandomValues, which is cryptographically secure." }, + { "q": "What is the difference between UUID, Hex, and Base64?", "a": "UUID v4 is a 128-bit standard format. Hex is raw random bytes in hexadecimal. Base64 is the same bytes encoded for compactness." }, + { "q": "Can I use these tokens in production?", "a": "UUID v4 tokens are safe for production. For Hex and Base64, the security depends on the byte length you choose." } + ], + "ruFaq": [ + { "q": "UUID криптографически безопасны?", "a": "Да. UUID v4 генерируется через crypto.getRandomValues браузера, что обеспечивает криптографическую стойкость." }, + { "q": "В чём разница между UUID, Hex и Base64?", "a": "UUID v4 — стандартный 128-битный формат. Hex — сырые случайные байты в шестнадцатеричном виде. Base64 — те же байты в компактном кодировании." }, + { "q": "Можно ли использовать токены в продакшене?", "a": "UUID v4 безопасны для продакшена. Для Hex и Base64 безопасность зависит от выбранной длины в байтах." } ] } diff --git a/src/content/generators/wheel.json b/src/content/generators/wheel.json index 833e758..61fcbef 100644 --- a/src/content/generators/wheel.json +++ b/src/content/generators/wheel.json @@ -34,5 +34,15 @@ "Случайный выбор победителя розыгрыша.", "Принятие решений в команде.", "Распределение ролей или задач между участниками." + ], + "faq": [ + { "q": "How many items can I add?", "a": "Between 2 and 24 items. The wheel automatically resizes to fit all segments." }, + { "q": "Is the spin truly random?", "a": "Yes. The stopping position is determined by a cryptographically secure random value." }, + { "q": "Can I save my wheel?", "a": "Not yet. You can bookmark the page or copy your item list to reuse it later." } + ], + "ruFaq": [ + { "q": "Сколько элементов можно добавить?", "a": "От 2 до 24. Колесо автоматически подстраивается под количество сегментов." }, + { "q": "Вращение действительно случайное?", "a": "Да. Конечная позиция определяется криптографически стойким случайным значением." }, + { "q": "Можно ли сохранить колесо?", "a": "Пока нет. Можно добавить страницу в закладки или скопировать список элементов для повторного использования." } ] } diff --git a/src/i18n/translations.ts b/src/i18n/translations.ts index 53d6c64..6e3b8d3 100644 --- a/src/i18n/translations.ts +++ b/src/i18n/translations.ts @@ -10,6 +10,8 @@ export const translations = { copyAll: "Copy all", howToUse: "How to use", whenToUse: "When to use", + faq: "FAQ", + about: "About", generate: "Generate", draw: "Draw", @@ -66,6 +68,18 @@ export const translations = { "Simple tools for raffles, games, and everything that needs randomness.", brandLabel: "randify", backToAll: "All generators", + + aboutTitle: "About Randify", + aboutMission: + "Randify exists to make randomness simple, fast, and accessible to everyone. Whether you are picking a winner, rolling dice, or generating a secure password, you should not need to install an app or create an account. Just open the page and get your result.", + aboutHistory: + "The idea for Randify came from a simple frustration: every time we needed a quick random number or a coin flip, we ended up on cluttered websites full of ads, pop-ups, and unnecessary steps. We wanted something cleaner — a tool that just works, without distractions.\n\nWe started with a single number generator and gradually expanded the collection based on what people actually use: dice for board games, wheels for raffles, passwords for security, and more. Each tool was built with the same philosophy — minimal interface, instant results, zero friction.", + aboutHowItWorks: + "Everything on Randify runs entirely in your browser. No data is sent to any server, no personal information is collected, and no account is required. The random values are generated locally using the Web Crypto API, which provides cryptographically secure randomness. This means your passwords, numbers, and choices stay private — we never see them.", + aboutContact: + "Have a suggestion, found a bug, or just want to say hello? Drop us a line at hello@randify.pro — we read every message.", + aboutCta: + "Ready to give it a spin? Explore the generators and let chance do the work.", }, ru: { allGenerators: "Все генераторы", @@ -76,6 +90,8 @@ export const translations = { copyAll: "Копировать всё", howToUse: "Как пользоваться", whenToUse: "Когда использовать", + faq: "Частые вопросы", + about: "О проекте", generate: "Сгенерировать", draw: "Тянуть", @@ -132,6 +148,18 @@ export const translations = { "Простые инструменты для розыгрышей, игр и всего, что требует случайности.", brandLabel: "randify", backToAll: "Все генераторы", + + aboutTitle: "О проекте", + aboutMission: + "Randify создан, чтобы сделать случайность простой, быстрой и доступной каждому. Независимо от того, выбираете ли вы победителя, бросаете кубики или создаёте надёжный пароль — не нужно устанавливать приложение или регистрироваться. Просто откройте страницу и получите результат.", + aboutHistory: + "Идея Randify родилась из простого раздражения: каждый раз, когда нам нужно было быстро сгенерировать случайное число или подбросить монетку, мы попадали на перегруженные рекламой сайты с всплывающими окнами и лишними шагами. Мы хотели создать что-то чище — инструмент, который просто работает, без отвлекающих элементов.\n\nМы начали с одного генератора чисел и постепенно расширяли коллекцию, ориентируясь на реальные потребности: кубики для настольных игр, колесо фортуны для розыгрышей, генератор паролей для безопасности и многое другое. Каждый инструмент создавался по одному принципу — минимальный интерфейс, мгновенный результат, никаких препятствий.", + aboutHowItWorks: + "Всё на Randify работает полностью в вашем браузере. Данные не отправляются на сервер, личная информация не собирается, регистрация не требуется. Случайные значения генерируются локально с помощью Web Crypto API, который обеспечивает криптографически стойкую случайность. Это означает, что ваши пароли, числа и выбор остаются приватными — мы их не видим.", + aboutContact: + "Есть предложение, нашли ошибку или просто хотите поздороваться? Напишите нам на hello@randify.pro — мы читаем каждое сообщение.", + aboutCta: + "Готовы попробовать? Откройте генераторы и позвольте случайности сделать выбор за вас.", }, } as const; diff --git a/src/layouts/GeneratorLayout.astro b/src/layouts/GeneratorLayout.astro index 775d2be..067a8b0 100644 --- a/src/layouts/GeneratorLayout.astro +++ b/src/layouts/GeneratorLayout.astro @@ -2,6 +2,7 @@ import BaseLayout from "./BaseLayout.astro"; import YandexRTB from "../components/YandexRTB.astro"; import SeoBlock from "../components/SeoBlock.astro"; +import FaqBlock from "../components/FaqBlock.astro"; import { useT } from "../i18n/translations"; import type { Lang } from "../i18n/translations"; import type { Generator } from "../data/generators"; @@ -123,5 +124,14 @@ const breadcrumbSchema = { howTo={isRu ? generator.ruHowTo : generator.howTo} whenTo={isRu ? generator.ruWhenTo : generator.whenTo} /> + + { + (isRu ? generator.ruFaq : generator.faq) && ( + + ) + }
diff --git a/src/lib/generator-schema.ts b/src/lib/generator-schema.ts index 943d2e9..06b1aa9 100644 --- a/src/lib/generator-schema.ts +++ b/src/lib/generator-schema.ts @@ -19,6 +19,8 @@ export const generatorSchema = z whenTo: z.array(z.string()), ruHowTo: z.array(z.string()), ruWhenTo: z.array(z.string()), + faq: z.array(z.object({ q: z.string(), a: z.string() })).optional(), + ruFaq: z.array(z.object({ q: z.string(), a: z.string() })).optional(), }) .strict(); diff --git a/src/pages/about.astro b/src/pages/about.astro new file mode 100644 index 0000000..8061c34 --- /dev/null +++ b/src/pages/about.astro @@ -0,0 +1,95 @@ +--- +import BaseLayout from "@/layouts/BaseLayout.astro"; +import { useT } from "@/i18n/translations"; +import type { Lang } from "@/i18n/translations"; + +const lang = (Astro.currentLocale as Lang) || "en"; +const T = useT(lang); + +const aboutSchema = { + "@context": "https://schema.org", + "@type": "AboutPage", + name: T.aboutTitle, + description: T.aboutMission, + url: "https://randify.pro/about/", + inLanguage: lang, +}; +--- + + + diff --git a/src/components/generators/LetterGenerator.astro b/src/components/generators/LetterGenerator.astro new file mode 100644 index 0000000..90b5752 --- /dev/null +++ b/src/components/generators/LetterGenerator.astro @@ -0,0 +1,120 @@ +--- +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); +--- + +
+
+
+ + {T.copied} +
+
+ +
+ +
+
+ + diff --git a/src/components/generators/NamesGenerator.astro b/src/components/generators/NamesGenerator.astro new file mode 100644 index 0000000..0486f41 --- /dev/null +++ b/src/components/generators/NamesGenerator.astro @@ -0,0 +1,217 @@ +--- +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); + +const genderOptions = [ + { value: "male", label: isRu ? "Мужские" : "Male" }, + { value: "female", label: isRu ? "Женские" : "Female" }, + { value: "any", label: isRu ? "Любые" : "Any" }, +]; +--- + +
+
+ +
+ { + genderOptions.map(({ value, label }, i) => ( + + )) + } +
+
+ +
+
+ + + {T.copied} +
+
+ +
+ +
+
+ + diff --git a/src/components/generators/RpsGenerator.astro b/src/components/generators/RpsGenerator.astro new file mode 100644 index 0000000..25f565b --- /dev/null +++ b/src/components/generators/RpsGenerator.astro @@ -0,0 +1,145 @@ +--- +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); +--- + +
+
+

+ {isRu ? "Выберите ваш вариант:" : "Pick your move:"} +

+
+ + + +
+
+ +
+
+

+ {isRu ? "Выберите ход, чтобы начать игру" : "Pick a move to start the game"} +

+ + +
+
+
+ + diff --git a/src/components/generators/TeamsGenerator.astro b/src/components/generators/TeamsGenerator.astro new file mode 100644 index 0000000..0f98249 --- /dev/null +++ b/src/components/generators/TeamsGenerator.astro @@ -0,0 +1,313 @@ +--- +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); + +const itemsPlaceholder = isRu + ? "Алиса\nБоб\nКарол\nДэвид" + : "Alice\nBob\nCarol\nDavid"; +const teamCountLabel = isRu ? "Количество команд" : "Number of teams"; +const teamPrefix = isRu ? "Команда" : "Team"; +const errAddTwo = isRu + ? "Добавьте хотя бы 2 участника." + : "Add at least 2 participants."; +const errTeamsMin = isRu + ? "Минимум 2 команды." + : "Minimum 2 teams."; +const errTeamsMax = isRu + ? "Максимум 20 команд." + : "Maximum 20 teams."; +const errMorePlayers = isRu + ? "Участников должно быть больше, чем команд." + : "Participants must outnumber teams."; +--- + +
+
+
+
+ + +
+ +
+ + +
+
+
+ + + +
+
+
+ + +
+ +
+ +
+
+ + diff --git a/src/components/generators/YesNoGenerator.astro b/src/components/generators/YesNoGenerator.astro new file mode 100644 index 0000000..0d4a0dc --- /dev/null +++ b/src/components/generators/YesNoGenerator.astro @@ -0,0 +1,182 @@ +--- +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); + +const askPlaceholder = isRu ? "Задайте вопрос..." : "Ask anything..."; +const getAnswerLabel = isRu ? "Получить ответ" : "Get Answer"; +const confidenceLabel = isRu ? "Уверенность" : "Confidence"; +--- + +
+
+ + +
+ +
+
+ + + + + {T.copied} +
+
+ +
+ +
+
+ + diff --git a/src/content/generators/date.json b/src/content/generators/date.json new file mode 100644 index 0000000..801f28d --- /dev/null +++ b/src/content/generators/date.json @@ -0,0 +1,47 @@ +{ + "slug": "date", + "title": "Random Date", + "description": "Generate a random date between any two dates you choose.", + "icon": "calendar", + "status": "live", + "seoTitle": "Random Date Generator — Between Two Dates | Randify", + "seoDescription": "Generate a random date between any two dates instantly. Free online date picker for planning, games, and creative projects.", + "ruTitle": "Случайная дата", + "ruDescription": "Сгенерируйте случайную дату между любыми двумя датами.", + "ruSeoTitle": "Генератор случайных дат — между двумя датами | Randify", + "ruSeoDescription": "Сгенерируйте случайную дату в любом диапазоне мгновенно. Бесплатный инструмент для планирования, игр и творческих задач.", + "pageTitle": "Random Date Generator", + "ruPageTitle": "Генератор случайных дат", + "howTo": [ + "Select the start date in the From field.", + "Select the end date in the To field.", + "Click Generate — a random date within your range appears instantly.", + "Click the date to copy it to your clipboard." + ], + "whenTo": [ + "Picking a random deadline or milestone for a challenge.", + "Choosing a date for a hypothetical scenario or story setting.", + "Generating random historical dates for a quiz or trivia game." + ], + "ruHowTo": [ + "Выберите начальную дату в поле «От».", + "Выберите конечную дату в поле «До».", + "Нажмите «Сгенерировать» — случайная дата в диапазоне появится мгновенно.", + "Нажмите на дату, чтобы скопировать её в буфер обмена." + ], + "ruWhenTo": [ + "Выбор случайного дедлайна или вехи для челленджа.", + "Определение даты для гипотетического сценария или сюжета.", + "Генерация случайных исторических дат для викторины или игры." + ], + "faq": [ + { "q": "What date format is used?", "a": "Dates are displayed in a clear, locale-friendly format. You can copy the value and paste it wherever you need." }, + { "q": "Can I generate multiple random dates?", "a": "Currently, one date is generated per click. Click again to get another random date in the same range." }, + { "q": "Is the selection truly random?", "a": "Yes. The date is picked using a cryptographically secure random number generator, so every day in your range has an equal chance." } + ], + "ruFaq": [ + { "q": "Какой формат даты используется?", "a": "Даты отображаются в удобном локализованном формате. Вы можете скопировать значение и вставить его куда угодно." }, + { "q": "Можно ли сгенерировать несколько дат сразу?", "a": "Сейчас генерируется одна дата за раз. Нажмите снова, чтобы получить ещё одну случайную дату в том же диапазоне." }, + { "q": "Выбор действительно случайный?", "a": "Да. Дата выбирается с помощью криптографически стойкого генератора случайных чисел, поэтому каждый день в диапазоне имеет равный шанс." } + ] +} diff --git a/src/content/generators/letter.json b/src/content/generators/letter.json new file mode 100644 index 0000000..0c70188 --- /dev/null +++ b/src/content/generators/letter.json @@ -0,0 +1,47 @@ +{ + "slug": "letter", + "title": "Random Letter", + "description": "Pick a random letter from the alphabet.", + "icon": "type", + "status": "live", + "seoTitle": "Random Letter Generator — A to Z | Randify", + "seoDescription": "Pick a random letter from A to Z instantly. Free online letter randomizer for games, learning, and creative projects.", + "ruTitle": "Случайная буква", + "ruDescription": "Выберите случайную букву из алфавита.", + "ruSeoTitle": "Генератор случайных букв — А до Я | Randify", + "ruSeoDescription": "Выберите случайную букву алфавита мгновенно. Бесплатный рандомайзер букв для игр, обучения и творческих проектов.", + "pageTitle": "Random Letter Generator", + "ruPageTitle": "Генератор случайных букв", + "howTo": [ + "Click Generate to pick a random letter from A to Z.", + "The letter appears in large type for easy reading.", + "Click the letter to copy it to your clipboard.", + "Click again to generate a new random letter." + ], + "whenTo": [ + "Choosing a starting letter for a word game or alphabet challenge.", + "Picking a random category in a trivia or party game.", + "Teaching children letters and the alphabet in a fun way." + ], + "ruHowTo": [ + "Нажмите «Сгенерировать», чтобы выбрать случайную букву от А до Я.", + "Буква отображается крупным шрифтом для удобного чтения.", + "Нажмите на букву, чтобы скопировать её в буфер обмена.", + "Нажмите снова, чтобы сгенерировать новую случайную букву." + ], + "ruWhenTo": [ + "Выбор стартовой буквы для словесной игры или алфавитного челленджа.", + "Случайный выбор категории в викторине или настольной игре.", + "Обучение детей буквам и алфавиту в игровой форме." + ], + "faq": [ + { "q": "Does it include both uppercase and lowercase letters?", "a": "The generator produces uppercase letters by default. Lowercase is not currently supported." }, + { "q": "Can I exclude certain letters?", "a": "Not at the moment. Every letter from A to Z has an equal chance of being selected." }, + { "q": "Is this useful for language learning?", "a": "Yes. Teachers and students use it for alphabet drills, vocabulary games, and pronunciation practice." } + ], + "ruFaq": [ + { "q": "Включает ли генератор заглавные и строчные буквы?", "a": "Генератор выдаёт заглавные буквы по умолчанию. Строчные буквы пока не поддерживаются." }, + { "q": "Можно ли исключить определённые буквы?", "a": "Пока нет. Каждая буква от А до Я имеет равный шанс быть выбранной." }, + { "q": "Подходит ли для изучения языка?", "a": "Да. Учителя и ученики используют генератор для алфавитных упражнений, словарных игр и практики произношения." } + ] +} diff --git a/src/content/generators/names.json b/src/content/generators/names.json new file mode 100644 index 0000000..2b6d23c --- /dev/null +++ b/src/content/generators/names.json @@ -0,0 +1,47 @@ +{ + "slug": "names", + "title": "Random Name", + "description": "Generate a random first name by gender or any category.", + "icon": "user", + "status": "live", + "seoTitle": "Random Name Generator — First Names | Randify", + "seoDescription": "Generate random first names instantly. Pick male, female, or any names for characters, babies, games, and more.", + "ruTitle": "Случайное имя", + "ruDescription": "Сгенерируйте случайное имя по полу или из общего списка.", + "ruSeoTitle": "Генератор случайных имён | Randify", + "ruSeoDescription": "Генератор случайных имён: мужские, женские, любые. Для персонажей, игр, вдохновения и не только.", + "pageTitle": "Random Name Generator", + "ruPageTitle": "Генератор случайных имён", + "howTo": [ + "Select a category: Male, Female, or Any.", + "Click Generate to produce a random name.", + "The name appears instantly on the screen.", + "Click the name to copy it to your clipboard." + ], + "whenTo": [ + "Naming a character in a story, game, or RPG campaign.", + "Looking for baby name inspiration.", + "Generating placeholder names for mockups or test data." + ], + "ruHowTo": [ + "Выберите категорию: Мужские, Женские или Любые.", + "Нажмите «Сгенерировать», чтобы получить случайное имя.", + "Имя появится на экране мгновенно.", + "Нажмите на имя, чтобы скопировать его в буфер обмена." + ], + "ruWhenTo": [ + "Придумывание имени персонажа для рассказа, игры или НРИ.", + "Поиск вдохновения для имени ребёнка.", + "Генерация тестовых данных с реалистичными именами." + ], + "faq": [ + { "q": "How many names are in the database?", "a": "Hundreds of popular first names are included, covering common choices in English and Russian." }, + { "q": "Can I generate multiple names at once?", "a": "Currently, one name is shown per click. You can click Generate as many times as you like." }, + { "q": "Are the names culturally specific?", "a": "The list includes widely used names. Some may be more common in certain regions, but all are familiar internationally." } + ], + "ruFaq": [ + { "q": "Сколько имён в базе?", "a": "В базе сотни популярных имён, включая распространённые варианты на английском и русском языках." }, + { "q": "Можно ли сгенерировать несколько имён сразу?", "a": "Сейчас показывается одно имя за раз. Вы можете нажимать «Сгенерировать» столько раз, сколько нужно." }, + { "q": "Имена привязаны к конкретной культуре?", "a": "В списке широко распространённые имена. Некоторые чаще встречаются в определённых регионах, но все они международно узнаваемы." } + ] +} diff --git a/src/content/generators/rps.json b/src/content/generators/rps.json new file mode 100644 index 0000000..2a5b3ee --- /dev/null +++ b/src/content/generators/rps.json @@ -0,0 +1,47 @@ +{ + "slug": "rps", + "title": "Rock Paper Scissors", + "description": "Play Rock Paper Scissors against a random computer opponent.", + "icon": "hand", + "status": "live", + "seoTitle": "Rock Paper Scissors — Play Online | Randify", + "seoDescription": "Play Rock Paper Scissors online against a random computer opponent. Free, instant, and fair — no account needed.", + "ruTitle": "Камень Ножницы Бумага", + "ruDescription": "Сыграйте в Камень Ножницы Бумага против случайного компьютерного соперника.", + "ruSeoTitle": "Камень Ножницы Бумага — играть онлайн | Randify", + "ruSeoDescription": "Играйте в Камень Ножницы Бумага онлайн против компьютера. Бесплатно, мгновенно и честно — без регистрации.", + "pageTitle": "Rock Paper Scissors", + "ruPageTitle": "Камень Ножницы Бумага", + "howTo": [ + "Choose your move: Rock, Paper, or Scissors.", + "Click Play — the computer makes its random choice.", + "The result is shown instantly: win, lose, or draw.", + "Play again as many times as you like." + ], + "whenTo": [ + "Making a quick decision with a friend when neither wants to choose.", + "Killing time with a simple, familiar game.", + "Settling a tie or breaking a deadlock in any activity." + ], + "ruHowTo": [ + "Выберите свой ход: Камень, Ножницы или Бумага.", + "Нажмите «Играть» — компьютер делает случайный выбор.", + "Результат отображается мгновенно: победа, поражение или ничья.", + "Играйте снова сколько угодно раз." + ], + "ruWhenTo": [ + "Быстрое решение спора с другом, когда никто не хочет выбирать первым.", + "Коротание времени в простой и знакомой игре.", + "Разрешение ничьей или выход из тупика в любой активности." + ], + "faq": [ + { "q": "Is the computer choice truly random?", "a": "Yes. The computer's move is generated using a cryptographically secure random number generator, making every round fair and unpredictable." }, + { "q": "Can I play multiple rounds and track my score?", "a": "Currently, each round is independent. A score tracker may be added in a future update." }, + { "q": "Does the computer learn from my moves?", "a": "No. Each move is completely independent and random. The computer does not adapt or remember your previous choices." } + ], + "ruFaq": [ + { "q": "Выбор компьютера действительно случайный?", "a": "Да. Ход компьютера генерируется с помощью криптографически стойкого генератора случайных чисел, делая каждый раунд честным и непредсказуемым." }, + { "q": "Можно ли играть несколько раундов и вести счёт?", "a": "Сейчас каждый раунд независим. Счётчик побед может появиться в будущем обновлении." }, + { "q": "Учится ли компьютер на моих ходах?", "a": "Нет. Каждый ход полностью независимый и случайный. Компьютер не адаптируется и не запоминает ваши предыдущие выборы." } + ] +} diff --git a/src/content/generators/teams.json b/src/content/generators/teams.json new file mode 100644 index 0000000..9b629bf --- /dev/null +++ b/src/content/generators/teams.json @@ -0,0 +1,47 @@ +{ + "slug": "teams", + "title": "Team Splitter", + "description": "Split a list of people into random teams or groups.", + "icon": "users", + "status": "live", + "seoTitle": "Random Team Generator — Split into Groups | Randify", + "seoDescription": "Split any list of names into random teams instantly. Perfect for sports, workshops, classrooms, and party games.", + "ruTitle": "Разделение на команды", + "ruDescription": "Разделите список людей на случайные команды или группы.", + "ruSeoTitle": "Генератор случайных команд — разделение на группы | Randify", + "ruSeoDescription": "Разделите любой список имён на случайные команды мгновенно. Для спорта, занятий, воркшопов и вечеринок.", + "pageTitle": "Random Team Generator", + "ruPageTitle": "Генератор случайных команд", + "howTo": [ + "Enter the list of participants — one name per line.", + "Set the number of teams you want to create.", + "Click Generate — the tool shuffles and splits the list evenly.", + "Review the teams and copy or share the results." + ], + "whenTo": [ + "Organising random teams for a sports match or tournament.", + "Splitting a class or workshop into balanced groups.", + "Creating teams for a party game or team-building activity." + ], + "ruHowTo": [ + "Введите список участников — по одному имени на строку.", + "Укажите количество команд, которые нужно создать.", + "Нажмите «Сгенерировать» — инструмент перемешает и равномерно разделит список.", + "Просмотрите команды и скопируйте или поделитесь результатами." + ], + "ruWhenTo": [ + "Формирование случайных команд для спортивной игры или турнира.", + "Разделение класса или воркшопа на сбалансированные группы.", + "Создание команд для игры на вечеринке или тимбилдинга." + ], + "faq": [ + { "q": "What happens if the list cannot be divided evenly?", "a": "Teams are created as evenly as possible. If there is a remainder, some teams will have one extra member." }, + { "q": "Is there a limit to the number of participants?", "a": "You can add up to 500 names. For most events and activities, this limit is more than sufficient." }, + { "q": "Can I use this for non-name lists?", "a": "Yes. You can split any text items — tasks, topics, or objects — into random groups." } + ], + "ruFaq": [ + { "q": "Что если список нельзя разделить поровну?", "a": "Команды формируются максимально равномерно. Если остаётся остаток, некоторые команды будут на одного участника больше." }, + { "q": "Есть ли ограничение на количество участников?", "a": "Можно добавить до 500 имён. Для большинства мероприятий и активностей этого более чем достаточно." }, + { "q": "Можно ли использовать не только для имён?", "a": "Да. Вы можете разделить на группы любые текстовые элементы — задачи, темы или предметы." } + ] +} diff --git a/src/content/generators/yesno.json b/src/content/generators/yesno.json new file mode 100644 index 0000000..a1735dd --- /dev/null +++ b/src/content/generators/yesno.json @@ -0,0 +1,47 @@ +{ + "slug": "yesno", + "title": "Yes or No", + "description": "Get a random Yes, No, or Maybe answer to any question.", + "icon": "help-circle", + "status": "live", + "seoTitle": "Yes or No Generator — Random Answer | Randify", + "seoDescription": "Get an instant random Yes, No, or Maybe answer to any question. Free online decision maker when you need a quick verdict.", + "ruTitle": "Да или Нет", + "ruDescription": "Получите случайный ответ Да, Нет или Возможно на любой вопрос.", + "ruSeoTitle": "Генератор Да или Нет — случайный ответ | Randify", + "ruSeoDescription": "Мгновенный случайный ответ Да, Нет или Возможно на любой вопрос. Бесплатный онлайн-помощник для быстрых решений.", + "pageTitle": "Yes or No Generator", + "ruPageTitle": "Генератор Да или Нет", + "howTo": [ + "Type your question in the field (optional — it is just for context).", + "Click the button to get a random answer.", + "The result appears instantly: Yes, No, or Maybe.", + "A confidence percentage is shown alongside the answer." + ], + "whenTo": [ + "Making a trivial decision when you are stuck.", + "Settling a friendly debate or bet.", + "Adding a fun random element to a game or challenge." + ], + "ruHowTo": [ + "Введите свой вопрос в поле (по желанию — это просто для контекста).", + "Нажмите кнопку, чтобы получить случайный ответ.", + "Результат появится мгновенно: Да, Нет или Возможно.", + "Рядом с ответом отображается процент уверенности." + ], + "ruWhenTo": [ + "Принятие простого решения, когда вы не можете выбрать.", + "Разрешение дружеского спора или пари.", + "Добавление случайного элемента в игру или челлендж." + ], + "faq": [ + { "q": "Is the answer really random?", "a": "Yes. The outcome is generated using a cryptographically secure random number generator, giving each option a fair chance." }, + { "q": "What does the confidence percentage mean?", "a": "It is a fun visual indicator showing how 'certain' the random choice feels — it does not affect the actual result." }, + { "q": "Can I use this for serious decisions?", "a": "It is best for light-hearted or trivial choices. For important matters, trust your own judgment." } + ], + "ruFaq": [ + { "q": "Ответ действительно случайный?", "a": "Да. Результат генерируется с помощью криптографически стойкого генератора случайных чисел, давая каждому варианту равный шанс." }, + { "q": "Что означает процент уверенности?", "a": "Это забавный визуальный индикатор, показывающий, насколько «уверен» случайный выбор — он не влияет на сам результат." }, + { "q": "Можно ли использовать для серьёзных решений?", "a": "Лучше всего подходит для лёгких или шуточных выборов. В важных вопросах доверяйте собственному мнению." } + ] +} diff --git a/src/i18n/translations.ts b/src/i18n/translations.ts index 6e3b8d3..1592a7b 100644 --- a/src/i18n/translations.ts +++ b/src/i18n/translations.ts @@ -80,6 +80,20 @@ export const translations = { "Have a suggestion, found a bug, or just want to say hello? Drop us a line at hello@randify.pro — we read every message.", aboutCta: "Ready to give it a spin? Explore the generators and let chance do the work.", + + privacyTitle: "Privacy Policy", + privacyIntro: + "This Privacy Policy explains how Randify handles information when you use our website. We believe in transparency: we collect as little data as possible and never ask for personal information.", + privacyData: + "Randify does not collect, store, or process any personal data. All random values — numbers, passwords, dice rolls, card draws, and every other result — are generated entirely inside your browser. Nothing you enter or generate is sent to our servers. We do not require registration, we do not ask for your name or email to use the generators, and we have no access to your inputs or outputs.", + privacyCookies: + "We do not use tracking cookies. The only information stored on your device is your language preference (English or Russian), which is saved in localStorage so the site remembers your choice on future visits. This data never leaves your browser and is not shared with anyone.", + privacyThirdParty: + "We use two third-party services to keep Randify free and improve the experience:\n\n• Yandex.RTB — displays advertisements on some pages. Yandex may use cookies and similar technologies to show relevant ads. You can manage ad preferences through your browser settings or Yandex's opt-out tools.\n\n• Yandex.Metrika — helps us understand how visitors use the site (for example, which generators are most popular). This is anonymous aggregate data: we cannot identify individual users.", + privacyContact: + "If you have any questions about this policy or how we handle data, please contact us at hello@randify.pro.", + privacyChanges: + "We may update this Privacy Policy from time to time. Any changes will be posted on this page with an updated date. Since we do not collect contact information, we cannot notify users individually — please check this page occasionally for updates.", }, ru: { allGenerators: "Все генераторы", @@ -160,6 +174,20 @@ export const translations = { "Есть предложение, нашли ошибку или просто хотите поздороваться? Напишите нам на hello@randify.pro — мы читаем каждое сообщение.", aboutCta: "Готовы попробовать? Откройте генераторы и позвольте случайности сделать выбор за вас.", + + privacyTitle: "Политика конфиденциальности", + privacyIntro: + "Настоящая Политика конфиденциальности объясняет, как Randify работает с информацией при использовании сайта. Мы верим в прозрачность: собираем минимум данных и никогда не запрашиваем персональную информацию.", + privacyData: + "Randify не собирает, не хранит и не обрабатывает персональные данные. Все случайные значения — числа, пароли, броски кубиков, вытягивание карт и любые другие результаты — генерируются полностью внутри вашего браузера. Ничто из того, что вы вводите или генерируете, не отправляется на наши серверы. Мы не требуем регистрации, не просим имя или email для использования генераторов и не имеем доступа к вашим данным.", + privacyCookies: + "Мы не используем cookies для отслеживания. Единственная информация, которая сохраняется на вашем устройстве, — это предпочитаемый язык (английский или русский) в localStorage, чтобы сайт запоминал ваш выбор при следующем посещении. Эти данные не покидают браузер и никому не передаются.", + privacyThirdParty: + "Мы используем два сторонних сервиса, чтобы Randify оставался бесплатным и удобным:\n\n• Yandex.RTB — показывает рекламу на некоторых страницах. Яндекс может использовать cookies и аналогичные технологии для показа релевантной рекламы. Вы можете управлять настройками рекламы через параметры браузера или инструменты отказа Яндекса.\n\n• Яндекс.Метрика — помогает нам понимать, как посетители используют сайт (например, какие генераторы самые популярные). Это анонимные агрегированные данные: мы не можем идентифицировать отдельных пользователей.", + privacyContact: + "Если у вас есть вопросы об этой политике или о том, как мы работаем с данными, напишите нам на hello@randify.pro.", + privacyChanges: + "Мы можем обновлять эту Политику конфиденциальности время от времени. Любые изменения будут опубликованы на этой странице с обновлённой датой. Поскольку мы не собираем контактную информацию, мы не можем уведомлять пользователей индивидуально — пожалуйста, проверяйте эту страницу время от времени.", }, } as const; diff --git a/src/pages/generators/date.astro b/src/pages/generators/date.astro new file mode 100644 index 0000000..66627e7 --- /dev/null +++ b/src/pages/generators/date.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from '@/layouts/GeneratorLayout.astro'; +import DateGenerator from '@/components/generators/DateGenerator.astro'; +import { generators } from '@/data/generators'; + +const generator = generators.find((g) => g.slug === 'date')!; +--- + + + + diff --git a/src/pages/generators/letter.astro b/src/pages/generators/letter.astro new file mode 100644 index 0000000..05b5c6e --- /dev/null +++ b/src/pages/generators/letter.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from '@/layouts/GeneratorLayout.astro'; +import LetterGenerator from '@/components/generators/LetterGenerator.astro'; +import { generators } from '@/data/generators'; + +const generator = generators.find((g) => g.slug === 'letter')!; +--- + + + + diff --git a/src/pages/generators/names.astro b/src/pages/generators/names.astro new file mode 100644 index 0000000..446a342 --- /dev/null +++ b/src/pages/generators/names.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from '@/layouts/GeneratorLayout.astro'; +import NamesGenerator from '@/components/generators/NamesGenerator.astro'; +import { generators } from '@/data/generators'; + +const generator = generators.find((g) => g.slug === 'names')!; +--- + + + + diff --git a/src/pages/generators/rps.astro b/src/pages/generators/rps.astro new file mode 100644 index 0000000..ea634fc --- /dev/null +++ b/src/pages/generators/rps.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from '@/layouts/GeneratorLayout.astro'; +import RpsGenerator from '@/components/generators/RpsGenerator.astro'; +import { generators } from '@/data/generators'; + +const generator = generators.find((g) => g.slug === 'rps')!; +--- + + + + diff --git a/src/pages/generators/teams.astro b/src/pages/generators/teams.astro new file mode 100644 index 0000000..3680f99 --- /dev/null +++ b/src/pages/generators/teams.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from '@/layouts/GeneratorLayout.astro'; +import TeamsGenerator from '@/components/generators/TeamsGenerator.astro'; +import { generators } from '@/data/generators'; + +const generator = generators.find((g) => g.slug === 'teams')!; +--- + + + + diff --git a/src/pages/generators/yesno.astro b/src/pages/generators/yesno.astro new file mode 100644 index 0000000..d477485 --- /dev/null +++ b/src/pages/generators/yesno.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from '@/layouts/GeneratorLayout.astro'; +import YesNoGenerator from '@/components/generators/YesNoGenerator.astro'; +import { generators } from '@/data/generators'; + +const generator = generators.find((g) => g.slug === 'yesno')!; +--- + + + + diff --git a/src/pages/privacy.astro b/src/pages/privacy.astro new file mode 100644 index 0000000..796bf2b --- /dev/null +++ b/src/pages/privacy.astro @@ -0,0 +1,85 @@ +--- +import BaseLayout from "@/layouts/BaseLayout.astro"; +import { useT } from "@/i18n/translations"; +import type { Lang } from "@/i18n/translations"; + +const lang = (Astro.currentLocale as Lang) || "en"; +const T = useT(lang); +--- + + +
+ + +
+
+ + randify +
+

+ {T.privacyTitle} +

+ +
+ +
+
+

{T.privacyIntro}

+
+ +
+

Data Collection

+

{T.privacyData}

+
+ +
+

Cookies & Local Storage

+

{T.privacyCookies}

+
+ +
+

Third-Party Services

+ {T.privacyThirdParty.split("\n\n").map((paragraph) => ( +

{paragraph}

+ ))} +
+ +
+

Contact

+

{T.privacyContact}

+
+ +
+

Changes to This Policy

+

{T.privacyChanges}

+
+
+
+
diff --git a/src/pages/ru/generators/date.astro b/src/pages/ru/generators/date.astro new file mode 100644 index 0000000..66627e7 --- /dev/null +++ b/src/pages/ru/generators/date.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from '@/layouts/GeneratorLayout.astro'; +import DateGenerator from '@/components/generators/DateGenerator.astro'; +import { generators } from '@/data/generators'; + +const generator = generators.find((g) => g.slug === 'date')!; +--- + + + + diff --git a/src/pages/ru/generators/letter.astro b/src/pages/ru/generators/letter.astro new file mode 100644 index 0000000..05b5c6e --- /dev/null +++ b/src/pages/ru/generators/letter.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from '@/layouts/GeneratorLayout.astro'; +import LetterGenerator from '@/components/generators/LetterGenerator.astro'; +import { generators } from '@/data/generators'; + +const generator = generators.find((g) => g.slug === 'letter')!; +--- + + + + diff --git a/src/pages/ru/generators/names.astro b/src/pages/ru/generators/names.astro new file mode 100644 index 0000000..446a342 --- /dev/null +++ b/src/pages/ru/generators/names.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from '@/layouts/GeneratorLayout.astro'; +import NamesGenerator from '@/components/generators/NamesGenerator.astro'; +import { generators } from '@/data/generators'; + +const generator = generators.find((g) => g.slug === 'names')!; +--- + + + + diff --git a/src/pages/ru/generators/rps.astro b/src/pages/ru/generators/rps.astro new file mode 100644 index 0000000..ea634fc --- /dev/null +++ b/src/pages/ru/generators/rps.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from '@/layouts/GeneratorLayout.astro'; +import RpsGenerator from '@/components/generators/RpsGenerator.astro'; +import { generators } from '@/data/generators'; + +const generator = generators.find((g) => g.slug === 'rps')!; +--- + + + + diff --git a/src/pages/ru/generators/teams.astro b/src/pages/ru/generators/teams.astro new file mode 100644 index 0000000..3680f99 --- /dev/null +++ b/src/pages/ru/generators/teams.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from '@/layouts/GeneratorLayout.astro'; +import TeamsGenerator from '@/components/generators/TeamsGenerator.astro'; +import { generators } from '@/data/generators'; + +const generator = generators.find((g) => g.slug === 'teams')!; +--- + + + + diff --git a/src/pages/ru/generators/yesno.astro b/src/pages/ru/generators/yesno.astro new file mode 100644 index 0000000..d477485 --- /dev/null +++ b/src/pages/ru/generators/yesno.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from '@/layouts/GeneratorLayout.astro'; +import YesNoGenerator from '@/components/generators/YesNoGenerator.astro'; +import { generators } from '@/data/generators'; + +const generator = generators.find((g) => g.slug === 'yesno')!; +--- + + + + diff --git a/src/pages/ru/privacy.astro b/src/pages/ru/privacy.astro new file mode 100644 index 0000000..fb85f63 --- /dev/null +++ b/src/pages/ru/privacy.astro @@ -0,0 +1,85 @@ +--- +import BaseLayout from "@/layouts/BaseLayout.astro"; +import { useT } from "@/i18n/translations"; +import type { Lang } from "@/i18n/translations"; + +const lang = (Astro.currentLocale as Lang) || "en"; +const T = useT(lang); +--- + + +
+ + +
+
+ + randify +
+

+ {T.privacyTitle} +

+ +
+ +
+
+

{T.privacyIntro}

+
+ +
+

Сбор данных

+

{T.privacyData}

+
+ +
+

Cookies и локальное хранилище

+

{T.privacyCookies}

+
+ +
+

Сторонние сервисы

+ {T.privacyThirdParty.split("\n\n").map((paragraph) => ( +

{paragraph}

+ ))} +
+ +
+

Контакты

+

{T.privacyContact}

+
+ +
+

Изменения политики

+

{T.privacyChanges}

+
+
+
+
From a1cc10d0a2c7cb7afd9f369e304c936fbbf41e92 Mon Sep 17 00:00:00 2001 From: emil28092005 Date: Tue, 12 May 2026 16:22:26 +0300 Subject: [PATCH 44/44] feat: add 4 new generators (Palette, Lorem, Country, Weighted) - Color Palette: 7 harmony modes (Random, Analogous, Complementary, Triadic, Monochromatic, Split-Complementary, Tetradic), 3-8 colors, individual swatch copy + copy all - Lorem Ipsum: 1-20 paragraphs, Short/Medium/Long length, optional classic start, full a11y support - Random Country: 40 countries, region filter, flag emoji + capital + population, bilingual data - Weighted Random: dynamic item-weight rows, validation, single pick + N-trials with statistics bars All generators include EN/RU i18n, SEO metadata, FAQ, and follow project patterns. --- .gitignore | 1 + .../generators/CountryGenerator.astro | 249 ++++++++++++ .../generators/LoremGenerator.astro | 252 ++++++++++++ .../generators/PaletteGenerator.astro | 346 +++++++++++++++++ .../generators/WeightedGenerator.astro | 365 ++++++++++++++++++ src/content/generators/country.json | 49 +++ src/content/generators/lorem.json | 47 +++ src/content/generators/palette.json | 47 +++ src/content/generators/weighted.json | 47 +++ src/pages/generators/country.astro | 11 + src/pages/generators/lorem.astro | 11 + src/pages/generators/palette.astro | 11 + src/pages/generators/weighted.astro | 11 + src/pages/ru/generators/country.astro | 11 + src/pages/ru/generators/lorem.astro | 11 + src/pages/ru/generators/palette.astro | 11 + src/pages/ru/generators/weighted.astro | 11 + 17 files changed, 1491 insertions(+) create mode 100644 src/components/generators/CountryGenerator.astro create mode 100644 src/components/generators/LoremGenerator.astro create mode 100644 src/components/generators/PaletteGenerator.astro create mode 100644 src/components/generators/WeightedGenerator.astro create mode 100644 src/content/generators/country.json create mode 100644 src/content/generators/lorem.json create mode 100644 src/content/generators/palette.json create mode 100644 src/content/generators/weighted.json create mode 100644 src/pages/generators/country.astro create mode 100644 src/pages/generators/lorem.astro create mode 100644 src/pages/generators/palette.astro create mode 100644 src/pages/generators/weighted.astro create mode 100644 src/pages/ru/generators/country.astro create mode 100644 src/pages/ru/generators/lorem.astro create mode 100644 src/pages/ru/generators/palette.astro create mode 100644 src/pages/ru/generators/weighted.astro diff --git a/.gitignore b/.gitignore index f6d8805..7a14645 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ dist/ .env *.log .astro/ +.kimi/ diff --git a/src/components/generators/CountryGenerator.astro b/src/components/generators/CountryGenerator.astro new file mode 100644 index 0000000..7da02e3 --- /dev/null +++ b/src/components/generators/CountryGenerator.astro @@ -0,0 +1,249 @@ +--- +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); + +const regions = isRu + ? [ + { value: "all", label: "Все" }, + { value: "europe", label: "Европа" }, + { value: "asia", label: "Азия" }, + { value: "americas", label: "Америка" }, + { value: "africa", label: "Африка" }, + { value: "oceania", label: "Океания" }, + ] + : [ + { value: "all", label: "All" }, + { value: "europe", label: "Europe" }, + { value: "asia", label: "Asia" }, + { value: "americas", label: "Americas" }, + { value: "africa", label: "Africa" }, + { value: "oceania", label: "Oceania" }, + ]; +--- + +
+ +
+ + +
+ + +
+
+ +
+ + +
+ +
+
+ + diff --git a/src/components/generators/LoremGenerator.astro b/src/components/generators/LoremGenerator.astro new file mode 100644 index 0000000..244e80a --- /dev/null +++ b/src/components/generators/LoremGenerator.astro @@ -0,0 +1,252 @@ +--- +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); + +const lengthOptions = isRu + ? [ + { value: "short", label: "Короткие" }, + { value: "medium", label: "Средние" }, + { value: "long", label: "Длинные" }, + ] + : [ + { value: "short", label: "Short" }, + { value: "medium", label: "Medium" }, + { value: "long", label: "Long" }, + ]; + +const startLabel = isRu + ? "Начать с Lorem ipsum dolor sit amet" + : "Start with Lorem ipsum dolor sit amet"; +--- + +
+ +
+
+ +
+ + +
+ + +
+ + +
+
+ + +
+ + +
+ + +
+ +
+
+ + +
+
+ + {isRu ? "Результат" : "Result"} + + +
+ +
+
+
+ + diff --git a/src/components/generators/PaletteGenerator.astro b/src/components/generators/PaletteGenerator.astro new file mode 100644 index 0000000..8ce7b9b --- /dev/null +++ b/src/components/generators/PaletteGenerator.astro @@ -0,0 +1,346 @@ +--- +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); + +const i18n = { + harmonyLabel: isRu ? "Гармония" : "Harmony", + countLabel: isRu ? "Количество цветов" : "Number of colors", + copyAll: T.copyAll, + generate: T.generate, + copied: T.copied, + copy: T.copy, + modes: { + random: isRu ? "Случайная" : "Random", + analogous: isRu ? "Аналоговая" : "Analogous", + complementary: isRu ? "Комплементарная" : "Complementary", + triadic: isRu ? "Триадная" : "Triadic", + monochromatic: isRu ? "Монохромная" : "Monochromatic", + splitComplementary: isRu ? "Сплит-комплементарная" : "Split-Complementary", + tetradic: isRu ? "Тетрадная" : "Tetradic", + }, +}; +--- + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+ +
+
+ + + + + +
+ +
+
+ + diff --git a/src/components/generators/WeightedGenerator.astro b/src/components/generators/WeightedGenerator.astro new file mode 100644 index 0000000..0f01759 --- /dev/null +++ b/src/components/generators/WeightedGenerator.astro @@ -0,0 +1,365 @@ +--- +import { useT } from "../../i18n/translations"; +const isRu = Astro.url.pathname.startsWith("/ru"); +const T = useT(isRu ? "ru" : "en"); +--- + +
+
+
+ +
+ +
+ +
+ + +
+ + + + + +
+ +
+ + +
+
+
+ + diff --git a/src/content/generators/country.json b/src/content/generators/country.json new file mode 100644 index 0000000..e22ddb0 --- /dev/null +++ b/src/content/generators/country.json @@ -0,0 +1,49 @@ +{ + "slug": "country", + "title": "Country", + "description": "Generate a random country with flag, capital, region, and population.", + "icon": "user", + "status": "live", + "seoTitle": "Random Country Generator — Flag, Capital, Region | Randify", + "seoDescription": "Get a random country with flag emoji, capital, region, and population. Free online country randomizer for games, quizzes, and travel inspiration.", + "ruTitle": "Страна", + "ruDescription": "Генерируйте случайную страну с флагом, столицей, регионом и населением.", + "ruSeoTitle": "Генератор случайных стран — флаг, столица, регион | Randify", + "ruSeoDescription": "Получайте случайную страну с эмодзи флага, столицей, регионом и населением. Бесплатный рандомайзер стран для игр, викторин и вдохновения.", + "pageTitle": "Random Country Generator", + "ruPageTitle": "Генератор случайных стран", + "howTo": [ + "Select a region filter or leave it on All to include every country.", + "Click Generate to pick a random country from the filtered list.", + "View the result card with flag, name, capital, region, and population.", + "Click Copy to copy the country details to your clipboard." + ], + "whenTo": [ + "Choosing a random destination for a travel challenge or game.", + "Creating quiz questions about capitals, flags, or geography.", + "Picking a random country for a school project or presentation.", + "Settling a friendly debate with a random geographic draw." + ], + "ruHowTo": [ + "Выберите фильтр по региону или оставьте «Все», чтобы включить все страны.", + "Нажмите «Сгенерировать», чтобы выбрать случайную страну из отфильтрованного списка.", + "Посмотрите карточку результата с флагом, названием, столицей, регионом и населением.", + "Нажмите «Копировать», чтобы скопировать данные о стране в буфер обмена." + ], + "ruWhenTo": [ + "Выбор случайного направления для путешественнического челленджа или игры.", + "Создание вопросов для викторин о столицах, флагах или географии.", + "Выбор случайной страны для школьного проекта или презентации.", + "Решение спора случайным географическим выбором." + ], + "faq": [ + { "q": "How many countries are in the generator?", "a": "The generator includes 40 countries from all inhabited continents, covering a wide range of regions, populations, and cultures." }, + { "q": "Can I filter by region?", "a": "Yes. You can filter by Europe, Asia, Americas, Africa, or Oceania, or choose All to include every country." }, + { "q": "Is the population data accurate?", "a": "Population figures are approximate and based on recent estimates. They are intended for casual use rather than precise research." } + ], + "ruFaq": [ + { "q": "Сколько стран в генераторе?", "a": "Генератор включает 40 стран со всех обитаемых континентов, охватывая широкий спектр регионов, населения и культур." }, + { "q": "Можно ли фильтровать по региону?", "a": "Да. Вы можете фильтровать по Европе, Азии, Америке, Африке или Океании, либо выбрать «Все», чтобы включить все страны." }, + { "q": "Данные о населении актуальны?", "a": "Численность населения приблизительная и основана на недавних оценках. Она предназначена для повседневного использования, а не для точных исследований." } + ] +} diff --git a/src/content/generators/lorem.json b/src/content/generators/lorem.json new file mode 100644 index 0000000..77979f4 --- /dev/null +++ b/src/content/generators/lorem.json @@ -0,0 +1,47 @@ +{ + "slug": "lorem", + "title": "Lorem Ipsum", + "description": "Generate random Lorem Ipsum placeholder text in paragraphs.", + "icon": "type", + "status": "live", + "seoTitle": "Lorem Ipsum Generator — Free Placeholder Text | Randify", + "seoDescription": "Generate random Lorem Ipsum placeholder text instantly. Choose paragraph count and length. Free online dummy text generator for designers and developers.", + "ruTitle": "Lorem Ipsum", + "ruDescription": "Генерируйте случайный текст-заполнитель Lorem Ipsum абзацами.", + "ruSeoTitle": "Генератор Lorem Ipsum — бесплатный текст-заполнитель | Randify", + "ruSeoDescription": "Генерируйте текст-заполнитель Lorem Ipsum мгновенно. Выбирайте количество абзацев и длину. Бесплатный генератор текста для дизайнеров и разработчиков.", + "pageTitle": "Lorem Ipsum Generator", + "ruPageTitle": "Генератор Lorem Ipsum", + "howTo": [ + "Choose the number of paragraphs (1–20) and the desired length: Short, Medium, or Long.", + "Optionally check \"Start with Lorem ipsum...\" to begin the first paragraph with the classic opening.", + "Click Generate to create the text, then use the Copy button to copy it to your clipboard." + ], + "whenTo": [ + "Filling mockups and wireframes with realistic placeholder text.", + "Testing typography, line height, and readability in designs.", + "Creating demo content for websites and applications.", + "Needing neutral Latin text that won't distract from layout." + ], + "ruHowTo": [ + "Выберите количество абзацев (1–20) и желаемую длину: Короткие, Средние или Длинные.", + "При желании отметьте «Начать с Lorem ipsum...», чтобы первый абзац начинался классически.", + "Нажмите «Сгенерировать», а затем «Копировать», чтобы скопировать текст в буфер обмена." + ], + "ruWhenTo": [ + "Заполнение макетов и каркасов реалистичным текстом-заполнителем.", + "Тестирование типографики, межстрочного интервала и читаемости в дизайнах.", + "Создание демонстрационного контента для сайтов и приложений.", + "Необходимость в нейтральном латинском тексте, который не отвлекает от макета." + ], + "faq": [ + { "q": "What is Lorem Ipsum?", "a": "Lorem Ipsum is a classic placeholder text used in publishing and graphic design. It has been the industry's standard dummy text since the 1500s." }, + { "q": "Can I choose how long each paragraph is?", "a": "Yes. You can select Short (2–4 sentences), Medium (4–6 sentences), or Long (6–10 sentences) per paragraph." }, + { "q": "Is the generated text truly random?", "a": "Yes. Each paragraph is built from a shuffled pool of classic Latin words, so every generation produces unique text." } + ], + "ruFaq": [ + { "q": "Что такое Lorem Ipsum?", "a": "Lorem Ipsum — это классический текст-заполнитель, используемый в издательском деле и графическом дизайне. Он является стандартным текстом-рыбой с XVI века." }, + { "q": "Можно ли выбрать длину каждого абзаца?", "a": "Да. Вы можете выбрать Короткие (2–4 предложения), Средние (4–6 предложений) или Длинные (6–10 предложений) абзацы." }, + { "q": "Сгенерированный текст действительно случайный?", "a": "Да. Каждый абзац собирается из перемешанного набора классических латинских слов, поэтому каждая генерация уникальна." } + ] +} diff --git a/src/content/generators/palette.json b/src/content/generators/palette.json new file mode 100644 index 0000000..12da908 --- /dev/null +++ b/src/content/generators/palette.json @@ -0,0 +1,47 @@ +{ + "slug": "palette", + "title": "Color Palette", + "description": "Generate harmonious color palettes with one click.", + "icon": "palette", + "status": "live", + "seoTitle": "Color Palette Generator — Random & Harmonic Palettes | Randify", + "seoDescription": "Generate beautiful color palettes instantly. Choose from Random, Analogous, Complementary, Triadic, and Monochromatic harmony modes. Free online tool for designers.", + "ruTitle": "Палитра цветов", + "ruDescription": "Генерируйте гармоничные палитры цветов одним нажатием.", + "ruSeoTitle": "Генератор палитры цветов — случайные и гармоничные палитры | Randify", + "ruSeoDescription": "Создавайте красивые палитры цветов мгновенно. Выбирайте режимы гармонии: случайный, аналоговый, комплементарный, триадный, монохромный. Бесплатный онлайн-инструмент для дизайнеров.", + "pageTitle": "Color Palette Generator", + "ruPageTitle": "Генератор палитры цветов", + "howTo": [ + "Select the number of colors and a harmony mode.", + "Click Generate to create a new palette.", + "Click any color swatch to copy its HEX code, or use Copy All to copy the entire palette." + ], + "whenTo": [ + "Designing a brand identity or logo color scheme.", + "Choosing colors for a website or app UI.", + "Creating illustrations, presentations, or social media graphics.", + "Exploring color theory and harmony relationships." + ], + "ruHowTo": [ + "Выберите количество цветов и режим гармонии.", + "Нажмите «Сгенерировать», чтобы создать новую палитру.", + "Нажмите на любой цвет, чтобы скопировать его HEX-код, или используйте «Копировать всё»." + ], + "ruWhenTo": [ + "Разработка фирменного стиля или цветовой схемы логотипа.", + "Выбор цветов для дизайна сайта или приложения.", + "Создание иллюстраций, презентаций или графики для соцсетей.", + "Изучение теории цвета и цветовых гармоний." + ], + "faq": [ + { "q": "What harmony modes are available?", "a": "Random, Analogous, Complementary, Triadic, and Monochromatic. Each mode creates colors based on a different relationship on the color wheel." }, + { "q": "Can I copy the whole palette at once?", "a": "Yes. Click the Copy All button to copy all HEX codes separated by commas to your clipboard." }, + { "q": "How many colors can I generate?", "a": "You can generate palettes from 3 to 8 colors using the count selector." } + ], + "ruFaq": [ + { "q": "Какие режимы гармонии доступны?", "a": "Случайный, аналоговый, комплементарный, триадный и монохромный. Каждый режим создаёт цвета на основе разных отношений на цветовом круге." }, + { "q": "Можно ли скопировать всю палитру сразу?", "a": "Да. Нажмите кнопку «Копировать всё», чтобы скопировать все HEX-коды через запятую в буфер обмена." }, + { "q": "Сколько цветов можно сгенерировать?", "a": "Вы можете создавать палитры от 3 до 8 цветов с помощью селектора количества." } + ] +} diff --git a/src/content/generators/weighted.json b/src/content/generators/weighted.json new file mode 100644 index 0000000..38e00de --- /dev/null +++ b/src/content/generators/weighted.json @@ -0,0 +1,47 @@ +{ + "slug": "weighted", + "title": "Weighted Random", + "description": "Pick a random item from a list using custom weights for each option.", + "icon": "list", + "status": "live", + "seoTitle": "Weighted Random Generator — Weighted Choice Picker | Randify", + "seoDescription": "Pick random items with custom weights. Perfect for raffles, weighted decisions, and probability experiments. Free online weighted random selector.", + "ruTitle": "Взвешенный случайный выбор", + "ruDescription": "Выбирайте случайный элемент из списка с учётом заданных весов для каждого варианта.", + "ruSeoTitle": "Генератор взвешенного случайного выбора | Randify", + "ruSeoDescription": "Выбирайте случайные элементы с заданными весами. Идеально для розыгрышей, взвешенных решений и экспериментов с вероятностями.", + "pageTitle": "Weighted Random Generator", + "ruPageTitle": "Генератор взвешенного случайного выбора", + "howTo": [ + "Add items to the list and assign a weight to each one. Higher weight means higher chance of being picked.", + "Click Pick to select one random item based on the weights.", + "Use Pick N times to run multiple trials and see the distribution statistics." + ], + "whenTo": [ + "Running a raffle where some participants have more entries than others.", + "Making weighted decisions when options have different priorities.", + "Simulating probability distributions or testing randomness.", + "Creating balanced teams or assignments with different skill weights." + ], + "ruHowTo": [ + "Добавьте элементы в список и назначьте каждому вес. Чем выше вес, тем больше шанс быть выбранным.", + "Нажмите «Выбрать», чтобы случайно выбрать один элемент с учётом весов.", + "Используйте «Выбрать N раз» для множественных испытаний и просмотра статистики распределения." + ], + "ruWhenTo": [ + "Проведение розыгрыша, где у некоторых участников больше шансов, чем у других.", + "Принятие взвешенных решений, когда у вариантов разные приоритеты.", + "Моделирование распределений вероятностей или тестирование случайности.", + "Создание сбалансированных команд или назначений с разными весами навыков." + ], + "faq": [ + { "q": "How does weighted random selection work?", "a": "Each item's chance of being picked is proportional to its weight. If item A has weight 2 and item B has weight 8, B is 4 times more likely to be selected than A." }, + { "q": "Can I use decimal weights?", "a": "Yes, you can use any positive number as a weight, including decimals. The generator normalizes all weights automatically." }, + { "q": "Is the result truly random?", "a": "Yes. The generator uses a cryptographically secure random number generator to ensure fair and unpredictable results every time." } + ], + "ruFaq": [ + { "q": "Как работает взвешенный случайный выбор?", "a": "Шанс выбора каждого элемента пропорционален его весу. Если у элемента А вес 2, а у элемента Б вес 8, то Б в 4 раза чаще будет выбран, чем А." }, + { "q": "Можно ли использовать дробные веса?", "a": "Да, весом может быть любое положительное число, включая десятичные дроби. Генератор автоматически нормализует все веса." }, + { "q": "Результат действительно случайный?", "a": "Да. Генератор использует криптографически стойкий генератор случайных чисел, чтобы гарантировать честный и непредсказуемый результат каждый раз." } + ] +} diff --git a/src/pages/generators/country.astro b/src/pages/generators/country.astro new file mode 100644 index 0000000..7ca9dac --- /dev/null +++ b/src/pages/generators/country.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import CountryGenerator from "@/components/generators/CountryGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "country")!; +--- + + + + diff --git a/src/pages/generators/lorem.astro b/src/pages/generators/lorem.astro new file mode 100644 index 0000000..fd121df --- /dev/null +++ b/src/pages/generators/lorem.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import LoremGenerator from "@/components/generators/LoremGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "lorem")!; +--- + + + + diff --git a/src/pages/generators/palette.astro b/src/pages/generators/palette.astro new file mode 100644 index 0000000..6786ff5 --- /dev/null +++ b/src/pages/generators/palette.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import PaletteGenerator from "@/components/generators/PaletteGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "palette")!; +--- + + + + diff --git a/src/pages/generators/weighted.astro b/src/pages/generators/weighted.astro new file mode 100644 index 0000000..ebcf2d8 --- /dev/null +++ b/src/pages/generators/weighted.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import WeightedGenerator from "@/components/generators/WeightedGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "weighted")!; +--- + + + + diff --git a/src/pages/ru/generators/country.astro b/src/pages/ru/generators/country.astro new file mode 100644 index 0000000..7ca9dac --- /dev/null +++ b/src/pages/ru/generators/country.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import CountryGenerator from "@/components/generators/CountryGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "country")!; +--- + + + + diff --git a/src/pages/ru/generators/lorem.astro b/src/pages/ru/generators/lorem.astro new file mode 100644 index 0000000..fd121df --- /dev/null +++ b/src/pages/ru/generators/lorem.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import LoremGenerator from "@/components/generators/LoremGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "lorem")!; +--- + + + + diff --git a/src/pages/ru/generators/palette.astro b/src/pages/ru/generators/palette.astro new file mode 100644 index 0000000..6786ff5 --- /dev/null +++ b/src/pages/ru/generators/palette.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import PaletteGenerator from "@/components/generators/PaletteGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "palette")!; +--- + + + + diff --git a/src/pages/ru/generators/weighted.astro b/src/pages/ru/generators/weighted.astro new file mode 100644 index 0000000..ebcf2d8 --- /dev/null +++ b/src/pages/ru/generators/weighted.astro @@ -0,0 +1,11 @@ +--- +import GeneratorLayout from "@/layouts/GeneratorLayout.astro"; +import WeightedGenerator from "@/components/generators/WeightedGenerator.astro"; +import { generators } from "@/data/generators"; + +const generator = generators.find((g) => g.slug === "weighted")!; +--- + + + +