refactoring: graph state

This commit is contained in:
2026-03-12 17:40:05 +01:00
parent bfd1a40332
commit 4bd2f1b458
9 changed files with 176 additions and 58 deletions

View File

@@ -0,0 +1,87 @@
/**
* 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<string>
/** Path nodes in the paused segment (yellow edges). */
connectionPathPausedSegmentNodeIds: Set<string>
/** Path nodes in the active segment (blue edges). */
connectionPathActiveSegmentNodeIds: Set<string>
/** 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[]
}