"use strict"; /** * Generates /public/og-image.png from the brand logo with the project * colours and name baked in. Run with `node scripts/build-og-image.mjs`. * Requires `sharp` (already a transitive Next.js dependency). * * The SVG embeds the Inter font directly via base64 so the image is * identical on every machine regardless of which fonts are installed. * If the font fetch fails we fall back to the system sans-serif at a * conservatively smaller size so the wordmark still fits. */ import { promises as fs } from "node:fs"; import path from "node:path"; import sharp from "sharp"; const ROOT = path.resolve(process.cwd()); const W = 1200; const H = 630; const palette = { bg: "#0f172a", bgAccent: "#1c2433", orange: "#fdb13c", teal: "#006672", gridLine: "rgba(255,255,255,0.04)", }; async function loadFontBase64() { // Check for a local font file first (committed to the repo so Docker // builds work offline). const localPath = path.join(ROOT, "public/fonts/inter-800.woff"); let buffer; try { buffer = await fs.readFile(localPath); } catch { // Fall back to Google Fonts. const url = "https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuDyYMZtrib2Ai-4.woff"; const res = await fetch(url); if (!res.ok) throw new Error(`Failed to fetch Inter: ${res.status}`); buffer = Buffer.from(await res.arrayBuffer()); } return buffer.toString("base64"); } const fontB64 = await loadFontBase64(); const svg = ` CONSTRUCTION OPERATIONS MEGAINFRASTRUCTURE Site operations dashboard · fuel · orders · workforce · sites `; const out = path.join(ROOT, "public", "og-image.png"); const buf = Buffer.from(svg); await sharp(buf, { density: 144 }) .resize({ width: W, height: H }) .png({ compressionLevel: 9 }) .toFile(out); const stat = await fs.stat(out); console.log(`Wrote ${out} (${(stat.size / 1024).toFixed(1)} KB, ${W}x${H})`);