website: nxdns.sh on astro and starlight, with the docs and 0.0.24 screenshots
Gates / frontend (push) Failing after 8m6s
Gates / package (push) Skipped
Gates / container (push) Skipped
Gates / test (push) Successful in 2m38s
Gates / test-aarch64 (push) Successful in 8m52s
CI / gates (push) Failing after 11m33s

This commit is contained in:
2026-10-21 17:03:01 +02:00
parent c21e5a9542
commit 93e2683e71
59 changed files with 8580 additions and 13 deletions
+7 -5
View File
@@ -13,14 +13,15 @@ PAGES = [
]
def img(name, scheme):
with open(f"{SHOTS}/{name}-{scheme}.png", "rb") as f:
def img(name, scheme, form=""):
with open(f"{SHOTS}/{name}-{scheme}{form}.png", "rb") as f:
data = base64.b64encode(f.read()).decode()
return f'<figure><img alt="{name} {scheme}" src="data:image/png;base64,{data}"><figcaption>{scheme}</figcaption></figure>'
return f'<figure><img alt="{name} {scheme}{form}" src="data:image/png;base64,{data}"><figcaption>{scheme}{form}</figcaption></figure>'
rows = "\n".join(
f'<section><h2>{html.escape(caption)}</h2><div class="pair">{img(name, "light")}{img(name, "dark")}</div></section>'
f'<section><h2>{html.escape(caption)}</h2><div class="pair">{img(name, "light")}{img(name, "dark")}</div>'
f'<div class="pair phone">{img(name, "light", "-phone")}{img(name, "dark", "-phone")}</div></section>'
for name, caption in PAGES
)
page = f"""<!doctype html>
@@ -33,12 +34,13 @@ body {{ margin: 0; padding: 24px 16px; background: var(--bg); color: var(--fg);
h1 {{ font-size: 20px; margin: 0 0 24px; }}
h2 {{ font-size: 15px; font-weight: 500; margin: 32px 0 12px; }}
.pair {{ display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }}
.phone {{ grid-template-columns: repeat(2, minmax(0, 300px)); margin-top: 16px; }}
@media (max-width: 800px) {{ .pair {{ grid-template-columns: 1fr; }} }}
figure {{ margin: 0; }}
img {{ width: 100%; height: auto; border: 1px solid var(--line); border-radius: 6px; display: block; }}
figcaption {{ color: var(--muted); font-size: 13px; margin-top: 4px; }}
</style></head>
<body><h1>nxdns admin — marketing screenshots (1440×900 @2x)</h1>
<body><h1>nxdns admin — marketing screenshots (1440×900 @2x, phone 390×844 @3x)</h1>
{rows}
</body></html>
"""
+16 -5
View File
@@ -21,11 +21,17 @@ async function settle(page) {
await page.waitForTimeout(2500);
}
// The phone form is 390px wide, where the admin UI switches to its drawer layout, and touch-only, so
// no hover state is drawn.
const FORMS = [
["", { viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2 }],
["-phone", { viewport: { width: 390, height: 844 }, deviceScaleFactor: 3, isMobile: true, hasTouch: true }],
];
const browser = await chromium.launch();
for (const scheme of ["light", "dark"]) {
for (const [suffix, form] of FORMS) for (const scheme of ["light", "dark"]) {
const context = await browser.newContext({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 2,
...form,
colorScheme: scheme,
timezoneId: "Europe/Paris",
locale: "en-GB",
@@ -38,10 +44,15 @@ for (const scheme of ["light", "dark"]) {
await page.waitForURL((u) => !u.pathname.startsWith("/login"));
for (const [name, path] of PAGES) {
await page.goto(BASE + path);
await page.mouse.move(1430, 890);
if (!form.hasTouch) await page.mouse.move(1430, 890);
await settle(page);
if (name === "activity") await page.waitForTimeout(8000);
const file = `${outDir}/${name}-${scheme}.png`;
// A phone stacks the health cards and filters above the episodes, so the first screen shows none.
if (name === "diagnostics" && form.hasTouch) {
await page.getByRole("heading", { name: "Active" }).evaluate((h) => h.scrollIntoView({ block: "start" }));
await page.waitForTimeout(500);
}
const file = `${outDir}/${name}-${scheme}${suffix}.png`;
await page.screenshot({ path: file });
console.log(file);
}