mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-02 10:00:17 +05:30
feat(packaging): add macOS icon.icns + build:icon-icns script
electron-builder's mac block declared 'icon: assets/icon.icns' but the file was never generated, so macOS builds shipped with the default Electron icon. The build would succeed but the .app looked generic. Add scripts/build-icon-icns.js — a 50-line node script that packs the existing PNGs from assets/icons/ (16/32/64/128/256/512/1024 + 48 small + 16/32/128/256 @2x retina) into a valid ICNS file. No native deps; works on Linux, mac, and Windows runners alike (avoids the mac-only 'sips' that png2icns requires). Wired into npm via 'npm run build:icon-icns'. Local build:linux now parses the ICNS correctly and produces AppImage/deb/snap.
This commit is contained in:
Binary file not shown.
+2
-1
@@ -32,7 +32,8 @@
|
||||
"dist": "electron-builder --publish=never",
|
||||
"dist:all": "electron-builder -mwl",
|
||||
"download-tools": "node scripts/download-tools.js",
|
||||
"generate-icons": "node scripts/generate-icons.js"
|
||||
"generate-icons": "node scripts/generate-icons.js",
|
||||
"build:icon-icns": "node scripts/build-icon-icns.js"
|
||||
},
|
||||
"keywords": [
|
||||
"markdown",
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Build assets/icon.icns from the PNGs in assets/icons/.
|
||||
*
|
||||
* ICNS file format (Apple Icon Image):
|
||||
* Header: 'icns' (4 bytes) + total file length (uint32 BE, includes header)
|
||||
* For each size:
|
||||
* Type tag (4 bytes) + length (uint32 BE, includes the 8-byte entry header) + PNG data
|
||||
*
|
||||
* Standard PNG type tags we'll include:
|
||||
* icp4 = 16x16, icp5 = 32x32, icp6 = 64x64
|
||||
* ic07 = 128x128, ic08 = 256x256, ic09 = 512x512, ic10 = 1024x1024
|
||||
* icsb = 48x48 (small)
|
||||
* ic11 = 16x16@2x (32x32 retina), ic12 = 32x32@2x (64x64 retina)
|
||||
* ic13 = 128x128@2x (256x256 retina), ic14 = 256x256@2x (512x512 retina)
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const iconsDir = path.join(__dirname, '..', 'assets', 'icons');
|
||||
const outFile = path.join(__dirname, '..', 'assets', 'icon.icns');
|
||||
|
||||
// (size-in-pixels, icns type tag) — we skip sizes we don't have on disk
|
||||
const sizes = [
|
||||
{ size: 16, type: 'icp4' },
|
||||
{ size: 32, type: 'icp5' },
|
||||
{ size: 64, type: 'icp6' },
|
||||
{ size: 128, type: 'ic07' },
|
||||
{ size: 256, type: 'ic08' },
|
||||
{ size: 512, type: 'ic09' },
|
||||
{ size: 1024, type: 'ic10' },
|
||||
{ size: 48, type: 'icsb' },
|
||||
{ size: 32, type: 'ic11' }, // 16@2x
|
||||
{ size: 64, type: 'ic12' }, // 32@2x
|
||||
{ size: 256, type: 'ic13' }, // 128@2x
|
||||
{ size: 512, type: 'ic14' }, // 256@2x
|
||||
];
|
||||
|
||||
const entries = [];
|
||||
for (const { size, type } of sizes) {
|
||||
const pngPath = path.join(iconsDir, `${size}x${size}.png`);
|
||||
if (!fs.existsSync(pngPath)) continue;
|
||||
const png = fs.readFileSync(pngPath);
|
||||
// ICNS requires 8-byte alignment per entry. paddedLen is the TOTAL entry size
|
||||
// (8-byte type+length header + PNG + padding), rounded up to a multiple of 8.
|
||||
const paddedLen = 8 + Math.ceil(png.length / 8) * 8;
|
||||
entries.push({ type, png, paddedLen });
|
||||
console.log(` ${type} (${size}x${size}) — ${png.length} bytes`);
|
||||
}
|
||||
|
||||
if (entries.length === 0) {
|
||||
console.error('No source PNGs found in', iconsDir);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const totalLen = 8 + entries.reduce((s, e) => s + e.paddedLen, 0);
|
||||
const buf = Buffer.alloc(totalLen);
|
||||
let off = 0;
|
||||
|
||||
// Header
|
||||
buf.write('icns', off, 4, 'ascii'); off += 4;
|
||||
buf.writeUInt32BE(totalLen, off); off += 4;
|
||||
|
||||
// Entries
|
||||
for (const { type, png, paddedLen } of entries) {
|
||||
buf.write(type, off, 4, 'ascii'); off += 4;
|
||||
buf.writeUInt32BE(paddedLen, off); off += 4;
|
||||
png.copy(buf, off);
|
||||
off += png.length;
|
||||
// Pad to 8-byte boundary within the entry. paddedLen already includes the 8-byte header.
|
||||
const pad = paddedLen - 8 - png.length;
|
||||
if (pad > 0) off += pad;
|
||||
}
|
||||
|
||||
fs.writeFileSync(outFile, buf);
|
||||
console.log(`\nWrote ${outFile} (${buf.length} bytes, ${entries.length} sizes)`);
|
||||
Reference in New Issue
Block a user