#!/usr/bin/env node /** * Generate Tauri app icons from suite mark SVGs. * Usage: node mobile/scripts/sync-app-icons.mjs [ultimail|ultidrive|...|all] */ import { execSync } from "node:child_process" import { mkdirSync, readFileSync } from "node:fs" import { dirname, join } from "node:path" import { fileURLToPath } from "node:url" import sharp from "sharp" const __dirname = dirname(fileURLToPath(import.meta.url)) const root = join(__dirname, "../..") const APPS = { ultimail: { mark: "public/ultimail-mark.svg" }, ultidrive: { mark: "public/ultidrive-mark.svg" }, ulticalmeet: { mark: "public/agenda-mark.svg" }, ultiai: { mark: "public/ultiai-mark.svg" }, contacts: { mark: "public/contacts-mark.svg" }, } async function sourcePng(appId, markRel) { const markPath = join(root, markRel) const outDir = join(root, "mobile/.icon-cache") mkdirSync(outDir, { recursive: true }) const outPath = join(outDir, `${appId}-1024.png`) // Android adaptive icons mask ~66% of the canvas — keep the mark inside the safe zone. const canvas = 1024 const safe = Math.round(canvas * 0.62) const inset = Math.floor((canvas - safe) / 2) const mark = await sharp(readFileSync(markPath), { density: 300 }) .resize(safe, safe, { fit: "contain", background: { r: 255, g: 255, b: 255, alpha: 0 }, }) .png() .toBuffer() await sharp({ create: { width: canvas, height: canvas, channels: 4, background: { r: 255, g: 255, b: 255, alpha: 0 }, }, }) .composite([{ input: mark, top: inset, left: inset }]) .png() .toFile(outPath) return outPath } async function syncOne(appId) { const spec = APPS[appId] if (!spec) throw new Error(`Unknown app: ${appId}`) const png = await sourcePng(appId, spec.mark) const appDir = join(root, `mobile/apps/${appId}`) execSync(`pnpm exec tauri icon "${png}"`, { cwd: appDir, stdio: "inherit", env: { ...process.env, TAURI_CLI_CONFIG_DEPTH: "5" }, }) console.log(`Icons synced for ${appId}`) } const arg = process.argv[2] ?? "ultimail" const targets = arg === "all" ? Object.keys(APPS) : [arg] for (const appId of targets) { await syncOne(appId) }