新增 build-mac.js,产出 ad-hoc 签名的 .app 与 DMG。只能在 macOS 上 构建:DMG 依赖 hdiutil,且 Apple Silicon 拒绝执行未签名二进制,改名 与改 plist 后必须用 codesign 重签。 按官方 darwin-arm64 运行时的真实结构处理四处易错点:用 ditto 解压以 保留 framework 符号链接、删除重打包后失效的 ElectronAsarIntegrity、 为改名后的 helper 补上 CFBundleExecutable、签名由内向外且单独签 Squirrel 的 ShipIt。 打包版数据目录改为按平台决定:macOS 走 appData,避免写进 DMG 挂载后 只读的 .app 内部并在升级覆盖时丢失书库;Windows 便携版行为不变。 窗口图标在非 Windows 平台改用 PNG。 .gitignore 的 dist 规则补上前导斜杠。此前它同时匹配 icons/dist/, 导致构建与单测依赖的图标从未入库,新克隆的仓库无法构建。
63 lines
2.0 KiB
JavaScript
63 lines
2.0 KiB
JavaScript
// 从 icons/dist/<theme>/icon-*.png 生成 macOS 的 .icns。
|
|
// 用途:Windows 上没有 iconutil,但 icns 自 10.7 起支持直接内嵌 PNG,
|
|
// 因此容器可以手工拼出来,不必依赖 macOS 工具链。
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const DIST = path.join(__dirname, '..', 'dist');
|
|
|
|
// OSType -> 该槽位要求的像素尺寸。ic11/ic12/ic13/ic14 是 @2x 变体,
|
|
// 像素数等于逻辑尺寸的两倍,复用同一批 PNG 即可。
|
|
const SLOTS = [
|
|
['icp4', 16],
|
|
['icp5', 32],
|
|
['ic11', 32],
|
|
['ic12', 64],
|
|
['ic07', 128],
|
|
['ic13', 256],
|
|
['ic08', 256],
|
|
['ic14', 512],
|
|
['ic09', 512],
|
|
['ic10', 1024]
|
|
];
|
|
|
|
const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
|
|
function pngSize(buf) {
|
|
if (!buf.subarray(0, 8).equals(PNG_SIGNATURE)) throw new Error('不是 PNG 文件');
|
|
if (buf.readUInt32BE(8) !== 13 || buf.toString('latin1', 12, 16) !== 'IHDR') {
|
|
throw new Error('PNG 缺少 IHDR');
|
|
}
|
|
return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) };
|
|
}
|
|
|
|
function build(theme) {
|
|
const chunks = [];
|
|
for (const [osType, size] of SLOTS) {
|
|
const file = path.join(DIST, theme, `icon-${size}.png`);
|
|
const png = fs.readFileSync(file);
|
|
const dim = pngSize(png);
|
|
if (dim.width !== size || dim.height !== size) {
|
|
throw new Error(`${file} 期望 ${size}x${size},实际 ${dim.width}x${dim.height}`);
|
|
}
|
|
const header = Buffer.alloc(8);
|
|
header.write(osType, 0, 4, 'latin1');
|
|
header.writeUInt32BE(png.length + 8, 4);
|
|
chunks.push(header, png);
|
|
}
|
|
|
|
const body = Buffer.concat(chunks);
|
|
const header = Buffer.alloc(8);
|
|
header.write('icns', 0, 4, 'latin1');
|
|
header.writeUInt32BE(body.length + 8, 4);
|
|
|
|
const out = path.join(DIST, `book-ai-${theme}.icns`);
|
|
fs.writeFileSync(out, Buffer.concat([header, body]));
|
|
return out;
|
|
}
|
|
|
|
for (const theme of ['dark', 'light']) {
|
|
const out = build(theme);
|
|
console.log(`${path.basename(out)} ${(fs.statSync(out).size / 1024).toFixed(1)} KB`);
|
|
}
|