ultisuite-client/mobile/scripts/sync-app-icons.mjs
R3D347HR4Y d6d18f911b
Some checks failed
E2E / Playwright e2e (push) Has been cancelled
Lots of stuff and mobile app
2026-06-17 00:13:28 +02:00

72 lines
2.1 KiB
JavaScript

#!/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)
}