ice/scripts/generate-icons.js
Deco Vander 6661fff1b9 Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-06 00:49:25 -04:00

102 lines
No EOL
3.9 KiB
JavaScript

#!/usr/bin/env node
/**
* Simple PWA icon generator for Great Lakes Ice Report
* Creates basic icons using Canvas API for different sizes
*/
const fs = require('fs');
const path = require('path');
// Icon sizes needed for PWA
const SIZES = [72, 96, 128, 144, 152, 192, 384, 512];
const MASKABLE_SIZES = [192, 512];
// Create icons directory if it doesn't exist
const iconsDir = path.join(__dirname, '../public/icons');
if (!fs.existsSync(iconsDir)) {
fs.mkdirSync(iconsDir, { recursive: true });
}
/**
* Generate SVG icon content
*/
function generateSVG(size, isMaskable = false) {
const padding = isMaskable ? size * 0.1 : 0; // 10% padding for maskable icons
const iconSize = size - (padding * 2);
const iconOffset = padding;
return `<?xml version="1.0" encoding="UTF-8"?>
<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#2196F3;stop-opacity:1" />
<stop offset="100%" style="stop-color:#1976D2;stop-opacity:1" />
</linearGradient>
</defs>
<!-- Background circle -->
<circle cx="${size/2}" cy="${size/2}" r="${size/2}" fill="url(#bg)"/>
<!-- Ice crystal/snowflake icon -->
<g transform="translate(${iconOffset + iconSize/2}, ${iconOffset + iconSize/2})">
<!-- Main cross -->
<line x1="${-iconSize*0.3}" y1="0" x2="${iconSize*0.3}" y2="0" stroke="white" stroke-width="${iconSize*0.08}" stroke-linecap="round"/>
<line x1="0" y1="${-iconSize*0.3}" x2="0" y2="${iconSize*0.3}" stroke="white" stroke-width="${iconSize*0.08}" stroke-linecap="round"/>
<!-- Diagonal lines -->
<line x1="${-iconSize*0.21}" y1="${-iconSize*0.21}" x2="${iconSize*0.21}" y2="${iconSize*0.21}" stroke="white" stroke-width="${iconSize*0.06}" stroke-linecap="round"/>
<line x1="${iconSize*0.21}" y1="${-iconSize*0.21}" x2="${-iconSize*0.21}" y2="${iconSize*0.21}" stroke="white" stroke-width="${iconSize*0.06}" stroke-linecap="round"/>
<!-- Small decorative elements -->
<circle cx="0" cy="0" r="${iconSize*0.04}" fill="white"/>
<circle cx="${iconSize*0.15}" cy="0" r="${iconSize*0.02}" fill="white"/>
<circle cx="${-iconSize*0.15}" cy="0" r="${iconSize*0.02}" fill="white"/>
<circle cx="0" cy="${iconSize*0.15}" r="${iconSize*0.02}" fill="white"/>
<circle cx="0" cy="${-iconSize*0.15}" r="${iconSize*0.02}" fill="white"/>
</g>
${isMaskable ? `<!-- Safe zone indicator (invisible) -->
<circle cx="${size/2}" cy="${size/2}" r="${size*0.4}" fill="none" stroke="none"/>` : ''}
</svg>`;
}
// Generate all required icon sizes
console.log('🎨 Generating PWA icons...');
// Generate regular icons
for (const size of SIZES) {
const svgContent = generateSVG(size, false);
// Save as SVG
const svgFilename = `icon-${size}.svg`;
const svgFilepath = path.join(iconsDir, svgFilename);
fs.writeFileSync(svgFilepath, svgContent);
console.log(`✅ Generated ${svgFilename}`);
}
// Generate maskable icons
for (const size of MASKABLE_SIZES) {
const svgContent = generateSVG(size, true);
const svgFilename = `icon-${size}-maskable.svg`;
const svgFilepath = path.join(iconsDir, svgFilename);
fs.writeFileSync(svgFilepath, svgContent);
console.log(`✅ Generated ${svgFilename} (maskable)`);
}
// Create a simple favicon.ico reference
const faviconSVG = generateSVG(32, false);
fs.writeFileSync(path.join(iconsDir, 'favicon.svg'), faviconSVG);
console.log('🎉 Icon generation complete!');
console.log('📁 Icons saved to:', iconsDir);
console.log('');
console.log('📝 Note: SVG icons are generated for development.');
console.log(' For production, convert these to PNG using a tool like:');
console.log(' - sharp (Node.js)');
console.log(' - ImageMagick');
console.log(' - Online converters');
console.log('');
console.log('💡 To use PNG icons, update the file extensions in manifest.json');