Add the dashboard table framework design and Codex briefs

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-20 20:54:12 +00:00
co-authored by Claude Fable 5.1
parent 0f17480be7
commit 8889a4e351
5 changed files with 312 additions and 0 deletions
@@ -0,0 +1,42 @@
// usage: node measure.mjs <port> <outdir> [widths]
import { chromium } from "playwright";
import fs from "node:fs";
const [port, outdir, widthsArg] = process.argv.slice(2);
const base = `http://127.0.0.1:${port}`;
const widths = (widthsArg || "390,768,1280,1920,2560").split(",").map(Number);
const pages = ["/dashboard","/dashboard/runs","/dashboard/runs/1","/dashboard/runs/2","/dashboard/articles","/dashboard/articles/1","/dashboard/ratings","/dashboard/ratings?tab=events","/dashboard/interests","/dashboard/feeds","/dashboard/feeds?status=added","/dashboard/profile","/dashboard/stats","/dashboard/jobs","/dashboard/settings","/dashboard/settings/history","/dashboard/users","/","/issues","/account"];
fs.mkdirSync(outdir, { recursive: true });
const browser = await chromium.launch();
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
const page = await ctx.newPage();
await page.goto(`${base}/login`);
await page.fill('input[name="username"]', "admin");
await page.fill('input[name="password"]', "adminpassword123");
await page.click('button[type="submit"]');
await page.waitForLoadState("networkidle");
const report = [];
for (const w of widths) {
await page.setViewportSize({ width: w, height: 1000 });
for (const p of pages) {
const resp = await page.goto(`${base}${p}`, { waitUntil: "networkidle" });
const status = resp?.status();
const info = await page.evaluate(() => {
const de = document.documentElement;
const container = document.querySelector(".dashboard, main > section, main > div, main");
const tables = [...document.querySelectorAll("table")].map((t) => {
const host = t.closest(".scroll-x") || t.parentElement;
const r = t.getBoundingClientRect();
return { cls: t.className, ths: t.querySelectorAll("thead th").length, rows: t.querySelectorAll("tbody tr").length, tableW: Math.round(r.width), hostW: Math.round(host.getBoundingClientRect().width), hostScroll: host.scrollWidth, hostClient: host.clientWidth, overflow: host.scrollWidth > host.clientWidth + 1 };
});
const culprits = [...document.querySelectorAll("body *")].filter((el) => el.scrollWidth > el.clientWidth + 1 && getComputedStyle(el).overflowX === "visible" && !el.closest("table")).slice(0, 5).map((el) => `${el.tagName.toLowerCase()}.${String(el.className).split(" ").slice(0,2).join(".")} ${el.scrollWidth}>${el.clientWidth}`);
return { pageOverflow: de.scrollWidth > de.clientWidth, docW: de.clientWidth, containerW: container ? Math.round(container.getBoundingClientRect().width) : null, tables, culprits };
});
const name = p.replace(/[\/?=]/g, "_").replace(/^_/, "") || "root";
if (w === 1280 || w === 2560 || w === 390) await page.screenshot({ path: `${outdir}/${w}-${name}.png`, fullPage: true });
report.push({ w, p, status, ...info });
const bad = info.tables.filter((t) => t.overflow);
console.log(`${w} ${p} status=${status} page=${info.pageOverflow ? "OVERFLOW" : "ok"} container=${info.containerW} tables=${info.tables.length} scrolling=${bad.length}${bad.length ? " [" + bad.map((t) => `${t.cls || "-"}:${t.hostScroll}>${t.hostClient}`).join(", ") + "]" : ""}${info.culprits.length ? " culprits=" + info.culprits.join(" | ") : ""}`);
}
}
fs.writeFileSync(`${outdir}/report.json`, JSON.stringify(report, null, 1));
await browser.close();