#!/usr/bin/env npx tsx /** * Standalone org chart image generator. * * Renders each of the 5 org chart styles to PNG using Playwright (headless Chromium). * This gives us browser-native emoji rendering, full CSS support, and pixel-perfect output. * * Usage: * npx tsx scripts/generate-org-chart-images.ts * * Output: tmp/org-chart-images/
${tree} ${PAPERCLIP_WATERMARK}
`; } // ── Main ─────────────────────────────────────────────────────── async function main() { const outDir = path.resolve("tmp/org-chart-images"); fs.mkdirSync(outDir, { recursive: true }); const browser = await chromium.launch(); const context = await browser.newContext({ deviceScaleFactor: 2, // retina quality }); const sizes = ["sm", "med", "lg"] as const; const results: string[] = []; for (const style of STYLES) { // README sizes for (const size of sizes) { const page = await context.newPage(); const html = buildHtml(style, ORGS[size], false); await page.setContent(html, { waitUntil: "networkidle" }); // Wait for fonts to load await page.waitForFunction(() => document.fonts.ready); await page.waitForTimeout(300); // Fit to content const box = await page.evaluate(() => { const el = document.querySelector(".org-tree")!; const rect = el.getBoundingClientRect(); return { width: Math.ceil(rect.width) + 32, height: Math.ceil(rect.height) + 32, }; }); await page.setViewportSize({ width: Math.max(box.width, 400), height: Math.max(box.height, 300), }); const filename = `${style.key}-${size}.png`; await page.screenshot({ path: path.join(outDir, filename), clip: { x: 0, y: 0, width: Math.max(box.width, 400), height: Math.max(box.height, 300), }, }); await page.close(); results.push(filename); console.log(` ✓ ${filename}`); } // OG card (1200×630) { const page = await context.newPage(); await page.setViewportSize({ width: 1200, height: 630 }); const html = buildHtml(style, OG_ORG, true); // For OG, center the tree in a fixed viewport const ogHtml = html.replace( "", ``, ); await page.setContent(ogHtml, { waitUntil: "networkidle" }); await page.waitForFunction(() => document.fonts.ready); await page.waitForTimeout(300); const filename = `${style.key}-og.png`; await page.screenshot({ path: path.join(outDir, filename), clip: { x: 0, y: 0, width: 1200, height: 630 }, }); await page.close(); results.push(filename); console.log(` ✓ ${filename}`); } } await browser.close(); // Build an HTML comparison page let compHtml = ` Org Chart Style Comparison

Org Chart Export — Style Comparison

5 styles × 3 org sizes + OG cards. All rendered via Playwright (browser-native emojis, full CSS).

`; for (const style of STYLES) { compHtml += `

${style.name}

README — Small / Medium / Large
OG Card (1200×630)
`; } compHtml += ``; fs.writeFileSync(path.join(outDir, "comparison.html"), compHtml); console.log(`\n✓ All done! ${results.length} images generated.`); console.log(` Open: tmp/org-chart-images/comparison.html`); } main().catch((e) => { console.error(e); process.exit(1); });