#!/usr/bin/env node // Side-by-side sheets: the redesign package's preview (left) beside a served page (right), at one width, full page. // One headless Chromium (Playwright's cached build, never Chrome.app), pages taken one after the other. // node tools/site-redesign/sheets.mjs --pkg --site --out [--width 1440] [--routes /,/miner] import { createRequire } from 'node:module'; import { existsSync, mkdirSync, writeFileSync } from 'node:fs'; import { join, resolve } from 'node:path'; import { pathToFileURL } from 'node:url'; const arg = (k, d) => { const i = process.argv.indexOf(k); return i > 0 ? process.argv[i + 1] : d; }; const PW = [process.env.PLAYWRIGHT_CORE, '/Users/joshm/Projects/viva-tracking/node_modules/playwright-core/package.json'].filter(Boolean).find(p => existsSync(p)); const CHROME = process.env.PLAYWRIGHT_CHROMIUM || `${process.env.HOME}/Library/Caches/ms-playwright/chromium-1234/chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing`; if (!PW || !existsSync(CHROME)) { console.error('no Playwright Chromium on this machine'); process.exit(2); } const { chromium } = createRequire(PW)('playwright-core'); const pkg = resolve(arg('--pkg')), site = arg('--site', 'http://127.0.0.1:4173'), out = resolve(arg('--out', 'sheets')), width = Number(arg('--width', 1440)); const routes = arg('--routes', '/,/miner,/live,/wallet,/explorer,/litepaper,/bench,/evidence,/ledger,/miners,/dev-fee,/faucet,/journey,/provenance,/claims,/randomx').split(','); mkdirSync(out, { recursive: true }); const browser = await chromium.launch({ executablePath: CHROME, args: ['--renderer-process-limit=1'] }); const ctx = await browser.newContext({ viewport: { width, height: 900 }, deviceScaleFactor: 1, colorScheme: 'dark', reducedMotion: 'reduce' }); const page = await ctx.newPage(); await page.route(/fonts\.(googleapis|gstatic)\.com/, r => r.abort()); async function shot(url, file, theme) { await page.goto(url, { waitUntil: 'load', timeout: 60000 }); if (theme) await page.evaluate(t => { document.documentElement.setAttribute('data-theme', t); try { localStorage.setItem('igneum.theme', t); localStorage.setItem('igneum-theme', t); } catch (e) {} }, theme); await page.waitForTimeout(1500); await page.evaluate(() => { document.querySelectorAll('.reveal').forEach(e => e.classList.add('visible', 'in')); window.scrollTo(0, 0); }); await page.screenshot({ path: file, fullPage: true, animations: 'disabled', timeout: 30000 }); } const sheets = []; for (const r of routes) { const name = r === '/' ? 'home' : r.slice(1).replace(/\//g, '-'); const left = join(out, `${name}-pkg.png`), right = join(out, `${name}-site.png`), sheet = join(out, `${name}-sheet.png`); try { await shot(pathToFileURL(pkg).href + '#' + r, left, 'dark'); } catch (e) { console.error(`${r} package: ${e.message}`); continue; } try { await shot(site + r, right, 'dark'); } catch (e) { console.error(`${r} site: ${e.message}`); continue; } const html = `

${r} at ${width}px: the package (left) and the served page (right)

package
served
`; const tmp = join(out, `${name}-sheet.html`); writeFileSync(tmp, html); await page.setViewportSize({ width: width * 2 + 72, height: 900 }); await page.goto(pathToFileURL(tmp).href, { waitUntil: 'load' }); await page.waitForTimeout(400); try { await page.screenshot({ path: sheet, fullPage: true, timeout: 30000 }); } catch (e) { console.error(`${r} sheet: ${e.message}`); } await page.setViewportSize({ width, height: 900 }); sheets.push(sheet); console.log(sheet); } await browser.close(); console.log(`${sheets.length} sheets in ${out}`);