Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| """High-quality screenshots of the Matrix Builder web app (Playwright Chromium). | |
| Captures crisp, retina-scale shots of the landing page and the controlled-build flow | |
| (hero → blueprint candidates → Matrix Bundle) for the README, docs, and marketing. | |
| Usage: | |
| python -m pip install playwright && python -m playwright install chromium | |
| cd apps/web && pnpm dev # or: pnpm build && pnpm start (serves http://localhost:3000) | |
| python apps/web/scripts/shoot.py --base-url http://localhost:3000 --out docs/assets/screenshots | |
| Keep Playwright transient: `python -m pip uninstall -y playwright` after capture. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import asyncio | |
| from pathlib import Path | |
| from playwright.async_api import TimeoutError as PWTimeout | |
| from playwright.async_api import async_playwright | |
| # Static routes worth documenting. The C4 work pages (/builder/*) require an authenticated | |
| # session, so the marketing sequence below drives the public /matrix-builder flow instead. | |
| ROUTES = [ | |
| ("home", "/"), | |
| ("matrix-builder", "/matrix-builder"), | |
| ("examples", "/examples"), | |
| ("docs", "/docs"), | |
| ] | |
| async def settle(page, ms: int = 900): | |
| await page.wait_for_timeout(ms) | |
| async def shot(page, out: Path, name: str, full: bool = False): | |
| await settle(page) | |
| p = out / f"{name}.png" | |
| await page.screenshot(path=str(p), full_page=full) | |
| print(f"shot: {p}") | |
| async def capture_routes(page, base: str, out: Path): | |
| for name, route in ROUTES: | |
| try: | |
| await page.goto(f"{base}{route}", wait_until="networkidle") | |
| await shot(page, out, name) | |
| except Exception as exc: # noqa: BLE001 | |
| print(f"skipped {name}: {exc}") | |
| async def capture_builder_flow(page, base: str, out: Path): | |
| """Drive the controlled-build flow for the marketing sequence (best-effort, defensive).""" | |
| try: | |
| await page.goto(f"{base}/matrix-builder", wait_until="networkidle") | |
| await shot(page, out, "01-hero") | |
| # Type an idea and generate blueprint candidates. | |
| try: | |
| box = page.locator('input[aria-label="Describe your idea"], .l-idea input').first | |
| await box.fill("A GitHub repo intelligence agent", timeout=4000) | |
| except PWTimeout: | |
| pass | |
| await page.get_by_text("Generate blueprint", exact=False).first.click(timeout=5000) | |
| await settle(page, 2600) # let the scanning phase finish | |
| await shot(page, out, "02-candidates") | |
| # Choose the recommended candidate → the Matrix Bundle. | |
| try: | |
| await page.get_by_text("Choose this", exact=False).first.click(timeout=5000) | |
| await settle(page, 1400) | |
| await shot(page, out, "03-bundle") | |
| except Exception as exc: # noqa: BLE001 | |
| print(f"skipped bundle: {exc}") | |
| except Exception as exc: # noqa: BLE001 | |
| print(f"skipped builder-flow: {exc}") | |
| async def main(): | |
| ap = argparse.ArgumentParser(description="Screenshot the Matrix Builder web app.") | |
| ap.add_argument("--base-url", default="http://localhost:3000") | |
| ap.add_argument("--out", default="docs/assets/screenshots") | |
| ap.add_argument("--width", type=int, default=1440) | |
| ap.add_argument("--height", type=int, default=900) | |
| ap.add_argument("--scale", type=int, default=2, help="Device scale (2 = retina).") | |
| ap.add_argument("--light", action="store_true", help="Light color scheme (default: dark).") | |
| args = ap.parse_args() | |
| out = Path(args.out).resolve() | |
| out.mkdir(parents=True, exist_ok=True) | |
| async with async_playwright() as pw: | |
| browser = await pw.chromium.launch(headless=True) | |
| ctx = await browser.new_context( | |
| viewport={"width": args.width, "height": args.height}, | |
| device_scale_factor=args.scale, | |
| color_scheme="light" if args.light else "dark", | |
| ) | |
| page = await ctx.new_page() | |
| await capture_builder_flow(page, args.base_url, out) | |
| await capture_routes(page, args.base_url, out) | |
| await browser.close() | |
| print("done") | |
| if __name__ == "__main__": | |
| asyncio.run(main()) | |