| 1 | #!/usr/bin/env node |
| 2 | // 🤖 tools/marketing-shots/web-check.mjs — headless screenshot of the seeded |
| 3 | // demo space, so you can eyeball that it matches the marketing web frame before |
| 4 | // capturing native. |
| 5 | // |
| 6 | // Mints a fresh sign-in link for Cass, opens the space in headless Chromium, |
| 7 | // switches to the Lit Room theme (warm cream, as in team-web.png), opens the |
| 8 | // flagship "The commons 🌱" thread, and writes a 2x screenshot. |
| 9 | // |
| 10 | // Usage: node tools/marketing-shots/web-check.mjs [output.png] [--thread "The commons"] |
| 11 | // default output: tools/marketing-shots/web-check.png |
| 12 | // Requires the demo server to be running (`./tools/dev shots up`) and |
| 13 | // .demo-state.json to exist (seed-team.mjs). Playwright is resolved from the |
| 14 | // npx cache (override with $LCHAT_PLAYWRIGHT). |
| 15 | |
| 16 | import { readState, mintDeviceLink } from "./demo-lib.mjs"; |
| 17 | |
| 18 | const PW = |
| 19 | process.env.LCHAT_PLAYWRIGHT || |
| 20 | "/Users/paulcampbell/.npm/_npx/e41f203b7505f1fb/node_modules/playwright/index.mjs"; |
| 21 | |
| 22 | function arg(name, def) { |
| 23 | const i = process.argv.indexOf(`--${name}`); |
| 24 | return i >= 0 && i + 1 < process.argv.length ? process.argv[i + 1] : def; |
| 25 | } |
| 26 | |
| 27 | const out = |
| 28 | process.argv[2] && !process.argv[2].startsWith("--") |
| 29 | ? process.argv[2] |
| 30 | : "tools/marketing-shots/web-check.png"; |
| 31 | const threadName = arg("thread", "The commons"); |
| 32 | |
| 33 | async function main() { |
| 34 | const s = readState(); |
| 35 | if (!s) { |
| 36 | console.error("web-check: no .demo-state.json — run seed-team.mjs first."); |
| 37 | process.exit(1); |
| 38 | } |
| 39 | const link = await mintDeviceLink(s.base, s.adminToken, s.cass, s.base, "You"); |
| 40 | |
| 41 | const { chromium } = await import(PW); |
| 42 | const browser = await chromium.launch(); |
| 43 | const ctx = await browser.newContext({ viewport: { width: 1280, height: 1024 }, deviceScaleFactor: 2 }); |
| 44 | const page = await ctx.newPage(); |
| 45 | |
| 46 | // Sign in via the add-device link (join.js claims it + stores keys). The link |
| 47 | // shows a confirm card first; "Sign in here in the browser" completes it. |
| 48 | await page.goto(link, { waitUntil: "networkidle" }); |
| 49 | await page.getByText("Sign in here in the browser").click(); |
| 50 | await page.waitForSelector("#thread-list .thread-row", { timeout: 20000 }); |
| 51 | // Pick the Lit Room theme + light appearance, matching the web frame. |
| 52 | await page.evaluate(() => { |
| 53 | localStorage.setItem("lchat_theme", "litroom"); |
| 54 | localStorage.setItem("lchat_appearance", "light"); |
| 55 | }); |
| 56 | await page.reload({ waitUntil: "networkidle" }); |
| 57 | |
| 58 | // Open the flagship thread from the list. |
| 59 | await page.waitForSelector("#thread-list .thread-row", { timeout: 20000 }); |
| 60 | const row = page.locator(".thread-row", { hasText: threadName }).first(); |
| 61 | await row.click(); |
| 62 | await page.waitForTimeout(1500); // let the feed fold + render |
| 63 | |
| 64 | await page.screenshot({ path: out }); |
| 65 | console.error(`web-check: wrote ${out}`); |
| 66 | await browser.close(); |
| 67 | } |
| 68 | |
| 69 | main().catch((e) => { |
| 70 | console.error("web-check:", e.message); |
| 71 | process.exit(1); |
| 72 | }); |
| 73 | |