kc-screenshot · git:20260714.ee88ce9 · 2026-07-14 · sha256 3392c4439b125801
kc-screenshot git:20260714.ee88ce9A
Immutable. This exact content never changes and is served at /api/v1/blob/3392c4439b125801.
---
name: kc-screenshot
description: Capture desktop + mobile, dark + light screenshots of the kube-coder dashboard SPA for visual QA of a UI change. Use when the user wants to see a frontend change rendered in a real browser rather than just unit-tested.
user-invocable: true
allowed-tools: Bash, Read
argument-hint: "[output dir] (default: /home/dev/screenshots)"
---
# Screenshot the dashboard SPA
Drives a headless Chromium across desktop (1280x800) and mobile (390x844)
viewports in dark + light themes, against a local dev harness that stubs auth.
Useful for changes that can't be fully unit-tested (terminal iframe, layout,
theming). Read the resulting PNGs with the Read tool — they render inline.
## How it works
- **`charts/workspace/web/dev_server.py <port>`** — a thin harness that imports
the real `charts/workspace/server.py` and monkeypatches `check_claude_auth`
to always return True, so the SPA can hit `/api/*` without the OAuth proxy.
The production dashboard on 6080 is untouched.
- **`charts/workspace/web/scripts/shoot.mjs`** — drives Chromium via
`playwright-core` and writes `phaseN-<view>-<viewport>-<theme>.png` files.
Sibling variants exist for specific surfaces: `shoot-hv.mjs` (hypervisor),
`shoot-msgchat.mjs`, `shoot-docs.mjs`, `shoot-toolbar-paste.mjs`.
## Steps
### 1. Build the SPA (the dev server serves `dist/`)
```bash
yarn --cwd charts/workspace/web build
```
Rebuild after any source edit — the dev server picks up the new `dist/` on the
next request, no restart needed.
### 2. Start the dev server on port 7070
```bash
cd /home/dev/kube-coder
nohup python3 charts/workspace/web/dev_server.py 7070 > /tmp/dev_server.log 2>&1 &
disown
sleep 2
curl -sf http://localhost:7070/ >/dev/null && echo "dev server up" || tail -20 /tmp/dev_server.log
```
### 3. Confirm the Chromium binary path (it drifts — never hardcode)
```bash
ls -d ~/.cache/ms-playwright/chromium*/chrome-linux*/chrome
```
If it's missing, install it: `yarn --cwd charts/workspace/web exec playwright install chromium`.
### 4. Run the shoot script
```bash
OUT="${ARGUMENTS:-/home/dev/screenshots}"
cd /home/dev/kube-coder/charts/workspace/web # must run where node_modules is
node scripts/shoot.mjs "$OUT"
ls -t "$OUT"/*.png | head
```
Then Read a couple of the output PNGs to visually confirm the change.
## Gotchas
- **Onboarding modal** — a first-run `.ob-scrim` overlay covers the page and
eats clicks. The shoot scripts suppress it with
`addInitScript(() => localStorage.setItem('kc.onboardingDone','true'))`. If
you script a capture by hand, do the same.
- **Theme forcing** — Chromium ignores `prefers-color-scheme` here; the scripts
set `data-theme="dark"` on `<html>` after navigation so the tokens.css dark
rules apply. Don't rely on the OS media query.
- **Routes 404 in-browser but 200 via curl** — usually `dev_server.py`'s auth
monkeypatch went stale against a `server.py` signature change. Check
`/tmp/dev_server.log` for a `TypeError` in `check_claude_auth`; the stubs
should swallow `*args, **kwargs`.
## Cleanup
```bash
pkill -f "dev_server.py 7070" 2>/dev/null || true
```