diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..3648408 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "diskern-site", + "runtimeExecutable": "npm", + "runtimeArgs": ["--prefix", "site", "run", "dev"], + "port": 5173 + } + ] +} diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml new file mode 100644 index 0000000..5e156ce --- /dev/null +++ b/.github/workflows/deploy-pages.yml @@ -0,0 +1,57 @@ +name: Deploy site to GitHub Pages + +on: + push: + branches: [main] + paths: ["site/**", ".github/workflows/deploy-pages.yml"] + workflow_dispatch: {} + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: site/package-lock.json + + - run: npm ci + working-directory: site + + - run: npm run build + working-directory: site + + # GitHub Pages has no server-side routing: a direct visit to + # /diskern/docs would 404 before React Router ever loads. Serving + # the same SPA shell as the 404 page lets the client-side router + # take over once the JS boots, so deep links and refreshes on + # future pages (e.g. /docs, /changelog) work. + - name: Add SPA fallback for client-side routing + run: cp dist/index.html dist/404.html + working-directory: site + + - uses: actions/upload-pages-artifact@v3 + with: + path: site/dist + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/app/src-tauri/icons/128x128.png b/app/src-tauri/icons/128x128.png index b1b4f12..84f789f 100644 Binary files a/app/src-tauri/icons/128x128.png and b/app/src-tauri/icons/128x128.png differ diff --git a/app/src-tauri/icons/128x128@2x.png b/app/src-tauri/icons/128x128@2x.png index e70a2e3..2fc0cb1 100644 Binary files a/app/src-tauri/icons/128x128@2x.png and b/app/src-tauri/icons/128x128@2x.png differ diff --git a/app/src-tauri/icons/32x32.png b/app/src-tauri/icons/32x32.png index 0087fdc..0eaaa66 100644 Binary files a/app/src-tauri/icons/32x32.png and b/app/src-tauri/icons/32x32.png differ diff --git a/app/src-tauri/icons/64x64.png b/app/src-tauri/icons/64x64.png index e8d508e..5b2e717 100644 Binary files a/app/src-tauri/icons/64x64.png and b/app/src-tauri/icons/64x64.png differ diff --git a/app/src-tauri/icons/Square107x107Logo.png b/app/src-tauri/icons/Square107x107Logo.png index 18054ee..db2c548 100644 Binary files a/app/src-tauri/icons/Square107x107Logo.png and b/app/src-tauri/icons/Square107x107Logo.png differ diff --git a/app/src-tauri/icons/Square142x142Logo.png b/app/src-tauri/icons/Square142x142Logo.png index 194575c..99185d0 100644 Binary files a/app/src-tauri/icons/Square142x142Logo.png and b/app/src-tauri/icons/Square142x142Logo.png differ diff --git a/app/src-tauri/icons/Square150x150Logo.png b/app/src-tauri/icons/Square150x150Logo.png index 8462720..f772d5f 100644 Binary files a/app/src-tauri/icons/Square150x150Logo.png and b/app/src-tauri/icons/Square150x150Logo.png differ diff --git a/app/src-tauri/icons/Square284x284Logo.png b/app/src-tauri/icons/Square284x284Logo.png index aafc4fa..2a5dcc2 100644 Binary files a/app/src-tauri/icons/Square284x284Logo.png and b/app/src-tauri/icons/Square284x284Logo.png differ diff --git a/app/src-tauri/icons/Square30x30Logo.png b/app/src-tauri/icons/Square30x30Logo.png index 6fa37c1..6d5bd4c 100644 Binary files a/app/src-tauri/icons/Square30x30Logo.png and b/app/src-tauri/icons/Square30x30Logo.png differ diff --git a/app/src-tauri/icons/Square310x310Logo.png b/app/src-tauri/icons/Square310x310Logo.png index 503288d..17da3de 100644 Binary files a/app/src-tauri/icons/Square310x310Logo.png and b/app/src-tauri/icons/Square310x310Logo.png differ diff --git a/app/src-tauri/icons/Square44x44Logo.png b/app/src-tauri/icons/Square44x44Logo.png index 6f76891..3c9c1db 100644 Binary files a/app/src-tauri/icons/Square44x44Logo.png and b/app/src-tauri/icons/Square44x44Logo.png differ diff --git a/app/src-tauri/icons/Square71x71Logo.png b/app/src-tauri/icons/Square71x71Logo.png index 6b45976..871b0a1 100644 Binary files a/app/src-tauri/icons/Square71x71Logo.png and b/app/src-tauri/icons/Square71x71Logo.png differ diff --git a/app/src-tauri/icons/Square89x89Logo.png b/app/src-tauri/icons/Square89x89Logo.png index e8f3bee..53cf075 100644 Binary files a/app/src-tauri/icons/Square89x89Logo.png and b/app/src-tauri/icons/Square89x89Logo.png differ diff --git a/app/src-tauri/icons/StoreLogo.png b/app/src-tauri/icons/StoreLogo.png index b192ebc..0046d05 100644 Binary files a/app/src-tauri/icons/StoreLogo.png and b/app/src-tauri/icons/StoreLogo.png differ diff --git a/app/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png b/app/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png index 04dfe85..2838e40 100644 Binary files a/app/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png and b/app/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png b/app/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png index aaae622..5afb737 100644 Binary files a/app/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png and b/app/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/app/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png b/app/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png index 592ace4..60a9366 100644 Binary files a/app/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png and b/app/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png b/app/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png index 1ad3a7f..dd65195 100644 Binary files a/app/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png and b/app/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png b/app/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png index 1dfb204..3ca7835 100644 Binary files a/app/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png and b/app/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/app/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png b/app/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png index 1117d8f..6a2b649 100644 Binary files a/app/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png and b/app/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png b/app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png index 98aaf4e..39ae5ec 100644 Binary files a/app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png and b/app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png b/app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png index 1425509..bcb58ef 100644 Binary files a/app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png and b/app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png b/app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png index 27718fc..ec381f6 100644 Binary files a/app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png and b/app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png b/app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png index d58c7f9..ce2846b 100644 Binary files a/app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png and b/app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png index c0c486f..8c57ac4 100644 Binary files a/app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png and b/app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png b/app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png index 9f61409..311dbea 100644 Binary files a/app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png and b/app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png b/app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png index 9b53bd5..b580b1e 100644 Binary files a/app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png and b/app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png index 45f1e04..a8a0abf 100644 Binary files a/app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png and b/app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png b/app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png index fd30ab5..4c49c24 100644 Binary files a/app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png and b/app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src-tauri/icons/icon.icns b/app/src-tauri/icons/icon.icns index e322758..1579093 100644 Binary files a/app/src-tauri/icons/icon.icns and b/app/src-tauri/icons/icon.icns differ diff --git a/app/src-tauri/icons/icon.ico b/app/src-tauri/icons/icon.ico index 8de970e..93fb9df 100644 Binary files a/app/src-tauri/icons/icon.ico and b/app/src-tauri/icons/icon.ico differ diff --git a/app/src-tauri/icons/icon.png b/app/src-tauri/icons/icon.png index f5cc05a..55f8649 100644 Binary files a/app/src-tauri/icons/icon.png and b/app/src-tauri/icons/icon.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-20x20@1x.png b/app/src-tauri/icons/ios/AppIcon-20x20@1x.png index 0dbf028..92a4d63 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-20x20@1x.png and b/app/src-tauri/icons/ios/AppIcon-20x20@1x.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-20x20@2x-1.png b/app/src-tauri/icons/ios/AppIcon-20x20@2x-1.png index db2d4eb..b4445ac 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-20x20@2x-1.png and b/app/src-tauri/icons/ios/AppIcon-20x20@2x-1.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-20x20@2x.png b/app/src-tauri/icons/ios/AppIcon-20x20@2x.png index db2d4eb..b4445ac 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-20x20@2x.png and b/app/src-tauri/icons/ios/AppIcon-20x20@2x.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-20x20@3x.png b/app/src-tauri/icons/ios/AppIcon-20x20@3x.png index 9aaf579..a82ae13 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-20x20@3x.png and b/app/src-tauri/icons/ios/AppIcon-20x20@3x.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-29x29@1x.png b/app/src-tauri/icons/ios/AppIcon-29x29@1x.png index 15b9dc1..a5e07bf 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-29x29@1x.png and b/app/src-tauri/icons/ios/AppIcon-29x29@1x.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-29x29@2x-1.png b/app/src-tauri/icons/ios/AppIcon-29x29@2x-1.png index 4d685d6..e195f42 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-29x29@2x-1.png and b/app/src-tauri/icons/ios/AppIcon-29x29@2x-1.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-29x29@2x.png b/app/src-tauri/icons/ios/AppIcon-29x29@2x.png index 4d685d6..e195f42 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-29x29@2x.png and b/app/src-tauri/icons/ios/AppIcon-29x29@2x.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-29x29@3x.png b/app/src-tauri/icons/ios/AppIcon-29x29@3x.png index 77e8a0d..21494d6 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-29x29@3x.png and b/app/src-tauri/icons/ios/AppIcon-29x29@3x.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-40x40@1x.png b/app/src-tauri/icons/ios/AppIcon-40x40@1x.png index db2d4eb..b4445ac 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-40x40@1x.png and b/app/src-tauri/icons/ios/AppIcon-40x40@1x.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-40x40@2x-1.png b/app/src-tauri/icons/ios/AppIcon-40x40@2x-1.png index 2933045..7ab800e 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-40x40@2x-1.png and b/app/src-tauri/icons/ios/AppIcon-40x40@2x-1.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-40x40@2x.png b/app/src-tauri/icons/ios/AppIcon-40x40@2x.png index 2933045..7ab800e 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-40x40@2x.png and b/app/src-tauri/icons/ios/AppIcon-40x40@2x.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-40x40@3x.png b/app/src-tauri/icons/ios/AppIcon-40x40@3x.png index 174b4c0..14130ef 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-40x40@3x.png and b/app/src-tauri/icons/ios/AppIcon-40x40@3x.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-512@2x.png b/app/src-tauri/icons/ios/AppIcon-512@2x.png index 30a53cc..05c5e72 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-512@2x.png and b/app/src-tauri/icons/ios/AppIcon-512@2x.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-60x60@2x.png b/app/src-tauri/icons/ios/AppIcon-60x60@2x.png index 174b4c0..14130ef 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-60x60@2x.png and b/app/src-tauri/icons/ios/AppIcon-60x60@2x.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-60x60@3x.png b/app/src-tauri/icons/ios/AppIcon-60x60@3x.png index 0a93aba..fc51bf3 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-60x60@3x.png and b/app/src-tauri/icons/ios/AppIcon-60x60@3x.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-76x76@1x.png b/app/src-tauri/icons/ios/AppIcon-76x76@1x.png index 31ebb68..54b90c4 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-76x76@1x.png and b/app/src-tauri/icons/ios/AppIcon-76x76@1x.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-76x76@2x.png b/app/src-tauri/icons/ios/AppIcon-76x76@2x.png index 403e4e2..61acdee 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-76x76@2x.png and b/app/src-tauri/icons/ios/AppIcon-76x76@2x.png differ diff --git a/app/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png b/app/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png index a61f0e5..d030b26 100644 Binary files a/app/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png and b/app/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png differ diff --git a/app/src/img/deskearn.png b/app/src/img/deskearn.png deleted file mode 100644 index 4851a52..0000000 Binary files a/app/src/img/deskearn.png and /dev/null differ diff --git a/site/.gitignore b/site/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/site/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/site/.oxlintrc.json b/site/.oxlintrc.json new file mode 100644 index 0000000..1255078 --- /dev/null +++ b/site/.oxlintrc.json @@ -0,0 +1,8 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["react", "oxc"], + "rules": { + "react/rules-of-hooks": "error", + "react/only-export-components": ["warn", { "allowConstantExport": true }] + } +} diff --git a/site/README.md b/site/README.md new file mode 100644 index 0000000..59e8ef6 --- /dev/null +++ b/site/README.md @@ -0,0 +1,28 @@ +# diskern-site + +Marketing/landing page for [Diskern](https://github.com/Coding-Moves/diskern), deployed to GitHub Pages. + +## Develop + +```sh +npm install +npm run dev +``` + +## Build + +```sh +npm run build # outputs to dist/ +npm run preview # serve the production build locally +``` + +## Deploy + +Pushing to `main` with changes under `site/` triggers +`.github/workflows/deploy-pages.yml`, which builds this project and +publishes `dist/` to GitHub Pages. See the root `docs/` for more. + +The Vite `base` in `vite.config.js` is set to `/diskern/` to match this +repo's GitHub Pages URL (`coding-moves.github.io/diskern/` or your +configured custom domain). If the repo is ever renamed, update `base` +to match. diff --git a/site/index.html b/site/index.html new file mode 100644 index 0000000..a2c7709 --- /dev/null +++ b/site/index.html @@ -0,0 +1,17 @@ + + + + + + + + Diskern — Understand your disk before you clean it + + +
+ + + diff --git a/site/package-lock.json b/site/package-lock.json new file mode 100644 index 0000000..5b90ae2 --- /dev/null +++ b/site/package-lock.json @@ -0,0 +1,1444 @@ +{ + "name": "site", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "site", + "version": "0.0.0", + "dependencies": { + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-router-dom": "^7.18.1" + }, + "devDependencies": { + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.3", + "oxlint": "^1.71.0", + "vite": "^8.1.1" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@oxlint/binding-android-arm-eabi": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.74.0.tgz", + "integrity": "sha512-+gHd12muVI9ZLBaWLPkHt3Fj7jihFjgQ1MGtBaRL8vWrWrI0P7dLUty/cHrHS0oqPYIRgQUJsPu2CExQuMcwNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-android-arm64": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.74.0.tgz", + "integrity": "sha512-xjKdoMB+H+RCOByv/7l7nfIGW9mlOisqYdcyC75UqYuQecLpReAeEYUf2CNeDEI3KtmUgxpRw/+c63y4AeF/Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-arm64": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.74.0.tgz", + "integrity": "sha512-iUK7wvc6sejMKsC+Pt67mntoF5weFcyEunhZfLJceU6gL419mexz5wBkSx/EnkFBExMLNtOi9fnDSc5xfK0IzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-x64": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.74.0.tgz", + "integrity": "sha512-ggKc/tn5SJ1u2yG2izC6VKODfYKV8MQ2AicJlNzOjuyrC29udvOef6/JzK2r32xqCnBDLFouR1VCkjzEI0/N9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-freebsd-x64": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.74.0.tgz", + "integrity": "sha512-u++dH/43jy9hTLbneaWlS0gla/Bp1JdwJ2zgevCl8nDFUh6qRCGMxcL0f0lb7By3A9p/LfFr+7cG4HU1hG856g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-gnueabihf": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.74.0.tgz", + "integrity": "sha512-Sj1zmtFDVTPeIbIz4ZfcXAbFHqCmKCXdCUlAJzvTF7I20NTH1RDpoF2PhkqNODutJzVhJYmm3oz0GwgY+tvE2g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-musleabihf": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.74.0.tgz", + "integrity": "sha512-//PKyQb/tQXcHArx2f7z+oVI/eMS2Jpv+edNuAtOrgIhWdGcpHxogveAxzmF2rpH1AIHp4Hq04RF/rgJdiICnQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-gnu": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.74.0.tgz", + "integrity": "sha512-/k1Me+aX2tjuH10K62mLS0y8cLkJBHX6Ce0xPK+eWeel4bSdEGZ8dv4+hYMzg0GrSmjwy4yAYsDPeEeKBft/2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-musl": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.74.0.tgz", + "integrity": "sha512-3tFSjBxc5D8/zvjEuLvOqcA8ZXKD0+6NuaVO/edeamNc49MoAsbfaC9s1UiwODwgF6slGaF8yJA2TPkukd77tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-ppc64-gnu": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.74.0.tgz", + "integrity": "sha512-9QggtPkSPXOCTu8Szis7auOK/sC7KdQaN+/TujP7YVVhzCAOhgdRfgv8uEz0r2tk5xdgus5rLYUrCDoZNtiRUw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-gnu": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.74.0.tgz", + "integrity": "sha512-VM5VPUJ4DJIWiK+AZn8FScUqMr6OFrCAYybMYjEEi7W13ParI64MByiXTkKMqZpBmvQ9zxl9Ebq2VUOiZRJYUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-musl": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.74.0.tgz", + "integrity": "sha512-SaDY1gh9rOA592J54g+gu5hkOFFQBZsMmIYHs+NRHG+Uq0OxtuuCXMWQ3vu1830Eugv5uMXyjG+bv2Z9y4IXjw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-s390x-gnu": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.74.0.tgz", + "integrity": "sha512-ZATQeHZCyr6MbDveg0obD5sxLHFOghtOdC5jwVwYlvFWqtFOxctgFEG6Ef/64hYvZrWyhyCckB10AelqLopeDA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-gnu": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.74.0.tgz", + "integrity": "sha512-+aIvJyrdeD7LwCQ2WYLMUWNmnbeDRSPb40aBYtPjD9+PTqUwgJnk+HK5yLfSMeqXrMrDhE9uTmtt2y50tvjhHw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-musl": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.74.0.tgz", + "integrity": "sha512-XyktaR8lhK2qWiCK0Tk8oYD+/cgn+oHA6ddRnxSSXUKkkojkV78CmShZUxQF+yrBFs0SuW+JBOPG6hecyc/iZg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-openharmony-arm64": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.74.0.tgz", + "integrity": "sha512-mzbjrPl4neaVUiJ1fUiEUxTGaSZBoiKtaoB6jmIpz9S+VOA2vDYmJpihQ82w6178V5jxziclTg8Cgj5yF6tTDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-arm64-msvc": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.74.0.tgz", + "integrity": "sha512-vUAe9okpS2Oa5+lX67lqHMuNUvfkleRKwrUDJ/WJBsgmddvZ1mrsh2HVmuFDRzqFELhaJhFaCNOuR6a7L3rtIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-ia32-msvc": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.74.0.tgz", + "integrity": "sha512-yyXXJyYYSXL4I8K8jAWjJs+J3fa9gH2JmEbo4f5adm+1tNC9itseicBNuwK7BDHvqQ5J534s+yDULu89vYL2ZQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-x64-msvc": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.74.0.tgz", + "integrity": "sha512-VTC9IYTIMrVUk/i6Ms1ohzzDKZFkWn0KU2OBbPBzgmVZ2V30165T/zK4LztTr0Xgp9fZ1qQZ1rsZAu/rEmySlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.3.tgz", + "integrity": "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/oxlint": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.74.0.tgz", + "integrity": "sha512-odGl2s2x5IOJoj3A0v1k0PGBXVFBZeZ2+AK/+K2MJur7Ghi3bkyX5NuLUWHKqa4js1wjep3hJeuTQJOlr+4+dA==", + "dev": true, + "license": "MIT", + "bin": { + "oxlint": "bin/oxlint" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxlint/binding-android-arm-eabi": "1.74.0", + "@oxlint/binding-android-arm64": "1.74.0", + "@oxlint/binding-darwin-arm64": "1.74.0", + "@oxlint/binding-darwin-x64": "1.74.0", + "@oxlint/binding-freebsd-x64": "1.74.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.74.0", + "@oxlint/binding-linux-arm-musleabihf": "1.74.0", + "@oxlint/binding-linux-arm64-gnu": "1.74.0", + "@oxlint/binding-linux-arm64-musl": "1.74.0", + "@oxlint/binding-linux-ppc64-gnu": "1.74.0", + "@oxlint/binding-linux-riscv64-gnu": "1.74.0", + "@oxlint/binding-linux-riscv64-musl": "1.74.0", + "@oxlint/binding-linux-s390x-gnu": "1.74.0", + "@oxlint/binding-linux-x64-gnu": "1.74.0", + "@oxlint/binding-linux-x64-musl": "1.74.0", + "@oxlint/binding-openharmony-arm64": "1.74.0", + "@oxlint/binding-win32-arm64-msvc": "1.74.0", + "@oxlint/binding-win32-ia32-msvc": "1.74.0", + "@oxlint/binding-win32-x64-msvc": "1.74.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=0.24.0", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.20", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.20.tgz", + "integrity": "sha512-lW616l85ucIQL+FocMmL7pQFPqBmwejrCMg+iPxyImlrANNJG9NHq/RkyCZopDhd8C3LA03PHRJDjkbGu8vvug==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.7" + } + }, + "node_modules/react-router": { + "version": "7.18.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.1.tgz", + "integrity": "sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.18.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.1.tgz", + "integrity": "sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg==", + "license": "MIT", + "dependencies": { + "react-router": "7.18.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/rolldown": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "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", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "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==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/vite": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", + "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.17", + "rolldown": "~1.1.5", + "tinyglobby": "^0.2.17" + }, + "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.3.0", + "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 + } + } + } + } +} diff --git a/site/package.json b/site/package.json new file mode 100644 index 0000000..3a745e6 --- /dev/null +++ b/site/package.json @@ -0,0 +1,24 @@ +{ + "name": "diskern-site", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "oxlint", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-router-dom": "^7.18.1" + }, + "devDependencies": { + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.3", + "oxlint": "^1.71.0", + "vite": "^8.1.1" + } +} diff --git a/site/public/favicon.svg b/site/public/favicon.svg new file mode 100644 index 0000000..edc42da --- /dev/null +++ b/site/public/favicon.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/site/src/App.jsx b/site/src/App.jsx new file mode 100644 index 0000000..f23ffdc --- /dev/null +++ b/site/src/App.jsx @@ -0,0 +1,18 @@ +import { Routes, Route } from 'react-router-dom' +import Layout from './components/Layout.jsx' +import Home from './pages/Home.jsx' +import NotFound from './pages/NotFound.jsx' + +// All routes share (nav + footer). To add a new page later — +// e.g. /docs or /changelog — create src/pages/Docs.jsx and add +// } /> below. +export default function App() { + return ( + + }> + } /> + } /> + + + ) +} diff --git a/site/src/components/Downloads.jsx b/site/src/components/Downloads.jsx new file mode 100644 index 0000000..d8d0913 --- /dev/null +++ b/site/src/components/Downloads.jsx @@ -0,0 +1,139 @@ +import { useMemo } from 'react' +import { useLatestRelease } from '../hooks/useLatestRelease.js' +import { + RELEASES_PAGE, + matchDownloads, + detectOS, + formatBytes, + formatDate, +} from '../lib/releases.js' +import { WindowsIcon, LinuxIcon, DownloadIcon } from './icons.jsx' + +const OS_ICONS = { + windows: WindowsIcon, + linux: LinuxIcon, +} + +export default function Downloads() { + const { status, release } = useLatestRelease() + const visitorOS = useMemo(() => detectOS(), []) + + return ( +
+

