← back to Quadrille Showroom

scripts/demo-record.js

66 lines

#!/usr/bin/env node
/**
 * demo-record.js — a ~30s "how to use it" guided demo of the showroom with
 * elegant on-screen captions. Drives a real Chromium and records WebM; convert
 * to MP4 afterward. Local 127.0.0.1 is auth-exempt.
 *
 * Run: NODE_PATH=$HOME/.npm-global/lib/node_modules node scripts/demo-record.js
 */
const path = require('path');
const fs = require('fs');
const { chromium } = require('playwright');

const URL = process.env.SHOWROOM_URL || 'http://127.0.0.1:7690/';
const OUT = path.join(__dirname, '..', 'recordings');
fs.mkdirSync(OUT, { recursive: true });
const sleep = (ms) => new Promise(r => setTimeout(r, ms));

async function caption(page, text) {
  await page.evaluate((t) => {
    let el = document.getElementById('__demo_cap');
    if (!el) {
      el = document.createElement('div');
      el.id = '__demo_cap';
      el.style.cssText = [
        'position:fixed','left:50%','bottom:8%','transform:translateX(-50%)','z-index:99999',
        'padding:14px 30px','background:rgba(14,12,9,0.82)','backdrop-filter:blur(8px)',
        'border:1px solid rgba(185,147,63,0.45)','border-radius:40px',
        'font-family:Georgia,serif','font-size:24px','letter-spacing:0.5px','color:#e9dcc0',
        'box-shadow:0 10px 40px rgba(0,0,0,0.5)','transition:opacity .4s','max-width:80vw','text-align:center'
      ].join(';');
      document.body.appendChild(el);
    }
    el.style.opacity = '0';
    setTimeout(() => { el.textContent = t; el.style.opacity = '1'; }, 180);
  }, text);
}

async function holdKey(page, key, ms) { await page.keyboard.down(key); await sleep(ms); await page.keyboard.up(key); }

(async () => {
  const browser = await chromium.launch({ args: ['--use-gl=angle', '--enable-webgl', '--ignore-gpu-blocklist'] });
  const ctx = await browser.newContext({ viewport: { width: 1600, height: 900 }, recordVideo: { dir: OUT, size: { width: 1600, height: 900 } } });
  const page = await ctx.newPage();
  await page.goto(URL, { waitUntil: 'domcontentloaded', timeout: 30000 });
  await page.waitForFunction(() => window._wingBoards && window._wingBoards.length > 0, { timeout: 25000 }).catch(() => {});
  await sleep(2500);

  // ~30s captioned sequence
  await caption(page, 'The Quadrille House · China Seas Showroom');           await sleep(3000);
  await caption(page, 'Walk through with  W · A · S · D');                    await holdKey(page, 'KeyW', 1500); await sleep(1500);
  await caption(page, 'Step up to a board — it opens to present the design'); await holdKey(page, 'KeyW', 600); await sleep(2600);
  await caption(page, 'Click a board to inspect it');                         await page.mouse.click(800, 430); await sleep(2200);
  await caption(page, 'Full spec sheet — 27″ × 27″ · straight match');        await sleep(3200);
  await caption(page, 'Fan the panels open to browse the design');           await page.click('#btn-fan-right').catch(()=>{}); await sleep(2800);
  await page.keyboard.press('Escape'); await sleep(600);
  await caption(page, 'Press  P  to Peruse — an auto-tour of the collection');await page.keyboard.press('KeyP'); await sleep(7000);
  await caption(page, 'Pause anytime with  Space  for more info');           await sleep(3000);
  await page.keyboard.press('Escape'); await sleep(500);
  await caption(page, 'The Quadrille House');                                 await sleep(2600);

  await page.close();
  const vid = await page.video().path().catch(() => null);
  await ctx.close(); await browser.close();
  if (vid && fs.existsSync(vid)) { const f = path.join(OUT, 'demo.webm'); fs.renameSync(vid, f); console.log('✓ demo webm:', f); }
})().catch(e => { console.error('DEMO FAILED:', e.message); process.exit(1); });