feat: runnersok

This commit is contained in:
2026-03-29 01:22:33 +01:00
parent 26b395272d
commit 9607ff3478
24 changed files with 1613 additions and 221 deletions

View File

@@ -4,7 +4,7 @@
* - **AbstractNodeProps<TData>** — Typed props (id, data, width?, height?, selected?) for your node.
* - **useAbstractNode(id, data)** — Flow context plus helpers: nodes, edges, setNodes, setEdges,
* updateData(partial), incomingEdges, outgoingEdges, sourceIds, targetIds. Calling updateData()
* also marks this node as a connection-path trigger so edges update on data changes.
* also marks the run state as dirty so the Run button shows pending changes.
* - **createAbstractNodeComponent(displayName, Component)** — Wraps with memo + nodePropsAreEqual.
*
* **Node lifecycle / connection status:** Nodes that can be updating, paused, or in error should
@@ -17,9 +17,23 @@
import React, { useCallback, useContext, useMemo } from 'react'
import { GraphContext } from './flowContext'
import { dispatchCanvasCommand } from '@/app/canvas/canvasStore'
import { nodePropsAreEqual } from './flowUtils'
import type { AppNode } from './nodeTypes'
import { useRunStore } from './runStore'
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
/** Data keys written by the render pipeline cache — these should NOT mark the run state as dirty. */
const CACHE_DATA_KEYS = new Set([
'cachedRenderedContent',
'cachedResolvedContent',
'cachedReasoningContent',
'cachedOutputValue',
'lastRunSourceSignature',
'outputMarkdown',
])
// ---------------------------------------------------------------------------
// Types
@@ -87,6 +101,7 @@ export function useAbstractNode<TData = Record<string, unknown>>(
const setNodes = graphCtx?.setNodes
const setEdges = graphCtx?.setEdges
const markDirty = useRunStore((s) => s.markDirty)
const updateData = useCallback(
(partial: Partial<TData>) => {
if (!setNodes) return
@@ -95,9 +110,12 @@ export function useAbstractNode<TData = Record<string, unknown>>(
n.id === id ? { ...n, data: { ...(n.data as object), ...partial } } : n
) as AppNode[]
)
dispatchCanvasCommand({ type: 'path/addTrigger', payload: id })
// Only mark dirty for user-facing changes, not internal cache writes from the render pipeline
const keys = Object.keys(partial)
const isCacheOnly = keys.length > 0 && keys.every((k) => CACHE_DATA_KEYS.has(k))
if (!isCacheOnly) markDirty()
},
[id, setNodes]
[id, setNodes, markDirty]
)
const incomingEdges = useMemo(