Computed flow layout, and mobile written into the design

The canvas lays itself out: a layered graph, left to right on a desktop and
top to bottom on a phone, with room reserved for the value each edge carries.
Nodes cannot be dragged and `NodeDef.position` is gone from the document —
a graph nobody can arrange is one worth keeping small, which is what keeps
flows atomic. Endpoints join the same layout, so their lanes and the
localStorage that remembered where they were dragged go too.

Mobile, per the new Responsive section of DESIGN-GUIDELINES.md: the dock caps
its width and wraps instead of running off the screen, the dashboard stacks
into one column rather than shrinking a wall panel to a fifth of its size, and
Home stops widening its grid track past the viewport. A Playwright project at
a phone's width fails the build when a screen no longer fits.

Along the way: publish is the checkmark that was already there rather than a
button that appears and disappears, with discard beside it on both the flow
and the dashboard; the brain reveals a neuron's name on the first tap; and the
port sparklines get room to breathe.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDSXaRhvqHYNevgDGmNAto
This commit is contained in:
2026-08-17 17:35:14 +02:00
co-authored by Claude Opus 5
parent e7a1466d7b
commit 39ee0e0aa5
37 changed files with 998 additions and 527 deletions
@@ -22,9 +22,10 @@
/*
* View mode's grid. Column count is per dashboard (`--widget-cols`), so a wall
* panel can be matched to its own width. There is no responsive fallback: the
* grid lives on a canvas of the panel's own pixel size, which is scaled to the
* viewport rather than reflowed into it.
* panel can be matched to its own width. It does not reflow: the grid lives on
* a canvas of the panel's own pixel size, which is scaled to the viewport.
* A phone gets `.widget-stacked` below instead, which is a different surface
* rather than a narrower version of this one.
*/
.widget-grid {
display: grid;
@@ -46,6 +47,25 @@
grid-row: var(--y) / span var(--h);
}
/*
* One column, in reading order, at the viewport's own width — what a phone
* gets instead of a wall panel shrunk to 18%. Arranging is not a phone
* feature, so the stored x/w are dropped and only the height a widget asked
* for survives: a chart still needs its room, a stat still does not.
* See DESIGN-GUIDELINES.md → Responsive.
*/
.widget-grid.widget-stacked,
.widget-grid.widget-stacked[data-placed] {
display: flex;
flex-direction: column;
}
.widget-grid.widget-stacked .widget-cell {
grid-column: auto;
grid-row: auto;
height: calc(var(--h) * 5rem + (var(--h) - 1) * 0.75rem);
}
/*
* uPlot, routed through the tokens. Its own legend is the hover readout as
* well — the value each line carried at the cursor — so it is styled as chart