Add frontend editor support for PickValueModule

Palette entry, icon mappings, FormPickValue component with mode
selector and grow-on-connect, type union updates across stores.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
John Chilton
2026-03-22 19:15:03 -04:00
co-authored by Claude Opus 4.6
parent 168d65d190
commit 999e73c8f3
8 changed files with 100 additions and 4 deletions
@@ -43,8 +43,12 @@
v-if="isSubworkflow"
:step="step"
@onUpdateStep="(id, step) => emit('onUpdateStep', id, step)" />
<FormPickValue
v-if="type == 'pick_value'"
:step="step"
@onChange="onChange" />
<FormInputCollection
v-if="type == 'data_collection_input'"
v-else-if="type == 'data_collection_input'"
:step="step"
:datatypes="datatypes"
:inputs="configForm?.inputs"
@@ -87,6 +91,7 @@ import FormDisplay from "@/components/Form/FormDisplay.vue";
import FormElement from "@/components/Form/FormElement.vue";
import FormInputCollection from "@/components/Workflow/Editor/Forms/FormInputCollection.vue";
import FormOutputLabel from "@/components/Workflow/Editor/Forms/FormOutputLabel.vue";
import FormPickValue from "@/components/Workflow/Editor/Forms/FormPickValue.vue";
const props = defineProps<{
step: Step;
@@ -0,0 +1,81 @@
<script setup lang="ts">
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<string, unknown>;
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());
</script>
<template>
<div>
<FormElement
id="mode"
:value="asToolState(toolState).mode"
title="Selection Mode"
type="select"
:options="modeOptions"
help="How to select among the connected inputs."
@input="onMode" />
</div>
</template>
@@ -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,
},
];
}
@@ -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,
+1
View File
@@ -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",
};
@@ -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<WorkflowStepIconProps>();
@@ -21,6 +21,7 @@ function getContentId(step: Step) {
data_input: () => "input",
parameter_input: () => "input",
pause: () => "pause",
pick_value: () => "pick_value",
});
}
+1 -1
View File
@@ -113,7 +113,7 @@ export interface NewStep {
tool_state: Record<string, unknown>;
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;