+ Hi! 👋 To get started, add a block to your workflow. You can do that by
+ clicking the round plus button beneath the Start block, on the left
+
+ ) : null;
+
+ const runABlock = (
+
+ To run a single block, click the play button on that block. We'll run the
+ block in the browser, live!
+
+ );
+
+ const runWorkflow = (
+
+ To run your entire workflow, click the large Run button, top right.
+
+ );
+
+ const addBlocks = (
+
+ Not finished? Add a block to your workflow by clicking the round plus
+ button before or after any other block.
+
+ );
+
+ const removeBlocks = (
+
+ Too much? On the top right of each block is an ellipsis (...). Click that
+ and select "Delete" to remove the block.
+
+ );
+
+ const steps = [
+ getStarted,
+ runABlock,
+ runWorkflow,
+ getStarted === null ? addBlocks : null,
+ getStarted === null ? removeBlocks : null,
+ ].filter((step) => step);
+
if (!workflowRun || !workflowRunTimeline) {
return (
-
-
- Hi! 👋 We're experimenting with a new feature called debugger.
-
-
- This debugger allows you to see the state of your workflow in a live
- browser.
-
-
- You can run individual blocks, instead of the whole workflow.
-
-
- To get started, press the play button on a block in your workflow.
-
+
+ {getStarted === null &&
This is going to be awesome! 🤗
}
+ {steps.map((step, index) => (
+
+ {step}
+
+ ))}
);
diff --git a/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx b/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx
index 5ea7d43d..49c7ad4c 100644
--- a/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx
+++ b/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx
@@ -485,8 +485,7 @@ function FlowRenderer({
* TODO(jdo): hack
*/
const getXLock = () => {
- const hasForLoopNode = nodes.some((node) => node.type === "loop");
- return hasForLoopNode ? 24 : 104;
+ return 24;
};
useOnChange(debugStore.isDebugMode, (newValue) => {
diff --git a/skyvern-frontend/src/routes/workflows/editor/WorkflowHeader.tsx b/skyvern-frontend/src/routes/workflows/editor/WorkflowHeader.tsx
index 19c3e499..142e60b5 100644
--- a/skyvern-frontend/src/routes/workflows/editor/WorkflowHeader.tsx
+++ b/skyvern-frontend/src/routes/workflows/editor/WorkflowHeader.tsx
@@ -1,4 +1,5 @@
import { SaveIcon } from "@/components/icons/SaveIcon";
+import { BrowserIcon } from "@/components/icons/BrowserIcon";
import { Button } from "@/components/ui/button";
import {
Tooltip,
@@ -10,7 +11,6 @@ import {
ChevronDownIcon,
ChevronUpIcon,
CopyIcon,
- Crosshair1Icon,
PlayIcon,
ReloadIcon,
} from "@radix-ui/react-icons";
@@ -103,21 +103,36 @@ function WorkflowHeader({
) : (
<>
-
+
+
+
+
+
+
+ {debugStore.isDebugMode
+ ? "Turn off Browser"
+ : "Turn on Browser"}
+
+
+
diff --git a/skyvern-frontend/src/routes/workflows/editor/Workspace.tsx b/skyvern-frontend/src/routes/workflows/editor/Workspace.tsx
index 9c6fc542..475861d4 100644
--- a/skyvern-frontend/src/routes/workflows/editor/Workspace.tsx
+++ b/skyvern-frontend/src/routes/workflows/editor/Workspace.tsx
@@ -114,8 +114,10 @@ function Workspace({
]);
// ---start fya: https://github.com/frontyardart
+ const hasForLoopNode = nodes.some((node) => node.type === "loop");
+
const initialBrowserPosition = {
- x: 600,
+ x: hasForLoopNode ? 600 : 520,
y: 132,
};
@@ -143,6 +145,18 @@ function Workspace({
const workflowChangesStore = useWorkflowHasChangesStore();
+ /**
+ * Open a new tab (not window) with the browser session URL.
+ */
+ const handleOnBreakout = () => {
+ if (activeDebugSession) {
+ const pbsId = activeDebugSession.browser_session_id;
+ if (pbsId) {
+ window.open(`${location.origin}/browser-session/${pbsId}`, "_blank");
+ }
+ }
+ };
+
const handleOnCycle = () => {
setOpenDialogue(true);
};
@@ -435,8 +449,14 @@ function Workspace({
{/* sub panels */}
{workflowPanelState.active && (
{
promote("dropdown");
}}
@@ -471,7 +491,7 @@ function Workspace({
}}
>
-
+
{workflowRunId && (
promote("browserWindow")}
>
diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/ActionNode/ActionNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/ActionNode/ActionNode.tsx
index 71246f05..d4904de1 100644
--- a/skyvern-frontend/src/routes/workflows/editor/nodes/ActionNode/ActionNode.tsx
+++ b/skyvern-frontend/src/routes/workflows/editor/nodes/ActionNode/ActionNode.tsx
@@ -34,7 +34,6 @@ import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
import { RunEngineSelector } from "@/components/EngineSelector";
import { ModelSelector } from "@/components/ModelSelector";
-import { useDebugStore } from "@/store/useDebugStore";
import { useBlockScriptStore } from "@/store/BlockScriptStore";
import { cn } from "@/util/utils";
import { useParams } from "react-router-dom";
@@ -53,7 +52,7 @@ function ActionNode({ id, data, type }: NodeProps) {
const { updateNodeData } = useReactFlow();
const [facing, setFacing] = useState<"front" | "back">("front");
const blockScriptStore = useBlockScriptStore();
- const { editable, debuggable, label } = data;
+ const { editable, label } = data;
const script = blockScriptStore.scripts[label];
const [inputs, setInputs] = useState({
url: data.url,
@@ -69,7 +68,6 @@ function ActionNode({ id, data, type }: NodeProps) {
engine: data.engine,
});
const { blockLabel: urlBlockLabel } = useParams();
- const debugStore = useDebugStore();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
workflowRun && statusIsRunningOrQueued(workflowRun);
@@ -77,7 +75,6 @@ function ActionNode({ id, data, type }: NodeProps) {
urlBlockLabel !== undefined && urlBlockLabel === label;
const thisBlockIsPlaying =
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
const rerender = useRerender({ prefix: "accordian" });
const nodes = useNodes();
@@ -125,7 +122,6 @@ function ActionNode({ id, data, type }: NodeProps) {
>
) {
const { updateNodeData } = useReactFlow();
- const { debuggable, editable, label } = data;
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
+ const { editable, label } = data;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -55,7 +52,6 @@ function CodeBlockNode({ id, data }: NodeProps) {
>
) {
- const { debuggable, editable, label } = data;
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
+ const { editable, label } = data;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -50,7 +47,6 @@ function DownloadNode({ id, data }: NodeProps) {
>
) {
const { updateNodeData } = useReactFlow();
const [facing, setFacing] = useState<"front" | "back">("front");
const blockScriptStore = useBlockScriptStore();
- const { debuggable, editable, label } = data;
+ const { editable, label } = data;
const script = blockScriptStore.scripts[label];
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -114,7 +111,6 @@ function ExtractionNode({ id, data, type }: NodeProps) {
>
) {
const { updateNodeData } = useReactFlow();
const [facing, setFacing] = useState<"front" | "back">("front");
const blockScriptStore = useBlockScriptStore();
- const { debuggable, editable, label } = data;
+ const { editable, label } = data;
const script = blockScriptStore.scripts[label];
- const debugStore = useDebugStore();
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -64,7 +62,6 @@ function FileDownloadNode({ id, data }: NodeProps) {
urlBlockLabel !== undefined && urlBlockLabel === label;
const thisBlockIsPlaying =
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
const [inputs, setInputs] = useState({
url: data.url,
navigationGoal: data.navigationGoal,
@@ -122,7 +119,6 @@ function FileDownloadNode({ id, data }: NodeProps) {
>
) {
const { updateNodeData } = useReactFlow();
- const { debuggable, editable, label } = data;
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
+ const { editable, label } = data;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -69,7 +66,6 @@ function FileParserNode({ id, data }: NodeProps) {
>
) {
const { updateNodeData } = useReactFlow();
- const { debuggable, editable, label } = data;
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
+ const { editable, label } = data;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -80,7 +77,6 @@ function FileUploadNode({ id, data }: NodeProps) {
>
) {
const { updateNodeData } = useReactFlow();
const [facing, setFacing] = useState<"front" | "back">("front");
const blockScriptStore = useBlockScriptStore();
- const { debuggable, editable, label } = data;
+ const { editable, label } = data;
const script = blockScriptStore.scripts[label];
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -119,7 +116,6 @@ function LoginNode({ id, data, type }: NodeProps) {
>
) {
if (!node) {
throw new Error("Node not found"); // not possible
}
- const { debuggable, editable, label } = data;
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
+ const { editable, label } = data;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -109,7 +106,6 @@ function LoopNode({ id, data }: NodeProps) {
>
) {
const { blockLabel: urlBlockLabel } = useParams();
- const debugStore = useDebugStore();
const { updateNodeData } = useReactFlow();
const [facing, setFacing] = useState<"front" | "back">("front");
const blockScriptStore = useBlockScriptStore();
- const { editable, debuggable, label } = data;
+ const { editable, label } = data;
const script = blockScriptStore.scripts[label];
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -58,7 +56,6 @@ function NavigationNode({ id, data, type }: NodeProps) {
urlBlockLabel !== undefined && urlBlockLabel === label;
const thisBlockIsPlaying =
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
const rerender = useRerender({ prefix: "accordian" });
const [inputs, setInputs] = useState({
allowDownloads: data.allowDownloads,
@@ -125,7 +122,6 @@ function NavigationNode({ id, data, type }: NodeProps) {
) {
const { updateNodeData } = useReactFlow();
- const { debuggable, editable, label } = data;
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
+ const { editable, label } = data;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -69,7 +66,6 @@ function PDFParserNode({ id, data }: NodeProps) {
>
) {
const { updateNodeData } = useReactFlow();
- const { debuggable, editable, label } = data;
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
+ const { editable, label } = data;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -71,7 +68,6 @@ function SendEmailNode({ id, data }: NodeProps) {
>
) {
/>
-
-
-
-
+ {inputs.useScriptCache && (
+
+
+
+
+
+
{
+ const value = (event.target.value ?? "").trim();
+ const v = value.length ? value : null;
+ handleChange("scriptCacheKey", v);
+ }}
+ />
-
{
- const value = (event.target.value ?? "").trim();
- const v = value.length ? value : null;
- handleChange("scriptCacheKey", v);
- }}
- />
-
+ )}
diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx
index b0e428be..7167ef3f 100644
--- a/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx
+++ b/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx
@@ -36,7 +36,6 @@ import { WorkflowDataSchemaInputGroup } from "@/components/DataSchemaInputGroup/
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
import { RunEngineSelector } from "@/components/EngineSelector";
import { ModelSelector } from "@/components/ModelSelector";
-import { useDebugStore } from "@/store/useDebugStore";
import { cn } from "@/util/utils";
import { NodeHeader } from "../components/NodeHeader";
import { useParams } from "react-router-dom";
@@ -48,10 +47,8 @@ function TaskNode({ id, data, type }: NodeProps
) {
const { updateNodeData } = useReactFlow();
const [facing, setFacing] = useState<"front" | "back">("front");
const blockScriptStore = useBlockScriptStore();
- const { debuggable, editable, label } = data;
+ const { editable, label } = data;
const script = blockScriptStore.scripts[label];
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -126,7 +123,6 @@ function TaskNode({ id, data, type }: NodeProps) {
>
) {
- const { debuggable, editable, label } = data;
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
+ const { editable, label } = data;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -82,7 +79,6 @@ function Taskv2Node({ id, data, type }: NodeProps) {
>
) {
const { updateNodeData } = useReactFlow();
- const { debuggable, editable, label } = data;
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
+ const { editable, label } = data;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -72,7 +69,6 @@ function TextPromptNode({ id, data }: NodeProps) {
>
) {
const { updateNodeData } = useReactFlow();
const [facing, setFacing] = useState<"front" | "back">("front");
const blockScriptStore = useBlockScriptStore();
- const { debuggable, editable, label } = data;
+ const { editable, label } = data;
const script = blockScriptStore.scripts[label];
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -77,7 +74,6 @@ function URLNode({ id, data, type }: NodeProps) {
>
) {
- const { debuggable, editable, label } = data;
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
+ const { editable, label } = data;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -49,7 +46,6 @@ function UploadNode({ id, data }: NodeProps) {
>
) {
const { updateNodeData } = useReactFlow();
const [facing, setFacing] = useState<"front" | "back">("front");
const blockScriptStore = useBlockScriptStore();
- const { debuggable, editable, label } = data;
+ const { editable, label } = data;
const script = blockScriptStore.scripts[label];
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -109,7 +106,6 @@ function ValidationNode({ id, data, type }: NodeProps) {
>
) {
const { updateNodeData } = useReactFlow();
- const { debuggable, editable, label } = data;
- const debugStore = useDebugStore();
- const elideFromDebugging = debugStore.isDebugMode && !debuggable;
+ const { editable, label } = data;
const { blockLabel: urlBlockLabel } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const workflowRunIsRunningOrQueued =
@@ -66,7 +63,6 @@ function WaitNode({ id, data, type }: NodeProps) {
>
onMouseDownCapture?.()}
>
-