// Drives the dev server with Playwright bundled chromium. // Captures /design-system-v2 in 3 quietware modes (dark/light/HC) + a fallback. import { chromium } from "playwright"; import { mkdir } from "node:fs/promises"; const OUT = "/tmp/lumotia-v0.3-screenshots"; await mkdir(OUT, { recursive: true }); const browser = await chromium.launch({ headless: true }); const ctx = await browser.newContext({ viewport: { width: 1440, height: 1024 }, deviceScaleFactor: 2, }); const page = await ctx.newPage(); // Capture console for diagnosis page.on("pageerror", e => console.error("[page error]", e.message)); page.on("console", m => { if (m.type() === "error") console.error("[console]", m.text()); }); async function setMode({ design, theme, contrast }) { await page.evaluate(({ design, theme, contrast }) => { const h = document.documentElement; if (design) h.setAttribute("data-design", design); else h.removeAttribute("data-design"); if (theme) h.setAttribute("data-theme", theme); else h.removeAttribute("data-theme"); if (contrast) h.setAttribute("data-contrast", contrast); else h.removeAttribute("data-contrast"); }, { design, theme, contrast }); } const CAPTURES = [ // route, modes { route: "/design-system-v2", name: "design-system" }, ]; const MODES = [ { id: "v02-dark", design: null, theme: null, contrast: null, label: "v0.2 baseline (no quietware)" }, { id: "quiet-dark", design: "quietware", theme: null, contrast: null, label: "Quietware dark" }, { id: "quiet-light", design: "quietware", theme: "light", contrast: null, label: "Quietware light" }, { id: "quiet-hc-dark", design: "quietware", theme: null, contrast: "high", label: "Quietware high-contrast (dark base)" }, { id: "quiet-hc-light", design: "quietware", theme: "light", contrast: "high", label: "Quietware high-contrast (light base)" }, ]; const BASE = "http://localhost:1420"; for (const capture of CAPTURES) { console.log(`\n=== ${capture.name} (${capture.route}) ===`); try { await page.goto(BASE + capture.route, { waitUntil: "networkidle", timeout: 30000 }); } catch (e) { console.error(`navigation failed: ${e.message}`); continue; } // Wait a beat for Svelte hydration + font load await page.waitForTimeout(1500); for (const mode of MODES) { await setMode(mode); await page.waitForTimeout(400); // CSS transitions const file = `${OUT}/${capture.name}__${mode.id}.png`; await page.screenshot({ path: file, fullPage: true }); const stats = await page.evaluate(() => ({ h: document.documentElement.dataset.design || "(none)", t: document.documentElement.dataset.theme || "(default)", c: document.documentElement.dataset.contrast || "(default)", })); console.log(` ${mode.id} design=${stats.h} theme=${stats.t} contrast=${stats.c}`); } } // Also try the root route — may render the Dictation/FirstRun page if Tauri APIs // gracefully degrade in browser context. Worth attempting; if it crashes we just skip. try { console.log("\n=== root route (best effort) ==="); await page.goto(BASE + "/", { waitUntil: "domcontentloaded", timeout: 15000 }); await page.waitForTimeout(2000); for (const mode of MODES.slice(0, 3)) { await setMode(mode); await page.waitForTimeout(400); const file = `${OUT}/root__${mode.id}.png`; await page.screenshot({ path: file, fullPage: false }); console.log(` root ${mode.id}`); } } catch (e) { console.error(`root route skipped: ${e.message}`); } // Files page — SPA navigation via the sidebar item. try { console.log("\n=== files page (best effort) ==="); await page.evaluate(() => { const items = Array.from(document.querySelectorAll('button, a, [role="button"], li')); const target = items.find(el => (el.textContent || "").trim().toLowerCase() === "files"); if (target) target.click(); }); await page.waitForTimeout(1500); for (const mode of MODES.slice(1, 3)) { await setMode(mode); await page.waitForTimeout(400); const file = `${OUT}/files__${mode.id}.png`; await page.screenshot({ path: file, fullPage: false }); console.log(` files ${mode.id}`); } } catch (e) { console.error(`files page skipped: ${e.message}`); } await browser.close(); console.log(`\nWrote screenshots to ${OUT}/`);