feat(build): add icon generation and external binary bundling
- Add scripts to generate application icons in multiple formats (ICO, ICNS, PNG) - Implement download scripts for uv and Node.js binaries for cross-platform support - Update build configuration to use new icon resources and bundled binaries - Remove old loading screen and unused build configurations - Fix application icon path resolution to use app resources directory
This commit is contained in:
169
scripts/generate-icons.mjs
Normal file
169
scripts/generate-icons.mjs
Normal file
@@ -0,0 +1,169 @@
|
||||
#!/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', 'login', 'blue_logo.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);
|
||||
}
|
||||
Reference in New Issue
Block a user