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]
> Adds `URLNode` to Skyvern frontend for URL navigation in workflows, updating node types, icons, and UI components.
>
>   - **New Feature**:
>     - Introduces `URLNode` component in `URLNode.tsx` for navigating to URLs in workflows.
>     - Adds `URLNode` type in `types.ts` and default data in `urlNodeDefaultData`.
>     - Updates `WorkflowBlockIcon.tsx` to include `ExternalLinkIcon` for `goto_url` type.
>   - **Integration**:
>     - Updates `index.ts` to include `URLNode` in `WorkflowBlockNode` and `nodeTypes`.
>     - Modifies `workflowEditorUtils.ts` to handle `goto_url` in `convertToNode()`, `createNode()`, and `getWorkflowBlock()`.
>     - Adds `URLBlock` type in `workflowTypes.ts` and `URLBlockYAML` in `workflowYamlTypes.ts`.
>   - **UI Enhancements**:
>     - Adds `Go to URL Block` to `WorkflowNodeLibraryPanel.tsx` with description and icon.
>     - Updates `helpContent.ts` to include tooltips and placeholders for `url`.
>
> <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 6837159776fa877a8565330e53667954c07ad2ee. It will automatically update as commits are pushed.</sup>

<!-- ELLIPSIS_HIDDEN -->
  • Loading branch information
wintonzheng committed Jan 30, 2025
1 parent 164193a commit 7c29369
Show file tree
Hide file tree
Showing 12 changed files with 203 additions and 7 deletions.
1 change: 0 additions & 1 deletion skyvern-frontend/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export const ProxyLocation = {
ResidentialJP: "RESIDENTIAL_JP",
ResidentialGB: "RESIDENTIAL_GB",
ResidentialFR: "RESIDENTIAL_FR",
ResidentialDE: "RESIDENTIAL_DE",
None: "NONE",
} as const;

Expand Down
3 changes: 0 additions & 3 deletions skyvern-frontend/src/components/ProxySelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@ function ProxySelector({ value, onChange, className }: Props) {
<SelectItem value={ProxyLocation.ResidentialFR}>
Residential (France)
</SelectItem>
<SelectItem value={ProxyLocation.ResidentialDE}>
Residential (Germany)
</SelectItem>
</SelectContent>
</Select>
);
Expand Down
5 changes: 5 additions & 0 deletions skyvern-frontend/src/routes/workflows/editor/helpContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export const helpTooltips = {
fileUrl: "The URL from which the file will be downloaded",
jsonSchema: "Specify a format for the extracted information from the file",
},
url: baseHelpTooltipContent,
};

export const placeholders = {
Expand Down Expand Up @@ -140,4 +141,8 @@ export const placeholders = {
fileUrl: basePlaceholderContent,
wait: basePlaceholderContent,
pdfParser: basePlaceholderContent,
url: {
...basePlaceholderContent,
url: "(required) Navigate to this URL: https://...",
},
};
103 changes: 103 additions & 0 deletions skyvern-frontend/src/routes/workflows/editor/nodes/URLNode/URLNode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react";
import type { URLNode } from "./types";
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback";
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
import { useState } from "react";
import { WorkflowBlockIcon } from "../WorkflowBlockIcon";
import { WorkflowBlockTypes } from "@/routes/workflows/types/workflowTypes";
import { EditableNodeTitle } from "../components/EditableNodeTitle";
import { NodeActionMenu } from "../NodeActionMenu";
import { Label } from "@/components/ui/label";
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
import { placeholders } from "../../helpContent";

function URLNode({ id, data, type }: NodeProps<URLNode>) {
const { updateNodeData } = useReactFlow();
const { editable } = data;
const deleteNodeCallback = useDeleteNodeCallback();
const [label, setLabel] = useNodeLabelChangeHandler({
id,
initialValue: data.label,
});
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });

const [inputs, setInputs] = useState({
url: data.url,
});

function handleChange(key: string, value: unknown) {
if (!editable) {
return;
}
setInputs({ ...inputs, [key]: value });
updateNodeData(id, { [key]: value });
}

