/** * Central state contracts for the graph: in-memory graph, connection path (edge visuals), * and node display status. Use these types to keep state flow predictable and readable. * * ## State flow (high level) * * 1. **Graph state** (nodes, edges) – Owned by useGraphStateWithHistory, persisted by useCanvasGraph. * Updates via setNodes/setEdges (with undo) or setNodesSilent/setEdgesSilent (no history). * * 2. **Connection path state** – Owned by useCanvasConnectionPath, provided via FlowContext. * Nodes report lifecycle (updating / error / paused) via useSyncConnectionStatus; the hook * updates context sets. Edges read those sets and getConnectionStatus() to decide color (default / updating / paused / error). * * 3. **Node display state** – Each node type derives its own UI state (e.g. loading, success, error, initial). * Rendering node uses displayStatus from useRenderingNodeState; other nodes can use getNodeDisplayStatus(). */ import type { AppNode, AppEdge } from './nodeTypes' // --------------------------------------------------------------------------- // Graph state (in-memory) // --------------------------------------------------------------------------- /** In-memory graph: nodes and edges. Updated via setNodes/setEdges; history in useGraphStateWithHistory. */ export type GraphState = { nodes: AppNode[] edges: AppEdge[] } // --------------------------------------------------------------------------- // Connection path state (edge visuals and path animation) // --------------------------------------------------------------------------- /** * Slice of FlowContext that drives edge status and path animation. * Nodes report updating/error/paused via useSyncConnectionStatus; this state is derived from those reports. * Edges use pathNodeIds, pausedSegmentNodeIds, activeSegmentNodeIds, errorTargetNodeIds in getConnectionStatus(). */ export type ConnectionPathState = { /** Node ids currently doing async work (e.g. render loading). Path edges show "updating" (blue). */ connectionPathUpdatingNodeIds: string[] /** Node ids that triggered the update (e.g. config changed). Path = downstream(trigger) ∩ upstream(updating). */ connectionPathTriggerNodeIds: string[] /** All node ids on the path. Edges with both endpoints here animate. */ connectionPathNodeIds: Set /** Path nodes in the paused segment (yellow edges). */ connectionPathPausedSegmentNodeIds: Set /** Path nodes in the active segment (blue edges). */ connectionPathActiveSegmentNodeIds: Set /** Node ids on hold (e.g. render in manual mode). */ connectionPathPausedNodeIds: string[] /** Node ids with error (incoming edges show red). */ connectionPathErrorNodeIds: string[] } // --------------------------------------------------------------------------- // Node display status (for UI: status indicator, empty state) // --------------------------------------------------------------------------- /** Status for node UI: what to show in NodeStatusIndicator and empty/error states. */ export type NodeDisplayStatus = 'initial' | 'loading' | 'success' | 'error' /** * Derive display status from node state. Use in nodes to drive NodeStatusIndicator and empty/error UI. * Priority: loading > error > hasContent (success) > initial. */ export function getNodeDisplayStatus(state: { loading: boolean error: unknown hasContent: boolean }): NodeDisplayStatus { if (state.loading) return 'loading' if (state.error != null) return 'error' if (state.hasContent) return 'success' return 'initial' } // --------------------------------------------------------------------------- // Stored graph state (persistence) // --------------------------------------------------------------------------- /** Shape of graph state when saved/loaded (e.g. localStorage). version for future migrations. */ export type StoredGraphState = { version: number nodes: unknown[] edges: unknown[] }