diff --git a/package.json b/package.json index b6d5842..4267de3 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,11 @@ "scripts": { "build:app": "tauri build", "build:vite": "tsc && vite build", + "build:web": "cd website && tsc && vite build", "clean:app": "cd src-tauri && cargo clean", "dev:app": "tauri dev", "dev:app:cpu": "tauri dev -- --no-default-features", + "dev:web": "cd website && pnpm dev", "build:app:cpu": "tauri build -- --no-default-features", "build:app:cuda": "tauri build --config src-tauri/tauri.cuda.conf.json", "dev:vite": "vite", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4192a50..943920b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -76,6 +76,43 @@ importers: specifier: ^7.0.4 version: 7.3.1(jiti@2.6.1)(lightningcss@1.32.0) + website: + dependencies: + framer-motion: + specifier: ^12.38.0 + version: 12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: + specifier: ^19.1.0 + version: 19.2.4 + react-dom: + specifier: ^19.1.0 + version: 19.2.4(react@19.2.4) + devDependencies: + '@tailwindcss/vite': + specifier: ^4.2.2 + version: 4.2.2(vite@7.3.1(jiti@2.6.1)(lightningcss@1.32.0)) + '@types/react': + specifier: ^19.1.8 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.1.6 + version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: ^4.6.0 + version: 4.7.0(vite@7.3.1(jiti@2.6.1)(lightningcss@1.32.0)) + tailwindcss: + specifier: ^4.2.2 + version: 4.2.2 + typescript: + specifier: ~5.8.3 + version: 5.8.3 + vite: + specifier: ^7.0.4 + version: 7.3.1(jiti@2.6.1)(lightningcss@1.32.0) + vite-imagetools: + specifier: ^10.0.0 + version: 10.0.0(rollup@4.60.1)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.32.0)) + packages: '@babel/code-frame@7.29.0': @@ -161,6 +198,9 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + '@esbuild/aix-ppc64@0.27.7': resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} @@ -317,6 +357,159 @@ packages: cpu: [x64] os: [win32] + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -336,6 +529,15 @@ packages: '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@rollup/pluginutils@5.4.0': + resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/rollup-android-arm-eabi@4.60.1': resolution: {integrity: sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==} cpu: [arm] @@ -770,6 +972,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -805,6 +1010,10 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + imagetools-core@9.1.0: + resolution: {integrity: sha512-xQjs+2vrxLnAjCq+omuNkd5UQTld9/bP8+YT0LyYTlKfuSQtgUBvqhUwGugzSAh6sCdN+LnROMuLswn5hZ9Fhg==} + engines: {node: '>=20.0.0'} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -955,6 +1164,15 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + semver@7.8.4: + resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -984,6 +1202,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + vite-imagetools@10.0.0: + resolution: {integrity: sha512-+83L32YPU/2BOHWhudO2+9T5HBvb3+0qHoUNN7fb0+XcAoXilx7aE25cDPWU5kBi5Yc750zYCvHxgfyR+tAuMA==} + engines: {node: '>=22.0.0'} + peerDependencies: + vite: '>=7.0.0' + vite@7.3.1: resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1159,6 +1383,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + '@esbuild/aix-ppc64@0.27.7': optional: true @@ -1237,6 +1466,102 @@ snapshots: '@esbuild/win32-x64@0.27.7': optional: true + '@img/colour@1.1.0': {} + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.11.1 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -1258,6 +1583,14 @@ snapshots: '@rolldown/pluginutils@1.0.0-beta.27': {} + '@rollup/pluginutils@5.4.0(rollup@4.60.1)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.4 + optionalDependencies: + rollup: 4.60.1 + '@rollup/rollup-android-arm-eabi@4.60.1': optional: true @@ -1599,6 +1932,8 @@ snapshots: escalade@3.2.0: {} + estree-walker@2.0.2: {} + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -1619,6 +1954,8 @@ snapshots: graceful-fs@4.2.11: {} + imagetools-core@9.1.0: {} + jiti@2.6.1: {} js-tokens@4.0.0: {} @@ -1750,6 +2087,39 @@ snapshots: semver@6.3.1: {} + semver@7.8.4: {} + + sharp@0.34.5: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.4 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + source-map-js@1.2.1: {} tailwindcss@4.2.2: {} @@ -1771,6 +2141,15 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + vite-imagetools@10.0.0(rollup@4.60.1)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.32.0)): + dependencies: + '@rollup/pluginutils': 5.4.0(rollup@4.60.1) + imagetools-core: 9.1.0 + sharp: 0.34.5 + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.32.0) + transitivePeerDependencies: + - rollup + vite@7.3.1(jiti@2.6.1)(lightningcss@1.32.0): dependencies: esbuild: 0.27.7 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 5ed0b5a..6cf92ca 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,2 +1,6 @@ +packages: + - website + allowBuilds: esbuild: true + sharp: true diff --git a/website/assets/screenshots/phokus_duplicates.png b/website/assets/screenshots/phokus_duplicates.png new file mode 100644 index 0000000..c3bb0db Binary files /dev/null and b/website/assets/screenshots/phokus_duplicates.png differ diff --git a/website/assets/screenshots/phokus_exploreClusters.png b/website/assets/screenshots/phokus_exploreClusters.png new file mode 100644 index 0000000..87f1103 Binary files /dev/null and b/website/assets/screenshots/phokus_exploreClusters.png differ diff --git a/website/assets/screenshots/phokus_galleryHero.jpg b/website/assets/screenshots/phokus_galleryHero.jpg new file mode 100644 index 0000000..30e63bd Binary files /dev/null and b/website/assets/screenshots/phokus_galleryHero.jpg differ diff --git a/website/assets/screenshots/phokus_lightboxWithTags.jpg b/website/assets/screenshots/phokus_lightboxWithTags.jpg new file mode 100644 index 0000000..47093ad Binary files /dev/null and b/website/assets/screenshots/phokus_lightboxWithTags.jpg differ diff --git a/website/assets/screenshots/phokus_semanticSearch.jpg b/website/assets/screenshots/phokus_semanticSearch.jpg new file mode 100644 index 0000000..60da1c3 Binary files /dev/null and b/website/assets/screenshots/phokus_semanticSearch.jpg differ diff --git a/website/assets/screenshots/phokus_timeline.jpg b/website/assets/screenshots/phokus_timeline.jpg new file mode 100644 index 0000000..f88f376 Binary files /dev/null and b/website/assets/screenshots/phokus_timeline.jpg differ diff --git a/website/assets/screenshots/phokus_videoPlayer.jpg b/website/assets/screenshots/phokus_videoPlayer.jpg new file mode 100644 index 0000000..4a380f0 Binary files /dev/null and b/website/assets/screenshots/phokus_videoPlayer.jpg differ diff --git a/website/index.html b/website/index.html new file mode 100644 index 0000000..e8b35d4 --- /dev/null +++ b/website/index.html @@ -0,0 +1,34 @@ + + + + + + + Phokus — your local media library, made searchable + + + + + + + + + + + + + + +
+ + + diff --git a/website/package.json b/website/package.json new file mode 100644 index 0000000..3013b1f --- /dev/null +++ b/website/package.json @@ -0,0 +1,27 @@ +{ + "name": "@phokus/website", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "framer-motion": "^12.38.0", + "react": "^19.1.0", + "react-dom": "^19.1.0" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.2.2", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react": "^4.6.0", + "tailwindcss": "^4.2.2", + "typescript": "~5.8.3", + "vite": "^7.0.4", + "vite-imagetools": "^10.0.0" + } +} diff --git a/website/public/brand/phokus-mark.svg b/website/public/brand/phokus-mark.svg new file mode 100644 index 0000000..aca9cc6 --- /dev/null +++ b/website/public/brand/phokus-mark.svg @@ -0,0 +1,15 @@ + + Phokus + + + + + + + + + + + + diff --git a/website/src/App.tsx b/website/src/App.tsx new file mode 100644 index 0000000..e77be81 --- /dev/null +++ b/website/src/App.tsx @@ -0,0 +1,478 @@ +import { PhokusMark } from "./components/PhokusMark"; +// Screenshots are 2560×1600 masters; imagetools downscales + transcodes to +// AVIF/WebP at build (see the Shot helper). `as=picture` must be the last query +// param so the `*as=picture` module declaration matches. +import heroShot from "../assets/screenshots/phokus_galleryHero.jpg?w=1600&format=avif;webp&quality=50&as=picture"; +import semanticShot from "../assets/screenshots/phokus_semanticSearch.jpg?w=1600&format=avif;webp&quality=50&as=picture"; +import exploreShot from "../assets/screenshots/phokus_exploreClusters.png?w=1600&format=avif;webp&quality=50&as=picture"; +import timelineShot from "../assets/screenshots/phokus_timeline.jpg?w=1600&format=avif;webp&quality=50&as=picture"; +import lightboxShot from "../assets/screenshots/phokus_lightboxWithTags.jpg?w=1600&format=avif;webp&quality=50&as=picture"; +import videoShot from "../assets/screenshots/phokus_videoPlayer.jpg?w=1600&format=avif;webp&quality=50&as=picture"; +import duplicatesShot from "../assets/screenshots/phokus_duplicates.png?w=1600&format=avif;webp&quality=50&as=picture"; + +const REPO_URL = "https://github.com/JezzWTF/phokus"; +const DOWNLOAD_URL = "https://github.com/JezzWTF/phokus/releases/latest"; + +function GitHubIcon({ className }: { className?: string }) { + return ( + + ); +} + +// Renders an imagetools `?as=picture` import: AVIF + WebP sources with the WebP +// fallback on the . `display:contents` so the layout/rounding lives on the +// , exactly as a bare would. width/height come from the transcode to +// reserve space and avoid layout shift. +type PictureImport = { sources: Record; img: { src: string; w: number; h: number } }; + +function Shot({ + pic, + alt, + className, + ariaHidden, +}: { + pic: PictureImport; + alt: string; + className?: string; + ariaHidden?: boolean; +}) { + return ( + + {Object.entries(pic.sources).map(([type, srcSet]) => ( + + ))} + {alt} + + ); +} + +function Header() { + return ( +
+
+ + + Phokus + + + + Download + +
+
+ ); +} + +function Hero() { + return ( +
+ {/* Product leads: the gallery dominates the right and bleeds off the frame, + cropped so the workspace appears to continue beyond the viewport. */} +
+ +
+
+
+
+ +
+
+

+ + Local-first · Windows desktop +

+

+ Your local media library, +
+ made searchable. +

+

+ Browse, understand, and curate large image and video folders — semantic search, visual + discovery, and duplicate cleanup over the files already on your computer. +

+ +

+ Windows 10/11 · Open source · On-device processing · No account +

+
+
+ + {/* Mobile/tablet: product sits below the copy, still bleeding off the right. */} +
+ +
+
+ ); +} + +function LocalFirst() { + const points = [ + { + title: "Your files stay put", + body: "Phokus reads the folders you point it at. Media and everything it derives — thumbnails, embeddings, tags, ratings — never leave your machine.", + }, + { + title: "On-device intelligence", + body: "Semantic search and AI tagging run locally. The models download once on first use; after that, search works offline.", + }, + { + title: "No account, no cloud", + body: "No sign-up, no telemetry, no server. The only network traffic is the one-time model downloads and checking for updates.", + }, + ]; + return ( +
+ +
+
+ +

+ A library that stays on your machine. +

+

+ Phokus runs entirely on your computer. Nothing is uploaded, and there's nothing to sign + into — your photos and everything Phokus learns about them stay with you. +

+
+
+ {points.map((p) => ( +
+

{p.title}

+

{p.body}

+
+ ))} +
+
+
+ ); +} + +function SectionEyebrow({ label }: { label: string }) { + return ( + + + {label} + + ); +} + +function SearchSection() { + return ( + + ); +} + +// Recurring aperture motif. One mark per section, vertically centered and bleeding +// off the LEFT or RIGHT edge, sides alternating down the page — a clean zigzag that +// never lets two marks collide at a section boundary (corner placement did). +// +// The iris is the full mark, but with blades reconstructed to meet the rim +// *tangentially* — each blade is an arc internally tangent to the rim (radius 3.93, +// center on the line out to the tip), so it kisses the circle and peels inward to +// the opening instead of crossing it. The shipped logo's blades use radius 10 +// (== rim), which reads fine at icon size but looks "broken" blown up huge; this is +// the same mark, geometrically clean at any scale. +const WM_BLADE = "M0,-4.18 A3.93,3.93 0 0 1 6.43,-7.66"; + +function EdgeMark({ side }: { side: "left" | "right" }) { + const place = side === "left" ? "-left-[19rem]" : "-right-[19rem]"; + return ( + + ); +} + +function ExploreSection() { + return ( +
+ +
+
+
+ +

