From 75c321bad2240a197bc8ec091a780ae68c770454 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 21 Sep 2026 11:22:32 +0200 Subject: [PATCH] feat(pwa): icon build script and committed PNG outputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 8b Task 2. @resvg/resvg-js renders public/icons/source.svg into the four PNG sizes the manifest + iOS expect: 192, 512, maskable-512 (Android safe-zone), and apple-touch-icon-180. Build is one-shot — outputs are committed, runtime doesn't ship the dep. --- package-lock.json | 229 +++++++++++++++++++++++++++++ package.json | 1 + public/icons/apple-touch-icon.png | Bin 0 -> 2137 bytes public/icons/icon-192.png | Bin 0 -> 2357 bytes public/icons/icon-512.png | Bin 0 -> 6498 bytes public/icons/icon-maskable-512.png | Bin 0 -> 6498 bytes scripts/build-icons.ts | 46 ++++++ 7 files changed, 276 insertions(+) create mode 100644 public/icons/apple-touch-icon.png create mode 100644 public/icons/icon-192.png create mode 100644 public/icons/icon-512.png create mode 100644 public/icons/icon-maskable-512.png create mode 100644 scripts/build-icons.ts diff --git a/package-lock.json b/package-lock.json index 4272f71..bf7aee0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "tailwind-merge": "^3.7.0" }, "devDependencies": { + "@resvg/resvg-js": "^2.6.2", "@tailwindcss/postcss": "^4.1.13", "@types/node": "^24.5.2", "@types/react": "^19.2.2", @@ -1819,6 +1820,234 @@ "integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==", "license": "MIT" }, + "node_modules/@resvg/resvg-js": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js/-/resvg-js-2.6.2.tgz", + "integrity": "sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">= 10" + }, + "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" + } + }, + "node_modules/@resvg/resvg-js-android-arm-eabi": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm-eabi/-/resvg-js-android-arm-eabi-2.6.2.tgz", + "integrity": "sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-android-arm64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm64/-/resvg-js-android-arm64-2.6.2.tgz", + "integrity": "sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-darwin-arm64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-arm64/-/resvg-js-darwin-arm64-2.6.2.tgz", + "integrity": "sha512-nmok2LnAd6nLUKI16aEB9ydMC6Lidiiq2m1nEBDR1LaaP7FGs4AJ90qDraxX+CWlVuRlvNjyYJTNv8qFjtL9+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-darwin-x64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-x64/-/resvg-js-darwin-x64-2.6.2.tgz", + "integrity": "sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm-gnueabihf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm-gnueabihf/-/resvg-js-linux-arm-gnueabihf-2.6.2.tgz", + "integrity": "sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm64-gnu": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-gnu/-/resvg-js-linux-arm64-gnu-2.6.2.tgz", + "integrity": "sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm64-musl": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-musl/-/resvg-js-linux-arm64-musl-2.6.2.tgz", + "integrity": "sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-x64-gnu": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-gnu/-/resvg-js-linux-x64-gnu-2.6.2.tgz", + "integrity": "sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-x64-musl": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-musl/-/resvg-js-linux-x64-musl-2.6.2.tgz", + "integrity": "sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-arm64-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-arm64-msvc/-/resvg-js-win32-arm64-msvc-2.6.2.tgz", + "integrity": "sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-ia32-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-ia32-msvc/-/resvg-js-win32-ia32-msvc-2.6.2.tgz", + "integrity": "sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-x64-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-x64-msvc/-/resvg-js-win32-x64-msvc-2.6.2.tgz", + "integrity": "sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.63.3", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.3.tgz", diff --git a/package.json b/package.json index a0379c5..ce44218 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "tailwind-merge": "^3.7.0" }, "devDependencies": { + "@resvg/resvg-js": "^2.6.2", "@tailwindcss/postcss": "^4.1.13", "@types/node": "^24.5.2", "@types/react": "^19.2.2", diff --git a/public/icons/apple-touch-icon.png b/public/icons/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..41958eeeb152582471803d701072a17d0adfb2df GIT binary patch literal 2137 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. + */ + +import { readFileSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import { Resvg } from "@resvg/resvg-js"; + +const root = join(process.cwd(), "public", "icons"); + +const svg = readFileSync(join(root, "source.svg"), "utf8"); + +const targets = [ + { file: "icon-192.png", size: 192 }, + { file: "icon-512.png", size: 512 }, + { file: "icon-maskable-512.png", size: 512 }, + { file: "apple-touch-icon.png", size: 180 }, +]; + +for (const target of targets) { + const png = new Resvg(svg, { + fitTo: { mode: "width", value: target.size }, + }) + .render() + .asPng(); + writeFileSync(join(root, target.file), png); + console.log(` + ${target.file} (${target.size}x${target.size})`); +} + +console.log("Done.");