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:
@@ -40,6 +40,7 @@ import {
|
||||
import useCustomToast from "@/hooks/useCustomToast"
|
||||
import { inCodeEditor, useShortcuts } from "@/lib/shortcuts"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { CanvasTitle } from "./CanvasTitle"
|
||||
import { CommandPalette } from "./CommandPalette"
|
||||
import { bindingsKey, deriveEdges, portOf, qualify } from "./deriveEdges"
|
||||
import { EdgeInspector, type InspectedEdge } from "./EdgeInspector"
|
||||
@@ -54,7 +55,6 @@ import {
|
||||
import { FIT_VIEW, FlowDock } from "./FlowDock"
|
||||
import { FlowNode, type FlowNodeData } from "./FlowNode"
|
||||
import { FlowPanel } from "./FlowPanel"
|
||||
import { FlowTabs } from "./FlowTabs"
|
||||
import { LiveEdge } from "./LiveEdge"
|
||||
import { NodePanel } from "./NodePanel"
|
||||
import "./flow.css"
|
||||
@@ -934,18 +934,11 @@ function FlowEditorInner({
|
||||
panelOpen && !editorExpanded && "md:right-[27rem]",
|
||||
)}
|
||||
>
|
||||
<FlowTabs
|
||||
flows={flows.data}
|
||||
active={flowName}
|
||||
saving={saving.isPending}
|
||||
hasDraft={detail.has_draft ?? false}
|
||||
publishing={publish.isPending || saving.isPending}
|
||||
onPublish={() => void publishFlow()}
|
||||
onEditFlow={() => {
|
||||
setSelectedId(null)
|
||||
setFlowPanelOpen(true)
|
||||
}}
|
||||
/>
|
||||
<CanvasTitle>
|
||||
<span className="truncate px-3 py-1.5 text-sm font-medium">
|
||||
{flowDoc.title || flowName}
|
||||
</span>
|
||||
</CanvasTitle>
|
||||
|
||||
<FlowDock
|
||||
flow={flowName}
|
||||
@@ -953,6 +946,14 @@ function FlowEditorInner({
|
||||
running={runMutation.isPending}
|
||||
enabled={detail.enabled ?? true}
|
||||
paused={paused}
|
||||
saving={saving.isPending}
|
||||
hasDraft={detail.has_draft ?? false}
|
||||
publishing={publish.isPending || saving.isPending}
|
||||
onEditFlow={() => {
|
||||
setSelectedId(null)
|
||||
setFlowPanelOpen(true)
|
||||
}}
|
||||
onPublish={() => void publishFlow()}
|
||||
logs={{
|
||||
open: logsOpen,
|
||||
node: logsNode,
|
||||
|
||||
Reference in New Issue
Block a user