fix: performance

This commit is contained in:
2026-03-14 22:39:38 +01:00
parent bcb9c5946d
commit ce58673d18
2 changed files with 47 additions and 2 deletions

View File

@@ -188,8 +188,21 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
const [isSelecting, setIsSelecting] = React.useState(false)
const [ariaAnnouncement, setAriaAnnouncement] = React.useState<string | null>(null)
const [fullscreenNodeId, setFullscreenNodeId] = React.useState<string | null>(null)
const graphApplyTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)
const GRAPH_APPLY_DEBOUNCE_MS = 300
useEffect(() => {
dispatchCanvasCommand({ type: 'graph/apply', payload: { nodes, edges } })
if (graphApplyTimeoutRef.current) clearTimeout(graphApplyTimeoutRef.current)
graphApplyTimeoutRef.current = setTimeout(() => {
graphApplyTimeoutRef.current = null
dispatchCanvasCommand({ type: 'graph/apply', payload: { nodes, edges } })
}, GRAPH_APPLY_DEBOUNCE_MS)
return () => {
if (graphApplyTimeoutRef.current) {
clearTimeout(graphApplyTimeoutRef.current)
graphApplyTimeoutRef.current = null
}
}
}, [nodes, edges])
const connectionPath = useCanvasConnectionPathFromStore()
@@ -218,7 +231,22 @@ export function CanvasPage({ projectId }: CanvasPageProps) {
rafRef.current = requestAnimationFrame(() => {
rafRef.current = null
const toApply = pendingChangesRef.current.splice(0, pendingChangesRef.current.length)
if (toApply.length > 0) setNodesSilent((nds) => applyNodeChanges(toApply, nds))
if (toApply.length === 0) return
setNodesSilent((nds) => {
// Drop dimension-only changes that don't change the node (e.g. React Flow re-reporting on visibility).
// Avoids graph/apply and store churn when nodes become visible with onlyRenderVisibleElements.
const filtered = toApply.filter((c) => {
const ch = c as NodeChange<Node> & { type?: string; dimensions?: { width?: number; height?: number } }
if (ch.type !== 'dimensions' || ch.dimensions == null) return true
const node = nds.find((n) => n.id === (ch as { id?: string }).id)
if (!node) return true
const nw = (node as Node & { width?: number }).width
const nh = (node as Node & { height?: number }).height
return nw !== ch.dimensions.width || nh !== ch.dimensions.height
})
if (filtered.length === 0) return nds
return applyNodeChanges(filtered, nds)
})
})
}
},

View File

@@ -38,6 +38,8 @@ export type RenderingNodeData = {
const DEFAULT_VIEWPORT_WIDTH = 1200
const DEFAULT_VIEWPORT_HEIGHT = 800
const RENDER_DEBOUNCE_MS = 250
/** Skip auto-run when we have cache and effect runs soon after mount (node became visible). */
const VISIBILITY_GRACE_MS = 400
/** Lifecycle state to pass to useSyncConnectionStatus so edge status (updating/error) stays in sync. */
export type RenderingNodeLifecycle = {
@@ -194,6 +196,8 @@ export function useRenderingNodeState(
const minLoadingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const lastManualRunTriggerRef = useRef(0)
const manualRunTriggerSyncedRef = useRef(false)
/** In auto mode, skip running when we have cache and we're in the grace window after mount (node became visible). */
const mountTimeRef = useRef<number>(0)
const updateDataRef = useRef(updateData)
updateDataRef.current = updateData
@@ -214,6 +218,7 @@ export function useRenderingNodeState(
useSyncConnectionStatus(id, lifecycle)
useEffect(() => {
if (mountTimeRef.current === 0) mountTimeRef.current = Date.now()
if (incomingIds.length === 0) {
setRenderedContent(null)
setResolvedContent(null)
@@ -258,6 +263,18 @@ export function useRenderingNodeState(
lastManualRunTriggerRef.current = runTrigger
}
// Auto mode: do not run when node just became visible (remount with cache). No pipeline run, no path/addTrigger.
if (effectiveUpdateMode === 'auto') {
const hasCache = Boolean(
(data?.cachedRenderedContent ?? data?.cachedResolvedContent) as string | undefined
)
const signatureUnchanged =
data?.lastRunSourceSignature != null && data.lastRunSourceSignature === sourceSignature
const withinVisibilityGrace =
mountTimeRef.current > 0 && Date.now() - mountTimeRef.current < VISIBILITY_GRACE_MS
if (hasCache && (signatureUnchanged || withinVisibilityGrace)) return
}
runIdRef.current += 1
const thisRunId = runIdRef.current
const signatureForThisRun = sourceSignature