Flow Studio
Driving Node Status Visualization
BaseNode reads nodeStatuses from designerModeStore to render status overlay badges on each canvas node. Overlays are hidden in design mode and visible in execution mode — components do not need to manage this visibility themselves.
BaseNode Status Overlay Pattern
// BaseNode.tsx (simplified)
import { useDesignerModeStore } from '@flow-studio/services';
import { shallow } from 'zustand/shallow';
interface BaseNodeProps {
id : string;
data : WorkflowNodeData;
}
export function BaseNode({ id, data }: BaseNodeProps) {
const { nodeStatus, currentMode } = useDesignerModeStore(
state => ({
nodeStatus : state.nodeStatuses[id] ?? null,
currentMode: state.currentMode
}),
shallow
);
const showOverlay = currentMode === 'execution' && nodeStatus !== null;
return (
<div className={`node node-${nodeStatus?.status ?? 'default'}`}>
<div className="node-header">{data.label}</div>
<div className="node-body">
{/* node-specific content (rendered by subclass) */}
</div>
{showOverlay && (
<div className={`node-status-badge status-${nodeStatus.status}`}>
<StatusIcon status={nodeStatus.status} />
{nodeStatus.durationMs != null &&
<span>{nodeStatus.durationMs}ms</span>}
</div>
)}
</div>
);
}
Status Badge CSS Classes
/* Node border color changes based on status */
.node-success { border-color: var(--success-color); }
.node-failed { border-color: var(--error-color); }
.node-running { border-color: var(--accent2); animation: pulse 1s infinite; }
.node-suspended { border-color: var(--accent3); animation: pulse-slow 2s infinite; }
.node-skipped { border-color: var(--warning-color); opacity: 0.6; }
.node-pending { border-color: var(--border-color); }
/* Status badge overlay (top-right corner of node) */
.node-status-badge { position: absolute; top: -8px; right: -8px; border-radius: 50%; }
.status-success { background: var(--success-color); }
.status-failed { background: var(--error-color); }
.status-running { background: var(--accent2); }
Custom Node Renderer — Extending BaseNode
// Custom nodes extend BaseNode — they inherit the status overlay for free
export function ApprovalNode({ id, data }: BaseNodeProps) {
return (
<BaseNode id={id} data={data}>
{/* Custom content inside the node */}
<div className="approval-actors">
<span>Actor: {data.config.actorId}</span>
</div>
</BaseNode>
);
}
// BaseNode handles mode detection and overlay rendering
// Custom nodes do NOT need to read from designerModeStore directly
Status overlays are additive: They render on top of the existing node design. Custom node renderers do not need to check
currentMode — BaseNode handles that. Custom nodes only need to render their own content; the status badge is injected automatically.