← back to Professional Directory

scripts/snapshot-mockups.js

51 lines

#!/usr/bin/env node
/**
 * Snapshot every generated mockup to a 1200×750 PNG (so the pitch modal
 * can show real thumbnails instead of slow live iframes).
 *
 * Reads:  data/mockups/<id>.html
 * Writes: data/mockups/<id>.png
 *
 * Usage:
 *   node scripts/snapshot-mockups.js          # snapshots all mockups missing PNG
 *   FORCE=1 node scripts/snapshot-mockups.js  # re-snapshots even if PNG exists
 */
const fs = require('fs');
const path = require('path');
const { chromium } = require('playwright');

const DIR = path.resolve(__dirname, '../data/mockups');
const FORCE = process.env.FORCE === '1';
const VIEWPORT = { width: 1200, height: 750 };

(async () => {
  const files = fs.readdirSync(DIR).filter(f => f.endsWith('.html'));
  console.log(`[snap] ${files.length} mockups in ${DIR}`);
  const browser = await chromium.launch({ headless: true });
  const context = await browser.newContext({ viewport: VIEWPORT, deviceScaleFactor: 1 });
  let ok = 0, skip = 0, fail = 0;

  for (const f of files) {
    const id = f.replace(/\.html$/, '');
    const png = path.join(DIR, `${id}.png`);
    if (fs.existsSync(png) && !FORCE) { skip++; continue; }
    const url = 'file://' + path.join(DIR, f);
    const page = await context.newPage();
    try {
      await page.goto(url, { waitUntil: 'networkidle', timeout: 20_000 });
      // Hide our injected chat widget so it doesn't dominate the thumbnail.
      await page.addStyleTag({ content: '#vw-chat-fab, #vw-chat-panel { display: none !important; }' });
      await page.waitForTimeout(400);   // let any animations settle
      await page.screenshot({ path: png, fullPage: false, type: 'png' });
      console.log(`[snap] ${id}.png  ✓`);
      ok++;
    } catch (e) {
      console.log(`[snap] ${id}  FAIL  ${e.message}`);
      fail++;
    }
    await page.close();
  }
  await browser.close();
  console.log(`[snap] done. ok=${ok} skip=${skip} fail=${fail}`);
})();