// Compose a demo's before and after captures into one side-by-side image.
//
// A README needs a single image referenced with plain markdown: two separate
// files need HTML tags to sit side by side, and those do not render in
// every markdown viewer. One composite renders everywhere.
//
// node scripts/compose.js hn [outPath]
const fs = require('fs');
const path = require('path');
const { chromium } = require('playwright-core');
const { repoRoot, fail } = require('./target');
const ROOT = repoRoot();
if (!ROOT) {
console.error('This tool composes demo captures and needs demos/ above it.');
process.exit(1);
}
const [, , id, outArg] = process.argv;
if (!id) {
console.error('usage: node scripts/compose.js [outPath]');
process.exit(1);
}
const dir = path.join(ROOT, 'demos', id);
const before = path.join(dir, 'desktop-before.png');
const after = path.join(dir, 'desktop-after.png');
for (const f of [before, after]) {
if (!fs.existsSync(f)) {
console.error(`missing ${path.relative(ROOT, f)} — run snap first`);
process.exit(1);
}
}
const out = outArg
? path.resolve(outArg)
: path.join(dir, 'before-after.png');
// Each half renders at this width; the pair plus the gap is the final image.
const HALF = 900;
const GAP = 24;
const asDataUri = (p) =>
`data:image/png;base64,${fs.readFileSync(p).toString('base64')}`;
(async () => {
const browser = await chromium.launch({ channel: 'chrome', headless: true });
const page = await browser.newPage({
viewport: { width: HALF * 2 + GAP, height: 600 },
deviceScaleFactor: 1,
});
await page.setContent(`