diff --git a/skyvern-frontend/src/components/WorkflowBlockInputSet.tsx b/skyvern-frontend/src/components/WorkflowBlockInputSet.tsx new file mode 100644 index 00000000..587b39a8 --- /dev/null +++ b/skyvern-frontend/src/components/WorkflowBlockInputSet.tsx @@ -0,0 +1,84 @@ +import { PlusIcon } from "@radix-ui/react-icons"; +import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; +import { WorkflowBlockParameterSelect } from "@/routes/workflows/editor/nodes/WorkflowBlockParameterSelect"; +import { useState, useEffect } from "react"; +import { Cross2Icon } from "@radix-ui/react-icons"; +import "./workflow-block-input-set.css"; +import { useWorkflowParametersState } from "@/routes/workflows/editor/useWorkflowParametersState"; + +type Props = { + onChange: (parameterKeys: Set) => void; + nodeId: string; + values: Set; +}; + +function WorkflowBlockInputSet(props: Props) { + const { nodeId, onChange, values } = props; + const [parameterKeys, setParameterKeys] = useState>(values); + const hasKeys = parameterKeys.size > 0; + const [workflowParameters] = useWorkflowParametersState(); + const availableParameterKeys = new Set( + workflowParameters.map((parameter) => parameter.key), + ); + + useEffect(() => { + onChange(new Set(parameterKeys)); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [parameterKeys]); + + return ( +
+
+ {hasKeys ? ( + Array.from(parameterKeys).map((parameterKey) => { + const missing = !availableParameterKeys.has(parameterKey); + + return ( +
+ {parameterKey} + { + setParameterKeys((prev) => { + const newSet = new Set(prev); + newSet.delete(parameterKey); + return newSet; + }); + }} + /> +
+ ); + }) + ) : ( +   + )} +
+
+ + +
+ +
+
+ + { + setParameterKeys((prev) => { + const newSet = new Set(prev); + newSet.add(parameterKey); + return newSet; + }); + }} + /> + +
+
+
+ ); +} + +export { WorkflowBlockInputSet }; diff --git a/skyvern-frontend/src/components/workflow-block-input-set.css b/skyvern-frontend/src/components/workflow-block-input-set.css new file mode 100644 index 00000000..8c077703 --- /dev/null +++ b/skyvern-frontend/src/components/workflow-block-input-set.css @@ -0,0 +1,27 @@ +.workflow-block-input-set { + font-size: 0.8rem; + width: 100%; + padding: 0.5rem; + padding-right: 2.25rem; + padding-left: 0.75rem; +} + +.workflow-block-input-set .ze-set { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + align-items: center; + justify-content: flex-start; +} + +.workflow-block-input-set .parameter-key { + background-color: rgba(255, 255, 255, 0.05); + padding: 0.25rem; + padding-left: 0.35rem; + border-radius: 0.25rem; +} + +.workflow-block-input-set .parameter-key.missing { + background-color: rgba(255, 0, 0, 0.1); + color: red; +} diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/CodeBlockNode/CodeBlockNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/CodeBlockNode/CodeBlockNode.tsx index 765266a8..021b5709 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/CodeBlockNode/CodeBlockNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/CodeBlockNode/CodeBlockNode.tsx @@ -1,4 +1,5 @@ import { Label } from "@/components/ui/label"; +import { WorkflowBlockInputSet } from "@/components/WorkflowBlockInputSet"; import { CodeEditor } from "@/routes/workflows/components/CodeEditor"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler"; @@ -19,6 +20,7 @@ function CodeBlockNode({ id, data }: NodeProps) { }); const [inputs, setInputs] = useState({ code: data.code, + parameterKeys: data.parameterKeys, }); return ( @@ -61,6 +63,20 @@ function CodeBlockNode({ id, data }: NodeProps) { }} /> +
+ + { + setInputs({ + ...inputs, + parameterKeys: Array.from(parameterKeys), + }); + updateNodeData(id, { parameterKeys: Array.from(parameterKeys) }); + }} + values={new Set(inputs.parameterKeys ?? [])} + /> +
| null; }; export type CodeBlockNode = Node; +const codeLead = ` +# This feature is currently in private beta. Please reach out to +# founders@skyvern.com to get access. +# +# Any parameter you've added to the "Input Parameters" list is available in +# global scope, by the same name. +# +# Any top-level variable you create is assigned to the output of this block. +# e.g., if you've written 'x = 5', then 'x' is included in the block output. +`; + export const codeBlockNodeDefaultData: CodeBlockNodeData = { editable: true, label: "", - code: `# This feature is currently in private beta. Please reach out to founders@skyvern.com to get access\n# All variables will be assigned to the output of this block.\n# Like 'x = 5', 'x' will be assigned to the output of this block.\n\n`, + code: codeLead, continueOnFailure: false, + parameterKeys: null, } as const; diff --git a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowNodeLibraryPanel.tsx b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowNodeLibraryPanel.tsx index 4f30d76f..7a8e2fd0 100644 --- a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowNodeLibraryPanel.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowNodeLibraryPanel.tsx @@ -6,7 +6,8 @@ import { AddNodeProps } from "../FlowRenderer"; import { WorkflowBlockNode } from "../nodes"; import { WorkflowBlockIcon } from "../nodes/WorkflowBlockIcon"; -const enableCodeBlock = import.meta.env.VITE_ENABLE_CODE_BLOCK === "true"; +const enableCodeBlock = + import.meta.env.VITE_ENABLE_CODE_BLOCK?.toLowerCase() === "true"; const nodeLibraryItems: Array<{ nodeType: NonNullable; diff --git a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts index 5dbd607c..19ba1f86 100644 --- a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts +++ b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts @@ -384,6 +384,7 @@ function convertToNode( data: { ...commonData, code: block.code, + parameterKeys: block.parameters.map((p) => p.key), }, }; } @@ -1140,6 +1141,7 @@ function getWorkflowBlock(node: WorkflowBlockNode): BlockYAML { return { ...base, block_type: "code", + parameter_keys: node.data.parameterKeys, code: node.data.code, }; } @@ -1842,6 +1844,7 @@ function convertBlocksToBlockYAML( ...base, block_type: "code", code: block.code, + parameter_keys: block.parameters.map((p) => p.key), }; return blockYaml; }