Make documentation standalone and generalize examples

This commit is contained in:
Melvin Strobl
2026-10-25 14:43:20 +01:00
parent 13bf0f76d7
commit bdb18b99da
57 changed files with 553 additions and 481 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* The house panels, at the size of the screen they hang on.
* Capture dashboards at their target screen size.
*
* `/view/{name}` is the wall-panel route: no sidebar, no editor. Shooting it
* at exactly the panel's pixels is the only way to see whether a tile fell off
+15 -15
View File
@@ -1,11 +1,10 @@
/**
* Visual verification for the integrated local stack (root `make verify`).
* Capture the app at desktop and phone sizes in both themes.
*
* Logs into the dashboard with the bootstrap superuser and captures the app
* shell plus the website hero in both themes. This is the standard "did the
* change actually work" gate — look at the PNGs, do not just trust the build.
* shell. Set WEBSITE_URL to also capture another site's landing page.
*
* Env (all set by the root Makefile):
* Env:
* APP_URL, WEBSITE_URL, FIRST_SUPERUSER, FIRST_SUPERUSER_PASSWORD
* SCREENSHOT_DIR (default: ./screenshots)
*/
@@ -13,14 +12,14 @@ import { mkdir } from "node:fs/promises"
import { chromium } from "@playwright/test"
const APP_URL = process.env.APP_URL || "http://app.localhost"
const WEBSITE_URL = process.env.WEBSITE_URL || "http://localhost"
const WEBSITE_URL = process.env.WEBSITE_URL
const EMAIL = process.env.FIRST_SUPERUSER
const PASSWORD = process.env.FIRST_SUPERUSER_PASSWORD
const OUT = process.env.SCREENSHOT_DIR || "screenshots"
if (!EMAIL || !PASSWORD) {
console.error(
"FIRST_SUPERUSER / FIRST_SUPERUSER_PASSWORD are unset — run from the root `make verify`.",
"Set FIRST_SUPERUSER and FIRST_SUPERUSER_PASSWORD before running this script.",
)
process.exit(1)
}
@@ -50,8 +49,8 @@ async function withTheme(browser, theme, viewport) {
}
// Chromium pins *.localhost to loopback (RFC 6761), so /etc/hosts cannot point
// it at Traefik. The containerised run (root `make verify-docker`) passes the
// mapping through here instead; empty on a host run.
// it at Traefik. A containerised run can pass the mapping through
// HOST_RESOLVER_RULES instead.
const RESOLVER = process.env.HOST_RESOLVER_RULES
const browser = await chromium.launch(
RESOLVER ? { args: [`--host-resolver-rules=${RESOLVER}`] } : {},
@@ -64,11 +63,12 @@ for (const theme of ["light", "dark"]) {
const context = await withTheme(browser, theme, viewport)
const page = await context.newPage()
await page.goto(`${WEBSITE_URL}/`, { waitUntil: "networkidle" })
// networkidle fires before the staggered entrance animations settle, which
// would capture buttons mid-fade and make contrast look broken.
await page.waitForTimeout(1500)
await page.screenshot({ path: `${dir}/website-hero.png` })
if (WEBSITE_URL) {
await page.goto(`${WEBSITE_URL}/`, { waitUntil: "networkidle" })
// Wait for entrance animations before capturing the page.
await page.waitForTimeout(1500)
await page.screenshot({ path: `${dir}/website-hero.png` })
}
await page.goto(`${APP_URL}/login`, { waitUntil: "networkidle" })
await page.screenshot({ path: `${dir}/app-login.png` })
@@ -223,8 +223,8 @@ async function captureDevices(page, dir) {
* A media tile drawing what a camera published: the Demo's Media screen,
* whose house is drawn by a node as heat.
*
* Skipped unless the Demo is seeded (root `make seed-hosted-demo`), since it
* is the one shot that needs a source of frames. The bytes arrive as
* Skipped unless the instance has the Demo's Media dashboard and frame source.
* The bytes arrive as
* a blob — pushed down the socket, or fetched with the session's credential,
* which no `img` could carry on its own — so a `blob:` source is the proof the
* whole path ran rather than that a picture is merely present.