performance

This commit is contained in:
2026-03-09 15:50:11 +01:00
parent 50d34bfb00
commit 88629e6dcc
3 changed files with 70 additions and 14 deletions

View File

@@ -20,10 +20,11 @@ import { NodeHeaderTitle } from '../base/NodeHeaderTitle'
import { NodeMenubar } from '../base/NodeMenubar'
import { NodeStatusIndicator } from '../base/NodeStatusIndicator'
import { MenubarItem, MenubarSeparator, MenubarSub, MenubarSubContent, MenubarSubTrigger } from '../ui/menubar'
import { Sparkles, ZoomIn, ZoomOut, RotateCcw } from 'lucide-react'
import { Sparkles, ZoomIn, ZoomOut, RotateCcw, RotateCw } from 'lucide-react'
import { InputHandle } from '../base/NodeHandles'
import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch'
import { Input } from '../ui/input'
import { Button } from '../ui/button'
export type RenderingNodeData = {
viewportWidth?: number
@@ -39,6 +40,7 @@ function RenderingNodeComponent({ id, data, width, height }: Props) {
const [renderedContent, setRenderedContent] = useState<string | null>(null)
const [error, setError] = useState<null | { kind: string; message: string }>(null)
const [loading, setLoading] = useState(false)
const [retryCount, setRetryCount] = useState(0)
const runIdRef = useRef(0)
const loadingStartedAtRef = useRef<number | null>(null)
const minLoadingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
@@ -151,6 +153,8 @@ function RenderingNodeComponent({ id, data, width, height }: Props) {
[nodes, connectedNodeIds]
)
const RENDER_DEBOUNCE_MS = 250
useEffect(() => {
if (!sourceContent && incomingIds.length > 0) {
setRenderedContent(null)
@@ -473,16 +477,17 @@ function RenderingNodeComponent({ id, data, width, height }: Props) {
}
}
run()
const debounceTimer = setTimeout(run, RENDER_DEBOUNCE_MS)
return () => {
cancelled = true
clearTimeout(debounceTimer)
if (minLoadingTimeoutRef.current != null) {
clearTimeout(minLoadingTimeoutRef.current)
minLoadingTimeoutRef.current = null
}
}
// Only re-run when inputs that affect the resolved output change (signatures + source).
}, [id, sourceContent, configTypeId, configSignature, edgesSignature, variablesSignature, functionsSignature, viewportWidth, viewportHeight])
// Only re-run when inputs that affect the resolved output change (signatures + source). Debounced to avoid excessive re-renders while typing. retryCount triggers re-run on Retry.
}, [id, sourceContent, configTypeId, configSignature, edgesSignature, variablesSignature, functionsSignature, viewportWidth, viewportHeight, retryCount])
const dimensions =
width != null && height != null && width > 0 && height > 0
@@ -646,7 +651,19 @@ function RenderingNodeComponent({ id, data, width, height }: Props) {
) : srcData?.errorHtml ? (
<div className="p-3 text-xs text-red-700 dark:text-red-400" dangerouslySetInnerHTML={{ __html: String(srcData.errorHtml) }} />
) : (
<div className="p-3 text-xs text-red-700 dark:text-red-400">{error.message}</div>
<div className="flex flex-col gap-2 p-3">
<p className="text-xs text-red-700 dark:text-red-400">{error.message}</p>
<Button
type="button"
variant="outline"
size="sm"
className="w-fit"
onClick={() => setRetryCount((c) => c + 1)}
>
<RotateCw className="size-3 mr-1" />
Retry
</Button>
</div>
)
) : loading ? (
<div className="flex min-h-0 flex-1 items-center justify-center text-xs text-muted-foreground">Rendering</div>