@ -156,9 +156,10 @@ export function ArrayNode({ id, data, selected }: NodeProps<ArrayNodeType>) {
// Auto-resize node height to fit all parsed lines so users don't need to scroll.
// Auto-resize node height to fit all parsed lines so users don't need to scroll.
useEffect ( ( ) = > {
useEffect ( ( ) = > {
const baseHeight = 360 ;
const headerHeight = 180 ;
const perItemHeight = 30 ;
const perItemHeight = 28 ;
const newHeight = Math . max ( baseHeight , 270 + previewItems . length * perItemHeight ) ;
const itemsMinHeight = 60 ;
const newHeight = headerHeight + Math . max ( itemsMinHeight , previewItems . length * perItemHeight + 8 ) ;
setNodes ( ( nodes ) = >
setNodes ( ( nodes ) = >
nodes . map ( ( node ) = > {
nodes . map ( ( node ) = > {
@ -174,8 +175,8 @@ export function ArrayNode({ id, data, selected }: NodeProps<ArrayNodeType>) {
id = { id }
id = { id }
selected = { selected }
selected = { selected }
hasError = { ! ! nodeData . error }
hasError = { ! ! nodeData . error }
minWidth = { 32 0 }
minWidth = { 30 0 }
minHeight = { 30 0}
minHeight = { 22 0}
>
>
< Handle type = "target" position = { Position . Left } id = "text" data - handletype = "text" / >
< Handle type = "target" position = { Position . Left } id = "text" data - handletype = "text" / >
@ -187,7 +188,7 @@ export function ArrayNode({ id, data, selected }: NodeProps<ArrayNodeType>) {
type = "button"
type = "button"
onClick = { handleAutoRouteToPrompts }
onClick = { handleAutoRouteToPrompts }
disabled = { previewItems . length === 0 }
disabled = { previewItems . length === 0 }
className = "nodrag nopan absolute top-2 right-2 z-10 shrink-0 p-1 rounded border border-neutral-600 bg-neutral-800/90 text-neutral-300 hover:bg-neutral-700 hover:text-neutral-100 disabled:opacity-50 disabled:cursor-not-allowed"
className = "nodrag nopan absolute top-2 right-2 z-10 shrink-0 p-1 bg-[#1a1a1a] rounded-md text-neutral-400 hover:text-neutral-100 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title = "Auto-route to Prompts"
title = "Auto-route to Prompts"
>
>
< svg className = "w-3.5 h-3.5" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" strokeWidth = { 2 } >
< svg className = "w-3.5 h-3.5" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" strokeWidth = { 2 } >
@ -196,12 +197,12 @@ export function ArrayNode({ id, data, selected }: NodeProps<ArrayNodeType>) {
< / button >
< / button >
< div className = "flex flex-col gap-2 flex-1 min-h-0" >
< div className = "flex flex-col gap-2 flex-1 min-h-0" >
< div className = "grid grid-cols-[auto_1fr] gap-2 items-center " >
< div className = "flex items-center gap-2 " >
< label className = "text-[11px] text-neutral-400" > Split < / label >
< label className = "shrink-0 text-[11px] text-neutral-400" > Split < / label >
< select
< select
value = { nodeData . splitMode }
value = { nodeData . splitMode }
onChange = { handleBasicModeChange }
onChange = { handleBasicModeChange }
className = "nodrag nopan bg-neutral-900 border border-neutral-700 rounded px-2 py-1 text-[11px] text-neutral-100 focus:outline-none focus:ring-1 focus:ring-neutral-600"
className = "nodrag nopan flex-1 min-w-0 text-[11px] py-1 px-2 bg-[#1a1a1a] rounded-md focus:outline-none focus:ring-1 focus:ring-neutral-600 text-white "
>
>
< option value = "delimiter" > Delimiter < / option >
< option value = "delimiter" > Delimiter < / option >
< option value = "newline" > Newline < / option >
< option value = "newline" > Newline < / option >
@ -210,69 +211,69 @@ export function ArrayNode({ id, data, selected }: NodeProps<ArrayNodeType>) {
< / div >
< / div >
{ nodeData . splitMode === "delimiter" && (
{ nodeData . splitMode === "delimiter" && (
< div className = "grid grid-cols-[auto_1fr] gap-2 items-center " >
< div className = "flex items-center gap-2 " >
< label className = "text-[11px] text-neutral-400" > By < / label >
< label className = "shrink-0 text-[11px] text-neutral-400" > By < / label >
< input
< input
value = { nodeData . delimiter }
value = { nodeData . delimiter }
onChange = { ( e ) = > updateNodeData ( id , { delimiter : e.target.value } ) }
onChange = { ( e ) = > updateNodeData ( id , { delimiter : e.target.value } ) }
placeholder = "*"
placeholder = "*"
className = "nodrag nopan bg-neutral-900 border border-neutral-700 rounded px-2 py-1 text-[11px] text-neutral-100 focus:outline-none focus:ring-1 focus:ring-neutral-600"
className = "nodrag nopan flex-1 min-w-0 text-[11px] py-1 px-2 bg-[#1a1a1a] rounded-md focus:outline-none focus:ring-1 focus:ring-neutral-600 text-white "
/ >
/ >
< / div >
< / div >
) }
) }
{ nodeData . splitMode === "regex" && (
{ nodeData . splitMode === "regex" && (
< div className = "grid grid-cols-[auto_1fr] gap-2 items-center " >
< div className = "flex items-center gap-2 " >
< label className = "text-[11px] text-neutral-400" > By < / label >
< label className = "shrink-0 text-[11px] text-neutral-400" > By < / label >
< input
< input
value = { nodeData . regexPattern }
value = { nodeData . regexPattern }
onChange = { ( e ) = > updateNodeData ( id , { regexPattern : e.target.value } ) }
onChange = { ( e ) = > updateNodeData ( id , { regexPattern : e.target.value } ) }
placeholder = "/\\n+/"
placeholder = "/\\n+/"
className = "nodrag nopan bg-neutral-900 border border-neutral-700 rounded px-2 py-1 text-[11px] text-neutral-100 focus:outline-none focus:ring-1 focus:ring-neutral-600"
className = "nodrag nopan flex-1 min-w-0 text-[11px] py-1 px-2 bg-[#1a1a1a] rounded-md focus:outline-none focus:ring-1 focus:ring-neutral-600 text-white "
/ >
/ >
< / div >
< / div >
) }
) }
< div className = "border border-neutral-700 rounded" >
< div >
< button
< button
type = "button"
type = "button"
onClick = { ( ) = > setShowAdvanced ( ( v ) = > ! v ) }
onClick = { ( ) = > setShowAdvanced ( ( v ) = > ! v ) }
className = "nodrag nopan w-full flex items-center justify-between px-2 py-1 text-[11px] text-neutral-300 hover:bg-neutral-800/50 "
className = "nodrag nopan flex items-center gap-1 text-[11px] text-neutral-500 hover:text-neutral-300 transition-colors "
>
>
< svg className = { ` w-3 h-3 transition-transform ${ showAdvanced ? "rotate-90" : "" } ` } fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" strokeWidth = { 2 } >
< path strokeLinecap = "round" strokeLinejoin = "round" d = "M9 5l7 7-7 7" / >
< / svg >
< span > Advanced < / span >
< span > Advanced < / span >
< span className = "text-[10px] text-neutral-500" > { showAdvanced ? "Hide" : "Show" } < / span >
< / button >
< / button >
{ showAdvanced && (
{ showAdvanced && (
< div className = "px-2 pb-2 pt-1 flex flex-col gap-2 border-t border-neutral-700" >
< div className = "px-2 pt-1.5 pb-0.5 flex items-center gap-3" >
< div className = "flex items-center gap-3" >
< label className = "flex items-center gap-1.5 text-[11px] text-neutral-300" >
< label className = "flex items-center gap-1.5 text-[11px] text-neutral-300" >
< input
< input
type = "checkbox"
type = "checkbox"
checked = { nodeData . trimItems }
checked = { nodeData . trimItems }
onChange = { ( e ) = > updateNodeData ( id , { trimItems : e.target.checked } ) }
onChange = { ( e ) = > updateNodeData ( id , { trimItems : e.target.checked } ) }
className = "nodrag nopan w-3 h-3 rounded bg-[#1a1a1a] text-neutral-600 focus:ring-1 focus:ring-neutral-600 focus:ring-offset-0"
className = "nodrag nopan w-3 h-3"
/ >
/ >
Trim
Trim
< / label >
< / label >
< label className = "flex items-center gap-1.5 text-[11px] text-neutral-300" >
< label className = "flex items-center gap-1.5 text-[11px] text-neutral-300" >
< input
< input
type = "checkbox"
type = "checkbox"
checked = { nodeData . removeEmpty }
checked = { nodeData . removeEmpty }
onChange = { ( e ) = > updateNodeData ( id , { removeEmpty : e.target.checked } ) }
onChange = { ( e ) = > updateNodeData ( id , { removeEmpty : e.target.checked } ) }
className = "nodrag nopan w-3 h-3 rounded bg-[#1a1a1a] text-neutral-600 focus:ring-1 focus:ring-neutral-600 focus:ring-offset-0"
className = "nodrag nopan w-3 h-3"
/ >
/ >
Remove empty
Remove empty
< / label >
< / label >
< / div >
< / div >
< / div >
) }
) }
< / div >
< / div >
< div className = "text-[10px] uppercase tracking-wide text-neutral-500" >
< div className = "mt-1 text-[10px] uppercase tracking-wide text-neutral-500" >
Parsed Items ( { previewItems . length } )
Parsed Items ( { previewItems . length } )
< / div >
< / div >
< div className = "relative flex-1 min-h-[90px] border border-neutral-700 rounded bg-neutral-900/40 " >
< div className = "relative min-h-[50px] border border-neutral-700/40 rounded-md bg-[#1a1a1a] " >
{ nodeData . error ? (
{ nodeData . error ? (
< div className = "p-2 text-[11px] text-red-400" > { nodeData . error } < / div >
< div className = "p-2 text-[11px] text-red-400" > { nodeData . error } < / div >
) : previewItems . length === 0 ? (
) : previewItems . length === 0 ? (
@ -282,24 +283,23 @@ export function ArrayNode({ id, data, selected }: NodeProps<ArrayNodeType>) {
{ previewItems . map ( ( item , index ) = > {
{ previewItems . map ( ( item , index ) = > {
const isSelected = nodeData . selectedOutputIndex === index ;
const isSelected = nodeData . selectedOutputIndex === index ;
return (
return (
< div key = { ` ${ index } - ${ item } ` } className = "relative pr-8" >
< button
< button
key = { ` ${ index } - ${ item } ` }
type = "button"
type = "button"
onClick = { ( ) = >
onClick = { ( ) = >
updateNodeData ( id , {
updateNodeData ( id , {
selectedOutputIndex : isSelected ? null : index ,
selectedOutputIndex : isSelected ? null : index ,
} )
} )
}
}
className = { ` nodrag nopan w-[calc(100%-1rem)] mx-2 my-1 rounded border px-2 py-1 text-[11px] text-left truncate transition-colors ${
className = { ` nodrag nopan w-[calc(100%-1rem)] mx-2 my-0.5 rounded-md px-2 py-1 text-[11px] text-left truncate transition-colors ${
isSelected
isSelected
? "border-blue-500 bg-blue-900/40 text-blue-200"
? "bg-blue-900/40 text-blue-200 ring-1 ring-blue-500/60"
: "border-neutral-700 bg-neutral-900/80 text-neutral-200 hover:bg-neutral-800"
: "bg-neutral-800/60 text-neutral-300 hover:bg-neutral-700/60"
} ` }
} ` }
title = { isSelected ? "Selected for next connection (click to unselect)" : "Click to select for next connection" }
title = { isSelected ? "Selected for next connection (click to unselect)" : "Click to select for next connection" }
>
>
{ index + 1 } . { item }
{ index + 1 } . { item }
< / button >
< / button >
< / div >
) ;
) ;
} ) }
} ) }
< / div >
< / div >