Download

+

+ Pick your platform. All builds are unsigned for now — see the release + notes for details. +

+ +
+ {status === 'loading' && } + {status === 'error' && } + {status === 'success' && ( + + )} +
+
+ ) +} + +function LoadingState() { + return ( +
+
+ Fetching the latest release… +
+ ) +} + +function FallbackState() { + return ( +
+

+ Couldn't load the latest release automatically — GitHub's API limits + anonymous requests, or your connection blipped. +

+

+ + + {' '}View all releases on GitHub + +

+
+ ) +} + +function ReleaseState({ release, visitorOS }) { + const downloads = matchDownloads(release.assets) + const version = release.name || release.tag_name + const notes = (release.body || '').trim() + const truncatedNotes = + notes.length > 420 ? `${notes.slice(0, 420).trimEnd()}…` : notes + + return ( + <> +
+ {version} + + Released {formatDate(release.published_at)} + +
+ + {downloads.length === 0 ? ( +
+

+ No installers are attached to this release yet.{' '} + + Check the release page + + . +

+
+ ) : ( +
+ {downloads.map((d) => { + const Icon = OS_ICONS[d.os] ?? DownloadIcon + const recommended = d.os === visitorOS + return ( + + + + {d.label} + + {d.sublabel} + {d.size ? ` · ${formatBytes(d.size)}` : ''} + + + {recommended && You} + + ) + })} +
+ )} + + {visitorOS === 'mac' && ( +

+ A native macOS build isn't available yet — it's on the roadmap. +

+ )} + + {notes && ( + <> +

{truncatedNotes}

+ + Read full release notes on GitHub → + + + )} + + ) +} diff --git a/site/src/components/Footer.jsx b/site/src/components/Footer.jsx new file mode 100644 index 0000000..d87866c --- /dev/null +++ b/site/src/components/Footer.jsx @@ -0,0 +1,17 @@ +export default function Footer() { + return ( + + ) +} diff --git a/site/src/components/Hero.jsx b/site/src/components/Hero.jsx new file mode 100644 index 0000000..894ab81 --- /dev/null +++ b/site/src/components/Hero.jsx @@ -0,0 +1,19 @@ +export default function Hero() { + return ( +
+ Diskern logo +

Diskern

+

Understand your disk before you clean it.

+

+ Diskern scans your drives and shows you exactly where the space went — + biggest folders, duplicate files, old caches — before you delete + anything. It's a read-only analyzer first: nothing gets removed until + you tell it to. +

+
+ ) +} diff --git a/site/src/components/HowItWorks.jsx b/site/src/components/HowItWorks.jsx new file mode 100644 index 0000000..ba5c0d8 --- /dev/null +++ b/site/src/components/HowItWorks.jsx @@ -0,0 +1,48 @@ +import { EyeIcon, CopyIcon, ShieldIcon, NoTrashIcon } from './icons.jsx' + +const FEATURES = [ + { + icon: EyeIcon, + title: 'Read-only scanning', + description: + "Diskern only reads your filesystem to build its picture of what's using space. It never touches a file during a scan.", + }, + { + icon: CopyIcon, + title: 'Duplicate detection', + description: + 'Finds exact duplicate files by content hash, not just name, so you can see what’s actually safe to consolidate.', + }, + { + icon: ShieldIcon, + title: 'Rules-based safety', + description: + 'System and application-critical paths are recognized and flagged, so you don’t accidentally target something the OS needs.', + }, + { + icon: NoTrashIcon, + title: 'Never hard-deletes', + description: + 'Anything you remove goes to your OS trash/recycle bin first — Diskern never permanently erases a file on your behalf.', + }, +] + +export default function HowItWorks() { + return ( +
+

How it works

+

+ Diskern is built to be trustworthy first, fast second. +

+
+ {FEATURES.map(({ icon: Icon, title, description }) => ( +
+ +

{title}

+

{description}

+
+ ))} +
+
+ ) +} diff --git a/site/src/components/Layout.jsx b/site/src/components/Layout.jsx new file mode 100644 index 0000000..d0e19c1 --- /dev/null +++ b/site/src/components/Layout.jsx @@ -0,0 +1,16 @@ +import { Outlet } from 'react-router-dom' +import NavBar from './NavBar.jsx' +import Footer from './Footer.jsx' + +// Shared shell for every route: nav + footer stay put, swaps +// in whichever page matched. Add new routes in App.jsx and they get +// the nav/theme-toggle/footer for free. +export default function Layout() { + return ( +
+ + +
+
+ ) +} diff --git a/site/src/components/NavBar.jsx b/site/src/components/NavBar.jsx new file mode 100644 index 0000000..269707c --- /dev/null +++ b/site/src/components/NavBar.jsx @@ -0,0 +1,49 @@ +import { Link } from 'react-router-dom' +import { useTheme } from '../context/ThemeContext.jsx' +import { SunIcon, MoonIcon, GithubIcon } from './icons.jsx' + +const REPO_URL = 'https://github.com/Coding-Moves/diskern' + +export default function NavBar() { + const { theme, toggleTheme } = useTheme() + + return ( +
+
+ + + Diskern + + +
+ + + + + +
+
+
+ ) +} diff --git a/site/src/components/icons.jsx b/site/src/components/icons.jsx new file mode 100644 index 0000000..0bf2dd8 --- /dev/null +++ b/site/src/components/icons.jsx @@ -0,0 +1,150 @@ +// Small hand-rolled icon set so the site doesn't need an icon-library +// dependency for half a dozen glyphs. Each icon inherits color from +// its parent via `currentColor` and sizes via the CSS on its wrapper. + +export function SunIcon(props) { + return ( + + ) +} + +export function MoonIcon(props) { + return ( + + ) +} + +export function GithubIcon(props) { + return ( + + ) +} + +export function WindowsIcon(props) { + return ( + + ) +} + +export function LinuxIcon(props) { + return ( + + ) +} + +export function DownloadIcon(props) { + return ( + + ) +} + +export function EyeIcon(props) { + return ( + + ) +} + +export function CopyIcon(props) { + return ( + + ) +} + +export function ShieldIcon(props) { + return ( + + ) +} + +export function NoTrashIcon(props) { + return ( + + ) +} diff --git a/site/src/context/ThemeContext.jsx b/site/src/context/ThemeContext.jsx new file mode 100644 index 0000000..90f1f48 --- /dev/null +++ b/site/src/context/ThemeContext.jsx @@ -0,0 +1,34 @@ +import { createContext, useContext, useMemo, useState } from 'react' + +const ThemeContext = createContext(null) + +// Plain React state, not localStorage: the choice lives only for the +// current page load and resets on refresh. That's a deliberate +// tradeoff — localStorage would persist the preference across visits, +// but it also silently fails in some embedded/private-browsing +// contexts, and this is a small marketing site where that edge case +// isn't worth the complexity. If you want persistence later, swap the +// useState below for a small useEffect-backed localStorage hook. +export function ThemeProvider({ children }) { + const [theme, setTheme] = useState('dark') + + const value = useMemo( + () => ({ + theme, + toggleTheme: () => setTheme((t) => (t === 'dark' ? 'light' : 'dark')), + }), + [theme], + ) + + return ( + +
{children}
+
+ ) +} + +export function useTheme() { + const ctx = useContext(ThemeContext) + if (!ctx) throw new Error('useTheme must be used within a ThemeProvider') + return ctx +} diff --git a/site/src/hooks/useLatestRelease.js b/site/src/hooks/useLatestRelease.js new file mode 100644 index 0000000..87e2681 --- /dev/null +++ b/site/src/hooks/useLatestRelease.js @@ -0,0 +1,41 @@ +import { useEffect, useState } from 'react' +import { RELEASES_API } from '../lib/releases.js' + +// Fetches the latest GitHub release once on mount. GitHub's REST API +// allows 60 unauthenticated requests per hour per IP — plenty for a +// visitor loading this page a few times, but easy to exhaust while +// developing locally with fast refresh. When that happens (or the +// network fails, or the repo somehow has no releases yet) `status` +// becomes 'error' and the caller should fall back to linking straight +// at the GitHub Releases page instead of trying to render assets. +export function useLatestRelease() { + const [status, setStatus] = useState('loading') // 'loading' | 'success' | 'error' + const [release, setRelease] = useState(null) + + useEffect(() => { + let cancelled = false + + fetch(RELEASES_API, { + headers: { Accept: 'application/vnd.github+json' }, + }) + .then((res) => { + if (!res.ok) throw new Error(`GitHub API responded ${res.status}`) + return res.json() + }) + .then((data) => { + if (cancelled) return + setRelease(data) + setStatus('success') + }) + .catch(() => { + if (cancelled) return + setStatus('error') + }) + + return () => { + cancelled = true + } + }, []) + + return { status, release } +} diff --git a/site/src/lib/releases.js b/site/src/lib/releases.js new file mode 100644 index 0000000..c26798b --- /dev/null +++ b/site/src/lib/releases.js @@ -0,0 +1,89 @@ +export const REPO = 'Coding-Moves/diskern' +export const RELEASES_API = `https://api.github.com/repos/${REPO}/releases/latest` +export const RELEASES_PAGE = `https://github.com/${REPO}/releases` + +// One entry per download button we're able to offer. `match` looks at +// the release asset's filename (lowercased) and returns true if this +// button should be built from it. Order matters: first match wins, +// so more specific extensions (.appimage) should stay ahead of +// anything that could double-match. +const ASSET_TYPES = [ + { + id: 'windows', + os: 'windows', + label: 'Download for Windows', + sublabel: '.msi installer', + match: (name) => name.endsWith('.msi') || name.endsWith('.exe'), + }, + { + id: 'debian', + os: 'linux', + label: 'Download for Debian/Ubuntu', + sublabel: '.deb package', + match: (name) => name.endsWith('.deb'), + }, + { + id: 'fedora', + os: 'linux', + label: 'Download for Fedora/RHEL', + sublabel: '.rpm package', + match: (name) => name.endsWith('.rpm'), + }, + { + id: 'appimage', + os: 'linux', + label: 'Download for Linux (AppImage)', + sublabel: '.AppImage — runs on most distros', + match: (name) => name.endsWith('.appimage'), + }, +] + +// Builds the list of download buttons to render from a GitHub release's +// `assets` array, matching each known asset type against the first +// release asset whose filename fits. +export function matchDownloads(assets = []) { + return ASSET_TYPES.map((type) => { + const asset = assets.find((a) => type.match(a.name.toLowerCase())) + if (!asset) return null + return { + id: type.id, + os: type.os, + label: type.label, + sublabel: type.sublabel, + url: asset.browser_download_url, + size: asset.size, + } + }).filter(Boolean) +} + +// Best-effort OS guess from the browser, used only to highlight the +// most likely download for the visitor — never to hide the others. +export function detectOS() { + if (typeof navigator === 'undefined') return 'unknown' + const ua = `${navigator.userAgent} ${navigator.platform ?? ''}`.toLowerCase() + if (ua.includes('win')) return 'windows' + if (ua.includes('mac')) return 'mac' + if (ua.includes('linux') || ua.includes('x11')) return 'linux' + return 'unknown' +} + +export function formatBytes(bytes) { + if (!bytes) return '' + const units = ['B', 'KB', 'MB', 'GB'] + let value = bytes + let unitIndex = 0 + while (value >= 1024 && unitIndex < units.length - 1) { + value /= 1024 + unitIndex += 1 + } + return `${value.toFixed(value >= 10 || unitIndex === 0 ? 0 : 1)} ${units[unitIndex]}` +} + +export function formatDate(iso) { + if (!iso) return '' + return new Date(iso).toLocaleDateString(undefined, { + year: 'numeric', + month: 'long', + day: 'numeric', + }) +} diff --git a/site/src/main.jsx b/site/src/main.jsx new file mode 100644 index 0000000..2023922 --- /dev/null +++ b/site/src/main.jsx @@ -0,0 +1,16 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import { BrowserRouter } from 'react-router-dom' +import './styles/index.css' +import App from './App.jsx' +import { ThemeProvider } from './context/ThemeContext.jsx' + +createRoot(document.getElementById('root')).render( + + + + + + + , +) diff --git a/site/src/pages/Home.jsx b/site/src/pages/Home.jsx new file mode 100644 index 0000000..977a75c --- /dev/null +++ b/site/src/pages/Home.jsx @@ -0,0 +1,13 @@ +import Hero from '../components/Hero.jsx' +import Downloads from '../components/Downloads.jsx' +import HowItWorks from '../components/HowItWorks.jsx' + +export default function Home() { + return ( +
+ + + +
+ ) +} diff --git a/site/src/pages/NotFound.jsx b/site/src/pages/NotFound.jsx new file mode 100644 index 0000000..f7963eb --- /dev/null +++ b/site/src/pages/NotFound.jsx @@ -0,0 +1,12 @@ +import { Link } from 'react-router-dom' + +export default function NotFound() { + return ( +
+

Page not found

+

+ Back to the Diskern homepage +

+
+ ) +} diff --git a/site/src/styles/index.css b/site/src/styles/index.css new file mode 100644 index 0000000..7c6389b --- /dev/null +++ b/site/src/styles/index.css @@ -0,0 +1,454 @@ +/* ---------- Theme variables ---------- */ +/* Everything that changes between dark/light lives here as a CSS + variable. Components never hardcode colors — they reference + var(--something), so flipping [data-theme] repaints the whole page. */ + +[data-theme='dark'] { + --bg: #0b0d10; + --bg-elevated: #12161c; + --bg-elevated-hover: #171c24; + --text: #e9edf1; + --text-muted: #9aa4b2; + --accent: #3ddc97; + --accent-strong: #2bb989; + --accent-text: #05130d; + --border: #232a33; + --shadow: 0 8px 24px rgba(0, 0, 0, 0.35); + --danger: #ff6b6b; + color-scheme: dark; +} + +[data-theme='light'] { + --bg: #f7f8fa; + --bg-elevated: #ffffff; + --bg-elevated-hover: #f0f2f5; + --text: #12161c; + --text-muted: #5b6472; + --accent: #1f9d6c; + --accent-strong: #178056; + --accent-text: #ffffff; + --border: #e2e5ea; + --shadow: 0 8px 24px rgba(18, 22, 28, 0.08); + --danger: #d1453b; + color-scheme: light; +} + +/* ---------- Reset ---------- */ + +* { + box-sizing: border-box; +} + +html, +body { + margin: 0; + padding: 0; +} + +body { + font-family: + -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, + sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'; +} + +#root { + min-height: 100vh; +} + +img, +svg { + display: block; + max-width: 100%; +} + +a { + color: inherit; +} + +button { + font: inherit; +} + +/* ---------- App shell ---------- */ + +.app-shell { + min-height: 100vh; + background: var(--bg); + color: var(--text); + transition: + background-color 0.15s ease, + color 0.15s ease; +} + +.container { + width: 100%; + max-width: 1080px; + margin: 0 auto; + padding: 0 24px; +} + +/* ---------- Nav ---------- */ + +.nav { + border-bottom: 1px solid var(--border); + position: sticky; + top: 0; + background: var(--bg); + z-index: 10; +} + +.nav-inner { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px 24px; + max-width: 1080px; + margin: 0 auto; +} + +.brand { + display: flex; + align-items: center; + gap: 10px; + text-decoration: none; + color: var(--text); + font-weight: 700; + font-size: 1.1rem; +} + +.brand-logo { + width: 28px; + height: 28px; + border-radius: 7px; +} + +.nav-actions { + display: flex; + align-items: center; + gap: 8px; +} + +.icon-button { + display: inline-flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 8px; + border: 1px solid var(--border); + background: var(--bg-elevated); + color: var(--text); + cursor: pointer; + transition: background-color 0.15s ease; +} + +.icon-button:hover { + background: var(--bg-elevated-hover); +} + +.icon-button svg { + width: 18px; + height: 18px; +} + +/* ---------- Hero ---------- */ + +.hero { + padding: 88px 24px 64px; + text-align: center; +} + +.hero-logo { + width: 72px; + height: 72px; + border-radius: 16px; + margin: 0 auto 24px; +} + +.hero h1 { + font-size: clamp(2.1rem, 5vw, 3.2rem); + margin: 0 0 12px; + letter-spacing: -0.02em; +} + +.hero-tagline { + font-size: clamp(1.05rem, 2.4vw, 1.3rem); + color: var(--accent); + font-weight: 600; + margin: 0 0 20px; +} + +.hero-description { + max-width: 640px; + margin: 0 auto; + color: var(--text-muted); + font-size: 1.05rem; + line-height: 1.65; +} + +/* ---------- Section shared ---------- */ + +section { + padding: 56px 24px; +} + +.section-title { + text-align: center; + font-size: 1.6rem; + margin: 0 0 8px; +} + +.section-subtitle { + text-align: center; + color: var(--text-muted); + margin: 0 0 40px; + max-width: 560px; + margin-left: auto; + margin-right: auto; +} + +/* ---------- Downloads ---------- */ + +.downloads-card { + max-width: 720px; + margin: 0 auto; + background: var(--bg-elevated); + border: 1px solid var(--border); + border-radius: 16px; + padding: 32px; + box-shadow: var(--shadow); +} + +.downloads-meta { + display: flex; + flex-wrap: wrap; + align-items: baseline; + justify-content: center; + gap: 8px 12px; + text-align: center; + margin-bottom: 6px; +} + +.downloads-version { + font-size: 1.3rem; + font-weight: 700; +} + +.downloads-date { + color: var(--text-muted); + font-size: 0.9rem; +} + +.downloads-notes { + color: var(--text-muted); + font-size: 0.92rem; + line-height: 1.6; + white-space: pre-wrap; + max-height: 130px; + overflow: hidden; + text-align: left; + margin: 20px 0 0; + padding-top: 20px; + border-top: 1px solid var(--border); +} + +.downloads-notes-link { + display: block; + text-align: center; + margin-top: 12px; + font-size: 0.9rem; + color: var(--accent); + text-decoration: none; +} + +.downloads-notes-link:hover { + text-decoration: underline; +} + +.downloads-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 12px; + margin-top: 24px; +} + +.download-button { + display: flex; + align-items: center; + gap: 12px; + padding: 14px 16px; + border-radius: 10px; + border: 1px solid var(--border); + background: var(--bg); + color: var(--text); + text-decoration: none; + font-weight: 600; + font-size: 0.95rem; + transition: + border-color 0.15s ease, + transform 0.1s ease; +} + +.download-button:hover { + transform: translateY(-1px); + border-color: var(--accent); +} + +.download-button.is-recommended { + border-color: var(--accent); + background: color-mix(in srgb, var(--accent) 10%, var(--bg)); +} + +.download-button .os-icon { + width: 22px; + height: 22px; + flex-shrink: 0; +} + +.download-button .download-label { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} + +.download-button .download-sub { + font-weight: 400; + font-size: 0.78rem; + color: var(--text-muted); +} + +.recommended-badge { + display: inline-block; + margin-left: auto; + font-size: 0.7rem; + font-weight: 700; + color: var(--accent-text); + background: var(--accent); + padding: 2px 8px; + border-radius: 999px; + white-space: nowrap; +} + +.downloads-fallback { + text-align: center; + color: var(--text-muted); +} + +.downloads-fallback a { + color: var(--accent); + font-weight: 600; + text-decoration: none; +} + +.downloads-fallback a:hover { + text-decoration: underline; +} + +.downloads-skeleton { + display: flex; + flex-direction: column; + align-items: center; + gap: 14px; + color: var(--text-muted); + padding: 20px 0; +} + +.spinner { + width: 28px; + height: 28px; + border-radius: 50%; + border: 3px solid var(--border); + border-top-color: var(--accent); + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +/* ---------- How it works ---------- */ + +.features-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); + gap: 16px; + max-width: 900px; + margin: 0 auto; +} + +.feature-card { + background: var(--bg-elevated); + border: 1px solid var(--border); + border-radius: 14px; + padding: 22px; +} + +.feature-icon { + width: 32px; + height: 32px; + color: var(--accent); + margin-bottom: 14px; +} + +.feature-card h3 { + margin: 0 0 8px; + font-size: 1.02rem; +} + +.feature-card p { + margin: 0; + color: var(--text-muted); + font-size: 0.92rem; + line-height: 1.55; +} + +/* ---------- Footer ---------- */ + +.footer { + border-top: 1px solid var(--border); + padding: 28px 24px; + text-align: center; + color: var(--text-muted); + font-size: 0.85rem; +} + +.footer a { + color: var(--text-muted); + text-decoration: none; +} + +.footer a:hover { + color: var(--text); + text-decoration: underline; +} + +/* ---------- Not found ---------- */ + +.not-found { + text-align: center; + padding: 120px 24px; +} + +.not-found a { + color: var(--accent); + font-weight: 600; +} + +/* ---------- Responsive ---------- */ + +@media (max-width: 640px) { + .hero { + padding: 56px 20px 48px; + } + + section { + padding: 40px 20px; + } + + .downloads-card { + padding: 22px; + } +} diff --git a/site/vite.config.js b/site/vite.config.js new file mode 100644 index 0000000..1507479 --- /dev/null +++ b/site/vite.config.js @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// `base` must match the repo name because GitHub Pages serves project +// sites from https://.github.io// — every asset URL Vite +// generates gets this prefix. Wrong value = blank page with 404s in +// the browser console. +export default defineConfig({ + base: '/diskern/', + plugins: [react()], +})