From 75eadef0e1138b1290598d04731f53634b0c9c56 Mon Sep 17 00:00:00 2001 From: Jonathan Dobson Date: Wed, 6 Aug 2025 17:48:55 -0400 Subject: [PATCH] add Use Script Cache toggle; align toggles to right-hand side (#3121) --- .../src/components/FloatingWindow.tsx | 7 ++- skyvern-frontend/src/components/Orgwalled.tsx | 46 +++++++++++++++++++ .../src/hooks/useIsSkyvernUser.ts | 11 +++++ .../routes/workflows/editor/FlowRenderer.tsx | 1 + .../workflows/editor/WorkflowDebugger.tsx | 7 +-- .../workflows/editor/WorkflowEditor.tsx | 1 + .../editor/nodes/StartNode/StartNode.tsx | 18 ++++++++ .../workflows/editor/nodes/StartNode/types.ts | 1 + .../workflows/editor/workflowEditorUtils.ts | 3 ++ .../routes/workflows/types/workflowTypes.ts | 2 + .../workflows/types/workflowYamlTypes.ts | 1 + 11 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 skyvern-frontend/src/components/Orgwalled.tsx create mode 100644 skyvern-frontend/src/hooks/useIsSkyvernUser.ts diff --git a/skyvern-frontend/src/components/FloatingWindow.tsx b/skyvern-frontend/src/components/FloatingWindow.tsx index 37988744..563a5d49 100644 --- a/skyvern-frontend/src/components/FloatingWindow.tsx +++ b/skyvern-frontend/src/components/FloatingWindow.tsx @@ -18,6 +18,7 @@ import { import { flushSync } from "react-dom"; import Draggable from "react-draggable"; +import { OrgWalled } from "./Orgwalled"; import { Tooltip, TooltipContent, @@ -627,7 +628,11 @@ function FloatingWindow({ onClick={toggleMaximized} /> )} - {showPowerButton && cycle()} />} + {showPowerButton && ( + + cycle()} /> + + )}
{title}
{showReloadButton && ( diff --git a/skyvern-frontend/src/components/Orgwalled.tsx b/skyvern-frontend/src/components/Orgwalled.tsx new file mode 100644 index 00000000..f70b9126 --- /dev/null +++ b/skyvern-frontend/src/components/Orgwalled.tsx @@ -0,0 +1,46 @@ +import { useIsSkyvernUser } from "@/hooks/useIsSkyvernUser"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; + +import { cn } from "@/util/utils"; + +function OrgWalled({ + children, + className, +}: { + children: React.ReactNode; + className?: string; +}) { + const isSkyvernUser = useIsSkyvernUser(); + + if (!isSkyvernUser) { + return null; + } + + // Wrap children with visual indication for org-walled features + return ( + + + +
+ {children} +
+
+ +

This feature is only available to Skyvern organization members

+
+
+
+ ); +} + +export { OrgWalled }; diff --git a/skyvern-frontend/src/hooks/useIsSkyvernUser.ts b/skyvern-frontend/src/hooks/useIsSkyvernUser.ts new file mode 100644 index 00000000..f4fba272 --- /dev/null +++ b/skyvern-frontend/src/hooks/useIsSkyvernUser.ts @@ -0,0 +1,11 @@ +import { useUser } from "./useUser"; + +function useIsSkyvernUser() { + const user = useUser().get(); + const email = user?.email; + const isSkyvernUser = email?.toLowerCase().endsWith("@skyvern.com") ?? false; + + return isSkyvernUser; +} + +export { useIsSkyvernUser }; diff --git a/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx b/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx index 02ce47bd..2e729cf7 100644 --- a/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx @@ -349,6 +349,7 @@ function FlowRenderer({ max_screenshot_scrolls: data.settings.maxScreenshotScrolls, totp_verification_url: workflow.totp_verification_url, extra_http_headers: extraHttpHeaders, + use_cache: data.settings.useScriptCache, workflow_definition: { parameters: data.parameters, blocks: data.blocks, diff --git a/skyvern-frontend/src/routes/workflows/editor/WorkflowDebugger.tsx b/skyvern-frontend/src/routes/workflows/editor/WorkflowDebugger.tsx index cb33d8ee..3407de36 100644 --- a/skyvern-frontend/src/routes/workflows/editor/WorkflowDebugger.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/WorkflowDebugger.tsx @@ -24,7 +24,6 @@ import { Skeleton } from "@/components/ui/skeleton"; import { toast } from "@/components/ui/use-toast"; import { useCredentialGetter } from "@/hooks/useCredentialGetter"; import { useMountEffect } from "@/hooks/useMountEffect"; -import { useUser } from "@/hooks/useUser"; import { statusIsFinalized } from "@/routes/tasks/types.ts"; import { useSidebarStore } from "@/store/SidebarStore"; import { useWorkflowHasChangesStore } from "@/store/WorkflowHasChangesStore"; @@ -44,9 +43,6 @@ function WorkflowDebugger() { const credentialGetter = useCredentialGetter(); const queryClient = useQueryClient(); const [shouldFetchDebugSession, setShouldFetchDebugSession] = useState(false); - const user = useUser().get(); - const email = user?.email; - const isSkyvernUser = email?.toLowerCase().endsWith("@skyvern.com") ?? false; const { data: workflowRun } = useWorkflowRunQuery(); const { data: workflow } = useWorkflowQuery({ @@ -157,6 +153,7 @@ function WorkflowDebugger() { extraHttpHeaders: workflow.extra_http_headers ? JSON.stringify(workflow.extra_http_headers) : null, + useScriptCache: workflow.use_cache, }; const elements = getElements( @@ -235,7 +232,7 @@ function WorkflowDebugger() { initialHeight={360} showMaximizeButton={true} showMinimizeButton={true} - showPowerButton={blockLabel === undefined && isSkyvernUser} + showPowerButton={blockLabel === undefined} showReloadButton={true} // -- onCycle={handleOnCycle} diff --git a/skyvern-frontend/src/routes/workflows/editor/WorkflowEditor.tsx b/skyvern-frontend/src/routes/workflows/editor/WorkflowEditor.tsx index bcd0af0a..53a430ed 100644 --- a/skyvern-frontend/src/routes/workflows/editor/WorkflowEditor.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/WorkflowEditor.tsx @@ -58,6 +58,7 @@ function WorkflowEditor() { extraHttpHeaders: workflow.extra_http_headers ? JSON.stringify(workflow.extra_http_headers) : null, + useScriptCache: workflow.use_cache, }; const elements = getElements( diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/StartNode/StartNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/StartNode/StartNode.tsx index 27868ad4..bb80c484 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/StartNode/StartNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/StartNode/StartNode.tsx @@ -22,6 +22,7 @@ import { ModelSelector } from "@/components/ModelSelector"; import { WorkflowModel } from "@/routes/workflows/types/workflowTypes"; import { MAX_SCREENSHOT_SCROLLS_DEFAULT } from "../Taskv2Node/types"; import { KeyValueInput } from "@/components/KeyValueInput"; +import { OrgWalled } from "@/components/Orgwalled"; import { useWorkflowSettingsStore } from "@/store/WorkflowSettingsStore"; function StartNode({ id, data }: NodeProps) { @@ -59,6 +60,7 @@ function StartNode({ id, data }: NodeProps) { ? data.maxScreenshotScrolls : null, extraHttpHeaders: data.withWorkflowSettings ? data.extraHttpHeaders : null, + useScriptCache: data.withWorkflowSettings ? data.useScriptCache : false, }); useEffect(() => { @@ -131,11 +133,27 @@ function StartNode({ id, data }: NodeProps) { }} /> + +
+
+ + + { + handleChange("useScriptCache", value); + }} + /> +
+
+
{ handleChange("persistBrowserSession", value); diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/StartNode/types.ts b/skyvern-frontend/src/routes/workflows/editor/nodes/StartNode/types.ts index 8982a9ab..5f1fc32c 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/StartNode/types.ts +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/StartNode/types.ts @@ -12,6 +12,7 @@ export type WorkflowStartNodeData = { maxScreenshotScrolls: number | null; extraHttpHeaders: string | null; editable: boolean; + useScriptCache: boolean; }; export type OtherStartNodeData = { diff --git a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts index bdd858a5..f30bbe1e 100644 --- a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts +++ b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts @@ -698,6 +698,7 @@ function getElements( maxScreenshotScrolls: settings.maxScreenshotScrolls, extraHttpHeaders: settings.extraHttpHeaders, editable, + useScriptCache: settings.useScriptCache, }), ); @@ -1400,6 +1401,7 @@ function getWorkflowSettings(nodes: Array): WorkflowSettings { model: null, maxScreenshotScrolls: null, extraHttpHeaders: null, + useScriptCache: false, }; const startNodes = nodes.filter(isStartNode); const startNodeWithWorkflowSettings = startNodes.find( @@ -1417,6 +1419,7 @@ function getWorkflowSettings(nodes: Array): WorkflowSettings { model: data.model, maxScreenshotScrolls: data.maxScreenshotScrolls, extraHttpHeaders: data.extraHttpHeaders, + useScriptCache: data.useScriptCache, }; } return defaultSettings; diff --git a/skyvern-frontend/src/routes/workflows/types/workflowTypes.ts b/skyvern-frontend/src/routes/workflows/types/workflowTypes.ts index efb3b655..ba6c333f 100644 --- a/skyvern-frontend/src/routes/workflows/types/workflowTypes.ts +++ b/skyvern-frontend/src/routes/workflows/types/workflowTypes.ts @@ -501,6 +501,7 @@ export type WorkflowApiResponse = { created_at: string; modified_at: string; deleted_at: string | null; + use_cache: boolean; }; export type WorkflowSettings = { @@ -510,6 +511,7 @@ export type WorkflowSettings = { model: WorkflowModel | null; maxScreenshotScrolls: number | null; extraHttpHeaders: string | null; + useScriptCache: boolean; }; export type WorkflowModel = JsonObjectExtendable<{ model_name: string }>; diff --git a/skyvern-frontend/src/routes/workflows/types/workflowYamlTypes.ts b/skyvern-frontend/src/routes/workflows/types/workflowYamlTypes.ts index f7ab86ea..6f79ff71 100644 --- a/skyvern-frontend/src/routes/workflows/types/workflowYamlTypes.ts +++ b/skyvern-frontend/src/routes/workflows/types/workflowYamlTypes.ts @@ -14,6 +14,7 @@ export type WorkflowCreateYAMLRequest = { is_saved_task?: boolean; max_screenshot_scrolls?: number | null; extra_http_headers?: Record | null; + use_cache?: boolean; }; export type WorkflowDefinitionYAML = {