/** * Rasterize the circular mark into favicons and render the family OG image. * Usage: node scripts/render-social-assets.mjs */ import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import { execFileSync } from "node:child_process"; import { chromium } from "playwright-core"; const root = join(dirname(fileURLToPath(import.meta.url)), ".."); const pub = join(root, "public"); const mark = join(pub, "brand/vocalinux-mark-circle.svg"); const chrome = process.env.CHROME_PATH || ["/usr/bin/chromium-browser", "/usr/bin/chromium"].find(Boolean); function convertPng(size, dest, { paper } = {}) { const args = ["-background", paper ? "#f4f1e8" : "none", "-resize", `${size}x${size}`, mark]; if (paper) { args.push("-gravity", "center", "-extent", `${size}x${size}`); } args.push("-depth", "8", `png32:${dest}`); execFileSync("convert", args); } function toWebp(src, dest) { execFileSync("convert", [src, dest]); } const skipIcons = process.env.OG_ONLY === "1"; const sizes = [ [16, join(pub, "favicon-16x16.png")], [32, join(pub, "favicon-32x32.png")], [32, join(pub, "vocalinux-32x32.png")], [64, join(pub, "vocalinux-64x64.png")], [96, join(pub, "favicon-96x96.png")], [96, join(pub, "vocalinux-96x96.png")], [192, join(pub, "vocalinux-192x192.png")], [192, join(pub, "icon-192x192.png")], [512, join(pub, "vocalinux-512x512.png")], [512, join(pub, "icon-512x512.png")], [512, join(pub, "vocalinux.png")], ]; if (!skipIcons) { for (const [size, dest] of sizes) { convertPng(size, dest); if (dest.endsWith(".png") && !dest.includes("favicon-")) { const webp = dest.replace(/\.png$/i, ".webp"); if ( dest.includes("vocalinux-") || dest.endsWith("/vocalinux.png") ) { toWebp(dest, webp); } } } convertPng(180, join(pub, "apple-touch-icon.png"), { paper: true }); execFileSync("convert", [ join(pub, "favicon-16x16.png"), join(pub, "favicon-32x32.png"), join(pub, "favicon.ico"), ]); } const markSvg = readFileSync(mark, "utf8"); const markData = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(markSvg)}`; const tuxData = `data:image/svg+xml;charset=utf-8,${encodeURIComponent( readFileSync(join(pub, "brand/tux.svg"), "utf8"), )}`; const dotsData = `data:image/svg+xml;charset=utf-8,${encodeURIComponent( readFileSync(join(pub, "brand/paper-dots.svg"), "utf8"), )}`; const ogHtml = `
Vocalinux

Dictate locally.
In any app.

Hold Right Alt. Text lands in the focused window. Local engines by default.

X11 and Wayland No telemetry AGPL-3.0

vocalinux.com

Vocalinuxlistening
notes.txt
Ready to dictate
Hold Right Alt to record
the words go where you are already working
Tux, the Linux mascot
`; const browser = await chromium.launch({ executablePath: chrome, headless: true, args: ["--no-sandbox", "--disable-dev-shm-usage", "--disable-gpu"], }); const page = await browser.newPage({ viewport: { width: 1200, height: 630 } }); await page.setContent(ogHtml, { waitUntil: "load" }); await page.screenshot({ path: join(pub, "og-image.png"), type: "png" }); await browser.close(); execFileSync("convert", [join(pub, "og-image.png"), join(pub, "og-image.webp")]); console.log("rendered circular favicons and family OG image");