← back to Quadrille Showroom

scripts/record-table.mjs

70 lines

// record-table.mjs — screen-record the "At the Table" view (Phase 3):
// enter table mode → add samples from a few different boards → drag one → click a
// board to center the design. Playwright chromium + recordVideo, webm→mp4.
import pw from '/Users/macstudio3/.npm-global/lib/node_modules/playwright/index.js';
import { execSync } from 'node:child_process';
import fs from 'node:fs';
const { chromium } = pw;

const URL = 'http://127.0.0.1:7690/';
const OUT = 'recordings';
fs.mkdirSync(OUT, { recursive: true });

const errs = [];
const b = await chromium.launch({ args: ['--use-gl=angle', '--enable-webgl', '--ignore-gpu-blocklist'] });
const ctx = await b.newContext({ viewport: { width: 1600, height: 900 }, recordVideo: { dir: OUT, size: { width: 1600, height: 900 } } });
const pg = await ctx.newPage();
pg.on('pageerror', e => errs.push('PAGEERR ' + e.message));
pg.on('console', m => { if (m.type() === 'error') { const t = m.text(); if (!/SwiftShader|GL_|GPU stall|ReadPixels/.test(t)) errs.push('CONSOLE ' + t); } });

console.log('→ loading', URL);
await pg.goto(URL, { waitUntil: 'networkidle', timeout: 40000 }).catch(() => {});
await pg.waitForTimeout(3000);

console.log('→ enter At-the-Table view');
await pg.evaluate(() => window._viewmode.set('table'));
await pg.waitForTimeout(2000);

// Add samples from three different boards so they don't de-dup.
for (const idx of [24, 28, 32]) {
  await pg.evaluate((i) => {
    const boards = window._qh.wingBoards || [];
    const b = boards[i];
    if (b) window._qh.focusOnWing(b);          // center that design
  }, idx);
  await pg.waitForTimeout(1400);
  await pg.evaluate(() => window._viewmode.addSample());  // drop its swatch on the table
  await pg.waitForTimeout(1100);
}

// Drag the first sample card to a new spot on the table.
const card = await pg.$('.vm-sample');
if (card) {
  const box = await card.boundingBox();
  if (box) {
    await pg.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
    await pg.mouse.down();
    await pg.mouse.move(box.x + 180, box.y + 60, { steps: 20 });
    await pg.mouse.move(box.x + 260, box.y - 40, { steps: 20 });
    await pg.mouse.up();
  }
}
await pg.waitForTimeout(1500);

console.log('→ done recording; errors:', errs.length);
errs.slice(0, 6).forEach(e => console.log('   ' + e.slice(0, 120)));
const samples = await pg.evaluate(() => document.querySelectorAll('.vm-sample').length);
console.log('   samples on table:', samples);

await ctx.close();  // finalizes the webm
await b.close();

// rename newest webm + transcode to mp4
const webms = fs.readdirSync(OUT).filter(f => f.endsWith('.webm')).map(f => ({ f, t: fs.statSync(`${OUT}/${f}`).mtimeMs })).sort((a, z) => z.t - a.t);
if (webms.length) {
  const src = `${OUT}/${webms[0].f}`, dstW = `${OUT}/table-view.webm`, dstM = `${OUT}/table-view.mp4`;
  fs.renameSync(src, dstW);
  try { execSync(`ffmpeg -y -i "${dstW}" -c:v libx264 -pix_fmt yuv420p "${dstM}"`, { stdio: 'ignore' }); console.log('✓ mp4:', dstM); }
  catch { console.log('✓ webm (no ffmpeg mp4):', dstW); }
}