@ -36,36 +36,39 @@ export function SplitGridNode({ id, data, selected }: NodeProps<SplitGridNodeTyp
} , [ id , regenerateNode ] ) ;
} , [ id , regenerateNode ] ) ;
return (
return (
< BaseNode
< >
id = { id }
< BaseNode
selected = { selected }
id = { id }
hasError = { nodeData . status === "error" }
selected = { selected }
>
hasError = { nodeData . status === "error" }
{ /* Image input handle */ }
fullBleed
< Handle
>
type = "target"
{ /* Image input handle */ }
position = { Position . Left }
< Handle
id = "image"
type = "target"
data - handletype = "image"
position = { Position . Left }
/ >
id = "image"
data - handletype = "image"
style = { { zIndex : 10 } }
/ >
{ /* Reference output handle for visual links to child nodes */ }
{ /* Reference output handle for visual links to child nodes */ }
< Handle
< Handle
type = "source"
type = "source"
position = { Position . Right }
position = { Position . Right }
id = "reference"
id = "reference"
data - handletype = "reference"
data - handletype = "reference"
className = "!bg-gray-500"
className = "!bg-gray-500"
/ >
style = { { zIndex : 10 } }
/ >
< div className = "flex-1 flex flex-col min-h-0 gap-2" >
{ /* Full-bleed preview area */ }
{ /* Preview/Status area */ }
{ nodeData . sourceImage ? (
{ nodeData . sourceImage ? (
< div className = "relative w-full flex-1 min-h-0 " >
< div className = "relative w-full h-full " >
< img
< img
src = { nodeData . sourceImage }
src = { nodeData . sourceImage }
alt = "Source grid"
alt = "Source grid"
className = "w-full h-full object-contain rounded "
className = "w-full h-full object-cover rounded-lg "
/ >
/ >
{ /* Grid overlay visualization */ }
{ /* Grid overlay visualization */ }
< div
< div
@ -85,7 +88,7 @@ export function SplitGridNode({ id, data, selected }: NodeProps<SplitGridNodeTyp
< / div >
< / div >
{ /* Loading overlay */ }
{ /* Loading overlay */ }
{ nodeData . status === "loading" && (
{ nodeData . status === "loading" && (
< div className = "absolute inset-0 bg-neutral-900/70 rounded flex items-center justify-center" >
< div className = "absolute inset-0 bg-neutral-900/70 rounded-lg flex items-center justify-center" >
< svg className = "w-6 h-6 animate-spin text-white" fill = "none" viewBox = "0 0 24 24" >
< svg className = "w-6 h-6 animate-spin text-white" fill = "none" viewBox = "0 0 24 24" >
< circle className = "opacity-25" cx = "12" cy = "12" r = "10" stroke = "currentColor" strokeWidth = "3" / >
< circle className = "opacity-25" cx = "12" cy = "12" r = "10" stroke = "currentColor" strokeWidth = "3" / >
< path className = "opacity-75" fill = "currentColor" d = "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" / >
< path className = "opacity-75" fill = "currentColor" d = "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" / >
@ -94,7 +97,7 @@ export function SplitGridNode({ id, data, selected }: NodeProps<SplitGridNodeTyp
) }
) }
< / div >
< / div >
) : (
) : (
< div className = "w-full flex-1 min-h-[112px] border border-dashed border-neutral-600 rounded flex flex-col items-center justify-center " >
< div className = "w-full h-full min-h-[112px] bg-neutral-900/40 flex flex-col items-center justify-center rounded-lg " >
{ nodeData . status === "error" ? (
{ nodeData . status === "error" ? (
< span className = "text-[10px] text-red-400 text-center px-2" >
< span className = "text-[10px] text-red-400 text-center px-2" >
{ nodeData . error || "Error" }
{ nodeData . error || "Error" }
@ -117,40 +120,43 @@ export function SplitGridNode({ id, data, selected }: NodeProps<SplitGridNodeTyp
< / div >
< / div >
) }
) }
{ /* Config summary */ }
{ /* Controls overlay pinned at bottom */ }
< div className = "flex items-center justify-between text-[10px] text-neutral-400 shrink-0" >
< div className = "absolute bottom-0 left-0 right-0 z-10 px-3 py-2 bg-neutral-900/80 backdrop-blur-sm rounded-b-lg space-y-1" >
< span > { nodeData . gridRows } x { nodeData . gridCols } grid ( { nodeData . targetCount } images ) < / span >
{ /* Config summary */ }
< button
< div className = "flex items-center justify-between text-[10px] text-neutral-400" >
onClick = { handleOpenSettings }
< span > { nodeData . gridRows } x { nodeData . gridCols } grid ( { nodeData . targetCount } images ) < / span >
className = "text-blue-400 hover:text-blue-300 transition-colors"
< button
>
onClick = { handleOpenSettings }
Settings
className = "nodrag nopan text-blue-400 hover:text-blue-300 transition-colors"
< / button >
>
< / div >
Settings
< / button >
< / div >
{ /* Child node count / status */ }
{ /* Child node count / status */ }
< div className = "flex items-center justify-between shrink-0 " >
< div className = "flex items-center justify-between" >
{ nodeData . isConfigured ? (
{ nodeData . isConfigured ? (
< div className = "text-[10px] text-neutral-500" >
< div className = "text-[10px] text-neutral-500" >
{ nodeData . childNodeIds . length } generate sets created
{ nodeData . childNodeIds . length } generate sets created
< / div >
< / div >
) : (
) : (
< div className = "text-[10px] text-amber-400" >
< div className = "text-[10px] text-amber-400" >
Not configured - click Settings
Not configured - click Settings
< / div >
< / div >
) }
) }
{ /* Split button */ }
{ /* Split button */ }
< button
< button
onClick = { handleSplit }
onClick = { handleSplit }
disabled = { isRunning || ! nodeData . isConfigured }
disabled = { isRunning || ! nodeData . isConfigured }
className = "px-2 py-0.5 text-[10px] border border-white hover:bg-white hover:text-neutral-900 disabled:border-neutral-600 disabled:text-neutral-600 disabled:cursor-not-allowed text-white rounded transition-colors"
className = "nodrag nopan px-2 py-0.5 text-[10px] border border-white hover:bg-white hover:text-neutral-900 disabled:border-neutral-600 disabled:text-neutral-600 disabled:cursor-not-allowed text-white rounded transition-colors"
title = { ! nodeData . isConfigured ? "Configure node first" : "Split grid" }
title = { ! nodeData . isConfigured ? "Configure node first" : "Split grid" }
>
>
Split
Split
< / button >
< / button >
< / div >
< / div >
< / div >
< / div >
< / BaseNode >
{ /* Settings Modal */ }
{ /* Settings Modal */ }
{ showSettings && (
{ showSettings && (
@ -160,6 +166,6 @@ export function SplitGridNode({ id, data, selected }: NodeProps<SplitGridNodeTyp
onClose = { handleCloseSettings }
onClose = { handleCloseSettings }
/ >
/ >
) }
) }
< / BaseNode >
< / >
) ;
) ;
}
}