refactoring: graph state
This commit is contained in:
@@ -2,9 +2,8 @@
|
||||
* Connection status: visual state of an edge (color/class).
|
||||
* Priority when multiple apply: error > paused > updating > default.
|
||||
*
|
||||
* Status is derived from node lifecycle state: nodes report updating / paused / error
|
||||
* via useSyncConnectionStatus() in nodeLifecycle.ts, which updates FlowContext sets.
|
||||
* Edges read those sets here to pick the single status per edge.
|
||||
* State flow: nodes report lifecycle via useSyncConnectionStatus() (nodeLifecycle.ts) → FlowContext
|
||||
* holds the sets → edges pass those sets into getConnectionStatus() here. See lib/graph/state.ts.
|
||||
*/
|
||||
|
||||
export type ConnectionStatus = 'default' | 'updating' | 'paused' | 'error'
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/**
|
||||
* FlowContext provides graph state, connection path state, and UI state to nodes and edges.
|
||||
*
|
||||
* ## 1. Graph state (nodes, edges)
|
||||
* - nodes, setNodes, edges, setEdges – owned by useCanvasGraph (history + persistence).
|
||||
*
|
||||
* ## 2. Connection path state (edge visuals and path animation)
|
||||
* - Nodes report lifecycle (updating / error / paused) via useSyncConnectionStatus() in nodeLifecycle.
|
||||
* - Context holds: connectionPathUpdatingNodeIds, connectionPathTriggerNodeIds, connectionPathNodeIds,
|
||||
* connectionPathPausedSegmentNodeIds, connectionPathActiveSegmentNodeIds, connectionPathPausedNodeIds,
|
||||
* connectionPathErrorNodeIds, plus add/remove/start/end callbacks.
|
||||
* - Edges use these in getConnectionStatus() for color (default / updating / paused / error). See connectionStatus.ts.
|
||||
*
|
||||
* ## 3. UI state
|
||||
* - renamingNodeId, fullscreenNodeId, connectionFrom (drag-from handle), flowActionsRef, isValidConnection.
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react'
|
||||
import type { Connection } from '@xyflow/react'
|
||||
import type { AppNode, AppEdge } from './nodeTypes'
|
||||
@@ -13,48 +30,36 @@ export type FlowActions = {
|
||||
}
|
||||
|
||||
export type FlowContextValue = {
|
||||
// ---- Graph state ----
|
||||
nodes: AppNode[]
|
||||
setNodes: (updater: AppNode[] | ((prev: AppNode[]) => AppNode[])) => void
|
||||
edges: AppEdge[]
|
||||
setEdges: (updater: AppEdge[] | ((prev: AppEdge[]) => AppEdge[])) => void
|
||||
|
||||
// ---- UI state ----
|
||||
renamingNodeId: string | null
|
||||
setRenamingNodeId: (id: string | null) => void
|
||||
/** Set when user starts dragging from an output handle; cleared on connect end. Used to highlight valid targets. */
|
||||
connectionFrom: ConnectionFrom
|
||||
setConnectionFrom: (v: ConnectionFrom) => void
|
||||
isValidConnection: (connection: Connection) => boolean
|
||||
/** Set by FlowKeyboardShortcuts so Node menubar can trigger paste / fit view. */
|
||||
flowActionsRef: React.MutableRefObject<FlowActions | null>
|
||||
/** When set, graph centers on this node and a fullscreen dialog shows the node. Cleared on close. */
|
||||
fullscreenNodeId: string | null
|
||||
setFullscreenNodeId: (id: string | null) => void
|
||||
/** Node ids currently updating (e.g. render loading, agent running). Only edges on those paths show the ant trail. */
|
||||
|
||||
// ---- Connection path state (edge status and path animation; see state.ts ConnectionPathState) ----
|
||||
connectionPathUpdatingNodeIds: string[]
|
||||
/** Node ids that triggered the current update (e.g. variable/config that changed). Path is restricted to downstream(trigger) ∩ upstream(updating). */
|
||||
connectionPathTriggerNodeIds: string[]
|
||||
/** Call when this node's output changed and may trigger downstream updates (e.g. variable value, config content). */
|
||||
addConnectionPathTrigger: (nodeId: string) => void
|
||||
/** All node ids on the path of an update. Edges with both endpoints in this set animate. */
|
||||
connectionPathNodeIds: Set<string>
|
||||
/** Path nodes in the "paused" segment (from trigger up to Archon on hold). Those edges are yellow. */
|
||||
connectionPathPausedSegmentNodeIds: Set<string>
|
||||
/** Path nodes not in the paused segment (downstream of pause). Only those edges are blue (updating). */
|
||||
connectionPathActiveSegmentNodeIds: Set<string>
|
||||
/** Nodes that are on hold (e.g. Renderer in manual mode waiting for Run). */
|
||||
connectionPathPausedNodeIds: string[]
|
||||
/** Add this node as paused (on hold); remove when user continues. */
|
||||
addConnectionPathPausedNode: (nodeId: string) => void
|
||||
/** Remove this node from paused. */
|
||||
removeConnectionPathPausedNode: (nodeId: string) => void
|
||||
/** Node ids that have an error (e.g. Render node). Incoming edges show error status (red). */
|
||||
connectionPathErrorNodeIds: string[]
|
||||
/** Call when this node has an error; remove when error is cleared. */
|
||||
addConnectionPathError: (nodeId: string) => void
|
||||
/** Call when this node's error is cleared. */
|
||||
removeConnectionPathError: (nodeId: string) => void
|
||||
/** Call when a path update starts for this node. Animation runs at least CONNECTION_PATH_UPDATE_MIN_MS. */
|
||||
startConnectionPathUpdate: (nodeId: string) => void
|
||||
/** Call when a path update ends for this node. If min duration not reached, animation continues until then. */
|
||||
endConnectionPathUpdate: (nodeId: string) => void
|
||||
}
|
||||
|
||||
|
||||
@@ -2,30 +2,21 @@
|
||||
* Node lifecycle: contract that nodes implement so the graph can show the right
|
||||
* connection status (edge colors) and path animation.
|
||||
*
|
||||
* ## State flow
|
||||
*
|
||||
* Nodes report lifecycle (updating / error / paused) via useSyncConnectionStatus(id, state).
|
||||
* This hook updates FlowContext sets; edges read them via getConnectionStatus() in connectionStatus.ts.
|
||||
* See lib/graph/state.ts for the overall state flow (graph state, connection path state).
|
||||
*
|
||||
* ## Lifecycle phases (conceptual)
|
||||
*
|
||||
* - **Idle** – Node is not on an active path; edges to/from it use default style.
|
||||
* - **Trigger** – Node's output changed (e.g. config content, variable value). Reported
|
||||
* automatically when the node calls `updateData()` from useAbstractNode. Downstream
|
||||
* path is computed from triggers + updating nodes.
|
||||
* - **Updating** – Node is doing async work (e.g. renderer loading, agent run triggered by renderer).
|
||||
* Report `updating: true` at start, `updating: false` when done. Incoming/outgoing
|
||||
* edges on the path show "updating" (blue).
|
||||
* - **Paused** – Node is on hold (e.g. renderer in manual mode waiting for Run after inputs changed).
|
||||
* Report `paused: true` when waiting, `paused: false` when not. Edges in the paused
|
||||
* segment show "paused" (yellow).
|
||||
* - **Error** – Node has an error to show. Report `error: true` when error is set,
|
||||
* `error: false` when cleared. Incoming edges to this node show "error" (red).
|
||||
* - **Idle** – Node is not on an active path; edges use default style.
|
||||
* - **Trigger** – Node's output changed; path is computed from triggers + updating nodes.
|
||||
* - **Updating** – Node is doing async work. Report `updating: true` → false. Path edges show blue.
|
||||
* - **Paused** – Node is on hold (e.g. manual mode waiting for Run). Report `paused: true` → false. Segment shows yellow.
|
||||
* - **Error** – Node has an error. Report `error: true` → false. Incoming edges show red.
|
||||
*
|
||||
* ## Connection status integration
|
||||
*
|
||||
* Edge status is derived in getConnectionStatus() from the sets that this lifecycle
|
||||
* feeds: connectionPathUpdatingNodeIds, connectionPathPausedNodeIds,
|
||||
* connectionPathErrorNodeIds (plus path/paused segment from graphPath). Priority:
|
||||
* error > paused > updating > default.
|
||||
*
|
||||
* Nodes that can be updating, paused, or in error should call useSyncConnectionStatus()
|
||||
* with their current state so edges update correctly.
|
||||
* Priority for edge status: error > paused > updating > default.
|
||||
*/
|
||||
|
||||
import { useContext, useEffect, useRef } from 'react'
|
||||
|
||||
87
frontend/src/lib/graph/state.ts
Normal file
87
frontend/src/lib/graph/state.ts
Normal 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[]
|
||||
}
|
||||
Reference in New Issue
Block a user