feat: generate PWA icons and assets (#33)
Some checks failed
Deploy to Coolify / Deploy to Development (pull_request) Has been cancelled
Deploy to Coolify / Deploy to Production (pull_request) Has been cancelled
Deploy to Coolify / Deploy to Test (pull_request) Has been cancelled
Pull Request Checks / Validate PR (pull_request) Has been cancelled
Deploy to Coolify / Code Quality (pull_request) Has been cancelled
Deploy to Coolify / Run Tests (pull_request) Has been cancelled
Some checks failed
Deploy to Coolify / Deploy to Development (pull_request) Has been cancelled
Deploy to Coolify / Deploy to Production (pull_request) Has been cancelled
Deploy to Coolify / Deploy to Test (pull_request) Has been cancelled
Pull Request Checks / Validate PR (pull_request) Has been cancelled
Deploy to Coolify / Code Quality (pull_request) Has been cancelled
Deploy to Coolify / Run Tests (pull_request) Has been cancelled
- Create icon.svg with pantry shelves design - Generate icon-192x192.png and icon-512x512.png - Generate maskable variants for better Android support - Create favicon.ico and apple-touch-icon.png - Generate placeholder screenshots (mobile + desktop) - Add icon generation scripts using sharp - Add npm script for easy regeneration Icon design features: - Emerald gradient background (#10b981) - Pantry shelves with jars, boxes, and cans - Clean, recognizable silhouette - Works at all sizes Closes #33
This commit is contained in:
70
app/scripts/generate-icons.js
Normal file
70
app/scripts/generate-icons.js
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env node
|
||||
import { readFile, writeFile } from 'fs/promises';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import sharp from 'sharp';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const publicDir = join(__dirname, '..', 'public');
|
||||
const svgPath = join(publicDir, 'icon.svg');
|
||||
|
||||
const sizes = [
|
||||
{ size: 192, name: 'icon-192x192.png', maskable: false },
|
||||
{ size: 512, name: 'icon-512x512.png', maskable: false },
|
||||
{ size: 192, name: 'icon-192x192-maskable.png', maskable: true },
|
||||
{ size: 512, name: 'icon-512x512-maskable.png', maskable: true },
|
||||
];
|
||||
|
||||
async function generateIcons() {
|
||||
console.log('Reading SVG icon...');
|
||||
const svgBuffer = await readFile(svgPath);
|
||||
|
||||
for (const { size, name, maskable } of sizes) {
|
||||
console.log(`Generating ${name}...`);
|
||||
|
||||
let buffer;
|
||||
if (maskable) {
|
||||
// Maskable icons need safe zone padding (80% of icon in center)
|
||||
// Create a transparent canvas with padding
|
||||
const paddedSize = size;
|
||||
const iconSize = Math.floor(size * 0.8);
|
||||
const offset = Math.floor((paddedSize - iconSize) / 2);
|
||||
|
||||
// Resize SVG to icon size
|
||||
const iconBuffer = await sharp(svgBuffer)
|
||||
.resize(iconSize, iconSize)
|
||||
.png()
|
||||
.toBuffer();
|
||||
|
||||
// Create transparent background and composite
|
||||
buffer = await sharp({
|
||||
create: {
|
||||
width: paddedSize,
|
||||
height: paddedSize,
|
||||
channels: 4,
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
||||
}
|
||||
})
|
||||
.composite([{
|
||||
input: iconBuffer,
|
||||
top: offset,
|
||||
left: offset
|
||||
}])
|
||||
.png()
|
||||
.toBuffer();
|
||||
} else {
|
||||
// Regular icon - full size
|
||||
buffer = await sharp(svgBuffer)
|
||||
.resize(size, size)
|
||||
.png()
|
||||
.toBuffer();
|
||||
}
|
||||
|
||||
await writeFile(join(publicDir, name), buffer);
|
||||
console.log(`✓ ${name}`);
|
||||
}
|
||||
|
||||
console.log('\n✅ All icons generated successfully!');
|
||||
}
|
||||
|
||||
generateIcons().catch(console.error);
|
||||
Reference in New Issue
Block a user