replace workflow parameter React Context with a zustand store; use everywhere (#3187)
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
import { PlusIcon } from "@radix-ui/react-icons";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
||||
import { WorkflowBlockParameterSelect } from "@/routes/workflows/editor/nodes/WorkflowBlockParameterSelect";
|
||||
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Cross2Icon } from "@radix-ui/react-icons";
|
||||
import "./workflow-block-input-set.css";
|
||||
import { useWorkflowParametersState } from "@/routes/workflows/editor/useWorkflowParametersState";
|
||||
|
||||
type Props = {
|
||||
onChange: (parameterKeys: Set<string>) => void;
|
||||
nodeId: string;
|
||||
@@ -16,7 +15,7 @@ function WorkflowBlockInputSet(props: Props) {
|
||||
const { nodeId, onChange, values } = props;
|
||||
const [parameterKeys, setParameterKeys] = useState<Set<string>>(values);
|
||||
const hasKeys = parameterKeys.size > 0;
|
||||
const [workflowParameters] = useWorkflowParametersState();
|
||||
const { parameters: workflowParameters } = useWorkflowParametersStore();
|
||||
const availableParameterKeys = new Set(
|
||||
workflowParameters.map((parameter) => parameter.key),
|
||||
);
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from "@/components/ui/select";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { useCredentialsQuery } from "../hooks/useCredentialsQuery";
|
||||
import { useWorkflowParametersState } from "../editor/useWorkflowParametersState";
|
||||
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
||||
import { WorkflowParameterValueType } from "../types/workflowTypes";
|
||||
import { PlusIcon } from "@radix-ui/react-icons";
|
||||
import {
|
||||
@@ -24,7 +24,7 @@ type Props = {
|
||||
function CredentialParameterSourceSelector({ value, onChange }: Props) {
|
||||
const { data: credentials, isFetching } = useCredentialsQuery();
|
||||
const { setIsOpen, setType } = useCredentialModalState();
|
||||
const [workflowParameters] = useWorkflowParametersState();
|
||||
const { parameters: workflowParameters } = useWorkflowParametersStore();
|
||||
const workflowParametersOfTypeCredentialId = workflowParameters.filter(
|
||||
(parameter) =>
|
||||
parameter.parameterType === "workflow" &&
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useNodes } from "@xyflow/react";
|
||||
import { useWorkflowParametersState } from "../editor/useWorkflowParametersState";
|
||||
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
||||
import { AppNode, isWorkflowBlockNode } from "../editor/nodes";
|
||||
import { getOutputParameterKey } from "../editor/workflowEditorUtils";
|
||||
import {
|
||||
@@ -16,7 +16,7 @@ type Props = {
|
||||
};
|
||||
|
||||
function SourceParameterKeySelector({ value, onChange }: Props) {
|
||||
const [workflowParameters] = useWorkflowParametersState();
|
||||
const { parameters: workflowParameters } = useWorkflowParametersStore();
|
||||
const nodes = useNodes<AppNode>();
|
||||
const contextParameterKeys = workflowParameters
|
||||
.filter((parameter) => parameter.parameterType !== "credential")
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
useWorkflowSave,
|
||||
type WorkflowSaveData,
|
||||
} from "@/store/WorkflowHasChangesStore";
|
||||
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
||||
import { useWorkflowTitleStore } from "@/store/WorkflowTitleStore";
|
||||
import { ReloadIcon } from "@radix-ui/react-icons";
|
||||
import {
|
||||
@@ -223,7 +224,7 @@ type Props = {
|
||||
onNodesChange: (changes: Array<NodeChange<AppNode>>) => void;
|
||||
onEdgesChange: (changes: Array<EdgeChange>) => void;
|
||||
initialTitle: string;
|
||||
initialParameters: ParametersState;
|
||||
// initialParameters: ParametersState;
|
||||
workflow: WorkflowApiResponse;
|
||||
onDebuggableBlockCountChange: (count: number) => void;
|
||||
onMouseDownCapture?: () => void;
|
||||
@@ -238,7 +239,7 @@ function FlowRenderer({
|
||||
onNodesChange,
|
||||
onEdgesChange,
|
||||
initialTitle,
|
||||
initialParameters,
|
||||
// initialParameters,
|
||||
workflow,
|
||||
onDebuggableBlockCountChange,
|
||||
onMouseDownCapture,
|
||||
@@ -247,7 +248,8 @@ function FlowRenderer({
|
||||
const reactFlowInstance = useReactFlow();
|
||||
const debugStore = useDebugStore();
|
||||
const { title, initializeTitle } = useWorkflowTitleStore();
|
||||
const [parameters] = useState<ParametersState>(initialParameters);
|
||||
// const [parameters] = useState<ParametersState>(initialParameters);
|
||||
const parameters = useWorkflowParametersStore((state) => state.parameters);
|
||||
const nodesInitialized = useNodesInitialized();
|
||||
const [shouldConstrainPan, setShouldConstrainPan] = useState(false);
|
||||
const onNodesChangeTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useEffect } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { ReactFlowProvider } from "@xyflow/react";
|
||||
|
||||
@@ -6,6 +7,7 @@ import { WorkflowSettings } from "../types/workflowTypes";
|
||||
import { getElements } from "./workflowEditorUtils";
|
||||
import { getInitialParameters } from "./utils";
|
||||
import { Workspace } from "./Workspace";
|
||||
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
||||
|
||||
function WorkflowDebugger() {
|
||||
const { workflowPermanentId } = useParams();
|
||||
@@ -13,6 +15,17 @@ function WorkflowDebugger() {
|
||||
workflowPermanentId,
|
||||
});
|
||||
|
||||
const setParameters = useWorkflowParametersStore(
|
||||
(state) => state.setParameters,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (workflow) {
|
||||
const initialParameters = getInitialParameters(workflow);
|
||||
setParameters(initialParameters);
|
||||
}
|
||||
}, [workflow, setParameters]);
|
||||
|
||||
if (!workflow) {
|
||||
return null;
|
||||
}
|
||||
@@ -42,7 +55,6 @@ function WorkflowDebugger() {
|
||||
<Workspace
|
||||
initialEdges={elements.edges}
|
||||
initialNodes={elements.nodes}
|
||||
initialParameters={getInitialParameters(workflow)}
|
||||
initialTitle={workflow.title}
|
||||
showBrowser={true}
|
||||
workflow={workflow}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { ReactFlowProvider } from "@xyflow/react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useEffect } from "react";
|
||||
import { useWorkflowQuery } from "../hooks/useWorkflowQuery";
|
||||
import { getElements } from "./workflowEditorUtils";
|
||||
import { LogoMinimized } from "@/components/LogoMinimized";
|
||||
import { WorkflowSettings } from "../types/workflowTypes";
|
||||
import { useGlobalWorkflowsQuery } from "../hooks/useGlobalWorkflowsQuery";
|
||||
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
||||
import { getInitialParameters } from "./utils";
|
||||
import { Workspace } from "./Workspace";
|
||||
|
||||
@@ -18,6 +20,17 @@ function WorkflowEditor() {
|
||||
const { data: globalWorkflows, isLoading: isGlobalWorkflowsLoading } =
|
||||
useGlobalWorkflowsQuery();
|
||||
|
||||
const setParameters = useWorkflowParametersStore(
|
||||
(state) => state.setParameters,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (workflow) {
|
||||
const initialParameters = getInitialParameters(workflow);
|
||||
setParameters(initialParameters);
|
||||
}
|
||||
}, [workflow, setParameters]);
|
||||
|
||||
if (isLoading || isGlobalWorkflowsLoading) {
|
||||
return (
|
||||
<div className="flex h-screen w-full items-center justify-center">
|
||||
@@ -60,7 +73,6 @@ function WorkflowEditor() {
|
||||
<Workspace
|
||||
initialEdges={elements.edges}
|
||||
initialNodes={elements.nodes}
|
||||
initialParameters={getInitialParameters(workflow)}
|
||||
initialTitle={workflow.title}
|
||||
showBrowser={false}
|
||||
workflow={workflow}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { createContext } from "react";
|
||||
import { ParametersState } from "./types";
|
||||
|
||||
type WorkflowParametersState = [
|
||||
ParametersState,
|
||||
React.Dispatch<React.SetStateAction<ParametersState>>,
|
||||
];
|
||||
|
||||
const WorkflowParametersStateContext = createContext<
|
||||
WorkflowParametersState | undefined
|
||||
>(undefined);
|
||||
|
||||
export { WorkflowParametersStateContext };
|
||||
@@ -44,7 +44,6 @@ import { FlowRenderer, type FlowRendererProps } from "./FlowRenderer";
|
||||
import { AppNode, isWorkflowBlockNode, WorkflowBlockNode } from "./nodes";
|
||||
import { WorkflowNodeLibraryPanel } from "./panels/WorkflowNodeLibraryPanel";
|
||||
import { WorkflowParametersPanel } from "./panels/WorkflowParametersPanel";
|
||||
import { ParametersState } from "./types";
|
||||
import { getWorkflowErrors } from "./workflowEditorUtils";
|
||||
import { WorkflowHeader } from "./WorkflowHeader";
|
||||
import {
|
||||
@@ -55,16 +54,12 @@ import {
|
||||
layout,
|
||||
startNode,
|
||||
} from "./workflowEditorUtils";
|
||||
import { WorkflowParametersStateContext } from "./WorkflowParametersStateContext";
|
||||
|
||||
const Constants = {
|
||||
NewBrowserCooldown: 30000,
|
||||
} as const;
|
||||
|
||||
type Props = Pick<
|
||||
FlowRendererProps,
|
||||
"initialTitle" | "initialParameters" | "workflow"
|
||||
> & {
|
||||
type Props = Pick<FlowRendererProps, "initialTitle" | "workflow"> & {
|
||||
initialNodes: Array<AppNode>;
|
||||
initialEdges: Array<Edge>;
|
||||
showBrowser?: boolean;
|
||||
@@ -82,15 +77,12 @@ function Workspace({
|
||||
initialNodes,
|
||||
initialEdges,
|
||||
initialTitle,
|
||||
initialParameters,
|
||||
showBrowser = false,
|
||||
workflow,
|
||||
}: Props) {
|
||||
const { blockLabel, workflowPermanentId } = useParams();
|
||||
const { workflowPanelState, setWorkflowPanelState, closeWorkflowPanel } =
|
||||
useWorkflowPanelStore();
|
||||
const [parameters, setParameters] =
|
||||
useState<ParametersState>(initialParameters);
|
||||
const debugStore = useDebugStore();
|
||||
const [debuggableBlockCount, setDebuggableBlockCount] = useState(0);
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||
@@ -335,208 +327,204 @@ function Workspace({
|
||||
}
|
||||
|
||||
return (
|
||||
<WorkflowParametersStateContext.Provider
|
||||
value={[parameters, setParameters]}
|
||||
>
|
||||
<div className="relative h-full w-full">
|
||||
<Dialog
|
||||
open={openDialogue}
|
||||
onOpenChange={(open) => {
|
||||
if (!open && cycleBrowser.isPending) {
|
||||
return;
|
||||
}
|
||||
setOpenDialogue(open);
|
||||
}}
|
||||
>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Cycle (Get a new browser)</DialogTitle>
|
||||
<DialogDescription>
|
||||
<div className="pb-2 pt-4 text-sm text-slate-400">
|
||||
{cycleBrowser.isPending ? (
|
||||
<>
|
||||
Cooking you up a fresh browser...
|
||||
<AnimatedWave text=".‧₊˚ ⋅ ✨★ ‧₊˚ ⋅" />
|
||||
</>
|
||||
) : (
|
||||
"Abandon this browser for a new one. Are you sure?"
|
||||
)}
|
||||
</div>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
{!cycleBrowser.isPending && (
|
||||
<DialogClose asChild>
|
||||
<Button variant="secondary">Cancel</Button>
|
||||
</DialogClose>
|
||||
)}
|
||||
<Button
|
||||
variant="default"
|
||||
onClick={() => {
|
||||
cycleBrowser.mutate(workflowPermanentId!);
|
||||
}}
|
||||
disabled={cycleBrowser.isPending}
|
||||
>
|
||||
Yes, Continue{" "}
|
||||
{cycleBrowser.isPending && (
|
||||
<ReloadIcon className="ml-2 size-4 animate-spin" />
|
||||
<div className="relative h-full w-full">
|
||||
<Dialog
|
||||
open={openDialogue}
|
||||
onOpenChange={(open) => {
|
||||
if (!open && cycleBrowser.isPending) {
|
||||
return;
|
||||
}
|
||||
setOpenDialogue(open);
|
||||
}}
|
||||
>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Cycle (Get a new browser)</DialogTitle>
|
||||
<DialogDescription>
|
||||
<div className="pb-2 pt-4 text-sm text-slate-400">
|
||||
{cycleBrowser.isPending ? (
|
||||
<>
|
||||
Cooking you up a fresh browser...
|
||||
<AnimatedWave text=".‧₊˚ ⋅ ✨★ ‧₊˚ ⋅" />
|
||||
</>
|
||||
) : (
|
||||
"Abandon this browser for a new one. Are you sure?"
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
{!cycleBrowser.isPending && (
|
||||
<DialogClose asChild>
|
||||
<Button variant="secondary">Cancel</Button>
|
||||
</DialogClose>
|
||||
)}
|
||||
<Button
|
||||
variant="default"
|
||||
onClick={() => {
|
||||
cycleBrowser.mutate(workflowPermanentId!);
|
||||
}}
|
||||
disabled={cycleBrowser.isPending}
|
||||
>
|
||||
Yes, Continue{" "}
|
||||
{cycleBrowser.isPending && (
|
||||
<ReloadIcon className="ml-2 size-4 animate-spin" />
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* header panel */}
|
||||
<div
|
||||
className="absolute left-6 right-6 top-8 h-20"
|
||||
style={{ zIndex: rankedItems.header ?? 3 }}
|
||||
onMouseDownCapture={() => {
|
||||
promote("header");
|
||||
}}
|
||||
>
|
||||
<WorkflowHeader
|
||||
debuggableBlockCount={debuggableBlockCount}
|
||||
saving={workflowChangesStore.saveIsPending}
|
||||
parametersPanelOpen={
|
||||
{/* header panel */}
|
||||
<div
|
||||
className="absolute left-6 right-6 top-8 h-20"
|
||||
style={{ zIndex: rankedItems.header ?? 3 }}
|
||||
onMouseDownCapture={() => {
|
||||
promote("header");
|
||||
}}
|
||||
>
|
||||
<WorkflowHeader
|
||||
debuggableBlockCount={debuggableBlockCount}
|
||||
saving={workflowChangesStore.saveIsPending}
|
||||
parametersPanelOpen={
|
||||
workflowPanelState.active &&
|
||||
workflowPanelState.content === "parameters"
|
||||
}
|
||||
onParametersClick={() => {
|
||||
if (
|
||||
workflowPanelState.active &&
|
||||
workflowPanelState.content === "parameters"
|
||||
}
|
||||
onParametersClick={() => {
|
||||
if (
|
||||
workflowPanelState.active &&
|
||||
workflowPanelState.content === "parameters"
|
||||
) {
|
||||
closeWorkflowPanel();
|
||||
promote("header");
|
||||
} else {
|
||||
setWorkflowPanelState({
|
||||
active: true,
|
||||
content: "parameters",
|
||||
});
|
||||
promote("dropdown");
|
||||
}
|
||||
}}
|
||||
onSave={async () => {
|
||||
const errors = getWorkflowErrors(nodes);
|
||||
if (errors.length > 0) {
|
||||
toast({
|
||||
title: "Can not save workflow because of errors:",
|
||||
description: (
|
||||
<div className="space-y-2">
|
||||
{errors.map((error) => (
|
||||
<p key={error}>{error}</p>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
await saveWorkflow.mutateAsync();
|
||||
}}
|
||||
onRun={() => {
|
||||
) {
|
||||
closeWorkflowPanel();
|
||||
promote("header");
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* sub panels */}
|
||||
{workflowPanelState.active && (
|
||||
<div
|
||||
className="absolute right-6 top-[7.75rem]"
|
||||
style={{ zIndex: rankedItems.dropdown ?? 2 }}
|
||||
onMouseDownCapture={() => {
|
||||
} else {
|
||||
setWorkflowPanelState({
|
||||
active: true,
|
||||
content: "parameters",
|
||||
});
|
||||
promote("dropdown");
|
||||
}}
|
||||
>
|
||||
{workflowPanelState.content === "parameters" && (
|
||||
<WorkflowParametersPanel
|
||||
onMouseDownCapture={() => {
|
||||
promote("dropdown");
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{workflowPanelState.content === "nodeLibrary" && (
|
||||
<WorkflowNodeLibraryPanel
|
||||
onMouseDownCapture={() => {
|
||||
promote("dropdown");
|
||||
}}
|
||||
onNodeClick={(props) => {
|
||||
addNode(props);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
}
|
||||
}}
|
||||
onSave={async () => {
|
||||
const errors = getWorkflowErrors(nodes);
|
||||
if (errors.length > 0) {
|
||||
toast({
|
||||
title: "Can not save workflow because of errors:",
|
||||
description: (
|
||||
<div className="space-y-2">
|
||||
{errors.map((error) => (
|
||||
<p key={error}>{error}</p>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
await saveWorkflow.mutateAsync();
|
||||
}}
|
||||
onRun={() => {
|
||||
closeWorkflowPanel();
|
||||
promote("header");
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{debugStore.isDebugMode && (
|
||||
<div
|
||||
className="absolute right-6 top-[8.5rem] h-[calc(100vh-9.5rem)]"
|
||||
style={{ zIndex: rankedItems.history ?? 1 }}
|
||||
onMouseDownCapture={() => {
|
||||
closeWorkflowPanel();
|
||||
promote("history");
|
||||
}}
|
||||
>
|
||||
<div className="pointer-events-none absolute right-0 top-0 flex h-full w-[400px] flex-col items-end justify-end">
|
||||
<div className="pointer-events-auto relative h-full w-full overflow-hidden rounded-xl border-2 border-slate-500">
|
||||
<WorkflowDebuggerRun />
|
||||
</div>
|
||||
{/* sub panels */}
|
||||
{workflowPanelState.active && (
|
||||
<div
|
||||
className="absolute right-6 top-[7.75rem]"
|
||||
style={{ zIndex: rankedItems.dropdown ?? 2 }}
|
||||
onMouseDownCapture={() => {
|
||||
promote("dropdown");
|
||||
}}
|
||||
>
|
||||
{workflowPanelState.content === "parameters" && (
|
||||
<WorkflowParametersPanel
|
||||
onMouseDownCapture={() => {
|
||||
promote("dropdown");
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{workflowPanelState.content === "nodeLibrary" && (
|
||||
<WorkflowNodeLibraryPanel
|
||||
onMouseDownCapture={() => {
|
||||
promote("dropdown");
|
||||
}}
|
||||
onNodeClick={(props) => {
|
||||
addNode(props);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{debugStore.isDebugMode && (
|
||||
<div
|
||||
className="absolute right-6 top-[8.5rem] h-[calc(100vh-9.5rem)]"
|
||||
style={{ zIndex: rankedItems.history ?? 1 }}
|
||||
onMouseDownCapture={() => {
|
||||
closeWorkflowPanel();
|
||||
promote("history");
|
||||
}}
|
||||
>
|
||||
<div className="pointer-events-none absolute right-0 top-0 flex h-full w-[400px] flex-col items-end justify-end">
|
||||
<div className="pointer-events-auto relative h-full w-full overflow-hidden rounded-xl border-2 border-slate-500">
|
||||
<WorkflowDebuggerRun />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* infinite canvas */}
|
||||
<FlowRenderer
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
setNodes={setNodes}
|
||||
setEdges={setEdges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
initialTitle={initialTitle}
|
||||
initialParameters={initialParameters}
|
||||
workflow={workflow}
|
||||
onDebuggableBlockCountChange={(c) => setDebuggableBlockCount(c)}
|
||||
onMouseDownCapture={() => promote("infiniteCanvas")}
|
||||
zIndex={rankedItems.infiniteCanvas}
|
||||
/>
|
||||
{/* infinite canvas */}
|
||||
<FlowRenderer
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
setNodes={setNodes}
|
||||
setEdges={setEdges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
initialTitle={initialTitle}
|
||||
// initialParameters={initialParameters}
|
||||
workflow={workflow}
|
||||
onDebuggableBlockCountChange={(c) => setDebuggableBlockCount(c)}
|
||||
onMouseDownCapture={() => promote("infiniteCanvas")}
|
||||
zIndex={rankedItems.infiniteCanvas}
|
||||
/>
|
||||
|
||||
{/* browser */}
|
||||
{showBrowser && (
|
||||
<FloatingWindow
|
||||
title={browserTitle}
|
||||
bounded={false}
|
||||
initialPosition={initialBrowserPosition}
|
||||
initialWidth={initialWidth}
|
||||
initialHeight={initialHeight}
|
||||
showMaximizeButton={true}
|
||||
showMinimizeButton={true}
|
||||
showPowerButton={blockLabel === undefined && showPowerButton}
|
||||
showReloadButton={true}
|
||||
zIndex={rankedItems.browserWindow ?? 4}
|
||||
// --
|
||||
onCycle={handleOnCycle}
|
||||
onFocus={() => promote("browserWindow")}
|
||||
>
|
||||
{activeDebugSession &&
|
||||
activeDebugSession.browser_session_id &&
|
||||
!cycleBrowser.isPending ? (
|
||||
<BrowserStream
|
||||
interactive={interactor === "human"}
|
||||
browserSessionId={activeDebugSession.browser_session_id}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center gap-2 pb-2 pt-4 text-sm text-slate-400">
|
||||
Connecting to your browser...
|
||||
<AnimatedWave text=".‧₊˚ ⋅ ✨★ ‧₊˚ ⋅" />
|
||||
</div>
|
||||
)}
|
||||
</FloatingWindow>
|
||||
)}
|
||||
</div>
|
||||
</WorkflowParametersStateContext.Provider>
|
||||
{/* browser */}
|
||||
{showBrowser && (
|
||||
<FloatingWindow
|
||||
title={browserTitle}
|
||||
bounded={false}
|
||||
initialPosition={initialBrowserPosition}
|
||||
initialWidth={initialWidth}
|
||||
initialHeight={initialHeight}
|
||||
showMaximizeButton={true}
|
||||
showMinimizeButton={true}
|
||||
showPowerButton={blockLabel === undefined && showPowerButton}
|
||||
showReloadButton={true}
|
||||
zIndex={rankedItems.browserWindow ?? 4}
|
||||
// --
|
||||
onCycle={handleOnCycle}
|
||||
onFocus={() => promote("browserWindow")}
|
||||
>
|
||||
{activeDebugSession &&
|
||||
activeDebugSession.browser_session_id &&
|
||||
!cycleBrowser.isPending ? (
|
||||
<BrowserStream
|
||||
interactive={interactor === "human"}
|
||||
browserSessionId={activeDebugSession.browser_session_id}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center gap-2 pb-2 pt-4 text-sm text-slate-400">
|
||||
Connecting to your browser...
|
||||
<AnimatedWave text=".‧₊˚ ⋅ ✨★ ‧₊˚ ⋅" />
|
||||
</div>
|
||||
)}
|
||||
</FloatingWindow>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { useCredentialsQuery } from "@/routes/workflows/hooks/useCredentialsQuery";
|
||||
import CloudContext from "@/store/CloudContext";
|
||||
import { useContext } from "react";
|
||||
import { useWorkflowParametersState } from "../../useWorkflowParametersState";
|
||||
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
||||
import { CredentialsModal } from "@/routes/credentials/CredentialsModal";
|
||||
import { PlusIcon } from "@radix-ui/react-icons";
|
||||
import {
|
||||
@@ -30,8 +30,10 @@ type Props = {
|
||||
function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
|
||||
const { setIsOpen, setType } = useCredentialModalState();
|
||||
const nodes = useNodes<AppNode>();
|
||||
const [workflowParameters, setWorkflowParameters] =
|
||||
useWorkflowParametersState();
|
||||
const {
|
||||
parameters: workflowParameters,
|
||||
setParameters: setWorkflowParameters,
|
||||
} = useWorkflowParametersStore();
|
||||
const credentialParameters = workflowParameters.filter(
|
||||
(parameter) =>
|
||||
parameter.parameterType === "credential" ||
|
||||
@@ -170,16 +172,14 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
|
||||
<CredentialsModal
|
||||
onCredentialCreated={(id) => {
|
||||
onChange?.(id);
|
||||
setWorkflowParameters((prev) => {
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
parameterType: "credential",
|
||||
credentialId: id,
|
||||
key: id,
|
||||
},
|
||||
];
|
||||
});
|
||||
setWorkflowParameters([
|
||||
...workflowParameters,
|
||||
{
|
||||
parameterType: "credential",
|
||||
credentialId: id,
|
||||
key: id,
|
||||
},
|
||||
]);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MultiSelect } from "@/components/ui/multi-select";
|
||||
import { useWorkflowParametersState } from "../../useWorkflowParametersState";
|
||||
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
||||
import { HelpTooltip } from "@/components/HelpTooltip";
|
||||
import { helpTooltips } from "../../helpContent";
|
||||
|
||||
@@ -14,7 +14,7 @@ function ParametersMultiSelect({
|
||||
parameters,
|
||||
onParametersChange,
|
||||
}: Props) {
|
||||
const [workflowParameters] = useWorkflowParametersState();
|
||||
const { parameters: workflowParameters } = useWorkflowParametersStore();
|
||||
const keys = workflowParameters
|
||||
.map((parameter) => parameter.key)
|
||||
.concat(availableOutputParameters);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MultiSelect } from "@/components/ui/multi-select";
|
||||
import { useWorkflowParametersState } from "../../useWorkflowParametersState";
|
||||
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
||||
import { HelpTooltip } from "@/components/HelpTooltip";
|
||||
import { helpTooltips } from "../../helpContent";
|
||||
|
||||
@@ -14,7 +14,7 @@ function TaskNodeParametersPanel({
|
||||
parameters,
|
||||
onParametersChange,
|
||||
}: Props) {
|
||||
const [workflowParameters] = useWorkflowParametersState();
|
||||
const { parameters: workflowParameters } = useWorkflowParametersStore();
|
||||
const keys = workflowParameters
|
||||
.map((parameter) => parameter.key)
|
||||
.concat(availableOutputParameters);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEdges, useNodes } from "@xyflow/react";
|
||||
import { useWorkflowParametersState } from "../useWorkflowParametersState";
|
||||
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
||||
import { AppNode } from ".";
|
||||
import { getAvailableOutputParameterKeys } from "../workflowEditorUtils";
|
||||
import { PlusIcon } from "@radix-ui/react-icons";
|
||||
@@ -15,7 +15,7 @@ type Props = {
|
||||
|
||||
function WorkflowBlockParameterSelect({ nodeId, onAdd }: Props) {
|
||||
const [content, setContent] = useState("parameters");
|
||||
const [workflowParameters] = useWorkflowParametersState();
|
||||
const { parameters: workflowParameters } = useWorkflowParametersStore();
|
||||
const nodes = useNodes<AppNode>();
|
||||
const edges = useEdges();
|
||||
const outputParameterKeys = getAvailableOutputParameterKeys(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { useWorkflowParametersState } from "../useWorkflowParametersState";
|
||||
import { WorkflowParameterAddPanel } from "./WorkflowParameterAddPanel";
|
||||
import { ParametersState } from "../types";
|
||||
import { WorkflowParameterEditPanel } from "./WorkflowParameterEditPanel";
|
||||
@@ -26,6 +25,7 @@ import {
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { useReactFlow } from "@xyflow/react";
|
||||
import { useWorkflowHasChangesStore } from "@/store/WorkflowHasChangesStore";
|
||||
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
||||
import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area";
|
||||
import {
|
||||
WorkflowEditorParameterType,
|
||||
@@ -44,8 +44,10 @@ function WorkflowParametersPanel({ onMouseDownCapture }: Props) {
|
||||
const setHasChanges = useWorkflowHasChangesStore(
|
||||
(state) => state.setHasChanges,
|
||||
);
|
||||
const [workflowParameters, setWorkflowParameters] =
|
||||
useWorkflowParametersState();
|
||||
const {
|
||||
parameters: workflowParameters,
|
||||
setParameters: setWorkflowParameters,
|
||||
} = useWorkflowParametersStore();
|
||||
const [operationPanelState, setOperationPanelState] = useState<{
|
||||
active: boolean;
|
||||
operation: "add" | "edit";
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { useContext } from "react";
|
||||
import { WorkflowParametersStateContext } from "./WorkflowParametersStateContext";
|
||||
|
||||
function useWorkflowParametersState() {
|
||||
const value = useContext(WorkflowParametersStateContext);
|
||||
if (value === undefined) {
|
||||
throw new Error(
|
||||
"useWorkflowParametersState must be used within a WorkflowParametersStateProvider",
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export { useWorkflowParametersState };
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
getUpdatedParametersAfterLabelUpdateForSourceParameterKey,
|
||||
} from "../editor/workflowEditorUtils";
|
||||
import { useState } from "react";
|
||||
import { useWorkflowParametersState } from "../editor/useWorkflowParametersState";
|
||||
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
@@ -17,8 +17,10 @@ function useNodeLabelChangeHandler({ id, initialValue }: Props) {
|
||||
const [label, setLabel] = useState(initialValue);
|
||||
const nodes = useNodes<AppNode>();
|
||||
const { setNodes } = useReactFlow();
|
||||
const [workflowParameters, setWorkflowParameters] =
|
||||
useWorkflowParametersState();
|
||||
const {
|
||||
parameters: workflowParameters,
|
||||
setParameters: setWorkflowParameters,
|
||||
} = useWorkflowParametersStore();
|
||||
|
||||
function handleLabelChange(value: string) {
|
||||
const existingLabels = nodes
|
||||
|
||||
16
skyvern-frontend/src/store/WorkflowParametersStore.ts
Normal file
16
skyvern-frontend/src/store/WorkflowParametersStore.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { create } from "zustand";
|
||||
import { ParametersState } from "@/routes/workflows/editor/types";
|
||||
|
||||
interface WorkflowParametersStore {
|
||||
parameters: ParametersState;
|
||||
setParameters: (parameters: ParametersState) => void;
|
||||
}
|
||||
|
||||
const useWorkflowParametersStore = create<WorkflowParametersStore>((set) => {
|
||||
return {
|
||||
parameters: [],
|
||||
setParameters: (parameters: ParametersState) => set({ parameters }),
|
||||
};
|
||||
});
|
||||
|
||||
export { useWorkflowParametersStore };
|
||||
Reference in New Issue
Block a user