Act on a run, and read Home top-down
Docs / docs (push) Successful in 23s
Playwright Tests / test-playwright (1, 2) (push) Successful in 3m18s
Playwright Tests / test-playwright (2, 2) (push) Successful in 1m49s
pre-commit / pre-commit (push) Failing after 2m5s
Test Backend / test-backend (push) Successful in 2m39s
Compose Smoke Test / test-compose (push) Successful in 33s
Playwright Tests / merge-reports (push) Successful in 1m11s

Runs: a run can now be deleted (DELETE /runs/{id}, cancelling a live one
first), exported as csv from the screen's own filters, and its flow label
opens the flow. Its "Parameters" panel became "Inputs" and lists every
input the flow declares, marking the ones that took the flow's own value
rather than the run's — the comparison table resolves the same defaults
instead of printing "unset".

Home reads brain, dashboards, health, flows: the mosaic is one full-width
scrolling strip, and the flows list and the flow-activity rollups merged
into a single left-joined table so a flow's state and its numbers sit on
one row.

Charts take a drag to narrow the x window and a double click or tap to
come back out. UplotChart holds the scale and passes resetScales:false
while a window is held, which is what the old comment said made this
impossible.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019LrWVRguqbk33YzfEeUx5W
This commit is contained in:
2026-08-29 08:37:11 +02:00
co-authored by Claude Opus 5
parent 4215e057d1
commit 7e506b26c0
18 changed files with 1082 additions and 365 deletions
@@ -153,10 +153,13 @@ function Footprint({ dashboard }: { dashboard: DashboardDef_Output }) {
function Tile({
dashboard,
preview,
className,
}: {
dashboard: DashboardSummary
/** Read the document for a footprint, or settle for the name alone. */
preview: boolean
/** What the layout needs of it — a width, in the scrolling strip. */
className?: string
}) {
// The working copy, which is what the list itself is a summary of, so the
// preview shows what an editor would open rather than the last publish.
@@ -170,7 +173,10 @@ function Tile({
to="/dashboards/$name"
params={{ name: dashboard.name }}
data-testid="home-dashboard-tile"
className="grid content-start gap-2 rounded-lg border border-border p-2 transition-colors hover:bg-accent/50"
className={cn(
"grid content-start gap-2 rounded-lg border border-border p-2 transition-colors hover:bg-accent/50",
className,
)}
>
{preview && isPending ? (
<Skeleton className="aspect-video rounded-sm" />
@@ -213,19 +219,37 @@ export function byRecency<
)
}
/** The dashboards, as the shapes they are, beside the flows on the home view. */
/**
* The dashboards, as the shapes they are.
*
* Two layouts, because it is read two ways: a column of pairs where it sits
* beside something else, and one wide strip where it has the page to itself.
* A tile is `content-start` around an `aspect-video` footprint and so has no
* width of its own — the strip has to give it one.
*/
export function DashboardMosaic({
dashboards,
isPending,
row = false,
}: {
dashboards: DashboardSummary[]
isPending: boolean
/** One scrolling strip instead of a two-column grid. */
row?: boolean
}) {
const container = row
? "flex snap-x snap-mandatory gap-3 overflow-x-auto p-3"
: "grid gap-3 p-3 sm:grid-cols-2"
const tile = row ? "w-56 shrink-0 snap-start" : ""
if (isPending) {
return (
<div className="grid gap-3 p-3 sm:grid-cols-2">
<div className={container}>
{Array.from({ length: 2 }).map((_, index) => (
<Skeleton key={index} className="aspect-[4/3] rounded-lg" />
<Skeleton
key={index}
className={cn("aspect-[4/3] rounded-lg", tile)}
/>
))}
</div>
)
@@ -248,12 +272,13 @@ export function DashboardMosaic({
}
return (
<div className="grid gap-3 p-3 sm:grid-cols-2">
<div className={container}>
{dashboards.map((dashboard, index) => (
<Tile
key={dashboard.name}
dashboard={dashboard}
preview={index < PREVIEWS}
className={tile}
/>
))}
</div>