Browse Source

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
handoff-20260429-1057
shrimbly 6 months ago
parent
commit
52efbeaae9
  1. 30
      src/components/nodes/GenerateImageNode.tsx
  2. 30
      src/components/nodes/GenerateVideoNode.tsx

30
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<NanoBananaNo
prevStatusRef.current = nodeData.status;
}, [nodeData.status, nodeData.error]);
// Auto-resize node when output image changes
const prevOutputImageRef = useRef<string | null>(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 (
<>
<BaseNode

30
src/components/nodes/GenerateVideoNode.tsx

@ -9,6 +9,7 @@ import { GenerateVideoNodeData, ProviderType, SelectedModel, ModelInputDef } fro
import { ProviderModel, ModelCapability } from "@/lib/providers/types";
import { ModelSearchDialog } from "@/components/modals/ModelSearchDialog";
import { useToast } from "@/components/Toast";
import { getVideoDimensions, calculateNodeSize } from "@/utils/nodeDimensions";
// Video generation capabilities
const VIDEO_CAPABILITIES: ModelCapability[] = ["text-to-video", "image-to-video"];
@ -275,6 +276,35 @@ export function GenerateVideoNode({ id, data, selected }: NodeProps<GenerateVide
prevStatusRef.current = nodeData.status;
}, [nodeData.status, nodeData.error]);
// Auto-resize node when output video changes
const prevOutputVideoRef = useRef<string | null>(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 (
<>
<BaseNode

Loading…
Cancel
Save