From 8abdbe7a8f55e0d973bce2b6bd7da0dec65e9049 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Thu, 14 May 2026 19:18:09 +0800 Subject: [PATCH 1/7] feat(website): add release OG image generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Programmatic generator for per-release Open Graph images. Runs locally via `pnpm gen:og --version ` and writes a 2400x1260 (2x retina) PNG into the design-resources repo. Stack: satori (JSX/HTML -> SVG) + @resvg/resvg-js (SVG -> PNG) + satori-html (template) + sharp 8-bit palette re-encoding (~500 KB -> ~140 KB with no visible loss on this design — TinyPNG-style libimagequant quantization). Logo SVG is fetched from the assets.rspack.rs CDN at generation time so logo updates flow through without re-committing the template. Space Grotesk (SIL OFL) is committed under assets/fonts/. Background is a brick pattern of pass / running / fail / skip status icons (lucide circle-check / circle-x / loader / circle-dashed, color-matched to @rstest/browser-ui's status palette). Grounds the og frame in the test-runner identity rather than a generic gradient. Layout: 7x12 grid with corner icons pinned to canvas corners (1/4 visible), first/last row/col half-cropped at edges, odd rows offset by half a column for brick stagger. Per-icon stroke-opacity jitter (0.06-0.26) gives a hand-stippled feel; only the loader gets random rotation. A radialGradient vignette is baked into the bg SVG so the v{version} hero text reads cleanly through the wallpaper. The bg SVG is rasterized via Resvg and embedded as in the satori tree — satori's `background: url(data:...)` shorthand parser doesn't accept SVG data URIs. --- pnpm-lock.yaml | 580 ++++++++++++++++++ pnpm-workspace.yaml | 1 + scripts/dictionary.txt | 4 + website/AGENTS.md | 28 + website/README.md | 4 + website/package.json | 5 + .../scripts/og-image/assets/fonts/LICENSE.txt | 93 +++ .../assets/fonts/SpaceGrotesk-Bold.ttf | Bin 0 -> 116056 bytes .../assets/fonts/SpaceGrotesk-Regular.ttf | Bin 0 -> 114428 bytes website/scripts/og-image/cli.mts | 43 ++ website/scripts/og-image/render.mts | 152 +++++ website/scripts/og-image/template.mts | 111 ++++ 12 files changed, 1021 insertions(+) create mode 100644 website/scripts/og-image/assets/fonts/LICENSE.txt create mode 100644 website/scripts/og-image/assets/fonts/SpaceGrotesk-Bold.ttf create mode 100644 website/scripts/og-image/assets/fonts/SpaceGrotesk-Regular.ttf create mode 100644 website/scripts/og-image/cli.mts create mode 100644 website/scripts/og-image/render.mts create mode 100644 website/scripts/og-image/template.mts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index afb249ab2..8fcad24d7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1307,6 +1307,9 @@ importers: website: devDependencies: + '@resvg/resvg-js': + specifier: ^2.6.2 + version: 2.6.2 '@rsbuild/plugin-sass': specifier: ^1.5.2 version: 1.5.2(@rsbuild/core@2.0.6) @@ -1349,6 +1352,15 @@ importers: rspress-plugin-sitemap: specifier: ^1.2.1 version: 1.2.1 + satori: + specifier: ^0.26.0 + version: 0.26.0 + satori-html: + specifier: ^0.3.2 + version: 0.3.2 + sharp: + specifier: ^0.34.5 + version: 0.34.5 typescript: specifier: ^6.0.3 version: 6.0.3 @@ -1820,6 +1832,159 @@ packages: '@noble/hashes': optional: true + '@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] + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -2755,6 +2920,86 @@ packages: resolution: {integrity: sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==} engines: {node: '>=14.0.0'} + '@resvg/resvg-js-android-arm-eabi@2.6.2': + resolution: {integrity: sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + + '@resvg/resvg-js-android-arm64@2.6.2': + resolution: {integrity: sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@resvg/resvg-js-darwin-arm64@2.6.2': + resolution: {integrity: sha512-nmok2LnAd6nLUKI16aEB9ydMC6Lidiiq2m1nEBDR1LaaP7FGs4AJ90qDraxX+CWlVuRlvNjyYJTNv8qFjtL9+A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@resvg/resvg-js-darwin-x64@2.6.2': + resolution: {integrity: sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@resvg/resvg-js-linux-arm-gnueabihf@2.6.2': + resolution: {integrity: sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@resvg/resvg-js-linux-arm64-gnu@2.6.2': + resolution: {integrity: sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@resvg/resvg-js-linux-arm64-musl@2.6.2': + resolution: {integrity: sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@resvg/resvg-js-linux-x64-gnu@2.6.2': + resolution: {integrity: sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@resvg/resvg-js-linux-x64-musl@2.6.2': + resolution: {integrity: sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@resvg/resvg-js-win32-arm64-msvc@2.6.2': + resolution: {integrity: sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@resvg/resvg-js-win32-ia32-msvc@2.6.2': + resolution: {integrity: sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@resvg/resvg-js-win32-x64-msvc@2.6.2': + resolution: {integrity: sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@resvg/resvg-js@2.6.2': + resolution: {integrity: sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q==} + engines: {node: '>= 10'} + '@rsbuild/core@2.0.6': resolution: {integrity: sha512-0/u7oTgPp9NsL7E7qXzYiOOPAsOJiDbOr0FmG6gizJDIpYK8nospogNrwQ00SG0had9fdhLI7XkhP160IaLnWw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3227,6 +3472,11 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@shuding/opentype.js@1.4.0-beta.0': + resolution: {integrity: sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==} + engines: {node: '>= 8.0.0'} + hasBin: true + '@sinclair/typebox@0.34.33': resolution: {integrity: sha512-5HAV9exOMcXRUxo+9iYB5n09XxzCXnfy4VTNW4xnDv+FgjzAGY989C28BIdljKqmF+ZltUwujE3aossvcVtq6g==} @@ -4160,6 +4410,10 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + base64-js@0.0.8: + resolution: {integrity: sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==} + engines: {node: '>= 0.4'} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -4322,6 +4576,9 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} + camelize@1.0.1: + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} + caniuse-lite@1.0.30001792: resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} @@ -4542,9 +4799,26 @@ packages: cspell-ban-words@0.0.4: resolution: {integrity: sha512-w+18WPFAEmo2F+Fr4L29+GdY5ckOLN95WPwb/arfrtuzzB5VzQRFyIujo0T7pq+xFE0Z2gjfLn33Wk/u5ctNQQ==} + css-background-parser@0.1.0: + resolution: {integrity: sha512-2EZLisiZQ+7m4wwur/qiYJRniHX4K5Tc9w93MT3AS0WS1u5kaZ4FKXlOTBhOjc+CgEgPiGY+fX1yWD8UwpEqUA==} + + css-box-shadow@1.0.0-3: + resolution: {integrity: sha512-9jaqR6e7Ohds+aWwmhe6wILJ99xYQbfmK9QQB9CcMjDbTxPZjwEmUQpU91OG05Xgm8BahT5fW+svbsQGjS/zPg==} + + css-color-keywords@1.0.0: + resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} + engines: {node: '>=4'} + + css-gradient-parser@0.0.17: + resolution: {integrity: sha512-w2Xy9UMMwlKtou0vlRnXvWglPAceXCTtcmVSo8ZBUvqCV5aXEFP/PC6d+I464810I9FT++UACwTD5511bmGPUg==} + engines: {node: '>=16'} + css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + css-to-react-native@3.2.0: + resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} + css-tree@2.2.1: resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -4743,6 +5017,10 @@ packages: elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} + emoji-regex-xs@2.0.1: + resolution: {integrity: sha512-1QFuh8l7LqUcKe24LsPUNzjrzJQ7pgRwp1QMcZ5MX6mFplk2zQ08NVCM84++1cveaUUYtcCYHmeFEuNg16sU4g==} + engines: {node: '>=10.0.0'} + emoji-regex@10.5.0: resolution: {integrity: sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==} @@ -4842,6 +5120,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} @@ -4965,6 +5246,9 @@ packages: picomatch: optional: true + fflate@0.7.4: + resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==} + figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} @@ -5210,6 +5494,10 @@ packages: resolution: {integrity: sha512-VNNu8W4V3Sc64VyLgeQU3uB5PIq0CM4yyD+OKq0zKgMMOOQGftNfCiwDahpFasKYN+RNY9+29jBZUBW8x9O/8A==} hasBin: true + hex-rgb@4.3.0: + resolution: {integrity: sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==} + engines: {node: '>=6'} + hmac-drbg@1.0.1: resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} @@ -5781,6 +6069,9 @@ packages: resolution: {integrity: sha512-Zvpvd56i9FRV5kaJFiiY1t+FNMEH+dGEaLyQprqKlGHBAxJXmdSk+8tVsh6b9YlxbfyyuLrhJCkzwB+AmOBZ0g==} engines: {node: '>=20'} + linebreak@1.1.0: + resolution: {integrity: sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -6365,6 +6656,9 @@ packages: package-manager-detector@1.6.0: resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + pako@0.2.9: + resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} + pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} @@ -6376,6 +6670,9 @@ packages: resolution: {integrity: sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==} engines: {node: '>= 0.10'} + parse-css-color@0.2.1: + resolution: {integrity: sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==} + parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} @@ -6513,6 +6810,9 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.5.14: resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} engines: {node: ^10 || ^12 || >=14} @@ -7069,6 +7369,13 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + satori-html@0.3.2: + resolution: {integrity: sha512-wjTh14iqADFKDK80e51/98MplTGfxz2RmIzh0GqShlf4a67+BooLywF17TvJPD6phO0Hxm7Mf1N5LtRYvdkYRA==} + + satori@0.26.0: + resolution: {integrity: sha512-tkMFrfIs3l2mQ2JEcyW0ADTy3zGggFRFzi6Ef8YozQSFsFKEqaSO1Y8F9wJg4//PJGQauMalHGTUEkPrFwhVPA==} + engines: {node: '>=16'} + sax@1.6.0: resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} engines: {node: '>=11.0.0'} @@ -7135,6 +7442,10 @@ packages: engines: {node: '>= 0.10'} hasBin: true + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -7309,6 +7620,9 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string.prototype.codepointat@0.2.1: + resolution: {integrity: sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==} + string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} @@ -7530,6 +7844,9 @@ packages: resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} engines: {node: '>=0.6.0'} + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + tinybench@5.1.0: resolution: {integrity: sha512-LXKNtFualiKOm6gADe1UXPtf8+Nfn1CtPMEHAT33Fd2YjQatrujkDcK0+4wRC1X6t7fxUDXUs6BsvuIgfkDgDg==} engines: {node: '>=20.0.0'} @@ -7667,6 +7984,9 @@ packages: uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ultrahtml@1.6.0: + resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==} + unbash@3.0.0: resolution: {integrity: sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA==} engines: {node: '>=14'} @@ -7690,6 +8010,9 @@ packages: unhead@2.1.15: resolution: {integrity: sha512-MCt5T90mCWyr3Z6pUCdM9lVRXoMoVBlL7z7U4CYVIiaDiuzad/UCfLuMqz5MeNmpZUgoBCQnrucJimU7EZR+XA==} + unicode-trie@2.0.0: + resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} + unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} @@ -8000,6 +8323,9 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} + yoga-layout@3.2.1: + resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} + zimmerframe@1.1.4: resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} @@ -8554,6 +8880,102 @@ snapshots: '@exodus/bytes@1.15.0': {} + '@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.10.0 + 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 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -9452,6 +9874,57 @@ snapshots: '@remix-run/router@1.23.2': {} + '@resvg/resvg-js-android-arm-eabi@2.6.2': + optional: true + + '@resvg/resvg-js-android-arm64@2.6.2': + optional: true + + '@resvg/resvg-js-darwin-arm64@2.6.2': + optional: true + + '@resvg/resvg-js-darwin-x64@2.6.2': + optional: true + + '@resvg/resvg-js-linux-arm-gnueabihf@2.6.2': + optional: true + + '@resvg/resvg-js-linux-arm64-gnu@2.6.2': + optional: true + + '@resvg/resvg-js-linux-arm64-musl@2.6.2': + optional: true + + '@resvg/resvg-js-linux-x64-gnu@2.6.2': + optional: true + + '@resvg/resvg-js-linux-x64-musl@2.6.2': + optional: true + + '@resvg/resvg-js-win32-arm64-msvc@2.6.2': + optional: true + + '@resvg/resvg-js-win32-ia32-msvc@2.6.2': + optional: true + + '@resvg/resvg-js-win32-x64-msvc@2.6.2': + optional: true + + '@resvg/resvg-js@2.6.2': + optionalDependencies: + '@resvg/resvg-js-android-arm-eabi': 2.6.2 + '@resvg/resvg-js-android-arm64': 2.6.2 + '@resvg/resvg-js-darwin-arm64': 2.6.2 + '@resvg/resvg-js-darwin-x64': 2.6.2 + '@resvg/resvg-js-linux-arm-gnueabihf': 2.6.2 + '@resvg/resvg-js-linux-arm64-gnu': 2.6.2 + '@resvg/resvg-js-linux-arm64-musl': 2.6.2 + '@resvg/resvg-js-linux-x64-gnu': 2.6.2 + '@resvg/resvg-js-linux-x64-musl': 2.6.2 + '@resvg/resvg-js-win32-arm64-msvc': 2.6.2 + '@resvg/resvg-js-win32-ia32-msvc': 2.6.2 + '@resvg/resvg-js-win32-x64-msvc': 2.6.2 + '@rsbuild/core@2.0.6': dependencies: '@rspack/core': 2.0.3(@swc/helpers@0.5.21) @@ -10107,6 +10580,11 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@shuding/opentype.js@1.4.0-beta.0': + dependencies: + fflate: 0.7.4 + string.prototype.codepointat: 0.2.1 + '@sinclair/typebox@0.34.33': {} '@sindresorhus/merge-streams@2.3.0': {} @@ -11187,6 +11665,8 @@ snapshots: balanced-match@4.0.4: {} + base64-js@0.0.8: {} + base64-js@1.5.1: {} base64id@2.0.0: {} @@ -11376,6 +11856,8 @@ snapshots: camelcase@6.3.0: {} + camelize@1.0.1: {} + caniuse-lite@1.0.30001792: {} ccount@2.0.1: {} @@ -11615,6 +12097,14 @@ snapshots: cspell-ban-words@0.0.4: {} + css-background-parser@0.1.0: {} + + css-box-shadow@1.0.0-3: {} + + css-color-keywords@1.0.0: {} + + css-gradient-parser@0.0.17: {} + css-select@5.2.2: dependencies: boolbase: 1.0.0 @@ -11623,6 +12113,12 @@ snapshots: domutils: 3.2.2 nth-check: 2.1.1 + css-to-react-native@3.2.0: + dependencies: + camelize: 1.0.1 + css-color-keywords: 1.0.0 + postcss-value-parser: 4.2.0 + css-tree@2.2.1: dependencies: mdn-data: 2.0.28 @@ -11817,6 +12313,8 @@ snapshots: minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 + emoji-regex-xs@2.0.1: {} + emoji-regex@10.5.0: {} emoji-regex@8.0.0: {} @@ -11916,6 +12414,8 @@ snapshots: escalade@3.2.0: {} + escape-html@1.0.3: {} + escape-string-regexp@2.0.0: {} escape-string-regexp@4.0.0: {} @@ -12051,6 +12551,8 @@ snapshots: optionalDependencies: picomatch: 4.0.4 + fflate@0.7.4: {} + figures@6.1.0: dependencies: is-unicode-supported: 2.1.0 @@ -12387,6 +12889,8 @@ snapshots: heading-case@1.1.0: {} + hex-rgb@4.3.0: {} + hmac-drbg@1.0.1: dependencies: hash.js: 1.1.7 @@ -12955,6 +13459,11 @@ snapshots: dependencies: unicorn-magic: 0.4.0 + linebreak@1.1.0: + dependencies: + base64-js: 0.0.8 + unicode-trie: 2.0.0 + lines-and-columns@1.2.4: {} lines-and-columns@2.0.4: {} @@ -13875,6 +14384,8 @@ snapshots: package-manager-detector@1.6.0: {} + pako@0.2.9: {} + pako@1.0.11: {} parent-module@1.0.1: @@ -13889,6 +14400,11 @@ snapshots: pbkdf2: 3.1.5 safe-buffer: 5.2.1 + parse-css-color@0.2.1: + dependencies: + color-name: 1.1.4 + hex-rgb: 4.3.0 + parse-entities@4.0.2: dependencies: '@types/unist': 2.0.11 @@ -14011,6 +14527,8 @@ snapshots: possible-typed-array-names@1.1.0: {} + postcss-value-parser@4.2.0: {} + postcss@8.5.14: dependencies: nanoid: 3.3.12 @@ -14582,6 +15100,24 @@ snapshots: '@parcel/watcher': 2.5.6 optional: true + satori-html@0.3.2: + dependencies: + ultrahtml: 1.6.0 + + satori@0.26.0: + dependencies: + '@shuding/opentype.js': 1.4.0-beta.0 + css-background-parser: 0.1.0 + css-box-shadow: 1.0.0-3 + css-gradient-parser: 0.0.17 + css-to-react-native: 3.2.0 + emoji-regex-xs: 2.0.1 + escape-html: 1.0.3 + linebreak: 1.1.0 + parse-css-color: 0.2.1 + postcss-value-parser: 4.2.0 + yoga-layout: 3.2.1 + sax@1.6.0: {} saxes@6.0.0: @@ -14653,6 +15189,37 @@ snapshots: safe-buffer: 5.2.1 to-buffer: 1.2.2 + sharp@0.34.5: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.0 + 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 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -14864,6 +15431,8 @@ snapshots: get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 + string.prototype.codepointat@0.2.1: {} + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 @@ -15068,6 +15637,8 @@ snapshots: dependencies: setimmediate: 1.0.5 + tiny-inflate@1.0.3: {} + tinybench@5.1.0: {} tinyexec@1.1.2: {} @@ -15171,6 +15742,8 @@ snapshots: uc.micro@2.1.0: {} + ultrahtml@1.6.0: {} + unbash@3.0.0: {} unconfig-core@7.5.0: @@ -15196,6 +15769,11 @@ snapshots: dependencies: hookable: 6.0.1 + unicode-trie@2.0.0: + dependencies: + pako: 0.2.9 + tiny-inflate: 1.0.3 + unicorn-magic@0.1.0: {} unicorn-magic@0.3.0: {} @@ -15512,6 +16090,8 @@ snapshots: yoctocolors@2.1.2: {} + yoga-layout@3.2.1: {} + zimmerframe@1.1.4: {} zod@4.3.6: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index fe7db8f7c..b8efd04a6 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -13,6 +13,7 @@ allowBuilds: core-js-pure: false keytar: false lefthook: true + sharp: true svelte-preprocess: false autoInstallPeers: false diff --git a/scripts/dictionary.txt b/scripts/dictionary.txt index fb6e5ad04..7b50e3f6e 100644 --- a/scripts/dictionary.txt +++ b/scripts/dictionary.txt @@ -4,6 +4,7 @@ antd apng apos applescript +artboard Asus atrules autodocs @@ -54,6 +55,7 @@ fnames frontends fullhash gzipped +Grotesk icss idents iife @@ -116,7 +118,9 @@ publint pxtorem quasis quxx +rasterizes rebranded +resvg rolldown rootdir rsbuild diff --git a/website/AGENTS.md b/website/AGENTS.md index dee97f8ac..ed1418c09 100644 --- a/website/AGENTS.md +++ b/website/AGENTS.md @@ -14,8 +14,36 @@ This is the documentation website for Rstest, built with [Rspress](https://rspre pnpm dev # Start dev server pnpm build # Build for production pnpm preview # Preview production build +pnpm gen:og # Generate a release Open Graph image (see below) ``` +## Open Graph image generation + +Per-release og images live in [rstackjs/rstack-design-resources](https://github.com/rstackjs/rstack-design-resources) and are served by the `assets.rspack.rs` CDN. The template lives **in this repo** to keep design-resources as a passive PNG store. + +- `scripts/og-image/cli.mts` — entry, parses `--version`/`--description`/`--out` +- `scripts/og-image/render.mts` — fetches the Rstest logo SVG → rasterizes → composes with [satori](https://github.com/vercel/satori) → renders with [@resvg/resvg-js](https://github.com/yisibl/resvg-js) at 2x zoom for retina +- `scripts/og-image/template.mts` — [satori-html](https://github.com/natemoo-re/satori-html) template, modeled after the `Rsbuild og image 1.0` artboard in design-resources + +### Release workflow + +1. Run `pnpm gen:og --version --description ""` from `website/`. Use `--out` to write directly into a local clone of the design-resources repo at `rstest/assets/rstest-og-image-v{version-with-hyphens}.png` (e.g. `v0-5.png`). +2. Commit the PNG in the design-resources repo and open a PR — that repo is the only place release PNGs are stored. +3. After CDN deploy, the per-blog-post URL becomes available at `assets.rspack.rs/rstest/assets/rstest-og-image-v0-5.png`. The `head` function hook in `rspress.config.ts` routes each blog `routePath` to its matching URL. + +### Do + +- Use Space Grotesk (committed under `scripts/og-image/assets/fonts/` with SIL OFL license) +- Render at 2x via `Resvg({ fitTo: { mode: 'zoom', value: 2 } })` so the PNG stays crisp on retina displays +- Re-encode the resvg output as an 8-bit palette PNG via `sharp({ palette: true, quality: 90, ... })` — drops the file from ~500 KB to ~140 KB with no visible loss +- Fetch the logo from the canonical CDN URL at generation time, not from a committed copy + +### Don't + +- Don't depend on packages like `geist` that pull in framework peer deps (`next>=13.2`); commit raw `.ttf` files directly instead +- Don't write generated PNGs into this repo; they belong in design-resources +- Don't bake the logo into a static asset; always fetch the SVG so logo updates propagate automatically + ## Writing style guidelines When writing or editing documentation, follow these principles: diff --git a/website/README.md b/website/README.md index ed81d65a6..e5428200a 100644 --- a/website/README.md +++ b/website/README.md @@ -15,3 +15,7 @@ The same as Rspack: [Writing style guide](https://github.com/web-infra-dev/rspac For images you use in the document, it's better to upload them to the [rstackjs/rstack-design-resources](https://github.com/rstackjs/rstack-design-resources) repository, so the size of the current repository doesn't get too big. After you upload the images there, they will be automatically deployed under the . + +## Open Graph images + +`scripts/og-image/` generates per-release Open Graph images used by the `og:image` and `twitter:image` meta tags on each release blog post. See [AGENTS.md](./AGENTS.md#open-graph-image-generation) for the template architecture and release workflow. diff --git a/website/package.json b/website/package.json index 10b7e7953..4bf0ff584 100644 --- a/website/package.json +++ b/website/package.json @@ -9,9 +9,11 @@ "build": "rspress build", "build:agent-install": "node scripts/buildAgentInstall.mjs", "dev": "rspress dev", + "gen:og": "node --experimental-strip-types scripts/og-image/cli.mts", "preview": "rspress preview" }, "devDependencies": { + "@resvg/resvg-js": "^2.6.2", "@rsbuild/plugin-sass": "^1.5.2", "@rspress/core": "2.0.11", "@rspress/plugin-algolia": "2.0.11", @@ -26,6 +28,9 @@ "rsbuild-plugin-open-graph": "^1.1.2", "rspress-plugin-font-open-sans": "^1.0.3", "rspress-plugin-sitemap": "^1.2.1", + "satori": "^0.26.0", + "satori-html": "^0.3.2", + "sharp": "^0.34.5", "typescript": "^6.0.3" } } diff --git a/website/scripts/og-image/assets/fonts/LICENSE.txt b/website/scripts/og-image/assets/fonts/LICENSE.txt new file mode 100644 index 000000000..cb512b9af --- /dev/null +++ b/website/scripts/og-image/assets/fonts/LICENSE.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Space Grotesk Project Authors (https://github.com/floriankarsten/space-grotesk) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/website/scripts/og-image/assets/fonts/SpaceGrotesk-Bold.ttf b/website/scripts/og-image/assets/fonts/SpaceGrotesk-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f8eb245d1ec7fd02d77f5a9b55ef6afc4c9ed1c1 GIT binary patch literal 116056 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 z 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 literal 0 HcmV?d00001 diff --git a/website/scripts/og-image/assets/fonts/SpaceGrotesk-Regular.ttf b/website/scripts/og-image/assets/fonts/SpaceGrotesk-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..46aa5dada9c4833e55256a4f3437388f42c5f666 GIT binary patch literal 114428 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 [options] + +Options: + --version, -v Release version, e.g. 0.5 (required) + --description Optional tagline rendered below the version + --out, -o Output PNG path + (default: rstest-og-image-v.png in cwd) + --help, -h Show this help + +After generating, commit the PNG to rstackjs/rstack-design-resources +under rstest/assets/ so the assets.rspack.rs CDN can serve it.`); + process.exit(values.help ? 0 : 1); +} + +const versionSlug = values.version.replace(/\./g, '-'); +const outPath = path.resolve( + values.out ?? `rstest-og-image-v${versionSlug}.png`, +); + +const png = await renderOgImage({ + version: values.version, + description: values.description, +}); + +await mkdir(path.dirname(outPath), { recursive: true }); +await writeFile(outPath, png); + +console.log(`Wrote ${outPath} (${(png.length / 1024).toFixed(1)} KB)`); diff --git a/website/scripts/og-image/render.mts b/website/scripts/og-image/render.mts new file mode 100644 index 000000000..893c31edc --- /dev/null +++ b/website/scripts/og-image/render.mts @@ -0,0 +1,152 @@ +import { readFile } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { Resvg } from '@resvg/resvg-js'; +import satori from 'satori'; +import sharp from 'sharp'; +import { + buildTemplate, + CANVAS_HEIGHT, + CANVAS_WIDTH, + type TemplateOptions, +} from './template.mts'; + +const here = path.dirname(fileURLToPath(import.meta.url)); +const fontsDir = path.join(here, 'assets/fonts'); + +const LOGO_URL = 'https://assets.rspack.rs/rstest/rstest-logo.svg'; + +// Lucide-mirrored icon node markup, normalized to a 24x24 viewBox. +// Sources: lucide-react `circle-check`, `circle-x`, `loader`, `circle-dashed` +// — the same icons rendered by @rstest/browser-ui for pass / fail / running / +// skip status, see packages/browser-ui/src/utils/constants.tsx. +const ICON_PATHS = { + pass: '', + fail: '', + running: + '', + skip: '', +} as const; + +type IconKind = keyof typeof ICON_PATHS; + +// Hues mirror @rstest/browser-ui status colors (green/amber/red/gray Geist +// family) so the background reads as "test runner status" at first glance. +const ICON_COLORS: Record = { + pass: '#16a34a', + running: '#d97706', + fail: '#dc2626', + skip: '#71717a', +}; + +const ICON_KINDS: IconKind[] = ['pass', 'running', 'fail', 'skip']; + +// The center vignette is baked into the SVG (rather than layered via CSS) so +// satori's `background` shorthand doesn't have to handle a +// `radial-gradient(...), url(...)` layering with data URIs — which it +// doesn't. +function buildBackgroundSvg(width: number, height: number): string { + // iconSize=70 is the largest value that divides the vertical axis cleanly + // (rowSpacing = 1.5 * 70 = 105, 630 / 105 = 6 → rows=7 with the last row + // landing exactly on y=height). Horizontal can't divide cleanly at the + // same iconSize (1200/105 ≈ 11.43), so cols=12 with colSpacing forced to + // 1200/11 ≈ 109.09 — a ~4px deviation. The integer constraint for both + // axes simultaneously only admits iconSize ≤ 20 (GCD(800, 420) = 20), + // which is too small to read as a test status icon. + const iconSize = 70; + const scale = iconSize / 24; + const rows = 7; + const cols = 12; + const rowSpacing = height / (rows - 1); + const colSpacing = width / (cols - 1); + const OPACITY_BASE = 0.16; + const OPACITY_SPREAD = 0.1; + + let icons = ''; + for (let r = 0; r < rows; r++) { + // Brick pattern: offset by colSpacing/2 (not iconSize/2 — colSpacing is + // wider), intentionally breaking first/last-column alignment on odd rows. + const rowOffset = r % 2 === 1 ? colSpacing / 2 : 0; + const cy = r * rowSpacing; + for (let c = 0; c < cols; c++) { + const cx = c * colSpacing + rowOffset; + if (cx > width + iconSize / 2) continue; + const kind = ICON_KINDS[Math.floor(Math.random() * ICON_KINDS.length)]!; + const color = ICON_COLORS[kind]; + // Only `running` (an 8-spoke loader) reads as "spinner caught mid-tick" + // when rotated. Pass/fail/skip are radially symmetric — rotating them + // just looks like a wobble. + const rotation = + kind === 'running' ? (Math.random() * 360).toFixed(1) : '0'; + const opacity = ( + OPACITY_BASE + + (Math.random() * 2 - 1) * OPACITY_SPREAD + ).toFixed(3); + icons += `${ICON_PATHS[kind]}`; + } + } + + const vignette = ``; + + return `${icons}${vignette}`; +} + +function svgToPngDataUrl(svg: string, width: number): string { + const png = new Resvg(svg, { fitTo: { mode: 'width', value: width } }) + .render() + .asPng(); + return `data:image/png;base64,${png.toString('base64')}`; +} + +async function fetchLogoSvg(): Promise { + const response = await fetch(LOGO_URL); + if (!response.ok) { + throw new Error( + `Failed to fetch logo from ${LOGO_URL}: ${response.status} ${response.statusText}`, + ); + } + return response.text(); +} + +export type RenderOptions = Omit< + TemplateOptions, + 'logoDataUrl' | 'backgroundDataUrl' +>; + +export async function renderOgImage(opts: RenderOptions): Promise { + const [regular, bold, logoSvg] = await Promise.all([ + readFile(path.join(fontsDir, 'SpaceGrotesk-Regular.ttf')), + readFile(path.join(fontsDir, 'SpaceGrotesk-Bold.ttf')), + fetchLogoSvg(), + ]); + + const tree = buildTemplate({ + ...opts, + // 512px source stays crisp when the og PNG is rendered at 2x. + logoDataUrl: svgToPngDataUrl(logoSvg, 512), + backgroundDataUrl: svgToPngDataUrl( + buildBackgroundSvg(CANVAS_WIDTH, CANVAS_HEIGHT), + CANVAS_WIDTH, + ), + }); + + const svg = await satori(tree, { + width: CANVAS_WIDTH, + height: CANVAS_HEIGHT, + fonts: [ + { name: 'Space Grotesk', data: regular, weight: 400, style: 'normal' }, + { name: 'Space Grotesk', data: bold, weight: 700, style: 'normal' }, + ], + }); + + // 2x zoom for retina; 2400x1260 fits Twitter/Facebook's ~5MB / 8192px limits. + const resvg = new Resvg(svg, { fitTo: { mode: 'zoom', value: 2 } }); + + // Re-encode as 8-bit palette PNG via libimagequant (TinyPNG-style lossy + // quantization). The resvg truecolor output is ~500 KB; palette mode drops + // it to ~140 KB with no visible loss on this design (vignette + stroked + // icons quantize cleanly into 256 colors). + return sharp(resvg.render().asPng()) + .png({ palette: true, quality: 90, effort: 10, compressionLevel: 9 }) + .toBuffer(); +} diff --git a/website/scripts/og-image/template.mts b/website/scripts/og-image/template.mts new file mode 100644 index 000000000..9ed1d8263 --- /dev/null +++ b/website/scripts/og-image/template.mts @@ -0,0 +1,111 @@ +import { html } from 'satori-html'; + +export const CANVAS_WIDTH = 1200; +export const CANVAS_HEIGHT = 630; + +export interface TemplateOptions { + version: string; + description?: string; + logoDataUrl: string; + backgroundDataUrl: string; +} + +/** + * The background is a pre-rendered PNG (icons + vignette baked together) + * placed as an absolutely-positioned `` behind the content — satori's + * `background-image: url(...)` shorthand stumbles on data URIs but handles + * `` reliably. + * + * satori quirk: every container needs an explicit `display: flex`, since + * satori treats undeclared display as `none`. + */ +export function buildTemplate({ + version, + description, + logoDataUrl, + backgroundDataUrl, +}: TemplateOptions) { + return html` +
+ +
+ +
+ Rstest +
+
+ +
+ v${version} +
+ +
+ ${description ?? ''} +
+
+ `; +} From 86bc34ffc35ea8d76b1366e2f18b78fa48d5a7d6 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Fri, 15 May 2026 12:01:01 +0800 Subject: [PATCH 2/7] refactor(website): drop sharp from og image generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove `sharp` from the OG image pipeline and emit the raw resvg PNG directly. Users compress the file manually (TinyPNG / Squoosh / ImageOptim) before committing it to design-resources — the CLI now prints a tip with the TinyPNG URL on its own line so terminals make it trivially clickable. - Drop `sharp` devDep + workspace allowBuild entry - Remove libimagequant re-encoding from `render.mts` - Print compression tip + clickable URL in `cli.mts` - Update `website/AGENTS.md` to describe the manual compression step --- pnpm-lock.yaml | 287 ---------------------------- pnpm-workspace.yaml | 1 - website/AGENTS.md | 2 +- website/package.json | 1 - website/scripts/og-image/cli.mts | 4 + website/scripts/og-image/render.mts | 10 +- 6 files changed, 6 insertions(+), 299 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8fcad24d7..6772b3360 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1358,9 +1358,6 @@ importers: satori-html: specifier: ^0.3.2 version: 0.3.2 - sharp: - specifier: ^0.34.5 - version: 0.34.5 typescript: specifier: ^6.0.3 version: 6.0.3 @@ -1832,159 +1829,6 @@ packages: '@noble/hashes': optional: true - '@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] - '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -7442,10 +7286,6 @@ packages: engines: {node: '>= 0.10'} hasBin: true - sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -8880,102 +8720,6 @@ snapshots: '@exodus/bytes@1.15.0': {} - '@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.10.0 - 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 - '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -15189,37 +14933,6 @@ snapshots: safe-buffer: 5.2.1 to-buffer: 1.2.2 - sharp@0.34.5: - dependencies: - '@img/colour': 1.1.0 - detect-libc: 2.1.2 - semver: 7.8.0 - 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 - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b8efd04a6..fe7db8f7c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -13,7 +13,6 @@ allowBuilds: core-js-pure: false keytar: false lefthook: true - sharp: true svelte-preprocess: false autoInstallPeers: false diff --git a/website/AGENTS.md b/website/AGENTS.md index ed1418c09..13ba4570e 100644 --- a/website/AGENTS.md +++ b/website/AGENTS.md @@ -35,7 +35,7 @@ Per-release og images live in [rstackjs/rstack-design-resources](https://github. - Use Space Grotesk (committed under `scripts/og-image/assets/fonts/` with SIL OFL license) - Render at 2x via `Resvg({ fitTo: { mode: 'zoom', value: 2 } })` so the PNG stays crisp on retina displays -- Re-encode the resvg output as an 8-bit palette PNG via `sharp({ palette: true, quality: 90, ... })` — drops the file from ~500 KB to ~140 KB with no visible loss +- Before committing the PNG to design-resources, run it through [TinyPNG](https://tinypng.com) (or Squoosh / ImageOptim / `pngquant`) — the raw resvg output is ~500 KB, palette quantization drops it to ~140 KB with no visible loss - Fetch the logo from the canonical CDN URL at generation time, not from a committed copy ### Don't diff --git a/website/package.json b/website/package.json index 4bf0ff584..0f04fcf01 100644 --- a/website/package.json +++ b/website/package.json @@ -30,7 +30,6 @@ "rspress-plugin-sitemap": "^1.2.1", "satori": "^0.26.0", "satori-html": "^0.3.2", - "sharp": "^0.34.5", "typescript": "^6.0.3" } } diff --git a/website/scripts/og-image/cli.mts b/website/scripts/og-image/cli.mts index 9fd11ebf0..8ec429573 100644 --- a/website/scripts/og-image/cli.mts +++ b/website/scripts/og-image/cli.mts @@ -41,3 +41,7 @@ await mkdir(path.dirname(outPath), { recursive: true }); await writeFile(outPath, png); console.log(`Wrote ${outPath} (${(png.length / 1024).toFixed(1)} KB)`); +console.log( + 'Tip: compress the PNG before committing — typically drops the file to ~1/4 the size with no visible loss.', +); +console.log(' https://tinypng.com (or Squoosh / ImageOptim)'); diff --git a/website/scripts/og-image/render.mts b/website/scripts/og-image/render.mts index 893c31edc..964a61b88 100644 --- a/website/scripts/og-image/render.mts +++ b/website/scripts/og-image/render.mts @@ -3,7 +3,6 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { Resvg } from '@resvg/resvg-js'; import satori from 'satori'; -import sharp from 'sharp'; import { buildTemplate, CANVAS_HEIGHT, @@ -141,12 +140,5 @@ export async function renderOgImage(opts: RenderOptions): Promise { // 2x zoom for retina; 2400x1260 fits Twitter/Facebook's ~5MB / 8192px limits. const resvg = new Resvg(svg, { fitTo: { mode: 'zoom', value: 2 } }); - - // Re-encode as 8-bit palette PNG via libimagequant (TinyPNG-style lossy - // quantization). The resvg truecolor output is ~500 KB; palette mode drops - // it to ~140 KB with no visible loss on this design (vignette + stroked - // icons quantize cleanly into 256 colors). - return sharp(resvg.render().asPng()) - .png({ palette: true, quality: 90, effort: 10, compressionLevel: 9 }) - .toBuffer(); + return resvg.render().asPng(); } From b934bc3c4a026e0f649f667a4271fda870f21ef8 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Fri, 15 May 2026 13:04:10 +0800 Subject: [PATCH 3/7] chore: add Squoosh / Optim / pngquant to cspell dictionary --- scripts/dictionary.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/dictionary.txt b/scripts/dictionary.txt index 7b50e3f6e..616e3d2a9 100644 --- a/scripts/dictionary.txt +++ b/scripts/dictionary.txt @@ -96,6 +96,7 @@ onclosetag onopentag ontext opencode +Optim osascript outbase outro @@ -107,6 +108,7 @@ picocolors pjpeg pluggable pmmmwh +pngquant pnpx postcssrc preact @@ -140,6 +142,7 @@ selfsigned sirv sokra speedscope +Squoosh srcset stacktracey styl From 674f4de5e757fc3053e7c384a42b56cc9c1688fd Mon Sep 17 00:00:00 2001 From: fi3ework Date: Fri, 15 May 2026 13:13:48 +0800 Subject: [PATCH 4/7] docs(website): clarify per-route OG wiring is a follow-up --- website/AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/AGENTS.md b/website/AGENTS.md index 13ba4570e..5346d61d2 100644 --- a/website/AGENTS.md +++ b/website/AGENTS.md @@ -29,7 +29,7 @@ Per-release og images live in [rstackjs/rstack-design-resources](https://github. 1. Run `pnpm gen:og --version --description ""` from `website/`. Use `--out` to write directly into a local clone of the design-resources repo at `rstest/assets/rstest-og-image-v{version-with-hyphens}.png` (e.g. `v0-5.png`). 2. Commit the PNG in the design-resources repo and open a PR — that repo is the only place release PNGs are stored. -3. After CDN deploy, the per-blog-post URL becomes available at `assets.rspack.rs/rstest/assets/rstest-og-image-v0-5.png`. The `head` function hook in `rspress.config.ts` routes each blog `routePath` to its matching URL. +3. After CDN deploy, the PNG is reachable at `assets.rspack.rs/rstest/assets/rstest-og-image-v0-5.png`. Wiring it up per blog `routePath` in `rspress.config.ts` is a separate follow-up — the site currently sets a single static `og:image` via `pluginOpenGraph`. ### Do From 2046f139bcfb96d42b51ec4abd2c1160d0370802 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Fri, 15 May 2026 14:41:02 +0800 Subject: [PATCH 5/7] revert(website): restore diffuse-gradient og image background Roll back the status-icon brick-pattern background introduced in the squashed predecessor to the randomized radial-gradient version. The status-icon pattern was visually too busy; the gradient reads cleaner as a release card. Calibrate AGENTS.md size note: raw resvg output is ~300 KB for the gradient version (the previous ~500 KB / ~140 KB numbers were status-icon era). Keeps drop-sharp, cspell dictionary, and CLI TinyPNG hint changes intact. --- website/AGENTS.md | 2 +- website/scripts/og-image/render.mts | 220 +++++++++++++++----------- website/scripts/og-image/template.mts | 37 ++--- 3 files changed, 140 insertions(+), 119 deletions(-) diff --git a/website/AGENTS.md b/website/AGENTS.md index 5346d61d2..df301d194 100644 --- a/website/AGENTS.md +++ b/website/AGENTS.md @@ -35,7 +35,7 @@ Per-release og images live in [rstackjs/rstack-design-resources](https://github. - Use Space Grotesk (committed under `scripts/og-image/assets/fonts/` with SIL OFL license) - Render at 2x via `Resvg({ fitTo: { mode: 'zoom', value: 2 } })` so the PNG stays crisp on retina displays -- Before committing the PNG to design-resources, run it through [TinyPNG](https://tinypng.com) (or Squoosh / ImageOptim / `pngquant`) — the raw resvg output is ~500 KB, palette quantization drops it to ~140 KB with no visible loss +- Before committing the PNG to design-resources, run it through [TinyPNG](https://tinypng.com) (or Squoosh / ImageOptim / `pngquant`) — the raw resvg output is ~300 KB and palette quantization typically drops it to ~1/4 the size with no visible loss - Fetch the logo from the canonical CDN URL at generation time, not from a committed copy ### Don't diff --git a/website/scripts/og-image/render.mts b/website/scripts/og-image/render.mts index 964a61b88..fe71b00d4 100644 --- a/website/scripts/og-image/render.mts +++ b/website/scripts/og-image/render.mts @@ -3,142 +3,170 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { Resvg } from '@resvg/resvg-js'; import satori from 'satori'; -import { - buildTemplate, - CANVAS_HEIGHT, - CANVAS_WIDTH, - type TemplateOptions, -} from './template.mts'; +import { buildTemplate, type TemplateOptions } from './template.mts'; const here = path.dirname(fileURLToPath(import.meta.url)); const fontsDir = path.join(here, 'assets/fonts'); const LOGO_URL = 'https://assets.rspack.rs/rstest/rstest-logo.svg'; -// Lucide-mirrored icon node markup, normalized to a 24x24 viewBox. -// Sources: lucide-react `circle-check`, `circle-x`, `loader`, `circle-dashed` -// — the same icons rendered by @rstest/browser-ui for pass / fail / running / -// skip status, see packages/browser-ui/src/utils/constants.tsx. -const ICON_PATHS = { - pass: '', - fail: '', - running: - '', - skip: '', -} as const; - -type IconKind = keyof typeof ICON_PATHS; - -// Hues mirror @rstest/browser-ui status colors (green/amber/red/gray Geist -// family) so the background reads as "test runner status" at first glance. -const ICON_COLORS: Record = { - pass: '#16a34a', - running: '#d97706', - fail: '#dc2626', - skip: '#71717a', +// Hues seeded from the rstest mascot palette. Lightness sits mid-range so +// gradients alpha-blend to vivid pastels over white instead of washed-out grey. +const HUE_PALETTE = [ + { h: 160, s: 80, l: 60 }, + { h: 130, s: 70, l: 62 }, + // Yellow's perceived weight is high; cap saturation below the other entries + // so it doesn't dominate the header when it lands there. + { h: 50, s: 75, l: 65 }, + { h: 20, s: 85, l: 65 }, + { h: 220, s: 85, l: 65 }, + { h: 270, s: 75, l: 68 }, +]; + +// Quadrant-anchored blob centers keep gradient mass off the central text +// column (logo, wordmark, v-number) and force multi-blob renders to spread. +type Quadrant = 'TL' | 'TR' | 'BL' | 'BR'; +const QUAD_CENTERS: Record = { + TL: { x: 18, y: 22 }, + TR: { x: 82, y: 22 }, + BL: { x: 18, y: 78 }, + BR: { x: 82, y: 78 }, }; -const ICON_KINDS: IconKind[] = ['pass', 'running', 'fail', 'skip']; - -// The center vignette is baked into the SVG (rather than layered via CSS) so -// satori's `background` shorthand doesn't have to handle a -// `radial-gradient(...), url(...)` layering with data URIs — which it -// doesn't. -function buildBackgroundSvg(width: number, height: number): string { - // iconSize=70 is the largest value that divides the vertical axis cleanly - // (rowSpacing = 1.5 * 70 = 105, 630 / 105 = 6 → rows=7 with the last row - // landing exactly on y=height). Horizontal can't divide cleanly at the - // same iconSize (1200/105 ≈ 11.43), so cols=12 with colSpacing forced to - // 1200/11 ≈ 109.09 — a ~4px deviation. The integer constraint for both - // axes simultaneously only admits iconSize ≤ 20 (GCD(800, 420) = 20), - // which is too small to read as a test status icon. - const iconSize = 70; - const scale = iconSize / 24; - const rows = 7; - const cols = 12; - const rowSpacing = height / (rows - 1); - const colSpacing = width / (cols - 1); - const OPACITY_BASE = 0.16; - const OPACITY_SPREAD = 0.1; - - let icons = ''; - for (let r = 0; r < rows; r++) { - // Brick pattern: offset by colSpacing/2 (not iconSize/2 — colSpacing is - // wider), intentionally breaking first/last-column alignment on odd rows. - const rowOffset = r % 2 === 1 ? colSpacing / 2 : 0; - const cy = r * rowSpacing; - for (let c = 0; c < cols; c++) { - const cx = c * colSpacing + rowOffset; - if (cx > width + iconSize / 2) continue; - const kind = ICON_KINDS[Math.floor(Math.random() * ICON_KINDS.length)]!; - const color = ICON_COLORS[kind]; - // Only `running` (an 8-spoke loader) reads as "spinner caught mid-tick" - // when rotated. Pass/fail/skip are radially symmetric — rotating them - // just looks like a wobble. - const rotation = - kind === 'running' ? (Math.random() * 360).toFixed(1) : '0'; - const opacity = ( - OPACITY_BASE + - (Math.random() * 2 - 1) * OPACITY_SPREAD - ).toFixed(3); - icons += `${ICON_PATHS[kind]}`; - } +function pickQuadrants(n: number): Quadrant[] { + // For 2 blobs always pick a diagonal pair. Adjacent pairs (TL+TR, TR+BR, + // BL+BR, TL+BL) cause two blobs to stack on the same side of the canvas, + // which looks especially bad in `tonal` scheme where both blobs share a + // hue family. + if (n === 2) { + return Math.random() < 0.5 ? ['TL', 'BR'] : ['TR', 'BL']; } + const all: Quadrant[] = ['TL', 'TR', 'BL', 'BR']; + for (let i = all.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + const tmp = all[i]!; + all[i] = all[j]!; + all[j] = tmp; + } + return all.slice(0, n); +} + +function pick(arr: T[]): T { + return arr[Math.floor(Math.random() * arr.length)]!; +} - const vignette = ``; +/** + * Build a soft, diffuse background by stacking 1–3 ellipse-shaped radial + * gradients over a white base, modeled on OpenAI's hero gradients. + * + * The end stop must keep the same hue and only drop alpha. Using the + * `transparent` keyword would interpolate RGB toward black (CSS spec: + * `transparent == rgba(0,0,0,0)`) and produce a grey halo around the + * bright center. + */ +function randomBackground(): string { + const base = pick(HUE_PALETTE); + + const schemeRoll = Math.random(); + const scheme: 'tonal' | 'duo' | 'tri' = + schemeRoll < 0.45 ? 'tonal' : schemeRoll < 0.85 ? 'duo' : 'tri'; + + // Off-axis hues sit 90°–150° from the base. A smaller offset (e.g. cyan + + // 60° = lavender) still reads as "same cool/warm family" and the two blobs + // can feel like the same color when placed close together. + const huePool: number[] = [base.h]; + if (scheme === 'duo' || scheme === 'tri') { + huePool.push((base.h + 90 + Math.round(Math.random() * 60) + 360) % 360); + } + if (scheme === 'tri') { + huePool.push((base.h - 90 - Math.round(Math.random() * 60) + 360) % 360); + } - return `${icons}${vignette}`; + // Weighted toward 1–2 blobs; 3+ overlapping blobs muddies the gradient. + const blobRoll = Math.random(); + const blobCount = blobRoll < 0.35 ? 1 : blobRoll < 0.85 ? 2 : 3; + const quads = pickQuadrants(blobCount); + + // Single-blob renders need a visibility floor — otherwise a small / dim / + // edge-anchored blob can disappear entirely against the white base. + const { minAlpha, maxAlpha, minSize } = + blobCount === 1 + ? { minAlpha: 0.4, maxAlpha: 0.55, minSize: 50 } + : { minAlpha: 0.3, maxAlpha: 0.5, minSize: 30 }; + + const blobs = quads.map((quad) => { + const seedHue = pick(huePool); + const hueShift = Math.round((Math.random() - 0.5) * 30); // ±15° + const h = (seedHue + hueShift + 360) % 360; + const s = clamp(base.s + Math.round((Math.random() - 0.5) * 20), 50, 95); + const l = clamp(base.l + Math.round((Math.random() - 0.5) * 12), 55, 78); + + const center = QUAD_CENTERS[quad]; + const x = Math.round(center.x + (Math.random() - 0.5) * 30); // anchor ±15% + const y = Math.round(center.y + (Math.random() - 0.5) * 25); // anchor ±12.5% + + const w = Math.round(minSize + Math.random() * (85 - minSize)); + const h2 = Math.round(minSize + Math.random() * (85 - minSize)); + + const alpha = (minAlpha + Math.random() * (maxAlpha - minAlpha)).toFixed(2); + // Tighter end-stop keeps the alpha falloff crisp. Long tails (>=80%) + // produce a "dingy beige/grey" halo for warm hues because pale tinted + // pixels read as muddy when they cover a wide area over white. + const endStop = Math.round(55 + Math.random() * 20); // 55% – 75% + + const start = `hsla(${h}, ${s}%, ${l}%, ${alpha})`; + const end = `hsla(${h}, ${s}%, ${l}%, 0)`; + return `radial-gradient(ellipse ${w}% ${h2}% at ${x}% ${y}%, ${start}, ${end} ${endStop}%)`; + }); + return `${blobs.join(', ')}, #ffffff`; } -function svgToPngDataUrl(svg: string, width: number): string { - const png = new Resvg(svg, { fitTo: { mode: 'width', value: width } }) - .render() - .asPng(); - return `data:image/png;base64,${png.toString('base64')}`; +function clamp(v: number, lo: number, hi: number): number { + return Math.max(lo, Math.min(hi, v)); } -async function fetchLogoSvg(): Promise { +async function fetchLogoAsPng(): Promise { const response = await fetch(LOGO_URL); if (!response.ok) { throw new Error( `Failed to fetch logo from ${LOGO_URL}: ${response.status} ${response.statusText}`, ); } - return response.text(); + const svgText = await response.text(); + // satori does not rasterize , so rasterize the logo to PNG + // first. 512px source stays crisp when the og PNG is rendered at 2x. + const resvg = new Resvg(svgText, { fitTo: { mode: 'width', value: 512 } }); + return resvg.render().asPng(); } -export type RenderOptions = Omit< - TemplateOptions, - 'logoDataUrl' | 'backgroundDataUrl' ->; +export type RenderOptions = Omit; export async function renderOgImage(opts: RenderOptions): Promise { - const [regular, bold, logoSvg] = await Promise.all([ + const [regular, bold, logoPng] = await Promise.all([ readFile(path.join(fontsDir, 'SpaceGrotesk-Regular.ttf')), readFile(path.join(fontsDir, 'SpaceGrotesk-Bold.ttf')), - fetchLogoSvg(), + fetchLogoAsPng(), ]); + const logoDataUrl = `data:image/png;base64,${logoPng.toString('base64')}`; const tree = buildTemplate({ ...opts, - // 512px source stays crisp when the og PNG is rendered at 2x. - logoDataUrl: svgToPngDataUrl(logoSvg, 512), - backgroundDataUrl: svgToPngDataUrl( - buildBackgroundSvg(CANVAS_WIDTH, CANVAS_HEIGHT), - CANVAS_WIDTH, - ), + logoDataUrl, + background: randomBackground(), }); const svg = await satori(tree, { - width: CANVAS_WIDTH, - height: CANVAS_HEIGHT, + width: 1200, + height: 630, fonts: [ { name: 'Space Grotesk', data: regular, weight: 400, style: 'normal' }, { name: 'Space Grotesk', data: bold, weight: 700, style: 'normal' }, ], }); - // 2x zoom for retina; 2400x1260 fits Twitter/Facebook's ~5MB / 8192px limits. + // Render at 2x for crisp output on high-DPI displays (Twitter/Facebook + // accept any reasonable aspect-correct size; 2400x1260 is well within their + // ~5MB / 8192px limits). const resvg = new Resvg(svg, { fitTo: { mode: 'zoom', value: 2 } }); return resvg.render().asPng(); } diff --git a/website/scripts/og-image/template.mts b/website/scripts/og-image/template.mts index 9ed1d8263..b0808cf87 100644 --- a/website/scripts/og-image/template.mts +++ b/website/scripts/og-image/template.mts @@ -1,20 +1,23 @@ import { html } from 'satori-html'; -export const CANVAS_WIDTH = 1200; -export const CANVAS_HEIGHT = 630; - export interface TemplateOptions { version: string; description?: string; logoDataUrl: string; - backgroundDataUrl: string; + background: string; } /** - * The background is a pre-rendered PNG (icons + vignette baked together) - * placed as an absolutely-positioned `` behind the content — satori's - * `background-image: url(...)` shorthand stumbles on data URIs but handles - * `` reliably. + * Layout: 1200x630. + * + * Centered column over a white + soft-gradient background: + * - Header row: logo + "Rstest" wordmark side-by-side + * - v{version} (display-sized hero) + * - Description (free-form tagline) + * + * design-resources has no rstest "logo + wordmark" combo SVG yet, so the + * wordmark is rendered separately in Space Grotesk while the logo is fetched + * at runtime from the canonical CDN URL. * * satori quirk: every container needs an explicit `display: flex`, since * satori treats undeclared display as `none`. @@ -23,7 +26,7 @@ export function buildTemplate({ version, description, logoDataUrl, - backgroundDataUrl, + background, }: TemplateOptions) { return html`
-
(arr: T[]): T { * `transparent == rgba(0,0,0,0)`) and produce a grey halo around the * bright center. */ -function randomBackground(): string { +function blobBackground(): string { const base = pick(HUE_PALETTE); const schemeRoll = Math.random(); @@ -125,6 +125,79 @@ function clamp(v: number, lo: number, hi: number): number { return Math.max(lo, Math.min(hi, v)); } +/** + * OpenAI / Stripe / Vercel-style aurora gradient: one oversize saturated + * blob anchored to an outer quadrant, optionally paired with a smaller + * off-hue halo in the diagonally opposite corner. Reads as "single dominant + * color family with a corner-anchored color leak." + * + * Two extents on purpose: primary fills 85%–115% so it dominates the frame + * while keeping directional gradient structure (anything past ~120% reads + * as a flat color block); accent stays at 45%–65% so the color-leak stays + * localized to its corner instead of overlapping with the primary across + * the whole canvas and muddying both into a uniform patch. + * + * Anchors stay in [[QUAD_CENTERS]] (well off the central text column), so + * even at the higher alpha the v-number and wordmark remain readable — + * inverse-square falloff drops the mid-canvas alpha to ~0.05–0.15. + */ +function auroraBackground(): string { + const base = pick(HUE_PALETTE); + + // 70/30: usually a single dominant blob; sometimes pair it with an off-hue + // accent in the diagonally opposite corner for the color-leak effect. + const blobCount = Math.random() < 0.7 ? 1 : 2; + const quads = pickQuadrants(blobCount); + + const blobs = quads.map((quad, i) => { + const isPrimary = i === 0; + + // Primary stays near the seed hue; the accent shifts 90°–150° so it + // reads as a complementary halo rather than a second blob of the same + // family. + const hueShift = isPrimary + ? Math.round((Math.random() - 0.5) * 25) + : 90 + Math.round(Math.random() * 60); + const h = (base.h + hueShift + 360) % 360; + const s = clamp(base.s + 5 + Math.round(Math.random() * 10), 70, 92); + const l = clamp(base.l + Math.round((Math.random() - 0.5) * 10), 55, 70); + + const center = QUAD_CENTERS[quad]; + const x = Math.round(center.x + (Math.random() - 0.5) * 20); + const y = Math.round(center.y + (Math.random() - 0.5) * 20); + + const w = isPrimary + ? Math.round(85 + Math.random() * 30) // 85% – 115% + : Math.round(45 + Math.random() * 20); // 45% – 65% + const h2 = isPrimary + ? Math.round(85 + Math.random() * 30) + : Math.round(45 + Math.random() * 20); + + const alpha = isPrimary + ? (0.55 + Math.random() * 0.2).toFixed(2) // 0.55 – 0.75 + : (0.45 + Math.random() * 0.17).toFixed(2); // 0.45 – 0.62 + + // Primary's softer falloff (68%–82%) lets it cover most of the canvas; + // accent's tighter falloff (55%–72%) keeps the color-leak localized to + // its quadrant, so adjacent blobs don't blend into a uniform patch. + const endStop = isPrimary + ? Math.round(68 + Math.random() * 14) + : Math.round(55 + Math.random() * 17); + + const start = `hsla(${h}, ${s}%, ${l}%, ${alpha})`; + const end = `hsla(${h}, ${s}%, ${l}%, 0)`; + return `radial-gradient(ellipse ${w}% ${h2}% at ${x}% ${y}%, ${start}, ${end} ${endStop}%)`; + }); + return `${blobs.join(', ')}, #ffffff`; +} + +function randomBackground(): string { + // 35/65 — mix in OpenAI-style aurora gradients alongside the original + // multi-blob spread. Aurora reads as a single dominant color family with + // a corner color leak; blob keeps the airy multi-spot diffuse feel. + return Math.random() < 0.35 ? auroraBackground() : blobBackground(); +} + async function fetchLogoAsPng(): Promise { const response = await fetch(LOGO_URL); if (!response.ok) { From d4f61853c8348cb0ade36a5087a2666ee4264344 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Fri, 15 May 2026 15:52:30 +0800 Subject: [PATCH 7/7] refactor(website): tighten gradient blob shape, drop aurora experiment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the aurora background variant — it didn't add enough over the blob gradient to justify a second mode. Tighten the remaining blob gradient: - Cap blob aspect ratio at ~1.4:1. Independent w/h sampling could otherwise produce ~2.8:1 "long bar" shapes. - For elongated blobs (>1.15:1) push the short-axis center to the nearer canvas edge so one long edge clips off-canvas — otherwise both long edges sit inside the frame and the blob reads as a bar floating mid-image. Result is a soft wash spilling in from the edge. - Extract `edgeHugCenter(visibleSemi, anchor)` helper so the wide / tall mirror branches share one implementation. --- website/scripts/og-image/render.mts | 103 +++++++++------------------- 1 file changed, 31 insertions(+), 72 deletions(-) diff --git a/website/scripts/og-image/render.mts b/website/scripts/og-image/render.mts index 4a0521873..118023701 100644 --- a/website/scripts/og-image/render.mts +++ b/website/scripts/og-image/render.mts @@ -64,7 +64,7 @@ function pick(arr: T[]): T { * `transparent == rgba(0,0,0,0)`) and produce a grey halo around the * bright center. */ -function blobBackground(): string { +function randomBackground(): string { const base = pick(HUE_PALETTE); const schemeRoll = Math.random(); @@ -102,11 +102,16 @@ function blobBackground(): string { const l = clamp(base.l + Math.round((Math.random() - 0.5) * 12), 55, 78); const center = QUAD_CENTERS[quad]; - const x = Math.round(center.x + (Math.random() - 0.5) * 30); // anchor ±15% - const y = Math.round(center.y + (Math.random() - 0.5) * 25); // anchor ±12.5% + let x = Math.round(center.x + (Math.random() - 0.5) * 30); // anchor ±15% + let y = Math.round(center.y + (Math.random() - 0.5) * 25); // anchor ±12.5% const w = Math.round(minSize + Math.random() * (85 - minSize)); - const h2 = Math.round(minSize + Math.random() * (85 - minSize)); + // Cap blob aspect ratio at ~1.4:1 so blobs read as soft ovals rather than + // long bars. Independent w/h sampling can otherwise produce ~2.8:1. + const MAX_RATIO = 1.4; + const hLo = Math.max(minSize, w / MAX_RATIO); + const hHi = Math.min(85, w * MAX_RATIO); + const h2 = Math.round(hLo + Math.random() * (hHi - hLo)); const alpha = (minAlpha + Math.random() * (maxAlpha - minAlpha)).toFixed(2); // Tighter end-stop keeps the alpha falloff crisp. Long tails (>=80%) @@ -114,6 +119,17 @@ function blobBackground(): string { // pixels read as muddy when they cover a wide area over white. const endStop = Math.round(55 + Math.random() * 20); // 55% – 75% + // Even within the 1.4:1 cap, an elongated blob whose long edges both sit + // inside the canvas reads as a "bar" floating mid-frame. Hide one long + // edge by pulling the blob's short-axis center toward the nearer canvas + // edge — the result is a soft wash spilling in from that edge. + const ASYM = 1.15; + if (w / h2 > ASYM) { + y = edgeHugCenter((h2 * endStop) / 100, center.y); + } else if (h2 / w > ASYM) { + x = edgeHugCenter((w * endStop) / 100, center.x); + } + const start = `hsla(${h}, ${s}%, ${l}%, ${alpha})`; const end = `hsla(${h}, ${s}%, ${l}%, 0)`; return `radial-gradient(ellipse ${w}% ${h2}% at ${x}% ${y}%, ${start}, ${end} ${endStop}%)`; @@ -126,76 +142,19 @@ function clamp(v: number, lo: number, hi: number): number { } /** - * OpenAI / Stripe / Vercel-style aurora gradient: one oversize saturated - * blob anchored to an outer quadrant, optionally paired with a smaller - * off-hue halo in the diagonally opposite corner. Reads as "single dominant - * color family with a corner-anchored color leak." - * - * Two extents on purpose: primary fills 85%–115% so it dominates the frame - * while keeping directional gradient structure (anything past ~120% reads - * as a flat color block); accent stays at 45%–65% so the color-leak stays - * localized to its corner instead of overlapping with the primary across - * the whole canvas and muddying both into a uniform patch. + * Place a blob's short-axis center near the nearer canvas edge (top vs bottom, + * or left vs right, depending on `anchor`) so one long edge of an elongated + * blob clips off-canvas instead of leaving both long edges visible mid-frame. * - * Anchors stay in [[QUAD_CENTERS]] (well off the central text column), so - * even at the higher alpha the v-number and wordmark remain readable — - * inverse-square falloff drops the mid-canvas alpha to ~0.05–0.15. + * `visibleSemi` is the visible semi-axis along the short axis + * (`shortDim * endStop / 100`). The -8 keeps the gradient core slightly inside + * the canvas so the wash still reads after the edge clip. */ -function auroraBackground(): string { - const base = pick(HUE_PALETTE); - - // 70/30: usually a single dominant blob; sometimes pair it with an off-hue - // accent in the diagonally opposite corner for the color-leak effect. - const blobCount = Math.random() < 0.7 ? 1 : 2; - const quads = pickQuadrants(blobCount); - - const blobs = quads.map((quad, i) => { - const isPrimary = i === 0; - - // Primary stays near the seed hue; the accent shifts 90°–150° so it - // reads as a complementary halo rather than a second blob of the same - // family. - const hueShift = isPrimary - ? Math.round((Math.random() - 0.5) * 25) - : 90 + Math.round(Math.random() * 60); - const h = (base.h + hueShift + 360) % 360; - const s = clamp(base.s + 5 + Math.round(Math.random() * 10), 70, 92); - const l = clamp(base.l + Math.round((Math.random() - 0.5) * 10), 55, 70); - - const center = QUAD_CENTERS[quad]; - const x = Math.round(center.x + (Math.random() - 0.5) * 20); - const y = Math.round(center.y + (Math.random() - 0.5) * 20); - - const w = isPrimary - ? Math.round(85 + Math.random() * 30) // 85% – 115% - : Math.round(45 + Math.random() * 20); // 45% – 65% - const h2 = isPrimary - ? Math.round(85 + Math.random() * 30) - : Math.round(45 + Math.random() * 20); - - const alpha = isPrimary - ? (0.55 + Math.random() * 0.2).toFixed(2) // 0.55 – 0.75 - : (0.45 + Math.random() * 0.17).toFixed(2); // 0.45 – 0.62 - - // Primary's softer falloff (68%–82%) lets it cover most of the canvas; - // accent's tighter falloff (55%–72%) keeps the color-leak localized to - // its quadrant, so adjacent blobs don't blend into a uniform patch. - const endStop = isPrimary - ? Math.round(68 + Math.random() * 14) - : Math.round(55 + Math.random() * 17); - - const start = `hsla(${h}, ${s}%, ${l}%, ${alpha})`; - const end = `hsla(${h}, ${s}%, ${l}%, 0)`; - return `radial-gradient(ellipse ${w}% ${h2}% at ${x}% ${y}%, ${start}, ${end} ${endStop}%)`; - }); - return `${blobs.join(', ')}, #ffffff`; -} - -function randomBackground(): string { - // 35/65 — mix in OpenAI-style aurora gradients alongside the original - // multi-blob spread. Aurora reads as a single dominant color family with - // a corner color leak; blob keeps the airy multi-spot diffuse feel. - return Math.random() < 0.35 ? auroraBackground() : blobBackground(); +function edgeHugCenter(visibleSemi: number, anchor: number): number { + const maxOffset = Math.max(0, visibleSemi - 8); + return anchor < 50 + ? Math.round(Math.random() * maxOffset) + : Math.round(100 - Math.random() * maxOffset); } async function fetchLogoAsPng(): Promise {