return (
<div>
<Handle
type="source"
position={Position.Bottom}
id="a"
className="opacity-0"
/>
<Handle
type="target"
position={Position.Top}
id="b"
className="opacity-0"
/>
<div className="w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4">
<div className="flex h-[2.75rem] justify-between">
<div className="flex gap-2">
<div className="flex h-[2.75rem] w-[2.75rem] items-center justify-center rounded border border-slate-600">
<WorkflowBlockIcon
workflowBlockType={WorkflowBlockTypes.URL}
className="size-6"
/>
</div>
<div className="flex flex-col gap-1">
<EditableNodeTitle
value={label}
editable={editable}
onChange={setLabel}
titleClassName="text-base"
inputClassName="text-base"
/>
<span className="text-xs text-slate-400">Go to URL Block</span>
</div>
</div>
<NodeActionMenu
onDelete={() => {
deleteNodeCallback(id);
}}
/>
</div>
<div className="space-y-4">
<div className="space-y-2">
<div className="flex justify-between">
<Label className="text-xs text-slate-300">URL</Label>
{isFirstWorkflowBlock ? (
<div className="flex justify-end text-xs text-slate-400">
Tip: Use the {"+"} button to add parameters!
</div>
) : null}
</div>
<WorkflowBlockInputTextarea
nodeId={id}
onChange={(value) => {
handleChange("url", value);
}}
value={inputs.url}
placeholder={placeholders[type]["url"]}
className="nopan text-xs"
/>
</div>
</div>
</div>
</div>
);
}

export { URLNode };
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Node } from "@xyflow/react";
import { NodeBaseData } from "../types";

export type URLNodeData = NodeBaseData & {
url: string;
};

export type URLNode = Node<URLNodeData, "url">;

export const urlNodeDefaultData: URLNodeData = {
label: "",
continueOnFailure: false,
url: "",
editable: true,
};

export function isUrlNode(node: Node): node is URLNode {
return node.type === "url";
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
CursorTextIcon,
DownloadIcon,
EnvelopeClosedIcon,
ExternalLinkIcon,
FileTextIcon,
ListBulletIcon,
LockOpen1Icon,
Expand Down Expand Up @@ -72,6 +73,9 @@ function WorkflowBlockIcon({ workflowBlockType, className }: Props) {
case "pdf_parser": {
return <FileTextIcon className={className} />;
}
case "goto_url": {
return <ExternalLinkIcon className={className} />;
}
}
}

Expand Down
6 changes: 5 additions & 1 deletion skyvern-frontend/src/routes/workflows/editor/nodes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import { PDFParserNode } from "./PDFParserNode/types";
import { PDFParserNode as PDFParserNodeComponent } from "./PDFParserNode/PDFParserNode";
import { Taskv2Node } from "./Taskv2Node/types";
import { Taskv2Node as Taskv2NodeComponent } from "./Taskv2Node/Taskv2Node";
import { URLNode } from "./URLNode/types";
import { URLNode as URLNodeComponent } from "./URLNode/URLNode";

export type UtilityNode = StartNode | NodeAdderNode;

Expand All @@ -57,7 +59,8 @@ export type WorkflowBlockNode =
| WaitNode
| FileDownloadNode
| PDFParserNode
| Taskv2Node;
| Taskv2Node
| URLNode;

