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:
@@ -157,7 +157,9 @@ function Failure({ event }: { event: EventRow }) {
|
||||
) : (
|
||||
<span className="size-4 shrink-0" />
|
||||
)}
|
||||
<span className="min-w-0 flex-1">
|
||||
{/* A traceback's first line runs long; it wraps rather than widening
|
||||
the card, and the rest is behind the chevron anyway. */}
|
||||
<span className="min-w-0 flex-1 break-words">
|
||||
<span className="font-mono text-sm">
|
||||
{event.node || event.flow || "engine"}
|
||||
</span>
|
||||
@@ -289,24 +291,28 @@ export function HealthActivity({ range }: { range: Range }) {
|
||||
<span className="min-w-0 flex-1 truncate font-mono">
|
||||
{run.flow}
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{/* Five fixed columns do not fit a phone. What caused a run
|
||||
is the one a narrow row can do without — the status, the
|
||||
duration and when it ran are why anyone reads this. */}
|
||||
<span className="hidden shrink-0 text-xs text-muted-foreground sm:inline">
|
||||
{run.source}
|
||||
</span>
|
||||
<span
|
||||
className={
|
||||
className={cn(
|
||||
"shrink-0",
|
||||
run.status === "error"
|
||||
? "text-destructive"
|
||||
: run.status === "ok"
|
||||
? "text-status-success"
|
||||
: "text-muted-foreground"
|
||||
}
|
||||
: "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
{run.status}
|
||||
</span>
|
||||
<span className="w-16 text-right text-muted-foreground">
|
||||
<span className="w-16 shrink-0 text-right text-muted-foreground">
|
||||
{si(run.duration_ms)} ms
|
||||
</span>
|
||||
<span className="w-16 text-right text-xs text-muted-foreground">
|
||||
<span className="w-16 shrink-0 text-right text-xs text-muted-foreground">
|
||||
{ago(run.started_at)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -362,8 +368,10 @@ export function HealthActivity({ range }: { range: Range }) {
|
||||
<span className="min-w-0 flex-1 truncate font-mono">
|
||||
{item.node}
|
||||
</span>
|
||||
<span className="text-muted-foreground">{item.reason}</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
<span className="min-w-0 max-w-[40%] truncate text-muted-foreground">
|
||||
{item.reason}
|
||||
</span>
|
||||
<span className="shrink-0 text-xs text-muted-foreground">
|
||||
{ago(item.ts)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -172,8 +172,10 @@ export function HealthOverview({
|
||||
<th className="px-3 pb-2 text-center font-medium">Lag</th>
|
||||
{/* A bounded share rather than all the slack: the columns
|
||||
beside it grow with their own content, so the numbers
|
||||
spread across the middle instead of huddling on the left. */}
|
||||
<th className="w-1/3 min-w-32 pb-2 pl-3 text-right font-medium">
|
||||
spread across the middle instead of huddling on the left.
|
||||
Its 128px floor is more than a phone has to spare, and a
|
||||
curve that narrow says nothing, so it goes below `sm`. */}
|
||||
<th className="hidden w-1/3 pb-2 pl-3 text-right font-medium sm:table-cell sm:min-w-32">
|
||||
Trend
|
||||
</th>
|
||||
</tr>
|
||||
@@ -181,11 +183,14 @@ export function HealthOverview({
|
||||
<tbody>
|
||||
{(flows ?? []).map((row: FlowRollup) => (
|
||||
<tr key={row.flow} className="border-t border-border">
|
||||
<td className="py-2 pr-3">
|
||||
<td className="max-w-0 py-2 pr-3">
|
||||
{/* `max-w-0` is what lets a cell truncate at all: without
|
||||
it the table sizes to the longest name and pushes the
|
||||
page sideways. */}
|
||||
<Link
|
||||
to="/flows/$flowName"
|
||||
params={{ flowName: row.flow }}
|
||||
className="font-mono hover:underline"
|
||||
className="block truncate font-mono hover:underline"
|
||||
>
|
||||
{row.flow || "—"}
|
||||
</Link>
|
||||
@@ -210,7 +215,7 @@ export function HealthOverview({
|
||||
</td>
|
||||
{/* The dot straddles the curve's right edge, so the cell
|
||||
keeps a little room for the half that hangs out. */}
|
||||
<td className="py-2 pr-2 pl-3 text-right">
|
||||
<td className="hidden py-2 pr-2 pl-3 text-right sm:table-cell">
|
||||
<Spark counts={row.spark} />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user