diff --git a/skyvern-frontend/src/components/FloatingWindow.tsx b/skyvern-frontend/src/components/FloatingWindow.tsx
index 03dceeda..06f5b413 100644
--- a/skyvern-frontend/src/components/FloatingWindow.tsx
+++ b/skyvern-frontend/src/components/FloatingWindow.tsx
@@ -158,6 +158,8 @@ function FloatingWindow({
zIndex,
// --
onCycle,
+ onFocus,
+ onBlur,
onInteract,
}: {
bounded?: boolean;
@@ -172,9 +174,11 @@ function FloatingWindow({
showPowerButton?: boolean;
showReloadButton?: boolean;
title: string;
- zIndex?: string;
+ zIndex?: number;
// --
onCycle?: () => void;
+ onFocus?: () => void;
+ onBlur?: () => void;
onInteract?: () => void;
}) {
const [reloadKey, setReloadKey] = useState(0);
@@ -217,6 +221,7 @@ function FloatingWindow({
}
| undefined
>(undefined);
+ const hasInitialized = useRef(false);
const os = getOs();
@@ -284,9 +289,10 @@ function FloatingWindow({
);
useEffect(() => {
- if (!initialWidth || !initialHeight) {
+ if (hasInitialized.current || !initialWidth || !initialHeight) {
return;
}
+ hasInitialized.current = true;
setSize({
left: initialPosition?.x ?? 0,
top: initialPosition?.y ?? 0,
@@ -533,6 +539,16 @@ function FloatingWindow({
className={cn("border-2 border-gray-700", {
"hover:border-slate-500": !isMaximized,
})}
+ handleStyles={{
+ bottomLeft: {
+ width: "40px",
+ height: "40px",
+ },
+ bottomRight: {
+ width: "40px",
+ height: "40px",
+ },
+ }}
minHeight={Constants.MinHeight}
minWidth={Constants.MinWidth}
// TODO: turn back on; turning off clears a resize bug atm
@@ -556,6 +572,7 @@ function FloatingWindow({
return;
}
+ onFocus?.();
setIsMinimized(false);
setIsResizing(true);
setDragStartSize({ ...size, left: position.x, top: position.y });
@@ -565,6 +582,7 @@ function FloatingWindow({
return;
}
+ onFocus?.();
onResize({ delta, direction, size });
}}
onResizeStop={() => {
@@ -581,7 +599,8 @@ function FloatingWindow({
onInteract?.()}
+ onFocus={onFocus}
+ onBlur={onBlur}
+ onMouseDownCapture={(e) => {
+ onInteract?.();
+ e.currentTarget.focus();
+ }}
onDoubleClick={() => {
toggleMaximized();
}}
diff --git a/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx b/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx
index ecb757a8..37ebde26 100644
--- a/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx
+++ b/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx
@@ -7,7 +7,6 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
-import { toast } from "@/components/ui/use-toast";
import { useOnChange } from "@/hooks/useOnChange";
import { useShouldNotifyWhenClosingTab } from "@/hooks/useShouldNotifyWhenClosingTab";
import { BlockActionContext } from "@/store/BlockActionContext";
@@ -17,7 +16,6 @@ import {
useWorkflowSave,
type WorkflowSaveData,
} from "@/store/WorkflowHasChangesStore";
-import { useWorkflowPanelStore } from "@/store/WorkflowPanelStore";
import { useWorkflowTitleStore } from "@/store/WorkflowTitleStore";
import { ReloadIcon } from "@radix-ui/react-icons";
import {
@@ -25,17 +23,15 @@ import {
BackgroundVariant,
Controls,
Edge,
- Panel,
PanOnScrollMode,
ReactFlow,
Viewport,
- useEdgesState,
useNodesInitialized,
- useNodesState,
useReactFlow,
+ NodeChange,
+ EdgeChange,
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";
-import { nanoid } from "nanoid";
import { useCallback, useEffect, useRef, useState } from "react";
import { useBlocker } from "react-router-dom";
import {
@@ -56,22 +52,13 @@ import {
ParameterYAML,
WorkflowParameterYAML,
} from "../types/workflowYamlTypes";
-import { WorkflowHeader } from "./WorkflowHeader";
-import { WorkflowParametersStateContext } from "./WorkflowParametersStateContext";
import {
BITWARDEN_CLIENT_ID_AWS_SECRET_KEY,
BITWARDEN_CLIENT_SECRET_AWS_SECRET_KEY,
BITWARDEN_MASTER_PASSWORD_AWS_SECRET_KEY,
} from "./constants";
import { edgeTypes } from "./edges";
-import {
- AppNode,
- isWorkflowBlockNode,
- nodeTypes,
- WorkflowBlockNode,
-} from "./nodes";
-import { WorkflowNodeLibraryPanel } from "./panels/WorkflowNodeLibraryPanel";
-import { WorkflowParametersPanel } from "./panels/WorkflowParametersPanel";
+import { AppNode, isWorkflowBlockNode, nodeTypes } from "./nodes";
import {
ParametersState,
parameterIsSkyvernCredential,
@@ -81,22 +68,14 @@ import {
import "./reactFlowOverrideStyles.css";
import {
convertEchoParameters,
- createNode,
- defaultEdge,
descendants,
- generateNodeLabel,
getAdditionalParametersForEmailBlock,
getOrderedChildrenBlocks,
getOutputParameterKey,
getWorkflowBlocks,
- getWorkflowErrors,
getWorkflowSettings,
layout,
- nodeAdderNode,
- startNode,
} from "./workflowEditorUtils";
-import { cn } from "@/util/utils";
-import { WorkflowDebuggerRun } from "@/routes/workflows/editor/WorkflowDebuggerRun";
import { useAutoPan } from "./useAutoPan";
function convertToParametersYAML(
@@ -237,38 +216,38 @@ function convertToParametersYAML(
}
type Props = {
+ nodes: Array
;
+ edges: Array;
+ setNodes: (nodes: Array) => void;
+ setEdges: (edges: Array) => void;
+ onNodesChange: (changes: Array>) => void;
+ onEdgesChange: (changes: Array) => void;
initialTitle: string;
- initialNodes: Array;
- initialEdges: Array;
initialParameters: ParametersState;
workflow: WorkflowApiResponse;
-};
-
-export type AddNodeProps = {
- nodeType: NonNullable;
- previous: string | null;
- next: string | null;
- parent?: string;
- connectingEdgeType: string;
+ onDebuggableBlockCountChange: (count: number) => void;
+ onMouseDownCapture?: () => void;
+ zIndex?: number;
};
function FlowRenderer({
+ nodes,
+ edges,
+ setNodes,
+ setEdges,
+ onNodesChange,
+ onEdgesChange,
initialTitle,
- initialEdges,
- initialNodes,
initialParameters,
workflow,
+ onDebuggableBlockCountChange,
+ onMouseDownCapture,
+ zIndex,
}: Props) {
const reactFlowInstance = useReactFlow();
const debugStore = useDebugStore();
- const { workflowPanelState, setWorkflowPanelState, closeWorkflowPanel } =
- useWorkflowPanelStore();
const { title, initializeTitle } = useWorkflowTitleStore();
- const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
- const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
- const [parameters, setParameters] =
- useState(initialParameters);
- const [debuggableBlockCount, setDebuggableBlockCount] = useState(0);
+ const [parameters] = useState(initialParameters);
const nodesInitialized = useNodesInitialized();
const [shouldConstrainPan, setShouldConstrainPan] = useState(false);
const onNodesChangeTimeoutRef = useRef(null);
@@ -324,8 +303,8 @@ function FlowRenderer({
}
}
- setDebuggableBlockCount(debuggable.length);
- }, [nodes, edges]);
+ onDebuggableBlockCountChange(debuggable.length);
+ }, [nodes, edges, onDebuggableBlockCountChange]);
const constructSaveData = useCallback((): WorkflowSaveData => {
const blocks = getWorkflowBlocks(nodes, edges);
@@ -371,88 +350,6 @@ function FlowRenderer({
return await saveWorkflow.mutateAsync();
}
- function addNode({
- nodeType,
- previous,
- next,
- parent,
- connectingEdgeType,
- }: AddNodeProps) {
- const newNodes: Array = [];
- const newEdges: Array = [];
- const id = nanoid();
- const existingLabels = nodes
- .filter(isWorkflowBlockNode)
- .map((node) => node.data.label);
- const node = createNode(
- { id, parentId: parent },
- nodeType,
- generateNodeLabel(existingLabels),
- );
- newNodes.push(node);
- if (previous) {
- const newEdge = {
- id: nanoid(),
- type: "edgeWithAddButton",
- source: previous,
- target: id,
- style: {
- strokeWidth: 2,
- },
- };
- newEdges.push(newEdge);
- }
- if (next) {
- const newEdge = {
- id: nanoid(),
- type: connectingEdgeType,
- source: id,
- target: next,
- style: {
- strokeWidth: 2,
- },
- };
- newEdges.push(newEdge);
- }
-
- if (nodeType === "loop") {
- // when loop node is first created it needs an adder node so nodes can be added inside the loop
- const startNodeId = nanoid();
- const adderNodeId = nanoid();
- newNodes.push(
- startNode(
- startNodeId,
- {
- withWorkflowSettings: false,
- editable: true,
- },
- id,
- ),
- );
- newNodes.push(nodeAdderNode(adderNodeId, id));
- newEdges.push(defaultEdge(startNodeId, adderNodeId));
- }
-
- const editedEdges = previous
- ? edges.filter((edge) => edge.source !== previous)
- : edges;
-
- const previousNode = nodes.find((node) => node.id === previous);
- const previousNodeIndex = previousNode
- ? nodes.indexOf(previousNode)
- : nodes.length - 1;
-
- // creating some memory for no reason, maybe check it out later
- const newNodesAfter = [
- ...nodes.slice(0, previousNodeIndex + 1),
- ...newNodes,
- ...nodes.slice(previousNodeIndex + 1),
- ];
-
- workflowChangesStore.setHasChanges(true);
- doLayout(newNodesAfter, [...editedEdges, ...newEdges]);
- }
-
function deleteNode(id: string) {
const node = nodes.find((node) => node.id === id);
if (!node || !isWorkflowBlockNode(node)) {
@@ -629,7 +526,11 @@ function FlowRenderer({
};
return (
- <>
+ onMouseDownCapture?.()}
+ >
-
- {
+ const dimensionChanges = changes.filter(
+ (change) => change.type === "dimensions",
+ );
+ const tempNodes = [...nodes];
+ dimensionChanges.forEach((change) => {
+ const node = tempNodes.find((node) => node.id === change.id);
+ if (node) {
+ if (node.measured?.width) {
+ node.measured.width = change.dimensions?.width;
+ }
+ if (node.measured?.height) {
+ node.measured.height = change.dimensions?.height;
+ }
+ }
+ });
+ if (dimensionChanges.length > 0) {
+ doLayout(tempNodes, edges);
+ }
+ if (
+ changes.some((change) => {
+ return (
+ change.type === "add" ||
+ change.type === "remove" ||
+ change.type === "replace"
+ );
+ })
+ ) {
+ workflowChangesStore.setHasChanges(true);
+ }
+ // throttle onNodesChange to prevent cascading React updates
+ if (onNodesChangeTimeoutRef.current === null) {
+ onNodesChange(changes);
+ onNodesChangeTimeoutRef.current = setTimeout(() => {
+ onNodesChangeTimeoutRef.current = null;
+ }, 33); // ~30fps throttle
+ }
}}
+ onEdgesChange={onEdgesChange}
+ nodeTypes={nodeTypes}
+ edgeTypes={edgeTypes}
+ colorMode="dark"
+ fitView={true}
+ fitViewOptions={{
+ maxZoom: 1,
+ }}
+ deleteKeyCode={null}
+ onMove={(_, viewport) => {
+ if (debugStore.isDebugMode && shouldConstrainPan) {
+ constrainPan(viewport);
+ }
+ }}
+ maxZoom={debugStore.isDebugMode ? 1 : 2}
+ minZoom={debugStore.isDebugMode ? 1 : 0.5}
+ panOnDrag={true}
+ panOnScroll={true}
+ panOnScrollMode={PanOnScrollMode.Vertical}
+ zoomOnDoubleClick={!debugStore.isDebugMode}
+ zoomOnPinch={!debugStore.isDebugMode}
+ zoomOnScroll={!debugStore.isDebugMode}
>
- {
- const dimensionChanges = changes.filter(
- (change) => change.type === "dimensions",
- );
- const tempNodes = [...nodes];
- dimensionChanges.forEach((change) => {
- const node = tempNodes.find((node) => node.id === change.id);
- if (node) {
- if (node.measured?.width) {
- node.measured.width = change.dimensions?.width;
- }
- if (node.measured?.height) {
- node.measured.height = change.dimensions?.height;
- }
- }
- });
- if (dimensionChanges.length > 0) {
- doLayout(tempNodes, edges);
- }
- if (
- changes.some((change) => {
- return (
- change.type === "add" ||
- change.type === "remove" ||
- change.type === "replace"
- );
- })
- ) {
- workflowChangesStore.setHasChanges(true);
- }
-
- // throttle onNodesChange to prevent cascading React updates
- if (onNodesChangeTimeoutRef.current === null) {
- onNodesChange(changes);
- onNodesChangeTimeoutRef.current = setTimeout(() => {
- onNodesChangeTimeoutRef.current = null;
- }, 33); // ~30fps throttle
- }
- }}
- onEdgesChange={onEdgesChange}
- nodeTypes={nodeTypes}
- edgeTypes={edgeTypes}
- colorMode="dark"
- fitView={true}
- fitViewOptions={{
- maxZoom: 1,
- }}
- deleteKeyCode={null}
- onMove={(_, viewport) => {
- if (debugStore.isDebugMode && shouldConstrainPan) {
- constrainPan(viewport);
- }
- }}
- maxZoom={debugStore.isDebugMode ? 1 : 2}
- minZoom={debugStore.isDebugMode ? 1 : 0.5}
- panOnDrag={true}
- panOnScroll={true}
- panOnScrollMode={PanOnScrollMode.Vertical}
- zoomOnDoubleClick={!debugStore.isDebugMode}
- zoomOnPinch={!debugStore.isDebugMode}
- zoomOnScroll={!debugStore.isDebugMode}
- >
-
-
- {debugStore.isDebugMode && (
-
-
-
- )}
-
- {
- if (
- workflowPanelState.active &&
- workflowPanelState.content === "parameters"
- ) {
- closeWorkflowPanel();
- } else {
- setWorkflowPanelState({
- active: true,
- content: "parameters",
- });
- }
- }}
- onSave={async () => {
- const errors = getWorkflowErrors(nodes);
- if (errors.length > 0) {
- toast({
- title: "Can not save workflow because of errors:",
- description: (
-
- {errors.map((error) => (
-
{error}
- ))}
-
- ),
- variant: "destructive",
- });
- return;
- }
- await handleSave();
- }}
- />
-
- {workflowPanelState.active && (
-
- {workflowPanelState.content === "parameters" && (
-
- )}
- {workflowPanelState.content === "nodeLibrary" && (
- {
- addNode(props);
- }}
- />
- )}
-
- )}
-
-
-
- >
+
+
+
+
+
);
}
-export { FlowRenderer };
+export { FlowRenderer, type Props as FlowRendererProps };
diff --git a/skyvern-frontend/src/routes/workflows/editor/WorkflowDebugger.tsx b/skyvern-frontend/src/routes/workflows/editor/WorkflowDebugger.tsx
index f53c0762..b49c99c9 100644
--- a/skyvern-frontend/src/routes/workflows/editor/WorkflowDebugger.tsx
+++ b/skyvern-frontend/src/routes/workflows/editor/WorkflowDebugger.tsx
@@ -1,175 +1,18 @@
-import { AxiosError } from "axios";
-import { ReloadIcon } from "@radix-ui/react-icons";
-import { useEffect, useRef, useState } from "react";
import { useParams } from "react-router-dom";
-import { useMutation, useQueryClient } from "@tanstack/react-query";
import { ReactFlowProvider } from "@xyflow/react";
-import { getClient } from "@/api/AxiosClient";
-import { DebugSessionApiResponse } from "@/api/types";
-import { AnimatedWave } from "@/components/AnimatedWave";
-import { BrowserStream } from "@/components/BrowserStream";
-import { FloatingWindow } from "@/components/FloatingWindow";
-import { Button } from "@/components/ui/button";
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- DialogClose,
-} from "@/components/ui/dialog";
-import { toast } from "@/components/ui/use-toast";
-import { useCredentialGetter } from "@/hooks/useCredentialGetter";
-import { useMountEffect } from "@/hooks/useMountEffect";
-import { statusIsFinalized } from "@/routes/tasks/types.ts";
-import { useBlockScriptsQuery } from "@/routes/workflows/hooks/useBlockScriptsQuery";
-import { useBlockScriptStore } from "@/store/BlockScriptStore";
-import { useSidebarStore } from "@/store/SidebarStore";
-import { useWorkflowHasChangesStore } from "@/store/WorkflowHasChangesStore";
-import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
import { useWorkflowQuery } from "../hooks/useWorkflowQuery";
-import { useDebugSessionQuery } from "../hooks/useDebugSessionQuery";
import { WorkflowSettings } from "../types/workflowTypes";
-import { FlowRenderer } from "./FlowRenderer";
import { getElements } from "./workflowEditorUtils";
import { getInitialParameters } from "./utils";
-
-const Constants = {
- NewBrowserCooldown: 30000,
-} as const;
+import { Workspace } from "./Workspace";
function WorkflowDebugger() {
- const { blockLabel, workflowPermanentId } = useParams();
- const [openDialogue, setOpenDialogue] = useState(false);
- const [activeDebugSession, setActiveDebugSession] =
- useState(null);
- const [showPowerButton, setShowPowerButton] = useState(true);
- const credentialGetter = useCredentialGetter();
- const queryClient = useQueryClient();
- const [shouldFetchDebugSession, setShouldFetchDebugSession] = useState(false);
- const blockScriptStore = useBlockScriptStore();
-
- const { data: workflowRun } = useWorkflowRunQuery();
-
+ const { workflowPermanentId } = useParams();
const { data: workflow } = useWorkflowQuery({
workflowPermanentId,
});
- const { data: blockScripts } = useBlockScriptsQuery({
- workflowPermanentId,
- });
-
- const { data: debugSession } = useDebugSessionQuery({
- workflowPermanentId,
- enabled: shouldFetchDebugSession && !!workflowPermanentId,
- });
-
- const setCollapsed = useSidebarStore((state) => {
- return state.setCollapsed;
- });
-
- const workflowChangesStore = useWorkflowHasChangesStore();
-
- const handleOnCycle = () => {
- setOpenDialogue(true);
- };
-
- useMountEffect(() => {
- setCollapsed(true);
- workflowChangesStore.setHasChanges(false);
-
- if (workflowPermanentId) {
- queryClient.removeQueries({
- queryKey: ["debugSession", workflowPermanentId],
- });
- setShouldFetchDebugSession(true);
- }
- });
-
- useEffect(() => {
- blockScriptStore.setScripts(blockScripts ?? {});
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [blockScripts]);
-
- const afterCycleBrowser = () => {
- setOpenDialogue(false);
- setShowPowerButton(false);
-
- if (powerButtonTimeoutRef.current) {
- clearTimeout(powerButtonTimeoutRef.current);
- }
-
- powerButtonTimeoutRef.current = setTimeout(() => {
- setShowPowerButton(true);
- }, Constants.NewBrowserCooldown);
- };
-
- const cycleBrowser = useMutation({
- mutationFn: async (id: string) => {
- const client = await getClient(credentialGetter, "sans-api-v1");
- return client.post(`/debug-session/${id}/new`);
- },
- onSuccess: (response) => {
- const newDebugSession = response.data;
- setActiveDebugSession(newDebugSession);
-
- queryClient.invalidateQueries({
- queryKey: ["debugSession", workflowPermanentId],
- });
-
- toast({
- title: "Browser cycled",
- variant: "success",
- description: "Your browser has been cycled.",
- });
-
- afterCycleBrowser();
- },
- onError: (error: AxiosError) => {
- toast({
- variant: "destructive",
- title: "Failed to cycle browser",
- description: error.message,
- });
-
- afterCycleBrowser();
- },
- });
-
- const intervalRef = useRef(null);
- const powerButtonTimeoutRef = useRef(null);
-
- useEffect(() => {
- if (
- (!debugSession || !debugSession.browser_session_id) &&
- shouldFetchDebugSession &&
- workflowPermanentId
- ) {
- intervalRef.current = setInterval(() => {
- queryClient.invalidateQueries({
- queryKey: ["debugSession", workflowPermanentId],
- });
- }, 2000);
- } else {
- if (intervalRef.current) {
- clearInterval(intervalRef.current);
- intervalRef.current = null;
- }
-
- if (debugSession) {
- setActiveDebugSession(debugSession);
- }
- }
-
- return () => {
- if (intervalRef.current) {
- clearInterval(intervalRef.current);
- }
- };
- }, [debugSession, shouldFetchDebugSession, workflowPermanentId, queryClient]);
-
if (!workflow) {
return null;
}
@@ -193,110 +36,18 @@ function WorkflowDebugger() {
true,
);
- const isFinalized = workflowRun ? statusIsFinalized(workflowRun) : null;
- const interactor = workflowRun && isFinalized === false ? "agent" : "human";
- const browserTitle = interactor === "agent" ? `Browser [๐ค]` : `Browser [๐ค]`;
-
- // ---start fya: https://github.com/frontyardart
- const initialBrowserPosition = {
- x: 600,
- y: 132,
- };
-
- const windowWidth = window.innerWidth;
- const rightPadding = 567;
- const initialWidth = Math.max(
- 512,
- windowWidth - initialBrowserPosition.x - rightPadding,
- );
- const initialHeight = (initialWidth / 16) * 9;
- // ---end fya
-
return (
-
-
-
-
- {activeDebugSession &&
- activeDebugSession.browser_session_id &&
- !cycleBrowser.isPending ? (
-
- ) : (
-
- Connecting to your browser...
-
-
- )}
-
);
}
diff --git a/skyvern-frontend/src/routes/workflows/editor/WorkflowEditor.tsx b/skyvern-frontend/src/routes/workflows/editor/WorkflowEditor.tsx
index c1425b4d..05dc1c25 100644
--- a/skyvern-frontend/src/routes/workflows/editor/WorkflowEditor.tsx
+++ b/skyvern-frontend/src/routes/workflows/editor/WorkflowEditor.tsx
@@ -1,26 +1,15 @@
-import { useEffect } from "react";
-import { useMountEffect } from "@/hooks/useMountEffect";
-import { useBlockScriptsQuery } from "@/routes/workflows/hooks/useBlockScriptsQuery";
-import { useBlockScriptStore } from "@/store/BlockScriptStore";
-import { useSidebarStore } from "@/store/SidebarStore";
-import { useWorkflowHasChangesStore } from "@/store/WorkflowHasChangesStore";
import { ReactFlowProvider } from "@xyflow/react";
import { useParams } from "react-router-dom";
import { useWorkflowQuery } from "../hooks/useWorkflowQuery";
-import { FlowRenderer } from "./FlowRenderer";
import { getElements } from "./workflowEditorUtils";
import { LogoMinimized } from "@/components/LogoMinimized";
import { WorkflowSettings } from "../types/workflowTypes";
import { useGlobalWorkflowsQuery } from "../hooks/useGlobalWorkflowsQuery";
import { getInitialParameters } from "./utils";
+import { Workspace } from "./Workspace";
function WorkflowEditor() {
const { workflowPermanentId } = useParams();
- const setCollapsed = useSidebarStore((state) => {
- return state.setCollapsed;
- });
- const workflowChangesStore = useWorkflowHasChangesStore();
- const blockScriptStore = useBlockScriptStore();
const { data: workflow, isLoading } = useWorkflowQuery({
workflowPermanentId,
@@ -29,20 +18,6 @@ function WorkflowEditor() {
const { data: globalWorkflows, isLoading: isGlobalWorkflowsLoading } =
useGlobalWorkflowsQuery();
- const { data: blockScripts } = useBlockScriptsQuery({
- workflowPermanentId,
- });
-
- useMountEffect(() => {
- setCollapsed(true);
- workflowChangesStore.setHasChanges(false);
- });
-
- useEffect(() => {
- blockScriptStore.setScripts(blockScripts ?? {});
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [blockScripts]);
-
if (isLoading || isGlobalWorkflowsLoading) {
return (
@@ -82,11 +57,12 @@ function WorkflowEditor() {
return (
-
diff --git a/skyvern-frontend/src/routes/workflows/editor/WorkflowHeader.tsx b/skyvern-frontend/src/routes/workflows/editor/WorkflowHeader.tsx
index e077bcc9..8ab77701 100644
--- a/skyvern-frontend/src/routes/workflows/editor/WorkflowHeader.tsx
+++ b/skyvern-frontend/src/routes/workflows/editor/WorkflowHeader.tsx
@@ -29,6 +29,7 @@ type Props = {
parametersPanelOpen: boolean;
onParametersClick: () => void;
onSave: () => void;
+ onRun?: () => void;
saving: boolean;
};
@@ -37,6 +38,7 @@ function WorkflowHeader({
parametersPanelOpen,
onParametersClick,
onSave,
+ onRun,
saving,
}: Props) {
const { title, setTitle } = useWorkflowTitleStore();
@@ -148,6 +150,7 @@ function WorkflowHeader({