feat: simplify paths

This commit is contained in:
2026-03-14 07:52:34 +01:00
parent 353c2db335
commit b642a7aab2
18 changed files with 541 additions and 506 deletions

View File

@@ -120,7 +120,6 @@ function AnimatedEdgeInner({
</>
)
}
function edgePropsAreEqual(prev: EdgeProps, next: EdgeProps): boolean {
return (
prev.id === next.id &&
@@ -142,3 +141,4 @@ function edgePropsAreEqual(prev: EdgeProps, next: EdgeProps): boolean {
}
export const AnimatedEdge = memo(AnimatedEdgeInner, edgePropsAreEqual)

View File

@@ -69,7 +69,6 @@ export function BaseNode({
"hover:ring-1",
selected && "border-primary/50 shadow-[0_0_0_2px_hsl(var(--primary)_/_0.15)] dark:border-primary/35 dark:shadow-[0_0_0_2px_hsl(var(--primary)_/_0.1)]",
connectionPathRole === "trigger" && "connection-path-trigger",
connectionPathRole === "updating" && "connection-path-updating",
connectionPathRole === "on-path" && "connection-path-on-path",
className,
)}

View File

@@ -33,7 +33,6 @@ import {
DropdownMenuLabel,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { cn } from '@/lib/utils'
import { useTheme } from '@/lib/themeContext'
import {
useRenderingNodeState,
@@ -170,13 +169,11 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
type="button"
size="sm"
variant="outline"
disabled={state.loading}
className={cn(
'h-7 gap-1.5 rounded-r-none border-r-0 px-2.5 text-xs',
state.effectiveUpdateMode === 'manual' &&
state.hasPendingInputs &&
'border-l-2 border-l-amber-500 bg-amber-500/10 hover:bg-amber-500/15 dark:bg-amber-500/15 dark:hover:bg-amber-500/20'
)}
disabled={
state.loading ||
(state.effectiveUpdateMode === 'manual' && !state.hasPendingInputs)
}
className="h-7 gap-1.5 rounded-r-none border-r-0 px-2.5 text-xs"
onClick={(e) => {
e.stopPropagation()
state.incrementRunTrigger()
@@ -184,7 +181,9 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
title={
state.effectiveUpdateMode === 'manual' && state.hasPendingInputs
? 'Inputs changed — click to render'
: undefined
: state.effectiveUpdateMode === 'manual' && !state.hasPendingInputs
? 'No new data to render'
: undefined
}
>
{state.loading ? (
@@ -200,12 +199,7 @@ function RenderingNodeComponent({ id, data, width, height, selected }: Props) {
type="button"
size="sm"
variant="outline"
className={cn(
'h-7 min-w-[4.5rem] gap-1 rounded-l-none pl-2 pr-1.5 text-xs font-normal',
state.effectiveUpdateMode === 'manual' &&
state.hasPendingInputs &&
'bg-amber-500/10 hover:bg-amber-500/15 dark:bg-amber-500/15 dark:hover:bg-amber-500/20'
)}
className="h-7 min-w-[4.5rem] gap-1 rounded-l-none pl-2 pr-1.5 text-xs font-normal"
aria-label="Update mode"
onClick={(e) => e.stopPropagation()}
>

View File

@@ -39,11 +39,10 @@ const DEFAULT_VIEWPORT_WIDTH = 1200
const DEFAULT_VIEWPORT_HEIGHT = 800
const RENDER_DEBOUNCE_MS = 250
/** Lifecycle state to pass to useSyncConnectionStatus so edge status (updating/paused/error) stays in sync. */
/** Lifecycle state to pass to useSyncConnectionStatus so edge status (updating/error) stays in sync. */
export type RenderingNodeLifecycle = {
updating: boolean
error: boolean
paused: boolean
}
export type RenderingNodeState = {
@@ -102,14 +101,21 @@ export function useRenderingNodeState(
data: RenderingNodeData | undefined
): RenderingNodeState {
const {
nodes,
edges,
nodes: contextNodes,
edges: contextEdges,
setNodes,
setEdges,
sourceIds: incomingIds,
updateData,
} = useAbstractNode<RenderingNodeData>(id, data ?? {})
// Subscribe to store graph so we re-render when any node/edge changes (e.g. ascendant data).
// Context only exposes a ref, so we wouldn't re-render when another node updates otherwise.
const storeNodes = useCanvasStore((s) => s.graph.nodes)
const storeEdges = useCanvasStore((s) => s.graph.edges)
const nodes = storeNodes.length > 0 ? storeNodes : contextNodes
const edges = storeEdges.length > 0 ? storeEdges : contextEdges
const nodesEdgesRef = useRef({ nodes, edges })
nodesEdgesRef.current = { nodes, edges }
@@ -189,8 +195,6 @@ export function useRenderingNodeState(
const lastManualRunTriggerRef = useRef(0)
const manualRunTriggerSyncedRef = useRef(false)
const triggerNodeIds = useCanvasStore((s) => s.path.triggerNodeIds)
const updateDataRef = useRef(updateData)
updateDataRef.current = updateData
const setNodesRef = useRef(setNodes)
@@ -200,13 +204,12 @@ export function useRenderingNodeState(
const hasPendingInputs =
effectiveUpdateMode === 'manual' &&
!loading &&
triggerNodeIds.length > 0 &&
incomingIds.length > 0 &&
sourceSignature !== lastRunSourceSignature
const lifecycle = useMemo<RenderingNodeLifecycle>(
() => ({ updating: loading, error: error != null, paused: hasPendingInputs }),
[loading, error, hasPendingInputs]
() => ({ updating: loading, error: error != null }),
[loading, error]
)
useSyncConnectionStatus(id, lifecycle)