// Sinh src-tauri/icons/favicon.ico tu app/templates/favicon.svg (icon exe). // Dung: node tools/gen_favicon_ico.js // (Can @resvg/resvg-js — npm install @resvg/resvg-js trong thu muc lam viec, // hoac chay trong thu muc da cai. Output: src-tauri/icons/favicon.ico.) // ICO chua cac size 16/24/32/48/64/128 — Windows dung cho exe icon. const fs = require('fs'); const path = require('path'); const ROOT = path.resolve(__dirname, '..'); const SVG = path.join(ROOT, 'app', 'templates', 'favicon.svg'); const OUT = path.join(ROOT, 'src-tauri', 'icons', 'favicon.ico'); let Resvg; try { ({ Resvg } = require('@resvg/resvg-js')); } catch (e) { console.error('Thieu @resvg/resvg-js. Chay: npm install @resvg/resvg-js'); process.exit(1); } const svg = fs.readFileSync(SVG, 'utf8'); const SIZES = [16, 24, 32, 48, 64, 128]; function buildIco(images) { const header = Buffer.alloc(6); header.writeUInt16LE(0, 0); header.writeUInt16LE(1, 2); header.writeUInt16LE(images.length, 4); const entries = []; const datas = []; let offset = 6 + 16 * images.length; for (const { size, data } of images) { const entry = Buffer.alloc(16); const dim = size >= 256 ? 0 : size; entry.writeUInt8(dim, 0); entry.writeUInt8(dim, 1); entry.writeUInt8(0, 2); entry.writeUInt8(0, 3); entry.writeUInt16LE(1, 4); entry.writeUInt16LE(32, 6); entry.writeUInt32LE(data.length, 8); entry.writeUInt32LE(offset, 12); entries.push(entry); datas.push(data); offset += data.length; } return Buffer.concat([header, ...entries, ...datas]); } const pngs = SIZES.map(s => ({ size: s, data: new Resvg(svg, { fitTo: { mode: 'width', value: s }, background: 'rgba(0,0,0,0)' }).render().asPng(), })); fs.writeFileSync(OUT, buildIco(pngs)); console.log('favicon.ico ->', OUT, fs.statSync(OUT).size, 'bytes');