+ See the shape of everything you've shot. +

+

+ Explore groups your library into visual clusters — sets of similar shots you can open and + wander, gathered by what they look like rather than where they're filed. A tag cloud + surfaces recurring themes, and the Timeline walks everything by the date it was taken. +

+
+
+ +
+
+
+ +
+
+
+ ); +} + +function CurateSection() { + return ( +
+ +
+
+
+ +
+
+ +

+ Rate, tag, and dig in — without losing your place. +

+

+ Open anything full-screen with zoom and pan, set ratings and favourites, edit tags, or + search within an image for look-alikes. Video gets a proper player too — scrubbing, + volume, speed, and fullscreen. +

+
+
+
+ +
+
+
+ ); +} + +function CleanupSection() { + return ( +
+ +
+
+ +

+ Find exact duplicates and reclaim the space. +

+

+ A three-pass scan — size, then a sample hash, then a full hash — groups byte-identical files + so you can review each set and bulk-delete with confidence. Nothing is removed without your + say-so. +

+
+
+ +
+
+
+ ); +} + +function TechFactsSection() { + const facts = [ + { k: "Platform", v: "Windows 10 / 11 (x64). The installer fetches the WebView2 runtime if it's missing." }, + { k: "Media", v: "Common image formats plus video — thumbnails and metadata come from a bundled FFmpeg." }, + { k: "On-device AI", v: "CLIP embeddings power semantic search (~580 MB); an optional WD tagger (~1.3 GB) adds tags." }, + { k: "Inference", v: "CPU / DirectML in the standard build; an optional CUDA build accelerates NVIDIA GPUs." }, + { k: "Storage", v: "A local SQLite database and thumbnail cache in your app-data folder. Nothing in the cloud." }, + { k: "License & build", v: "MIT, open source. The installer is currently unsigned — see the note below." }, + ]; + return ( +
+
+ +

+ What you're actually running. +

+
+
+ {facts.map((f) => ( +
+
{f.k}
+
{f.v}
+
+ ))} +
+
+ ); +} + +function DownloadSection() { + return ( +
+ +
+ +

+ Point Phokus at a folder. +

+

+ Free and open source. Your library is indexed on your machine — the download is just the app. +

+ +

+ Heads up: this build isn't code-signed yet, so + Windows SmartScreen will warn that the publisher is unrecognised — choose{" "} + More info → Run anyway. An NVIDIA CUDA build is on the + releases page too. +

+
+
+ ); +} + +function Footer() { + return ( + + ); +} + +export function App() { + return ( +
+
+
+ + + + + + + + +
+
+
+ ); +} diff --git a/website/src/components/PhokusMark.tsx b/website/src/components/PhokusMark.tsx new file mode 100644 index 0000000..11ee6fd --- /dev/null +++ b/website/src/components/PhokusMark.tsx @@ -0,0 +1,33 @@ +// Phokus aperture mark — copied (not imported) from the desktop app so the +// website stays decoupled from the Tauri renderer. Inherits currentColor. +const BLADE = "M0,-4.18 A10,10 0 0 1 6.43,-7.66"; + +export function PhokusMark({ + className, + dotClassName, +}: { + className?: string; + dotClassName?: string; +}) { + return ( + + ); +} diff --git a/website/src/images.d.ts b/website/src/images.d.ts new file mode 100644 index 0000000..f18b931 --- /dev/null +++ b/website/src/images.d.ts @@ -0,0 +1,9 @@ +// Typing for vite-imagetools `?as=picture` imports. The query ends in `as=picture`, +// so this wildcard matches each screenshot import. Shape mirrors imagetools' Picture. +declare module "*as=picture" { + const picture: { + sources: Record; + img: { src: string; w: number; h: number }; + }; + export default picture; +} diff --git a/website/src/main.tsx b/website/src/main.tsx new file mode 100644 index 0000000..c42978a --- /dev/null +++ b/website/src/main.tsx @@ -0,0 +1,13 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { App } from "./App"; +import "./styles/index.css"; + +const root = document.getElementById("root"); +if (!root) throw new Error("Root element #root not found"); + +createRoot(root).render( + + + , +); diff --git a/website/src/styles/index.css b/website/src/styles/index.css new file mode 100644 index 0000000..8c1c5dc --- /dev/null +++ b/website/src/styles/index.css @@ -0,0 +1,37 @@ +@import "tailwindcss"; + +/* Phokus brand tokens — near-black surfaces, hairline edges, one amber accent + (the iris focal point). Borrowed from the app, not copied literally. */ +@theme { + --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif; + --font-display: "Space Grotesk", "Inter", ui-sans-serif, sans-serif; + + --color-canvas: #0a0b0d; + --color-surface: #0f1116; + --color-surface-2: #161922; + --color-edge: #ffffff14; + --color-edge-strong: #ffffff2b; + --color-ink: #e8e9ec; + --color-muted: #9a9ca3; + --color-faint: #6b6e76; + --color-amber: #e6a23c; + --color-amber-soft: #f0bd6f; +} + +html { + scroll-behavior: smooth; +} + +body { + background-color: var(--color-canvas); + color: var(--color-ink); + font-family: var(--font-sans); + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } +} diff --git a/website/src/vite-env.d.ts b/website/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/website/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/website/tsconfig.json b/website/tsconfig.json new file mode 100644 index 0000000..4ea7409 --- /dev/null +++ b/website/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noEmit": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "isolatedModules": true, + "moduleDetection": "force", + "useDefineForClassFields": true + }, + "include": ["src", "vite.config.ts"] +} diff --git a/website/vite.config.ts b/website/vite.config.ts new file mode 100644 index 0000000..1a4d0bd --- /dev/null +++ b/website/vite.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import tailwindcss from "@tailwindcss/vite"; +import { imagetools } from "vite-imagetools"; + +// Static product site for phokus.jezz.wtf. Isolated from the Tauri renderer — +// no imports from ../src. Built and served by Ctrl on the Oracle VM. +// Screenshots are imported with ?as=picture and transcoded to AVIF/WebP at build. +export default defineConfig({ + plugins: [react(), tailwindcss(), imagetools({ removeMetadata: true })], +});