// Generates branding/social-preview.png (1280x640) -- the SnapOtter social/OG card. // Echoes the landing hero: trust badges, the "Every file tool you need / Nothing // leaves your network" headline, the supporting subhead, and the five modality // cards. Rendered from HTML with the real brand fonts via headless Chromium. // Run: node scripts/branding/generate-social-preview.mjs // // The same output is the canonical OG image; sync it to apps/landing/public/og-image.png // and apps/web/public/og-image.png (see scripts/branding/sync-og.sh). import { rmSync, writeFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { chromium } from "@playwright/test"; const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); const out = resolve(repoRoot, "branding/social-preview.png"); // Temp HTML lives in branding/ so the page origin is file:// and can load the // sibling logo and the ../apps fonts as file:// subresources. const htmlPath = resolve(repoRoot, "branding/.og.html"); // Enterprise trust badges (subset of Hero.astro's list). const BADGES = ["On-premise", "GDPR & HIPAA", "Air-gap", "SAML SSO", "Open source"]; // lucide icon inner-SVG (stroke), matching the icons used in CategoryCards.astro. const ICON = { image: '', video: '', music: '', fileText: '', database: '', }; // Mirrors CategoryCards.astro (label/blurb/color/icon). Counts are toolSection() // section counts (image 64, video 29, audio 17, pdf 25, files 22 = 157); refresh // with: tsx -e "import('@snapotter/shared')..." over toolSection (see git history). const CARDS = [ { label: "Image Tools", count: 64, blurb: "Resize, convert, and enhance", color: "#E07832", icon: ICON.image, }, { label: "Video Tools", count: 29, blurb: "Trim, convert, and caption", color: "#BE4A3C", icon: ICON.video, }, { label: "Audio Tools", count: 17, blurb: "Convert, trim, and transcribe", color: "#2C7A75", icon: ICON.music, }, { label: "PDF & Documents", count: 25, blurb: "Merge, split, and convert", color: "#44568C", icon: ICON.fileText, }, { label: "File Tools", count: 22, blurb: "Convert and transform", color: "#5C8642", icon: ICON.database, }, ]; const total = CARDS.reduce((s, c) => s + c.count, 0); const badgesHtml = BADGES.map( (b) => `${b}`, ).join(""); const cardsHtml = CARDS.map( (c) => `
${c.icon} ${c.count} tools
${c.label}
${c.blurb}
`, ).join(""); const html = `
SnapOtter${total} tools
${badgesHtml}
Every file tool you need.Nothing leaves your network.
The file-processing suite for teams that keep sensitive data in-house.
${cardsHtml}
`; writeFileSync(htmlPath, html); try { const browser = await chromium.launch(); const ctx = await browser.newContext({ viewport: { width: 1280, height: 640 } }); const page = await ctx.newPage(); await page.goto("file://" + htmlPath, { waitUntil: "load" }); await page.evaluate(async () => { await document.fonts.ready; }); await page.waitForTimeout(150); await page.screenshot({ path: out }); await browser.close(); console.log("wrote " + out); } finally { rmSync(htmlPath, { force: true }); }