From 52efbeaae9b314635374cdc6bb4cc814d324e127 Mon Sep 17 00:00:00 2001 From: shrimbly Date: Mon, 12 Jan 2026 13:55:35 +1300 Subject: [PATCH] feat(10-01): auto-resize generate nodes on output - GenerateImageNode auto-resizes when outputImage changes - GenerateVideoNode auto-resizes when outputVideo changes - Uses getImageDimensions/getVideoDimensions to extract aspect ratio - Applies calculateNodeSize for constrained dimensions - Uses requestAnimationFrame to avoid React Flow conflicts - Tracks previous output to prevent redundant resizes --- src/components/nodes/GenerateImageNode.tsx | 30 ++++++++++++++++++++++ src/components/nodes/GenerateVideoNode.tsx | 30 ++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/components/nodes/GenerateImageNode.tsx b/src/components/nodes/GenerateImageNode.tsx index 23e2578e..4606a69b 100644 --- a/src/components/nodes/GenerateImageNode.tsx +++ b/src/components/nodes/GenerateImageNode.tsx @@ -9,6 +9,7 @@ import { NanoBananaNodeData, AspectRatio, Resolution, ModelType, ProviderType, S import { ProviderModel, ModelCapability } from "@/lib/providers/types"; import { ModelSearchDialog } from "@/components/modals/ModelSearchDialog"; import { useToast } from "@/components/Toast"; +import { getImageDimensions, calculateNodeSize } from "@/utils/nodeDimensions"; // All 10 aspect ratios supported by both models const ASPECT_RATIOS: AspectRatio[] = ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"]; @@ -397,6 +398,35 @@ export function GenerateImageNode({ id, data, selected }: NodeProps(null); + useEffect(() => { + // Only resize when outputImage transitions from null/different to a new value + if (!nodeData.outputImage || nodeData.outputImage === prevOutputImageRef.current) { + prevOutputImageRef.current = nodeData.outputImage ?? null; + return; + } + prevOutputImageRef.current = nodeData.outputImage; + + // Use requestAnimationFrame to avoid React Flow update conflicts + requestAnimationFrame(() => { + getImageDimensions(nodeData.outputImage!).then((dims) => { + if (!dims) return; + + const aspectRatio = dims.width / dims.height; + const newSize = calculateNodeSize(aspectRatio); + + setNodes((nodes) => + nodes.map((node) => + node.id === id + ? { ...node, style: { ...node.style, width: newSize.width, height: newSize.height } } + : node + ) + ); + }); + }); + }, [id, nodeData.outputImage, setNodes]); + return ( <> (null); + useEffect(() => { + // Only resize when outputVideo transitions from null/different to a new value + if (!nodeData.outputVideo || nodeData.outputVideo === prevOutputVideoRef.current) { + prevOutputVideoRef.current = nodeData.outputVideo ?? null; + return; + } + prevOutputVideoRef.current = nodeData.outputVideo; + + // Use requestAnimationFrame to avoid React Flow update conflicts + requestAnimationFrame(() => { + getVideoDimensions(nodeData.outputVideo!).then((dims) => { + if (!dims) return; + + const aspectRatio = dims.width / dims.height; + const newSize = calculateNodeSize(aspectRatio); + + setNodes((nodes) => + nodes.map((node) => + node.id === id + ? { ...node, style: { ...node.style, width: newSize.width, height: newSize.height } } + : node + ) + ); + }); + }); + }, [id, nodeData.outputVideo, setNodes]); + return ( <>