Skip to content

Commit

Permalink
🔄 synced local 'skyvern-frontend/src/' with remote 'skyvern-frontend/…
Browse files Browse the repository at this point in the history
…src/'

<!-- ELLIPSIS_HIDDEN -->

> [!IMPORTANT]
> Add support for context parameters in the workflow editor UI, including new components and refactoring for improved parameter and node label handling.
>
>   - **Behavior**:
>     - Add `SourceParameterKeySelector` component for selecting context and output parameter keys.
>     - Support context parameters in `WorkflowParameterAddPanel` and `WorkflowParameterEditPanel`.
>     - Update `FlowRenderer` to handle context parameters in `convertToParametersYAML()`.
>   - **Node Handling**:
>     - Refactor node label change handling using `useNodeLabelChangeHandler` hook.
>     - Update nodes like `CodeBlockNode`, `DownloadNode`, `FileParserNode`, etc., to use the new label change handler.
>   - **Parameter Management**:
>     - Add context parameter type to `ParametersState` in `FlowRenderer`.
>     - Update `WorkflowParametersPanel` to manage context parameters.
>   - **Utilities**:
>     - Add `getUpdatedParametersAfterLabelUpdateForSourceParameterKey()` in `workflowEditorUtils`.
>     - Refactor `HorizontallyResizingInput` to use `ComponentPropsWithoutRef`.
>
> <sup>This description was created by </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=Skyvern-AI%2Fskyvern-cloud&utm_source=github&utm_medium=referral)<sup> for b83dcfc423379b99c008c4121cbc594ea0c0ed27. It will automatically update as commits are pushed.</sup>

<!-- ELLIPSIS_HIDDEN -->
  • Loading branch information
ykeremy committed Oct 3, 2024
1 parent 9e50456 commit 6d31312
Show file tree
Hide file tree
Showing 18 changed files with 342 additions and 270 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useNodes } from "@xyflow/react";
import { useWorkflowParametersState } from "../editor/useWorkflowParametersState";
import { AppNode } from "../editor/nodes";
import { getOutputParameterKey } from "../editor/workflowEditorUtils";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";

type Props = {
value?: string;
onChange: (value: string) => void;
};

function SourceParameterKeySelector({ value, onChange }: Props) {
const [workflowParameters] = useWorkflowParametersState();
const nodes = useNodes<AppNode>();
const contextParameterKeys = workflowParameters
.filter((parameter) => parameter.parameterType !== "credential")
.map((parameter) => parameter.key);
const outputParameterKeys = nodes
.filter((node) => node.type !== "nodeAdder")
.map((node) => getOutputParameterKey(node.data.label));

return (
<Select value={value} onValueChange={onChange}>
<SelectTrigger>
<SelectValue placeholder="Select a parameter" />
</SelectTrigger>
<SelectContent>
{[...contextParameterKeys, ...outputParameterKeys].map(
(parameterKey) => (
<SelectItem key={parameterKey} value={parameterKey}>
{parameterKey}
</SelectItem>
),
)}
</SelectContent>
</Select>
);
}

