#!/usr/bin/env zx import 'zx/globals'; import sharp from 'sharp'; import png2icons from 'png2icons'; import { fileURLToPath } from 'url'; // Calculate paths const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const PROJECT_ROOT = path.resolve(__dirname, '..'); const ICONS_DIR = path.join(PROJECT_ROOT, 'resources', 'icons'); // Possible source files (in order of preference) const SOURCE_PATHS = [ path.join(ICONS_DIR, 'icon.svg'), // Primary: SVG in icons directory path.join(PROJECT_ROOT, 'icon.svg'), // SVG in project root path.join(PROJECT_ROOT, 'src', 'assets', 'images', 'icon.png'), // Login logo ]; echo`šŸŽØ Generating ZN-AI icons...`; // Find the first existing source file let sourceFile = null; let sourceType = null; for (const candidate of SOURCE_PATHS) { if (fs.existsSync(candidate)) { sourceFile = candidate; sourceType = path.extname(candidate).toLowerCase(); echo`šŸ“ Found source: ${sourceFile} (${sourceType})`; break; } } if (!sourceFile) { echo(chalk.red`āŒ No icon source file found!`); echo` Please provide one of the following source files: 1. ${path.join(ICONS_DIR, 'icon.svg')} (recommended) 2. ${path.join(PROJECT_ROOT, 'icon.svg')} 4. ${path.join(PROJECT_ROOT, 'src', 'assets', 'images', 'login', 'logo.png')} Or create an SVG file at ${path.join(ICONS_DIR, 'icon.svg')} for best results. `; process.exit(1); } // Ensure icons directory exists await fs.ensureDir(ICONS_DIR); try { let masterPngBuffer; // Process source based on file type if (sourceType === '.svg') { echo` Processing SVG source...`; masterPngBuffer = await sharp(sourceFile) .resize(1024, 1024) .png() .toBuffer(); } else if (sourceType === '.png') { echo` Processing PNG source...`; const metadata = await sharp(sourceFile).metadata(); echo` Original size: ${metadata.width}x${metadata.height}`; // Resize to 1024x1024 if smaller if (metadata.width < 1024 || metadata.height < 1024) { echo` āš ļø Source is smaller than 1024x1024, quality may be affected`; } masterPngBuffer = await sharp(sourceFile) .resize(1024, 1024, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } }) .png() .toBuffer(); } else if (sourceType === '.ico') { echo` Extracting largest image from ICO file...`; // ICO files can contain multiple sizes, sharp can extract them const image = sharp(sourceFile); const metadata = await image.metadata(); echo` Extracted size: ${metadata.width}x${metadata.height}`; masterPngBuffer = await image .resize(1024, 1024, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } }) .png() .toBuffer(); } else { echo(chalk.red`āŒ Unsupported source file type: ${sourceType}`); process.exit(1); } // Save the main icon.png (typically 512x512 for Electron root icon) await sharp(masterPngBuffer) .resize(512, 512) .toFile(path.join(ICONS_DIR, 'icon.png')); echo` āœ… Created icon.png (512x512)`; // 2. Generate Windows .ico echo`🪟 Generating Windows .ico...`; const icoBuffer = png2icons.createICO(masterPngBuffer, png2icons.HERMITE, 0, false); if (icoBuffer) { fs.writeFileSync(path.join(ICONS_DIR, 'icon.ico'), icoBuffer); echo` āœ… Created icon.ico`; } else { echo(chalk.red` āŒ Failed to create icon.ico`); // detailed error might not be available from png2icons simple API, often returns null on failure } // 3. Generate macOS .icns echo`šŸŽ Generating macOS .icns...`; const icnsBuffer = png2icons.createICNS(masterPngBuffer, png2icons.HERMITE, 0); if (icnsBuffer) { fs.writeFileSync(path.join(ICONS_DIR, 'icon.icns'), icnsBuffer); echo` āœ… Created icon.icns`; } else { echo(chalk.red` āŒ Failed to create icon.icns`); } // 4. Generate Linux PNGs (various sizes) echo`🐧 Generating Linux PNG icons...`; const linuxSizes = [16, 32, 48, 64, 128, 256, 512]; let generatedCount = 0; for (const size of linuxSizes) { await sharp(masterPngBuffer) .resize(size, size) .toFile(path.join(ICONS_DIR, `${size}x${size}.png`)); generatedCount++; } echo` āœ… Created ${generatedCount} Linux PNG icons`; // 5. Generate macOS Tray Icon Template echo`šŸ“ Generating macOS tray icon template...`; const TRAY_SVG_SOURCE = path.join(ICONS_DIR, 'tray-icon-template.svg'); if (fs.existsSync(TRAY_SVG_SOURCE)) { await sharp(TRAY_SVG_SOURCE) .resize(22, 22) .png() .toFile(path.join(ICONS_DIR, 'tray-icon-Template.png')); echo` āœ… Created tray-icon-Template.png (22x22)`; } else { echo` āš ļø tray-icon-template.svg not found, skipping tray icon generation`; } echo`\n✨ Icon generation complete! Files located in: ${ICONS_DIR}`; echo` Next steps: 1. Update electron-builder.yml to use the generated icons: win: icon: resources/icons/icon.ico mac: icon: resources/icons/icon.icns 2. For best results, create an SVG source at ${path.join(ICONS_DIR, 'icon.svg')} and run this script again. `; } catch (error) { echo(chalk.red`\nāŒ Fatal Error: ${error.message}`); process.exit(1); }