@ -29,6 +29,7 @@ const EDGE_COLORS: Record<string, string> = {
text : "#2563eb" , // Blue for text connections
"3d" : "#06b6d4" , // Cyan for 3D connections
easeCurve : "#f59e0b" , // Amber for ease curve connections
loop : "#d946ef" , // Magenta for loop edges
} ;
export function EditableEdge ( {
@ -71,18 +72,23 @@ export function EditableEdge({
return ( targetNode . data as NanoBananaNodeData ) . status === "loading" ;
} ) ;
// Determine edge color based on handle type (orange if paused)
// Determine edge color based on handle type (magenta for loop edges, orange if paused)
const edgeColor = useMemo ( ( ) = > {
if ( edgeData ? . isLoop ) return EDGE_COLORS . loop ;
if ( hasPause ) return EDGE_COLORS . pause ;
// Use source handle to determine color (or target if source is not available)
// Strip numeric suffixes (e.g., "image-0" -> "image") for lookup
const handleType = sourceHandleId || targetHandleId || "" ;
const normalizedType = handleType . replace ( /-\d+$/ , "" ) ;
return EDGE_COLORS [ normalizedType ] || EDGE_COLORS . default ;
} , [ hasPause , sourceHandleId , targetHandleId ] ) ;
} , [ edgeData ? . isLoop , hasPause , sourceHandleId , targetHandleId ] ) ;
// Reference shared gradient by color key + selection state
const gradientId = useMemo ( ( ) = > {
if ( edgeData ? . isLoop ) {
const selectionKey = isConnectedToSelection ? "active" : "dimmed" ;
return getSharedGradientId ( "loop" , selectionKey ) ;
}
if ( hasPause ) {
const selectionKey = isConnectedToSelection ? "active" : "dimmed" ;
return getSharedGradientId ( "pause" , selectionKey ) ;
@ -93,7 +99,7 @@ export function EditableEdge({
const colorKey = normalizedType in EDGE_COLORS ? normalizedType : "default" ;
const selectionKey = isConnectedToSelection ? "active" : "dimmed" ;
return getSharedGradientId ( colorKey , selectionKey ) ;
} , [ hasPause , sourceHandleId , targetHandleId , isConnectedToSelection ] ) ;
} , [ edgeData ? . isLoop , hasPause , sourceHandleId , targetHandleId , isConnectedToSelection ] ) ;
// Calculate the path based on edge style
const [ edgePath , labelX , labelY ] = useMemo ( ( ) = > {
@ -265,6 +271,22 @@ export function EditableEdge({
< / g >
) }
{ /* Loop indicator at edge midpoint */ }
{ edgeData ? . isLoop && (
< foreignObject
x = { labelX - 28 }
y = { labelY - 12 }
width = { 56 }
height = { 24 }
className = "pointer-events-none"
>
< div className = "flex items-center justify-center gap-1 px-2 py-0.5 bg-neutral-800/90 border border-fuchsia-500/60 rounded-full text-[10px] font-medium" >
< span className = "text-fuchsia-300" > ↻ < / span >
< span className = "text-fuchsia-100" > { edgeData . loopCount || 3 } × < / span >
< / div >
< / foreignObject >
) }
{ /* Draggable handles on segments */ }
{ ( selected || isDragging ) &&
handlePositions . map ( ( handle , index ) = > (