export { SourceParameterKeySelector };
34 changes: 23 additions & 11 deletions skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ import { useEffect, useState } from "react";
import {
AWSSecretParameter,
BitwardenSensitiveInformationParameter,
ContextParameter,
WorkflowApiResponse,
WorkflowParameterValueType,
} from "../types/workflowTypes";
import {
BitwardenLoginCredentialParameterYAML,
BlockYAML,
ContextParameterYAML,
ParameterYAML,
WorkflowCreateYAMLRequest,
WorkflowParameterYAML,
Expand Down Expand Up @@ -65,7 +65,11 @@ import { ReloadIcon } from "@radix-ui/react-icons";

function convertToParametersYAML(
parameters: ParametersState,
): Array<WorkflowParameterYAML | BitwardenLoginCredentialParameterYAML> {
): Array<
| WorkflowParameterYAML
| BitwardenLoginCredentialParameterYAML
| ContextParameterYAML
> {
return parameters.map((parameter) => {
if (parameter.parameterType === "workflow") {
return {
Expand All @@ -77,6 +81,13 @@ function convertToParametersYAML(
? {}
: { default_value: parameter.defaultValue }),
};
} else if (parameter.parameterType === "context") {
return {
parameter_type: "context",
key: parameter.key,
description: parameter.description || null,
source_parameter_key: parameter.sourceParameterKey,
};
} else {
return {
parameter_type: "bitwarden_login_credential",
Expand All @@ -99,15 +110,21 @@ export type ParametersState = Array<
key: string;
parameterType: "workflow";
dataType: WorkflowParameterValueType;
description?: string;
description?: string | null;
defaultValue: unknown;
}
| {
key: string;
parameterType: "credential";
collectionId: string;
urlParameterKey: string;
description?: string;
description?: string | null;
}
| {
key: string;
parameterType: "context";
sourceParameterKey: string;
description?: string | null;
}
>;

Expand Down Expand Up @@ -225,15 +242,10 @@ function FlowRenderer({
(parameter) => {
return (
parameter.parameter_type === "aws_secret" ||
parameter.parameter_type === "bitwarden_sensitive_information" ||
parameter.parameter_type === "context"
parameter.parameter_type === "bitwarden_sensitive_information"
);
},
) as Array<
| AWSSecretParameter
| BitwardenSensitiveInformationParameter
| ContextParameter
>;
) as Array<AWSSecretParameter | BitwardenSensitiveInformationParameter>;

const echoParameters = convertEchoParameters(filteredParameters);

Expand Down
12 changes: 11 additions & 1 deletion skyvern-frontend/src/routes/workflows/editor/WorkflowEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ function WorkflowEditor() {
.filter(
(parameter) =>
parameter.parameter_type === "workflow" ||
parameter.parameter_type === "bitwarden_login_credential",
parameter.parameter_type === "bitwarden_login_credential" ||
parameter.parameter_type === "context",
)
.map((parameter) => {
if (parameter.parameter_type === "workflow") {
Expand All @@ -60,13 +61,22 @@ function WorkflowEditor() {
parameterType: "workflow",
dataType: parameter.workflow_parameter_type,
defaultValue: parameter.default_value,
description: parameter.description,
};
} else if (parameter.parameter_type === "context") {
return {
key: parameter.key,
parameterType: "context",
sourceParameterKey: parameter.source.key,
description: parameter.description,
};
} else {
return {
key: parameter.key,
parameterType: "credential",
collectionId: parameter.bitwarden_collection_id,
urlParameterKey: parameter.url_parameter_key,
description: parameter.description,
};
}
})}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
import { Label } from "@/components/ui/label";
import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback";
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
import { CodeIcon } from "@radix-ui/react-icons";
import {
Handle,
NodeProps,
Position,
useNodes,
useReactFlow,
} from "@xyflow/react";
import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react";
import { useState } from "react";
import { EditableNodeTitle } from "../components/EditableNodeTitle";
import { NodeActionMenu } from "../NodeActionMenu";
import type { CodeBlockNode } from "./types";
import { useState } from "react";
import {
getUniqueLabelForExistingNode,
getUpdatedNodesAfterLabelUpdateForParameterKeys,
} from "../../workflowEditorUtils";
import { AppNode } from "..";

function CodeBlockNode({ id, data }: NodeProps<CodeBlockNode>) {
const { updateNodeData, setNodes } = useReactFlow();
const nodes = useNodes<AppNode>();
const { updateNodeData } = useReactFlow();
const deleteNodeCallback = useDeleteNodeCallback();
const [label, setLabel] = useState(data.label);
const [label, setLabel] = useNodeLabelChangeHandler({
id,
initialValue: data.label,
});
const [inputs, setInputs] = useState({
code: data.code,
});
Expand Down Expand Up @@ -52,22 +44,7 @@ function CodeBlockNode({ id, data }: NodeProps<CodeBlockNode>) {
<EditableNodeTitle
value={label}
editable={data.editable}
onChange={(value) => {
const existingLabels = nodes.map((n) => n.data.label);
const labelWithoutWhitespace = value.replace(/\s+/g, "_");
const newLabel = getUniqueLabelForExistingNode(
labelWithoutWhitespace,
existingLabels,
);
setLabel(newLabel);
setNodes(
getUpdatedNodesAfterLabelUpdateForParameterKeys(
id,
newLabel,
nodes as Array<AppNode>,
),
);
}}
onChange={setLabel}
titleClassName="text-base"
inputClassName="text-base"
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,19 @@
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback";
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
import { DownloadIcon } from "@radix-ui/react-icons";
import {
Handle,
NodeProps,
Position,
useNodes,
useReactFlow,
} from "@xyflow/react";
import { Handle, NodeProps, Position } from "@xyflow/react";
import { EditableNodeTitle } from "../components/EditableNodeTitle";
import { NodeActionMenu } from "../NodeActionMenu";
import type { DownloadNode } from "./types";
import { useState } from "react";
import {
getUniqueLabelForExistingNode,
getUpdatedNodesAfterLabelUpdateForParameterKeys,
} from "../../workflowEditorUtils";
import { AppNode } from "..";

function DownloadNode({ id, data }: NodeProps<DownloadNode>) {
const { setNodes } = useReactFlow();
const nodes = useNodes<AppNode>();
const [label, setLabel] = useNodeLabelChangeHandler({
id,
initialValue: data.label,
});
const deleteNodeCallback = useDeleteNodeCallback();
const [label, setLabel] = useState(data.label);

return (
<div>
Expand All @@ -49,22 +39,7 @@ function DownloadNode({ id, data }: NodeProps<DownloadNode>) {
<EditableNodeTitle
value={label}
editable={data.editable}
onChange={(value) => {
const existingLabels = nodes.map((n) => n.data.label);
const labelWithoutWhitespace = value.replace(/\s+/g, "_");
const newLabel = getUniqueLabelForExistingNode(
labelWithoutWhitespace,
existingLabels,
);
setLabel(newLabel);
setNodes(
getUpdatedNodesAfterLabelUpdateForParameterKeys(
id,
newLabel,
nodes as Array<AppNode>,
),
);
}}
onChange={setLabel}
titleClassName="text-base"
inputClassName="text-base"
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,23 @@
import { Input } from "@/components/ui/input";
import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback";
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
import { CursorTextIcon } from "@radix-ui/react-icons";
import {
Handle,
NodeProps,
Position,
useNodes,
useReactFlow,
} from "@xyflow/react";
import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react";
import { useState } from "react";
import { EditableNodeTitle } from "../components/EditableNodeTitle";
import { NodeActionMenu } from "../NodeActionMenu";
import type { FileParserNode } from "./types";
import { useState } from "react";
import {
getUniqueLabelForExistingNode,
getUpdatedNodesAfterLabelUpdateForParameterKeys,
} from "../../workflowEditorUtils";
import { AppNode } from "..";

function FileParserNode({ id, data }: NodeProps<FileParserNode>) {
const { updateNodeData, setNodes } = useReactFlow();
const { updateNodeData } = useReactFlow();
const deleteNodeCallback = useDeleteNodeCallback();
const nodes = useNodes<AppNode>();
const [label, setLabel] = useState(data.label);
const [inputs, setInputs] = useState({
fileUrl: data.fileUrl,
});
const [label, setLabel] = useNodeLabelChangeHandler({
id,
initialValue: data.label,
});

return (
<div>
Expand All @@ -51,22 +43,7 @@ function FileParserNode({ id, data }: NodeProps<FileParserNode>) {
<EditableNodeTitle
value={label}
editable={data.editable}
onChange={(value) => {
const existingLabels = nodes.map((n) => n.data.label);
const labelWithoutWhitespace = value.replace(/\s+/g, "_");
const newLabel = getUniqueLabelForExistingNode(
labelWithoutWhitespace,
existingLabels,
);
setLabel(newLabel);
setNodes(
getUpdatedNodesAfterLabelUpdateForParameterKeys(
id,
newLabel,
nodes as Array<AppNode>,
),
);
}}
onChange={setLabel}
titleClassName="text-base"
inputClassName="text-base"
/>
Expand Down
Loading

0 comments on commit 6d31312

Please sign in to comment.