export function isUtilityNode(node: AppNode): node is UtilityNode {
return node.type === "nodeAdder" || node.type === "start";
Expand Down Expand Up @@ -89,4 +92,5 @@ export const nodeTypes = {
fileDownload: memo(FileDownloadNodeComponent),
pdfParser: memo(PDFParserNodeComponent),
taskv2: memo(Taskv2NodeComponent),
url: memo(URLNodeComponent),
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@ export const workflowBlockTitle: {
wait: "Wait",
pdf_parser: "PDF Parser",
task_v2: "Task v2",
goto_url: "Go to URL",
};
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,17 @@ const nodeLibraryItems: Array<{
title: "Task v2 Block",
description: "Runs a Skyvern v2 Task",
},
{
nodeType: "url",
icon: (
<WorkflowBlockIcon
workflowBlockType={WorkflowBlockTypes.URL}
className="size-6"
/>
),
title: "Go to URL Block",
description: "Navigates to a URL",
},
{
nodeType: "textPrompt",
icon: (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
FileDownloadBlockYAML,
PDFParserBlockYAML,
Taskv2BlockYAML,
URLBlockYAML,
} from "../types/workflowYamlTypes";
import {
EMAIL_BLOCK_SENDER,
Expand Down Expand Up @@ -88,6 +89,7 @@ import { fileDownloadNodeDefaultData } from "./nodes/FileDownloadNode/types";
import { ProxyLocation } from "@/api/types";
import { pdfParserNodeDefaultData } from "./nodes/PDFParserNode/types";
import { taskv2NodeDefaultData } from "./nodes/Taskv2Node/types";
import { urlNodeDefaultData } from "./nodes/URLNode/types";

export const NEW_NODE_LABEL_PREFIX = "block_";

Expand Down Expand Up @@ -450,6 +452,18 @@ function convertToNode(
},
};
}

case "goto_url": {
return {
...identifiers,
...common,
type: "url",
data: {
...commonData,
url: block.url,
},
};
}
}
}

Expand Down Expand Up @@ -843,6 +857,17 @@ function createNode(
},
};
}
case "url": {
return {
...identifiers,
...common,
type: "url",
data: {
...urlNodeDefaultData,
label,
},
};
}
}
}

Expand Down Expand Up @@ -1094,6 +1119,13 @@ function getWorkflowBlock(node: WorkflowBlockNode): BlockYAML {
json_schema: JSONParseSafe(node.data.jsonSchema),
};
}
case "url": {
return {
...base,
block_type: "goto_url",
url: node.data.url,
};
}
default: {
throw new Error("Invalid node type for getWorkflowBlock");
}
Expand Down Expand Up @@ -1754,6 +1786,14 @@ function convertBlocksToBlockYAML(
};
return blockYaml;
}
case "goto_url": {
const blockYaml: URLBlockYAML = {
...base,
block_type: "goto_url",
url: block.url,
};
return blockYaml;
}
}
});
}
Expand Down
9 changes: 8 additions & 1 deletion skyvern-frontend/src/routes/workflows/types/workflowTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ export type WorkflowBlock =
| WaitBlock
| FileDownloadBlock
| PDFParserBlock
| Taskv2Block;
| Taskv2Block
| URLBlock;

export const WorkflowBlockTypes = {
Task: "task",
Expand All @@ -176,6 +177,7 @@ export const WorkflowBlockTypes = {
FileDownload: "file_download",
PDFParser: "pdf_parser",
Taskv2: "task_v2",
URL: "goto_url",
} as const;

export function isTaskVariantBlock(item: {
Expand Down Expand Up @@ -389,6 +391,11 @@ export type PDFParserBlock = WorkflowBlockBase & {
json_schema: Record<string, unknown> | null;
};

export type URLBlock = WorkflowBlockBase & {
block_type: "goto_url";
url: string;
};

export type WorkflowDefinition = {
parameters: Array<Parameter>;
blocks: Array<WorkflowBlock>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ export type BlockYAML =
| WaitBlockYAML
| FileDownloadBlockYAML
| PDFParserBlockYAML
| Taskv2BlockYAML;
| Taskv2BlockYAML
| URLBlockYAML;

export type BlockYAMLBase = {
block_type: WorkflowBlockType;
Expand Down Expand Up @@ -283,3 +284,8 @@ export type PDFParserBlockYAML = BlockYAMLBase & {
file_url: string;
json_schema: Record<string, unknown> | null;
};

export type URLBlockYAML = BlockYAMLBase & {
block_type: "goto_url";
url: string;
};

0 comments on commit 7c29369

Please sign in to comment.