import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; import { createRequire } from 'node:module'; import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { spawnSync } from 'node:child_process'; // All platform assets derive from the editable web SVG. Requires workspace dependencies. const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); const desktop = join(root, 'apps/desktop'); const require = createRequire(join(desktop, 'package.json')); const cli = require.resolve('@tauri-apps/cli/tauri.js'); const scratch = join(root, 'artifacts/icon-build'); const source = join(root, 'apps/web/public/favicon.svg'); const svg = readFileSync(source, 'utf8'); const body = svg.replace(/]*>/, '').replace('', ''); mkdirSync(scratch, { recursive: true }); function render(input, output, sizes = []) { mkdirSync(output, { recursive: true }); const result = spawnSync(process.execPath, [cli, 'icon', input, '--output', output, ...sizes.flatMap(size => ['--png', String(size)])], { cwd: desktop, stdio: 'inherit' }); if (result.error) throw result.error; if (result.status !== 0) throw new Error(`Icon generation failed: ${input}`); } function variant(name, contents, size, destination) { const input = join(scratch, `${name}.svg`); const output = join(scratch, name); writeFileSync(input, `${contents}\n`); render(input, output, [size]); mkdirSync(dirname(destination), { recursive: true }); copyFileSync(join(output, `${size}x${size}.png`), destination); } const native = join(scratch, 'desktop'); render(source, native); for (const name of ['32x32.png', '64x64.png', '128x128.png', '128x128@2x.png', 'icon.png', 'icon.ico', 'icon.icns']) { copyFileSync(join(native, name), join(desktop, 'src-tauri/icons', name)); } copyFileSync(join(native, '32x32.png'), join(root, 'apps/web/public/favicon-32.png')); const background = ''; variant('touch', background + body, 180, join(root, 'apps/web/public/apple-touch-icon.png')); variant('mobile', background + body, 1024, join(root, 'apps/mobile/assets/icon.png')); // The complete orb fits inside Android's central 66/108 safe-zone circle. variant('adaptive', `${body}`, 1024, join(root, 'apps/mobile/assets/adaptive-icon.png')); const mono = body.replace(/[\s\S]*?<\/defs>/, '').replace(/]*\/>/, '') .replaceAll('url(#note)', '#FFFFFF'); variant('monochrome', `${mono}`, 1024, join(root, 'apps/mobile/assets/monochrome-icon.png')); console.log('Updated desktop, Android, and browser icons.');