@ -10,7 +10,15 @@ interface SplitGridSettingsModalProps {
onClose : ( ) = > void ;
onClose : ( ) = > void ;
}
}
const TARGET_COUNT_OPTIONS = [ 4 , 5 , 6 , 8 , 9 , 10 ] as const ;
const LAYOUT_OPTIONS = [
{ rows : 2 , cols : 2 } ,
{ rows : 1 , cols : 5 } ,
{ rows : 2 , cols : 3 } ,
{ rows : 3 , cols : 2 } ,
{ rows : 2 , cols : 4 } ,
{ rows : 3 , cols : 3 } ,
{ rows : 2 , cols : 5 } ,
] 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 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 RESOLUTIONS : Resolution [ ] = [ "1K" , "2K" , "4K" ] ;
@ -19,17 +27,9 @@ const MODELS: { value: ModelType; label: string }[] = [
{ value : "nano-banana-pro" , label : "Nano Banana Pro" } ,
{ value : "nano-banana-pro" , label : "Nano Banana Pro" } ,
] ;
] ;
// Calculate grid dimensions from target count
const findLayoutIndex = ( rows : number , cols : number ) : number = > {
const getGridDimensions = ( count : number ) : { rows : number ; cols : number } = > {
const idx = LAYOUT_OPTIONS . findIndex ( l = > l . rows === rows && l . cols === cols ) ;
const grids : Record < number , { rows : number ; cols : number } > = {
return idx >= 0 ? idx : 2 ; // default to 2x3
4 : { rows : 2 , cols : 2 } ,
5 : { rows : 1 , cols : 5 } , // 1x5 vertical strip (e.g., for A+ content)
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 ( {
export function SplitGridSettingsModal ( {
@ -39,14 +39,17 @@ export function SplitGridSettingsModal({
} : SplitGridSettingsModalProps ) {
} : SplitGridSettingsModalProps ) {
const { updateNodeData , addNode , onConnect , addEdgeWithType , getNodeById } = useWorkflowStore ( ) ;
const { updateNodeData , addNode , onConnect , addEdgeWithType , getNodeById } = useWorkflowStore ( ) ;
const [ targetCount , setTargetCount ] = useState ( nodeData . targetCount ) ;
const [ selectedLayoutIndex , setSelectedLayoutIndex ] = useState (
findLayoutIndex ( nodeData . gridRows , nodeData . gridCols )
) ;
const [ defaultPrompt , setDefaultPrompt ] = useState ( nodeData . defaultPrompt ) ;
const [ defaultPrompt , setDefaultPrompt ] = useState ( nodeData . defaultPrompt ) ;
const [ aspectRatio , setAspectRatio ] = useState ( nodeData . generateSettings . aspectRatio ) ;
const [ aspectRatio , setAspectRatio ] = useState ( nodeData . generateSettings . aspectRatio ) ;
const [ resolution , setResolution ] = useState ( nodeData . generateSettings . resolution ) ;
const [ resolution , setResolution ] = useState ( nodeData . generateSettings . resolution ) ;
const [ model , setModel ] = useState ( nodeData . generateSettings . model ) ;
const [ model , setModel ] = useState ( nodeData . generateSettings . model ) ;
const [ useGoogleSearch , setUseGoogleSearch ] = useState ( nodeData . generateSettings . useGoogleSearch ) ;
const [ useGoogleSearch , setUseGoogleSearch ] = useState ( nodeData . generateSettings . useGoogleSearch ) ;
const { rows , cols } = getGridDimensions ( targetCount ) ;
const { rows , cols } = LAYOUT_OPTIONS [ selectedLayoutIndex ] ;
const targetCount = rows * cols ;
const isNanoBananaPro = model === "nano-banana-pro" ;
const isNanoBananaPro = model === "nano-banana-pro" ;
const handleCreate = useCallback ( ( ) = > {
const handleCreate = useCallback ( ( ) = > {
@ -162,8 +165,8 @@ export function SplitGridSettingsModal({
onClose ( ) ;
onClose ( ) ;
} , [
} , [
nodeId , targetCount , defaultPrompt , aspectRatio , resolution ,
nodeId , targetCount , defaultPrompt , aspectRatio , resolution ,
model , useGoogleSearch , rows , cols , getNodeById , addNode ,
model , useGoogleSearch , rows , cols , selectedLayoutIndex , getNodeById ,
updateNodeData , onConnect , addEdgeWithType , onClose
addNode , updateNodeData , onConnect , addEdgeWithType , onClose
] ) ;
] ) ;
const handleKeyDown = ( e : React.KeyboardEvent ) = > {
const handleKeyDown = ( e : React.KeyboardEvent ) = > {
@ -183,20 +186,21 @@ export function SplitGridSettingsModal({
< / h2 >
< / h2 >
< div className = "space-y-4" >
< div className = "space-y-4" >
{ /* Target coun t selector with visual preview */ }
{ /* Layou t selector with visual preview */ }
< div >
< div >
< label className = "block text-sm text-neutral-400 mb-2" >
< label className = "block text-sm text-neutral-400 mb-2" >
Number of Images
Grid Layout
< / label >
< / label >
< div className = "flex gap-2" >
< div className = "flex gap-2" >
{ TARGET_COUNT_OPTIONS . map ( ( count ) = > {
{ LAYOUT_OPTIONS . map ( ( layout , index ) = > {
const { rows : r , cols : c } = getGridDimensions ( count ) ;
const count = layout . rows * layout . cols ;
const isSelected = selectedLayoutIndex === index ;
return (
return (
< button
< button
key = { count }
key = { ` ${ layout . rows } x ${ layout . cols } ` }
onClick = { ( ) = > setTargetCount ( count ) }
onClick = { ( ) = > setSelectedLayoutIndex ( index ) }
className = { ` flex-1 p-2 rounded border transition-colors ${
className = { ` flex-1 p-2 rounded border transition-colors ${
targetCount === count
isSelected
? "border-blue-500 bg-blue-500/20"
? "border-blue-500 bg-blue-500/20"
: "border-neutral-600 hover:border-neutral-500"
: "border-neutral-600 hover:border-neutral-500"
} ` }
} ` }
@ -204,20 +208,21 @@ export function SplitGridSettingsModal({
< div
< div
className = "aspect-video mx-auto w-12 grid gap-0.5"
className = "aspect-video mx-auto w-12 grid gap-0.5"
style = { {
style = { {
gridTemplateColumns : ` repeat( ${ c } , 1fr) ` ,
gridTemplateColumns : ` repeat( ${ layout . cols } , 1fr) ` ,
gridTemplateRows : ` repeat( ${ r } , 1fr) ` ,
gridTemplateRows : ` repeat( ${ layout . rows } , 1fr) ` ,
} }
} }
>
>
{ Array . from ( { length : count } ) . map ( ( _ , i ) = > (
{ Array . from ( { length : count } ) . map ( ( _ , i ) = > (
< div
< div
key = { i }
key = { i }
className = { ` rounded-sm ${
className = { ` rounded-sm ${
targetCount === count ? "bg-blue-400" : "bg-neutral-500"
isSelected ? "bg-blue-400" : "bg-neutral-500"
} ` }
} ` }
/ >
/ >
) ) }
) ) }
< / div >
< / div >
< div className = "text-xs text-neutral-300 mt-1 text-center" > { count } < / div >
< div className = "text-xs text-neutral-300 mt-1 text-center" > { layout . rows } x { layout . cols } < / div >
< div className = "text-[10px] text-neutral-500 text-center" > { count } < / div >
< / button >
< / button >
) ;
) ;
} ) }
} ) }