( node: WFNode, ctx: RunContext, setRunState: (updater: (s: WorkflowRunState) => WorkflowRunState) => void, )
| 136 | // if it feeds an Add-to-Scene through Waits. |
| 137 | |
| 138 | async function executeExtensionNode( |
| 139 | node: WFNode, |
| 140 | ctx: RunContext, |
| 141 | setRunState: (updater: (s: WorkflowRunState) => WorkflowRunState) => void, |
| 142 | ): Promise<void> { |
| 143 | const { workflow, allExtensions, client, workspaceDir, nodeOutputs, nodeMap, |
| 144 | selectedImagePath, selectedImageData } = ctx |
| 145 | |
| 146 | const ext = getWorkflowExtension(node.data.extensionId ?? '', allExtensions) |
| 147 | |
| 148 | const resolveSource = (sourceId: string): NodeOutput | undefined => { |
| 149 | const realId = resolveDataSource(sourceId, workflow.edges, nodeMap) |
| 150 | return realId ? nodeOutputs.get(realId) : undefined |
| 151 | } |
| 152 | |
| 153 | let nodeInputPath: string | undefined |
| 154 | let nodeInputText: string | undefined |
| 155 | let nodeInputMeshPath: string | undefined |
| 156 | |
| 157 | const incomingEdges = workflow.edges.filter((e) => e.target === node.id) |
| 158 | |
| 159 | if (ext?.inputs && ext.inputs.length > 1) { |
| 160 | for (const edge of incomingEdges) { |
| 161 | const src = resolveSource(edge.source) |
| 162 | if (!src) continue |
| 163 | if (src.outputType === 'mesh') nodeInputMeshPath = src.filePath |
| 164 | else if (src.outputType === 'image') nodeInputPath = src.filePath |
| 165 | else if (src.filePath !== undefined) nodeInputPath = src.filePath |
| 166 | if (src.text !== undefined) nodeInputText = src.text |
| 167 | } |
| 168 | } else { |
| 169 | for (const edge of incomingEdges) { |
| 170 | const src = resolveSource(edge.source) |
| 171 | if (src?.filePath !== undefined) nodeInputPath = src.filePath |
| 172 | if (src?.text !== undefined) nodeInputText = src.text |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | const isModelNode = ext?.type === 'model' |
| 177 | |
| 178 | if (isModelNode) { |
| 179 | const activeImagePath = nodeInputPath ?? selectedImagePath |
| 180 | if (!selectedImageData && (!activeImagePath || activeImagePath.trim().length === 0)) { |
| 181 | throw new Error('No input image selected for model node') |
| 182 | } |
| 183 | const base64 = selectedImageData && nodeInputPath === undefined |
| 184 | ? selectedImageData |
| 185 | : await window.electron.fs.readFileBase64(activeImagePath as string) |
| 186 | const bytes = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)) |
| 187 | const blob = new Blob([bytes], { type: 'image/png' }) |
| 188 | const fname = activeImagePath?.split(/[\\/]/).pop() ?? 'image.png' |
| 189 | |
| 190 | const extraParams: Record<string, unknown> = {} |
| 191 | if (nodeInputMeshPath) { |
| 192 | const norm = nodeInputMeshPath.replace(/\\/g, '/') |
| 193 | extraParams.mesh_path = norm.startsWith(workspaceDir) |
| 194 | ? norm.slice(workspaceDir.length).replace(/^\//, '') |
| 195 | : norm |
no test coverage detected