Rework the dashboards onto the flow canvas
One shell for both editors. Flows and dashboards each get a searchable
overview under the padded shell, their editors move to the full-bleed
canvas, and the floating chrome is shared: a title bar that only says
what you are looking at, and a bottom dock carrying everything else —
the flow bar's status, settings and Publish moved down there, the
add-flow button moved to the overview.
Dashboards gain the rest of M4's visualization work:
- widgets are picked by clicking them, with the header as the drag
handle so a slider still slides and a switch still flips while
editing; settings moved into the flows' SidePanel
- react-grid-layout for drag and edge-resize, so the stored x/y finally
mean something; a dashboard nobody arranged is shelf-packed once
- a per-dashboard grid size, so a panel can be matched to its screen
- the chart widget, drawn with uPlot: several messages on one axis, fed
from the stored history plus the live socket tail, coloured from the
new --chart-1..5 ramp
- /view/{name}: the URL a wall panel is pointed at — no sidebar, no
footer, no editing, and no editor code, since routes are split
- a widget wired to a payload type it cannot carry, or wired to nothing
at all, carries the same red dot a failing node does; the picker
records the type it bound and WidgetDef refuses a mismatch on save
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H7LwYgJfpkbLCTeiAf8U4A
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Dashboard surface, routed through the design tokens.
|
||||
*
|
||||
* Scoped like `Flow/flow.css`: the grid maths and the react-grid-layout
|
||||
* overrides live beside the components that use them rather than in index.css,
|
||||
* so nothing here touches the byte-identical token blocks.
|
||||
*/
|
||||
|
||||
/* The same dot grid the flow viewport paints, as plain CSS: a dashboard has no
|
||||
zoom, so it needs none of React Flow's rescaling. */
|
||||
.dot-canvas {
|
||||
background-color: var(--background);
|
||||
background-image: radial-gradient(
|
||||
circle at 1px 1px,
|
||||
color-mix(in srgb, var(--muted-foreground) 30%, transparent) 1.5px,
|
||||
transparent 0
|
||||
);
|
||||
background-size: 24px 24px;
|
||||
}
|
||||
|
||||
/*
|
||||
* View mode's grid. Column count is per dashboard (`--widget-cols`), so a wall
|
||||
* panel can be matched to its own width. Below the large breakpoint the stored
|
||||
* placement is meaningless — three columns cannot hold a twelve-column
|
||||
* arrangement — so widgets stack full width and keep only their height.
|
||||
*/
|
||||
.widget-grid {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
grid-auto-rows: 5rem;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.widget-cell {
|
||||
grid-row: span var(--h);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 64rem) {
|
||||
.widget-grid {
|
||||
grid-template-columns: repeat(var(--widget-cols), minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.widget-cell {
|
||||
grid-column: span var(--w);
|
||||
}
|
||||
|
||||
/* Only once something has actually been placed; an untouched dashboard has
|
||||
every widget at 0,0 and would pile up on one cell. */
|
||||
.widget-grid[data-placed] .widget-cell {
|
||||
grid-column: var(--x) / span var(--w);
|
||||
grid-row: var(--y) / span var(--h);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
* furniture rather than replaced.
|
||||
*/
|
||||
.u-legend {
|
||||
font-size: 0.75rem;
|
||||
color: var(--muted-foreground);
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.u-legend .u-marker {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-width: 2px;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.u-legend .u-value {
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
.u-legend .u-series.u-off {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.u-cursor-x,
|
||||
.u-cursor-y {
|
||||
border-color: color-mix(in srgb, var(--muted-foreground) 55%, transparent);
|
||||
}
|
||||
|
||||
.u-select {
|
||||
background: color-mix(in srgb, var(--primary) 12%, transparent);
|
||||
}
|
||||
|
||||
/* react-grid-layout ships a red placeholder and a black handle glyph. */
|
||||
.react-grid-item.react-grid-placeholder {
|
||||
background: var(--primary);
|
||||
border-radius: var(--radius-lg);
|
||||
opacity: 0.12;
|
||||
}
|
||||
|
||||
.react-grid-item > .react-resizable-handle::after {
|
||||
border-right-color: var(--muted-foreground);
|
||||
border-bottom-color: var(--muted-foreground);
|
||||
}
|
||||
Reference in New Issue
Block a user