feat: introdce store

This commit is contained in:
2026-03-13 00:32:19 +01:00
parent 2d8f13aebb
commit fc265266c7
17 changed files with 2859 additions and 83 deletions

View File

@@ -5,8 +5,7 @@
* ## 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).
* This hook dispatches to the canvas store; edges read path state via selectors.
*
* ## Lifecycle phases (conceptual)
*
@@ -19,14 +18,14 @@
* Priority for edge status: error > paused > updating > default.
*/
import { useContext, useEffect, useRef } from 'react'
import { ConnectionPathContext } from './flowContext'
import { useEffect, useRef } from 'react'
import { dispatchCanvasCommand } from '@/app/canvas/canvasStore'
export type NodeLifecyclePhase = 'idle' | 'trigger' | 'updating' | 'paused' | 'error'
/**
* State that drives connection status for this node.
* Pass the current values from your node; the hook syncs them to FlowContext.
* Pass the current values from your node; the hook syncs them to the canvas store.
*/
export type NodeConnectionStatusState = {
/** Node is doing async work (e.g. loading, running). Incoming/outgoing path edges show blue. */
@@ -38,9 +37,9 @@ export type NodeConnectionStatusState = {
}
/**
* Syncs this node's lifecycle state to FlowContext so connection status (edge colors)
* Syncs this node's lifecycle state to the canvas store so connection status (edge colors)
* and path animation are correct. Call once per node with the current updating/error/paused
* state; the hook will add/remove this node from the appropriate sets.
* state; the hook will dispatch path commands to add/remove this node from the appropriate sets.
*
* Use in any node that can be updating, in error, or paused:
*
@@ -53,39 +52,33 @@ export function useSyncConnectionStatus(
nodeId: string,
state: NodeConnectionStatusState
): void {
const ctx = useContext(ConnectionPathContext)
const ctxRef = useRef(ctx)
ctxRef.current = ctx
const { updating, error, paused } = state
const prevRef = useRef({ updating: false, error: false, paused: false })
useEffect(() => {
const pathCtx = ctxRef.current
const prev = prevRef.current
const nowUpdating = Boolean(updating)
const nowError = Boolean(error)
const nowPaused = Boolean(paused)
if (prev.updating !== nowUpdating) {
if (nowUpdating) pathCtx?.startConnectionPathUpdate?.(nodeId)
else pathCtx?.endConnectionPathUpdate?.(nodeId)
if (nowUpdating) dispatchCanvasCommand({ type: 'path/startUpdate', payload: nodeId })
else dispatchCanvasCommand({ type: 'path/endUpdate', payload: nodeId })
prev.updating = nowUpdating
}
if (prev.error !== nowError) {
if (nowError) pathCtx?.addConnectionPathError?.(nodeId)
else pathCtx?.removeConnectionPathError?.(nodeId)
dispatchCanvasCommand({ type: 'path/setError', payload: { nodeId, error: nowError } })
prev.error = nowError
}
if (prev.paused !== nowPaused) {
if (nowPaused) pathCtx?.addConnectionPathPausedNode?.(nodeId)
else pathCtx?.removeConnectionPathPausedNode?.(nodeId)
dispatchCanvasCommand({ type: 'path/setPaused', payload: { nodeId, paused: nowPaused } })
prev.paused = nowPaused
}
return () => {
if (prevRef.current.updating) pathCtx?.endConnectionPathUpdate?.(nodeId)
if (prevRef.current.error) pathCtx?.removeConnectionPathError?.(nodeId)
if (prevRef.current.paused) pathCtx?.removeConnectionPathPausedNode?.(nodeId)
if (prevRef.current.updating) dispatchCanvasCommand({ type: 'path/endUpdate', payload: nodeId })
if (prevRef.current.error) dispatchCanvasCommand({ type: 'path/setError', payload: { nodeId, error: false } })
if (prevRef.current.paused) dispatchCanvasCommand({ type: 'path/setPaused', payload: { nodeId, paused: false } })
prevRef.current = { updating: false, error: false, paused: false }
}
}, [nodeId, updating, error, paused])