Просмотр исходного кода

feat(webui): derived ports follow a node's config, and stale edges go with them

fszontagh 1 месяц назад
Родитель
Сommit
d52ea021ba
1 измененных файлов с 23 добавлено и 5 удалено
  1. 23 5
      webui/src/pages/WorkflowEditorPage.tsx

+ 23 - 5
webui/src/pages/WorkflowEditorPage.tsx

@@ -36,7 +36,7 @@ import { useExecutionPinStore } from '../stores/executionPinStore'
 import type { ExecutionDetail, WorkflowNode as WorkflowNodeType, Connection as WorkflowConnection } from '../api/workflows'
 
 // Import extracted components
-import { WorkflowNode, NodeExecutionState, IterationResult } from '../components/workflow/WorkflowNode'
+import { WorkflowNode, NodeExecutionState, IterationResult, resolveOutputs } from '../components/workflow/WorkflowNode'
 import { ExecutionResultsPanel } from '../components/workflow/ExecutionResultsPanel'
 import { NodePickerSidebar } from '../components/workflow/NodePickerSidebar'
 import { DeleteConfirmModal } from '../components/workflow/DeleteConfirmModal'
@@ -908,6 +908,8 @@ function WorkflowEditorInner() {
           isTrigger: nodeDef?.isTrigger || false,
           icon: nodeDef?.icon,
           outputs,
+          dynamicOutputs: nodeDef?.dynamicOutputs,
+          inputs: nodeDef?.inputs,
           nodeId: n.id,
           onExecute: () => {}, // No-op in view mode
           onExecuteTrigger: () => {}, // No-op in view mode
@@ -981,6 +983,8 @@ function WorkflowEditorInner() {
             isTrigger: nodeDef?.isTrigger || false,
             icon: nodeDef?.icon,
             outputs,
+            dynamicOutputs: nodeDef?.dynamicOutputs,
+            inputs: nodeDef?.inputs,
             nodeId: node.id,
             disabled: node.disabled === true,
             onExecute: () => executeWorkflow(),
@@ -1320,6 +1324,8 @@ function WorkflowEditorInner() {
         isTrigger: nodeDef.isTrigger,
         icon: nodeDef.icon,
         outputs,
+        dynamicOutputs: nodeDef.dynamicOutputs,
+        inputs: nodeDef.inputs,
         nodeId,
         onExecute: () => executeWorkflow(),
         onExecuteTrigger: executeTrigger,
@@ -1624,16 +1630,28 @@ function WorkflowEditorInner() {
     }
   }, [nodes, edges, setNodes])
 
-  // Save node config
+  // Save node config. A node whose ports come from its config can lose one when
+  // a rule is deleted, so any edge left hanging off a vanished port goes too.
   const saveNodeConfig = () => {
     if (!selectedNodeData) return
+    const nodeId = selectedNodeData.id
+    const nodeDef = nodeDefsMap[selectedNodeData.type]
+
     setNodes((nds) =>
       nds.map((n) =>
-        n.id === selectedNodeData.id
-          ? { ...n, data: { ...n.data, config: editingConfig } }
-          : n
+        n.id === nodeId ? { ...n, data: { ...n.data, config: editingConfig } } : n
       )
     )
+
+    if (nodeDef?.dynamicOutputs) {
+      const surviving = new Set(
+        resolveOutputs(nodeDef.outputs, nodeDef.dynamicOutputs, editingConfig).map((o) => o.name)
+      )
+      setEdges((eds) =>
+        eds.filter((e) => e.source !== nodeId || !e.sourceHandle || surviving.has(e.sourceHandle))
+      )
+    }
+
     setHasChanges(true)
     setShowNodeConfig(false)
     setSelectedNodeData(null)