"use client"; import { useState, useCallback } from "react"; import { useWorkflowStore } from "@/store/workflowStore"; import { SplitGridNodeData, AspectRatio, Resolution, ModelType } from "@/types"; interface SplitGridSettingsModalProps { nodeId: string; nodeData: SplitGridNodeData; onClose: () => void; } const TARGET_COUNT_OPTIONS = [4, 6, 8, 9, 10] as const; const ASPECT_RATIOS: AspectRatio[] = ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"]; const RESOLUTIONS: Resolution[] = ["1K", "2K", "4K"]; const MODELS: { value: ModelType; label: string }[] = [ { value: "nano-banana", label: "Nano Banana" }, { value: "nano-banana-pro", label: "Nano Banana Pro" }, ]; // Calculate grid dimensions from target count const getGridDimensions = (count: number): { rows: number; cols: number } => { const grids: Record = { 4: { rows: 2, cols: 2 }, 6: { rows: 2, cols: 3 }, 8: { rows: 2, cols: 4 }, 9: { rows: 3, cols: 3 }, 10: { rows: 2, cols: 5 }, }; return grids[count] || { rows: 2, cols: 3 }; }; export function SplitGridSettingsModal({ nodeId, nodeData, onClose, }: SplitGridSettingsModalProps) { const { updateNodeData, addNode, onConnect, addEdgeWithType, getNodeById } = useWorkflowStore(); const [targetCount, setTargetCount] = useState(nodeData.targetCount); const [defaultPrompt, setDefaultPrompt] = useState(nodeData.defaultPrompt); const [aspectRatio, setAspectRatio] = useState(nodeData.generateSettings.aspectRatio); const [resolution, setResolution] = useState(nodeData.generateSettings.resolution); const [model, setModel] = useState(nodeData.generateSettings.model); const [useGoogleSearch, setUseGoogleSearch] = useState(nodeData.generateSettings.useGoogleSearch); const { rows, cols } = getGridDimensions(targetCount); const isNanoBananaPro = model === "nano-banana-pro"; const handleCreate = useCallback(() => { const splitNode = getNodeById(nodeId); if (!splitNode) return; // Node dimensions const imageInputWidth = 300; const imageInputHeight = 280; const promptWidth = 320; const promptHeight = 220; const nanoBananaWidth = 300; const nanoBananaHeight = 300; const horizontalGap = 40; const verticalGap = 30; // Calculate cluster dimensions // Layout: imageInput on left, nanoBanana on right, prompt below imageInput const clusterWidth = imageInputWidth + horizontalGap + nanoBananaWidth; const clusterHeight = Math.max(imageInputHeight, nanoBananaHeight) + verticalGap + promptHeight; const clusterGap = 60; // Start position to the right of the split node const startX = splitNode.position.x + 350; const startY = splitNode.position.y; const childNodeIds: SplitGridNodeData["childNodeIds"] = []; // Create node clusters for each grid cell for (let i = 0; i < targetCount; i++) { const row = Math.floor(i / cols); const col = i % cols; // Position for this cluster const clusterX = startX + col * (clusterWidth + clusterGap); const clusterY = startY + row * (clusterHeight + clusterGap); // Create imageInput node const imageInputId = addNode("imageInput", { x: clusterX, y: clusterY, }); // Create nanoBanana node (to the right of imageInput) const nanoBananaId = addNode("nanoBanana", { x: clusterX + imageInputWidth + horizontalGap, y: clusterY, }); // Update nanoBanana settings updateNodeData(nanoBananaId, { aspectRatio, resolution, model, useGoogleSearch, }); // Create prompt node (below imageInput) const promptId = addNode("prompt", { x: clusterX, y: clusterY + Math.max(imageInputHeight, nanoBananaHeight) + verticalGap, }); // Update prompt with default text updateNodeData(promptId, { prompt: defaultPrompt }); // Create connections: imageInput -> nanoBanana, prompt -> nanoBanana onConnect({ source: imageInputId, sourceHandle: "image", target: nanoBananaId, targetHandle: "image", }); onConnect({ source: promptId, sourceHandle: "text", target: nanoBananaId, targetHandle: "text", }); // Create reference edge from split node to imageInput (grey dotted line) addEdgeWithType({ source: nodeId, sourceHandle: "reference", target: imageInputId, targetHandle: "reference", }, "reference"); childNodeIds.push({ imageInput: imageInputId, prompt: promptId, nanoBanana: nanoBananaId, }); } // Update split node with configuration updateNodeData(nodeId, { targetCount, defaultPrompt, generateSettings: { aspectRatio, resolution, model, useGoogleSearch, }, childNodeIds, gridRows: rows, gridCols: cols, isConfigured: true, }); onClose(); }, [ nodeId, targetCount, defaultPrompt, aspectRatio, resolution, model, useGoogleSearch, rows, cols, getNodeById, addNode, updateNodeData, onConnect, addEdgeWithType, onClose ]); const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Escape") { onClose(); } }; return (

Split Grid Settings

{/* Target count selector with visual preview */}
{TARGET_COUNT_OPTIONS.map((count) => { const { rows: r, cols: c } = getGridDimensions(count); return ( ); })}

Grid will be split into {rows}x{cols} = {targetCount} images

{/* Default prompt */}