diff --git a/client/src/components/Workflow/Editor/Forms/FormDefault.vue b/client/src/components/Workflow/Editor/Forms/FormDefault.vue
index d69056932e0..205f9687206 100644
--- a/client/src/components/Workflow/Editor/Forms/FormDefault.vue
+++ b/client/src/components/Workflow/Editor/Forms/FormDefault.vue
@@ -43,8 +43,12 @@
v-if="isSubworkflow"
:step="step"
@onUpdateStep="(id, step) => emit('onUpdateStep', id, step)" />
+
+import { toRef, watch } from "vue";
+
+import type { Step } from "@/stores/workflowStepStore";
+
+import { useToolState } from "../composables/useToolState";
+
+import FormElement from "@/components/Form/FormElement.vue";
+
+interface ToolState {
+ mode: string;
+ num_inputs: number;
+}
+
+const props = defineProps<{
+ step: Step;
+}>();
+
+const stepRef = toRef(props, "step");
+const { toolState } = useToolState(stepRef);
+
+function asToolState(ts: unknown): ToolState {
+ const raw = ts as Record;
+ return {
+ mode: (raw.mode as string) ?? "first_non_null",
+ num_inputs: (raw.num_inputs as number) ?? 2,
+ };
+}
+
+function cleanToolState(): ToolState {
+ if (toolState.value) {
+ return asToolState({ ...toolState.value });
+ }
+ return { mode: "first_non_null", num_inputs: 2 };
+}
+
+const emit = defineEmits(["onChange"]);
+
+const modeOptions = [
+ { value: "first_non_null", label: "First non-null (error if all null)" },
+ { value: "first_or_skip", label: "First non-null (skip if all null)" },
+ { value: "the_only_non_null", label: "The only non-null (error if != 1)" },
+ { value: "all_non_null", label: "All non-null (as collection)" },
+];
+
+function onMode(newMode: string) {
+ const state = cleanToolState();
+ state.mode = newMode;
+ emit("onChange", state);
+}
+
+// Grow-on-connect: watch step connections, add terminal when last empty one gets connected
+watch(
+ () => props.step.input_connections,
+ (connections) => {
+ const state = cleanToolState();
+ const lastTerminalName = `input_${state.num_inputs}`;
+ if (connections && connections[lastTerminalName]) {
+ state.num_inputs = state.num_inputs + 1;
+ emit("onChange", state);
+ }
+ },
+ { deep: true }
+);
+
+// Dummy initial emit (same pattern as FormInputCollection — resets initialChange guard)
+emit("onChange", cleanToolState());
+
+
+
+
+
+
+
diff --git a/client/src/components/Workflow/Editor/modules/inputs.ts b/client/src/components/Workflow/Editor/modules/inputs.ts
index 4e299843d8a..fb4952d5737 100644
--- a/client/src/components/Workflow/Editor/modules/inputs.ts
+++ b/client/src/components/Workflow/Editor/modules/inputs.ts
@@ -1,5 +1,5 @@
import { faFile, faFolder } from "@fortawesome/free-regular-svg-icons";
-import { faPencilAlt } from "@fortawesome/free-solid-svg-icons";
+import { faCodeBranch, faPencilAlt } from "@fortawesome/free-solid-svg-icons";
import type { IconDefinition } from "font-awesome-6";
export interface WorkflowInput {
@@ -96,5 +96,11 @@ export function getWorkflowInputs(): WorkflowInput[] {
parameter_type: "directory_uri",
},
},
+ {
+ moduleId: "pick_value",
+ title: "Pick Value",
+ description: "Select among conditional branch outputs",
+ icon: faCodeBranch,
+ },
];
}
diff --git a/client/src/components/Workflow/Editor/modules/itemIcons.ts b/client/src/components/Workflow/Editor/modules/itemIcons.ts
index d21068ba8ab..24ba1dffad8 100644
--- a/client/src/components/Workflow/Editor/modules/itemIcons.ts
+++ b/client/src/components/Workflow/Editor/modules/itemIcons.ts
@@ -1,6 +1,7 @@
import {
faArrowRightFromBracket,
faArrowRightToBracket,
+ faCodeBranch,
faComment,
faFile,
faFolderOpen,
@@ -22,6 +23,7 @@ export const iconForType = {
parameter_input: faPen,
subworkflow: faSitemap,
pause: faPause,
+ pick_value: faCodeBranch,
},
input: faArrowRightToBracket,
output: faArrowRightFromBracket,
diff --git a/client/src/components/Workflow/icons.js b/client/src/components/Workflow/icons.js
index 02a5fa35476..02dd15d10c9 100644
--- a/client/src/components/Workflow/icons.js
+++ b/client/src/components/Workflow/icons.js
@@ -5,4 +5,5 @@ export default {
subworkflow: "fa-sitemap fa-rotate-270",
parameter_input: "fa-pencil-alt", // fa-pencil for older FontAwesome, fa-pencil-alt for newer
pause: "fa-pause",
+ pick_value: "fa-code-branch",
};
diff --git a/client/src/components/WorkflowInvocationState/WorkflowStepIcon.vue b/client/src/components/WorkflowInvocationState/WorkflowStepIcon.vue
index 7b103f3d27c..1b7b5a0a5b0 100644
--- a/client/src/components/WorkflowInvocationState/WorkflowStepIcon.vue
+++ b/client/src/components/WorkflowInvocationState/WorkflowStepIcon.vue
@@ -5,7 +5,7 @@ import { computed } from "vue";
import WorkflowIcons from "@/components/Workflow/icons";
interface WorkflowStepIconProps {
- stepType: "tool" | "data_input" | "data_collection_input" | "subworkflow" | "parameter_input" | "pause";
+ stepType: "tool" | "data_input" | "data_collection_input" | "subworkflow" | "parameter_input" | "pause" | "pick_value";
}
const props = defineProps();
diff --git a/client/src/stores/workflowNodeInspectorStore.ts b/client/src/stores/workflowNodeInspectorStore.ts
index e92683ac853..b1ad8b6dda5 100644
--- a/client/src/stores/workflowNodeInspectorStore.ts
+++ b/client/src/stores/workflowNodeInspectorStore.ts
@@ -21,6 +21,7 @@ function getContentId(step: Step) {
data_input: () => "input",
parameter_input: () => "input",
pause: () => "pause",
+ pick_value: () => "pick_value",
});
}
diff --git a/client/src/stores/workflowStepStore.ts b/client/src/stores/workflowStepStore.ts
index 94c5e287bbc..a5de55101ab 100644
--- a/client/src/stores/workflowStepStore.ts
+++ b/client/src/stores/workflowStepStore.ts
@@ -113,7 +113,7 @@ export interface NewStep {
tool_state: Record;
tool_version?: string;
tooltip?: string | null;
- type: "tool" | "data_input" | "data_collection_input" | "subworkflow" | "parameter_input" | "pause";
+ type: "tool" | "data_input" | "data_collection_input" | "subworkflow" | "parameter_input" | "pause" | "pick_value";
uuid?: string;
when?: string | null;
workflow_id?: string;