Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 34 additions & 29 deletions src/areas/workflows/nodes/ExtensionNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,8 @@ export default function ExtensionNode({ id, data, selected }: { id: string; data
const { updateNodeData } = useReactFlow()
const running = useWorkflowRunStore((s) => s.activeNodeId === id)

// Refs for handle alignment — support up to 2 inputs
const ioRowRef = useRef<HTMLDivElement>(null)
const ioRow2Ref = useRef<HTMLDivElement>(null)
const [handleTop, setHandleTop] = useState('50%')
const [handle2Top, setHandle2Top] = useState('50%')
const handleRefs = useRef<HTMLDivElement[]>([])
const [handleTops, setHandleTops] = useState<string[]>([])

const { modelExtensions, processExtensions } = useExtensionsStore()
const allExtensions = buildAllWorkflowExtensions(modelExtensions, processExtensions)
Expand All @@ -178,15 +175,14 @@ export default function ExtensionNode({ id, data, selected }: { id: string; data

// Align handles with their respective IO rows after mount
useLayoutEffect(() => {
if (ioRowRef.current) {
const center = ioRowRef.current.offsetTop + ioRowRef.current.offsetHeight / 2
setHandleTop(`${center}px`)
}
if (ioRow2Ref.current) {
const center = ioRow2Ref.current.offsetTop + ioRow2Ref.current.offsetHeight / 2
setHandle2Top(`${center}px`)
}
}, [isMulti])
setHandleTops(handleRefs.current.map((ref) => {
if (ref) {
const center = ref.offsetTop + ref.offsetHeight / 2
return `${center}px`
}
return '50%'
}))
}, [isMulti, inputs?.length])

const patchParam = useCallback((key: string, val: number | string) => {
const params = { ...data.params, [key]: val }
Expand Down Expand Up @@ -232,7 +228,7 @@ export default function ExtensionNode({ id, data, selected }: { id: string; data
</div>
) : (
// Single-input layout (existing behavior)
<div ref={ioRowRef} className="flex items-center justify-between px-3 py-2">
<div ref={(el) => { if (el) handleRefs.current[0] = el }} className="flex items-center justify-between px-3 py-2">
<span className={`inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-medium border ${TAG_CLS[ext?.input ?? ''] ?? 'border-zinc-700 bg-zinc-800 text-zinc-400'}`}>
{ext?.input ?? '—'}
</span>
Expand All @@ -250,31 +246,40 @@ export default function ExtensionNode({ id, data, selected }: { id: string; data
)

// ── Handles ──────────────────────────────────────────────────────────────
const handlesEl = (
const handlesEl = isMulti ? (
<>
{/* Primary input handle */}
<Handle
id="input-0"
type="target"
position={Position.Left}
style={{ background: HANDLE_COLOR[isMulti ? inputs[0] : (ext?.input ?? 'image')], width: 14, height: 14, border: '2.5px solid #18181b', top: handleTop }}
/>
{/* Secondary input handle (multi-input only) */}
{isMulti && (
{inputs.map((inputType, i) => (
<Handle
id="input-1"
key={i}
id={`input-${i}`}
type="target"
position={Position.Left}
style={{ background: HANDLE_COLOR[inputs[1]], width: 14, height: 14, border: '2.5px solid #18181b', top: handle2Top }}
style={{ background: HANDLE_COLOR[inputType], width: 14, height: 14, border: '2.5px solid #18181b', top: handleTops[i] ?? '50%' }}
/>
))}
{!isTerminal && (
<Handle
id="output"
type="source"
position={Position.Right}
style={{ background: outputColor, width: 14, height: 14, border: '2.5px solid #18181b', top: handleTops[0] ?? '50%' }}
/>
)}
{/* Output handle */}
</>
) : (
<>
<Handle
id="input-0"
type="target"
position={Position.Left}
style={{ background: HANDLE_COLOR[ext?.input ?? 'image'], width: 14, height: 14, border: '2.5px solid #18181b', top: handleTops[0] ?? '50%' }}
/>
{!isTerminal && (
<Handle
id="output"
type="source"
position={Position.Right}
style={{ background: outputColor, width: 14, height: 14, border: '2.5px solid #18181b', top: handleTop }}
style={{ background: outputColor, width: 14, height: 14, border: '2.5px solid #18181b', top: handleTops[0] ?? '50%' }}
/>
)}
</>
Expand Down
12 changes: 12 additions & 0 deletions src/areas/workflows/workflowRunStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,9 @@ async function executeExtensionNode(
const incomingEdges = workflow.edges.filter((e) => e.target === node.id)

if (ext?.inputs && ext.inputs.length > 1) {
const inputTypes = ext.inputs
const inputPaths = new Array<string | undefined>(inputTypes.length).fill(undefined)

for (const edge of incomingEdges) {
const src = resolveSource(edge.source)
if (!src) continue
Expand Down Expand Up @@ -426,6 +429,15 @@ async function executeExtensionNode(
const parts = (node.data.extensionId ?? '').split('/')
const extId = parts[0]
const nid = parts[1] ?? ''

const processParams: Record<string, unknown> = { ...(node.data.params as Record<string, unknown>) }
if (nodeInputMeshPath && nodeInputPath) {
// Texture node: mesh is filePath, all images in extra_image_paths
processParams.extra_image_paths = [nodeInputPath, ...extraImagePaths]
} else if (extraImagePaths.length > 0) {
processParams.extra_image_paths = extraImagePaths
}

const result = await window.electron.extensions.runProcess(
extId,
{
Expand Down