#!/usr/bin/env node /** * Generate iOS app icon from existing PNG source * * iOS requires a fully opaque 1024x1024 PNG with no alpha channel. * This script resizes the existing logo and ensures it has no alpha channel. */ const sharp = require('sharp'); const fs = require('fs'); const path = require('path'); // Configuration const ICON_SIZE = 1024; // Paths const SOURCE_PNG = path.join(__dirname, '../build/icons/sq2160x2160.png'); const OUTPUT_PATH = path.join( __dirname, '../ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png', ); async function generateIcon() { console.log('šŸŽØ Generating iOS app icon...\n'); // Step 1: Read source PNG console.log('šŸ“– Reading source PNG:', SOURCE_PNG); if (!fs.existsSync(SOURCE_PNG)) { throw new Error(`Source PNG not found: ${SOURCE_PNG}`); } console.log('āœ“ Source PNG found\n'); // Step 2: Resize and remove alpha channel console.log(`šŸ”„ Resizing to ${ICON_SIZE}x${ICON_SIZE} and removing alpha channel...`); const iconBuffer = await sharp(SOURCE_PNG) .resize(ICON_SIZE, ICON_SIZE, { fit: 'cover', position: 'center', }) .flatten({ background: { r: 100, g: 149, b: 237 } }) // Fallback background if source has transparency .removeAlpha() // Explicitly remove alpha channel .toColorspace('srgb') .png() .toBuffer(); console.log('āœ“ Icon resized\n'); // Step 3: Write to output file console.log('šŸ’¾ Writing icon to:', OUTPUT_PATH); // Ensure output directory exists const outputDir = path.dirname(OUTPUT_PATH); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } fs.writeFileSync(OUTPUT_PATH, iconBuffer); console.log('āœ“ Icon written\n'); // Step 4: Verify output console.log('šŸ” Verifying icon properties...'); const metadata = await sharp(OUTPUT_PATH).metadata(); const checks = { Dimensions: metadata.width === ICON_SIZE && metadata.height === ICON_SIZE, Format: metadata.format === 'png', Channels: metadata.channels === 3, 'No alpha': !metadata.hasAlpha, 'Color space': metadata.space === 'srgb', }; console.log('\nVerification results:'); for (const [check, passed] of Object.entries(checks)) { console.log(` ${passed ? 'āœ…' : 'āŒ'} ${check}: ${passed ? 'PASS' : 'FAIL'}`); } console.log('\nMetadata:'); console.log(` Size: ${metadata.width}x${metadata.height}`); console.log(` Format: ${metadata.format}`); console.log(` Channels: ${metadata.channels} (${metadata.hasAlpha ? 'RGBA' : 'RGB'})`); console.log(` Color space: ${metadata.space}`); const allPassed = Object.values(checks).every((v) => v); if (!allPassed) { throw new Error('āŒ Icon verification failed! Icon has incorrect properties.'); } console.log('\nāœ… SUCCESS! iOS app icon generated with no alpha channel.'); console.log('\nNext steps:'); console.log(' 1. Run: npm run sync:ios'); console.log(' 2. Test in iOS Simulator/device to verify no white frame'); } // Run the script generateIcon().catch((error) => { console.error('\nāŒ Error generating icon:', error.message); process.exit(1); });