matrix-builder / web /scripts /shoot.py
ruslanmv
Deploy: metrics + docs (Batch 12)
22b729d
Raw
History Blame Contribute Delete
4.16 kB
#!/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())