Add PWA icon generation and Grimoire logo component (#214)
* feat: add Grimoire logo SVG and generate PWA assets
- Add logo.svg with the official Grimoire logo and gradient
- Create GrimoireLogo React component for use in the app
- Add scripts/generate-pwa-icons.mjs to generate all PWA icons from SVG
- Regenerate all favicon and PWA icons from the new logo
- Update mobile welcome screen to show the logo instead of text
* feat: use transparent backgrounds for PWA icons and add theme gradient option
- Update generate-pwa-icons.mjs to output PNGs with transparent backgrounds
- Add gradient prop to GrimoireLogo component ("original" or "theme")
- Theme gradient matches text-grimoire-gradient CSS (yellow -> orange -> purple -> cyan)
- Mobile welcome screen now uses theme gradient to match ASCII art
* feat: use original gradient for mobile welcome logo
---------
Co-authored-by: Claude <noreply@anthropic.com>
557
package-lock.json
generated
@@ -104,6 +104,7 @@
|
||||
"jsdom": "^27.4.0",
|
||||
"postcss": "^8.4.49",
|
||||
"prettier": "^3.7.4",
|
||||
"sharp": "^0.34.5",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "~5.6.2",
|
||||
"typescript-eslint": "^8.18.2",
|
||||
@@ -857,6 +858,17 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
|
||||
"integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
|
||||
@@ -1579,6 +1591,496 @@
|
||||
"url": "https://github.com/sponsors/nzakas"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/colour": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
|
||||
"integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
|
||||
"integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
|
||||
"integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-riscv64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
|
||||
"integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
|
||||
"integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
|
||||
"integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-ppc64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
|
||||
"integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-riscv64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
|
||||
"integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-riscv64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-s390x": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
|
||||
"integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-wasm32": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
|
||||
"integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-ia32": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
|
||||
"integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
@@ -6793,6 +7295,16 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-node-es": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||
@@ -11152,6 +11664,51 @@
|
||||
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
||||
"integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@img/colour": "^1.0.0",
|
||||
"detect-libc": "^2.1.2",
|
||||
"semver": "^7.7.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
"preview": "vite preview",
|
||||
"test": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
"test:run": "vitest run"
|
||||
"test:run": "vitest run",
|
||||
"generate-icons": "node scripts/generate-pwa-icons.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
@@ -112,6 +113,7 @@
|
||||
"jsdom": "^27.4.0",
|
||||
"postcss": "^8.4.49",
|
||||
"prettier": "^3.7.4",
|
||||
"sharp": "^0.34.5",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "~5.6.2",
|
||||
"typescript-eslint": "^8.18.2",
|
||||
|
||||
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 422 B |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 209 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 288 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 3.4 KiB |
10
public/logo.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="121" height="160" viewBox="0 0 121 160" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M99.9028 38.8215C100.25 39.0958 100.453 39.5134 100.453 39.9553V62.7095C100.453 63.5082 99.8036 64.1556 99.0027 64.1556H81.8124C81.0115 64.1556 80.3622 63.5082 80.3622 62.7095V49.645C80.3622 49.2031 80.1596 48.7854 79.8122 48.5111L51.1258 25.8633C50.5984 25.4469 49.853 25.4469 49.3256 25.8633L20.6406 48.5111C20.2932 48.7854 20.0906 49.2031 20.0906 49.645V110.352C20.0906 110.794 20.2932 111.212 20.6406 111.486L49.3256 134.134C49.853 134.55 50.5984 134.55 51.1258 134.134L72.9219 116.925C73.6317 116.364 73.6587 115.299 72.9782 114.704L60.3109 103.619C59.3516 102.78 59.869 101.203 61.1403 101.091L80.3924 99.4036C80.9294 99.3566 81.3958 99.0163 81.6036 98.5203L89.0687 80.6918C89.565 79.5066 91.2487 79.5066 91.7449 80.6918L99.2101 98.5203C99.4178 99.0163 99.8843 99.3566 100.421 99.4036L119.675 101.091C120.946 101.203 121.464 102.78 120.504 103.619L105.912 116.389C105.511 116.74 105.336 117.281 105.455 117.799L109.8 136.764C110.086 138.012 108.726 138.987 107.631 138.32L91.8717 128.724C91.3531 128.408 90.6922 128.448 90.2159 128.824L51.1258 159.688C50.5984 160.104 49.853 160.104 49.3256 159.688L0.550024 121.179C0.202601 120.904 0 120.487 0 120.045V39.9553C0 39.5134 0.202601 39.0957 0.550024 38.8215L49.3256 0.312306C49.853 -0.104099 50.5984 -0.104102 51.1258 0.312296L99.9028 38.8215Z" fill="url(#paint0_radial_11084_22058)"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_11084_22058" cx="0" cy="0" r="1" gradientTransform="matrix(201.667 256.092 -193.67 266.667 -8.78247e-06 -0.459781)" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F9913E"/>
|
||||
<stop offset="0.480769" stop-color="#A05CF6"/>
|
||||
<stop offset="1" stop-color="#7188F3"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
256
scripts/generate-pwa-icons.mjs
Normal file
@@ -0,0 +1,256 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Generate PWA icons and favicons from the Grimoire logo SVG.
|
||||
*
|
||||
* Usage: node scripts/generate-pwa-icons.mjs
|
||||
*
|
||||
* Requires: npm install --save-dev sharp
|
||||
*
|
||||
* This script generates:
|
||||
* - favicon-16x16.png
|
||||
* - favicon-32x32.png
|
||||
* - favicon-192x192.png (PWA icon)
|
||||
* - favicon-512x512.png (PWA icon)
|
||||
* - favicon-192x192-maskable.png (PWA maskable icon with padding)
|
||||
* - favicon-512x512-maskable.png (PWA maskable icon with padding)
|
||||
* - apple-touch-icon.png (180x180)
|
||||
* - favicon.ico (multi-resolution ico file)
|
||||
*/
|
||||
|
||||
import sharp from "sharp";
|
||||
import { readFileSync, writeFileSync } from "fs";
|
||||
import { join, dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT_DIR = join(__dirname, "..");
|
||||
const PUBLIC_DIR = join(ROOT_DIR, "public");
|
||||
const LOGO_PATH = join(PUBLIC_DIR, "logo.svg");
|
||||
|
||||
// Read the SVG file
|
||||
const svgBuffer = readFileSync(LOGO_PATH);
|
||||
|
||||
// Icon sizes to generate
|
||||
const STANDARD_SIZES = [16, 32, 192, 512];
|
||||
const MASKABLE_SIZES = [192, 512];
|
||||
const APPLE_TOUCH_SIZE = 180;
|
||||
|
||||
// Transparent background for the icons
|
||||
const TRANSPARENT = { r: 0, g: 0, b: 0, alpha: 0 };
|
||||
|
||||
/**
|
||||
* Generate a standard icon (logo fills most of the space with small padding)
|
||||
*/
|
||||
async function generateStandardIcon(size, outputName) {
|
||||
// Add 10% padding on each side for standard icons
|
||||
const padding = Math.round(size * 0.1);
|
||||
const logoSize = size - padding * 2;
|
||||
|
||||
// Calculate logo dimensions maintaining aspect ratio (121:160)
|
||||
const aspectRatio = 121 / 160;
|
||||
const logoHeight = logoSize;
|
||||
const logoWidth = Math.round(logoHeight * aspectRatio);
|
||||
|
||||
// Center the logo
|
||||
const left = Math.round((size - logoWidth) / 2);
|
||||
const top = Math.round((size - logoHeight) / 2);
|
||||
|
||||
const resizedLogo = await sharp(svgBuffer)
|
||||
.resize(logoWidth, logoHeight, {
|
||||
fit: "contain",
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
||||
})
|
||||
.toBuffer();
|
||||
|
||||
await sharp({
|
||||
create: {
|
||||
width: size,
|
||||
height: size,
|
||||
channels: 4,
|
||||
background: TRANSPARENT,
|
||||
},
|
||||
})
|
||||
.composite([{ input: resizedLogo, left, top }])
|
||||
.png()
|
||||
.toFile(join(PUBLIC_DIR, outputName));
|
||||
|
||||
console.log(`Generated: ${outputName} (${size}x${size})`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a maskable icon (logo with more padding for safe zone)
|
||||
* Maskable icons need the important content within the "safe zone" (center 80%)
|
||||
*/
|
||||
async function generateMaskableIcon(size, outputName) {
|
||||
// Maskable icons need 20% padding (40% total safe zone margin)
|
||||
const padding = Math.round(size * 0.2);
|
||||
const logoSize = size - padding * 2;
|
||||
|
||||
// Calculate logo dimensions maintaining aspect ratio (121:160)
|
||||
const aspectRatio = 121 / 160;
|
||||
const logoHeight = logoSize;
|
||||
const logoWidth = Math.round(logoHeight * aspectRatio);
|
||||
|
||||
// Center the logo
|
||||
const left = Math.round((size - logoWidth) / 2);
|
||||
const top = Math.round((size - logoHeight) / 2);
|
||||
|
||||
const resizedLogo = await sharp(svgBuffer)
|
||||
.resize(logoWidth, logoHeight, {
|
||||
fit: "contain",
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
||||
})
|
||||
.toBuffer();
|
||||
|
||||
await sharp({
|
||||
create: {
|
||||
width: size,
|
||||
height: size,
|
||||
channels: 4,
|
||||
background: TRANSPARENT,
|
||||
},
|
||||
})
|
||||
.composite([{ input: resizedLogo, left, top }])
|
||||
.png()
|
||||
.toFile(join(PUBLIC_DIR, outputName));
|
||||
|
||||
console.log(`Generated: ${outputName} (${size}x${size}, maskable)`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate favicon.ico with multiple resolutions
|
||||
*/
|
||||
async function generateFavicon() {
|
||||
// Generate 16x16, 32x32, and 48x48 versions for the .ico file
|
||||
const sizes = [16, 32, 48];
|
||||
const pngBuffers = [];
|
||||
|
||||
for (const size of sizes) {
|
||||
const padding = Math.round(size * 0.1);
|
||||
const logoSize = size - padding * 2;
|
||||
const aspectRatio = 121 / 160;
|
||||
const logoHeight = logoSize;
|
||||
const logoWidth = Math.round(logoHeight * aspectRatio);
|
||||
const left = Math.round((size - logoWidth) / 2);
|
||||
const top = Math.round((size - logoHeight) / 2);
|
||||
|
||||
const resizedLogo = await sharp(svgBuffer)
|
||||
.resize(logoWidth, logoHeight, {
|
||||
fit: "contain",
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
||||
})
|
||||
.toBuffer();
|
||||
|
||||
const buffer = await sharp({
|
||||
create: {
|
||||
width: size,
|
||||
height: size,
|
||||
channels: 4,
|
||||
background: TRANSPARENT,
|
||||
},
|
||||
})
|
||||
.composite([{ input: resizedLogo, left, top }])
|
||||
.png()
|
||||
.toBuffer();
|
||||
|
||||
pngBuffers.push({ size, buffer });
|
||||
}
|
||||
|
||||
// Create a simple ICO file manually
|
||||
// ICO format: https://en.wikipedia.org/wiki/ICO_(file_format)
|
||||
const icoBuffer = createIcoFromPngs(pngBuffers);
|
||||
writeFileSync(join(PUBLIC_DIR, "favicon.ico"), icoBuffer);
|
||||
console.log("Generated: favicon.ico (16x16, 32x32, 48x48)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ICO file from PNG buffers
|
||||
*/
|
||||
function createIcoFromPngs(pngBuffers) {
|
||||
const numImages = pngBuffers.length;
|
||||
|
||||
// Calculate total size
|
||||
let dataOffset = 6 + numImages * 16; // Header (6) + Directory entries (16 each)
|
||||
const imageData = [];
|
||||
|
||||
for (const { size, buffer } of pngBuffers) {
|
||||
imageData.push({
|
||||
size,
|
||||
buffer,
|
||||
offset: dataOffset,
|
||||
});
|
||||
dataOffset += buffer.length;
|
||||
}
|
||||
|
||||
// Create the ICO buffer
|
||||
const totalSize = dataOffset;
|
||||
const ico = Buffer.alloc(totalSize);
|
||||
let offset = 0;
|
||||
|
||||
// ICO Header
|
||||
ico.writeUInt16LE(0, offset); // Reserved
|
||||
offset += 2;
|
||||
ico.writeUInt16LE(1, offset); // Type (1 = ICO)
|
||||
offset += 2;
|
||||
ico.writeUInt16LE(numImages, offset); // Number of images
|
||||
offset += 2;
|
||||
|
||||
// Directory entries
|
||||
for (const { size, buffer, offset: dataOff } of imageData) {
|
||||
ico.writeUInt8(size === 256 ? 0 : size, offset); // Width (0 means 256)
|
||||
offset += 1;
|
||||
ico.writeUInt8(size === 256 ? 0 : size, offset); // Height (0 means 256)
|
||||
offset += 1;
|
||||
ico.writeUInt8(0, offset); // Color palette
|
||||
offset += 1;
|
||||
ico.writeUInt8(0, offset); // Reserved
|
||||
offset += 1;
|
||||
ico.writeUInt16LE(1, offset); // Color planes
|
||||
offset += 2;
|
||||
ico.writeUInt16LE(32, offset); // Bits per pixel
|
||||
offset += 2;
|
||||
ico.writeUInt32LE(buffer.length, offset); // Image size
|
||||
offset += 4;
|
||||
ico.writeUInt32LE(dataOff, offset); // Image offset
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
// Image data
|
||||
for (const { buffer } of imageData) {
|
||||
buffer.copy(ico, offset);
|
||||
offset += buffer.length;
|
||||
}
|
||||
|
||||
return ico;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("Generating PWA icons from logo.svg...\n");
|
||||
|
||||
// Generate standard icons
|
||||
for (const size of STANDARD_SIZES) {
|
||||
const name =
|
||||
size === 16 || size === 32
|
||||
? `favicon-${size}x${size}.png`
|
||||
: `favicon-${size}x${size}.png`;
|
||||
await generateStandardIcon(size, name);
|
||||
}
|
||||
|
||||
// Generate maskable icons
|
||||
for (const size of MASKABLE_SIZES) {
|
||||
await generateMaskableIcon(size, `favicon-${size}x${size}-maskable.png`);
|
||||
}
|
||||
|
||||
// Generate Apple Touch Icon
|
||||
await generateStandardIcon(APPLE_TOUCH_SIZE, "apple-touch-icon.png");
|
||||
|
||||
// Generate favicon.ico
|
||||
await generateFavicon();
|
||||
|
||||
console.log("\nAll icons generated successfully!");
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error("Error generating icons:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import { Terminal } from "lucide-react";
|
||||
import { Button } from "./ui/button";
|
||||
import { Kbd, KbdGroup } from "./ui/kbd";
|
||||
import { Progress } from "./ui/progress";
|
||||
import { GrimoireLogo } from "./ui/grimoire-logo";
|
||||
import { MONTHLY_GOAL_SATS } from "@/services/supporters";
|
||||
import { useLiveQuery } from "dexie-react-hooks";
|
||||
import db from "@/services/db";
|
||||
@@ -86,12 +87,10 @@ export function GrimoireWelcome({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Mobile: Simple text */}
|
||||
<div className="md:hidden text-center">
|
||||
<h1 className="text-4xl font-bold text-grimoire-gradient mb-2">
|
||||
grimoire
|
||||
</h1>
|
||||
<p className="text-muted-foreground text-sm font-mono">
|
||||
{/* Mobile: Logo with gradient */}
|
||||
<div className="md:hidden flex flex-col items-center">
|
||||
<GrimoireLogo size={120} />
|
||||
<p className="text-muted-foreground text-sm font-mono mt-4">
|
||||
a nostr client for magicians
|
||||
</p>
|
||||
</div>
|
||||
|
||||
78
src/components/ui/grimoire-logo.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useId } from "react";
|
||||
|
||||
interface GrimoireLogoProps {
|
||||
className?: string;
|
||||
size?: number;
|
||||
/**
|
||||
* Which gradient style to use:
|
||||
* - "original": The original radial gradient (orange -> purple -> blue)
|
||||
* - "theme": Linear gradient matching text-grimoire-gradient (yellow -> orange -> purple -> cyan)
|
||||
*/
|
||||
gradient?: "original" | "theme";
|
||||
}
|
||||
|
||||
/**
|
||||
* Grimoire logo with the signature gradient.
|
||||
* The logo shape is a stylized pentagram/grimoire icon with a star element.
|
||||
*/
|
||||
export function GrimoireLogo({
|
||||
className,
|
||||
size = 160,
|
||||
gradient = "original",
|
||||
}: GrimoireLogoProps) {
|
||||
// Maintain original aspect ratio (121:160)
|
||||
const width = (size * 121) / 160;
|
||||
const height = size;
|
||||
|
||||
// Use unique ID to avoid conflicts when multiple logos are on the page
|
||||
const gradientId = useId();
|
||||
|
||||
return (
|
||||
<svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox="0 0 121 160"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={cn(className)}
|
||||
aria-label="Grimoire logo"
|
||||
>
|
||||
<path
|
||||
d="M99.9028 38.8215C100.25 39.0958 100.453 39.5134 100.453 39.9553V62.7095C100.453 63.5082 99.8036 64.1556 99.0027 64.1556H81.8124C81.0115 64.1556 80.3622 63.5082 80.3622 62.7095V49.645C80.3622 49.2031 80.1596 48.7854 79.8122 48.5111L51.1258 25.8633C50.5984 25.4469 49.853 25.4469 49.3256 25.8633L20.6406 48.5111C20.2932 48.7854 20.0906 49.2031 20.0906 49.645V110.352C20.0906 110.794 20.2932 111.212 20.6406 111.486L49.3256 134.134C49.853 134.55 50.5984 134.55 51.1258 134.134L72.9219 116.925C73.6317 116.364 73.6587 115.299 72.9782 114.704L60.3109 103.619C59.3516 102.78 59.869 101.203 61.1403 101.091L80.3924 99.4036C80.9294 99.3566 81.3958 99.0163 81.6036 98.5203L89.0687 80.6918C89.565 79.5066 91.2487 79.5066 91.7449 80.6918L99.2101 98.5203C99.4178 99.0163 99.8843 99.3566 100.421 99.4036L119.675 101.091C120.946 101.203 121.464 102.78 120.504 103.619L105.912 116.389C105.511 116.74 105.336 117.281 105.455 117.799L109.8 136.764C110.086 138.012 108.726 138.987 107.631 138.32L91.8717 128.724C91.3531 128.408 90.6922 128.448 90.2159 128.824L51.1258 159.688C50.5984 160.104 49.853 160.104 49.3256 159.688L0.550024 121.179C0.202601 120.904 0 120.487 0 120.045V39.9553C0 39.5134 0.202601 39.0957 0.550024 38.8215L49.3256 0.312306C49.853 -0.104099 50.5984 -0.104102 51.1258 0.312296L99.9028 38.8215Z"
|
||||
fill={`url(#${gradientId})`}
|
||||
/>
|
||||
<defs>
|
||||
{gradient === "original" ? (
|
||||
<radialGradient
|
||||
id={gradientId}
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientTransform="matrix(201.667 256.092 -193.67 266.667 -8.78247e-06 -0.459781)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#F9913E" />
|
||||
<stop offset="0.480769" stopColor="#A05CF6" />
|
||||
<stop offset="1" stopColor="#7188F3" />
|
||||
</radialGradient>
|
||||
) : (
|
||||
<linearGradient
|
||||
id={gradientId}
|
||||
x1="60.5"
|
||||
y1="0"
|
||||
x2="60.5"
|
||||
y2="160"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
{/* Matches text-grimoire-gradient CSS variables */}
|
||||
<stop offset="0%" stopColor="rgb(var(--gradient-1))" />
|
||||
<stop offset="33%" stopColor="rgb(var(--gradient-2))" />
|
||||
<stop offset="66%" stopColor="rgb(var(--gradient-3))" />
|
||||
<stop offset="100%" stopColor="rgb(var(--gradient-4))" />
|
||||
</linearGradient>
|
||||
)}
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||