show all of the scripts via a button (#3294)
This commit is contained in:
@@ -16,15 +16,22 @@ function BlockCodeEditor({
|
|||||||
blockLabel,
|
blockLabel,
|
||||||
blockType,
|
blockType,
|
||||||
script,
|
script,
|
||||||
|
title,
|
||||||
onClick,
|
onClick,
|
||||||
|
onExit,
|
||||||
}: {
|
}: {
|
||||||
blockLabel: string;
|
blockLabel: string;
|
||||||
blockType: WorkflowBlockType;
|
blockType?: WorkflowBlockType;
|
||||||
script: string | undefined;
|
script: string | undefined;
|
||||||
|
title?: string;
|
||||||
onClick?: (e: React.MouseEvent) => void;
|
onClick?: (e: React.MouseEvent) => void;
|
||||||
|
/**
|
||||||
|
* Return `false` to cancel the exit.
|
||||||
|
*/
|
||||||
|
onExit?: () => boolean;
|
||||||
}) {
|
}) {
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const blockTitle = workflowBlockTitle[blockType];
|
const blockTitle = blockType ? workflowBlockTitle[blockType] : title;
|
||||||
const toggleScriptForNodeCallback = useToggleScriptForNodeCallback();
|
const toggleScriptForNodeCallback = useToggleScriptForNodeCallback();
|
||||||
|
|
||||||
const cacheKeyValue = searchParams.get("cache-key-value");
|
const cacheKeyValue = searchParams.get("cache-key-value");
|
||||||
@@ -53,38 +60,51 @@ function BlockCodeEditor({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<header className="relative !mt-0 flex h-[2.75rem] justify-between gap-2">
|
<header className="relative !mt-0 flex h-[2.75rem] justify-between gap-2">
|
||||||
<div className="flex w-full gap-2">
|
{blockType ? (
|
||||||
<div className="relative flex h-[2.75rem] w-[2.75rem] items-center justify-center overflow-hidden rounded border border-slate-600">
|
<div className="flex w-full gap-2">
|
||||||
<WorkflowBlockIcon
|
<div className="relative flex h-[2.75rem] w-[2.75rem] items-center justify-center overflow-hidden rounded border border-slate-600">
|
||||||
workflowBlockType={blockType}
|
<WorkflowBlockIcon
|
||||||
className="size-6"
|
workflowBlockType={blockType}
|
||||||
/>
|
className="size-6"
|
||||||
<div className="absolute -left-3 top-8 flex h-4 w-16 origin-top-left -rotate-45 transform items-center justify-center bg-yellow-400">
|
/>
|
||||||
<span className="text-xs font-bold text-black">code</span>
|
<div className="absolute -left-3 top-8 flex h-4 w-16 origin-top-left -rotate-45 transform items-center justify-center bg-yellow-400">
|
||||||
</div>
|
<span className="text-xs font-bold text-black">code</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col gap-1">
|
</div>
|
||||||
{blockLabel}
|
|
||||||
<div className="flex w-full items-center justify-center gap-1">
|
<div className="flex w-full flex-col gap-1">
|
||||||
<span className="text-xs text-slate-400">{blockTitle}</span>
|
{blockLabel}
|
||||||
<div className="ml-auto scale-[60%] opacity-50">
|
<div className="flex w-full items-center justify-center gap-1">
|
||||||
<KeyIcon />
|
<span className="text-xs text-slate-400">{blockTitle}</span>
|
||||||
|
<div className="ml-auto scale-[60%] opacity-50">
|
||||||
|
<KeyIcon />
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-slate-400">
|
||||||
|
{cacheKeyValue === "" || !cacheKeyValue
|
||||||
|
? "(none)"
|
||||||
|
: cacheKeyValue}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-slate-400">
|
|
||||||
{cacheKeyValue === "" || !cacheKeyValue
|
|
||||||
? "(none)"
|
|
||||||
: cacheKeyValue}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
|
<header className="mt-0 flex h-[2.75rem] w-full items-center justify-center">
|
||||||
|
{title ?? blockLabel}
|
||||||
|
</header>
|
||||||
|
)}
|
||||||
<div className="absolute right-[-0.5rem] top-0 flex h-[2rem] w-[2rem] items-center justify-center rounded hover:bg-slate-800">
|
<div className="absolute right-[-0.5rem] top-0 flex h-[2rem] w-[2rem] items-center justify-center rounded hover:bg-slate-800">
|
||||||
<ExitIcon
|
<ExitIcon
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleScriptForNodeCallback({
|
if (onExit) {
|
||||||
label: blockLabel,
|
const result = onExit();
|
||||||
show: false,
|
|
||||||
});
|
if (result !== false) {
|
||||||
|
toggleScriptForNodeCallback({
|
||||||
|
label: blockLabel,
|
||||||
|
show: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
className="size-5 cursor-pointer"
|
className="size-5 cursor-pointer"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -459,7 +459,7 @@ function FlowRenderer({
|
|||||||
}) {
|
}) {
|
||||||
if (id) {
|
if (id) {
|
||||||
const node = nodes.find((node) => node.id === id);
|
const node = nodes.find((node) => node.id === id);
|
||||||
if (!node || !isWorkflowBlockNode(node)) {
|
if (!node) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,7 +469,7 @@ function FlowRenderer({
|
|||||||
(node) => "label" in node.data && node.data.label === label,
|
(node) => "label" in node.data && node.data.label === label,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!node || !isWorkflowBlockNode(node)) {
|
if (!node) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -417,6 +417,8 @@ function Workspace({
|
|||||||
{
|
{
|
||||||
withWorkflowSettings: false,
|
withWorkflowSettings: false,
|
||||||
editable: true,
|
editable: true,
|
||||||
|
label: "__start_block__",
|
||||||
|
showCode: false,
|
||||||
},
|
},
|
||||||
id,
|
id,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -10,16 +10,24 @@ import { DotsHorizontalIcon } from "@radix-ui/react-icons";
|
|||||||
import { OrgWalled } from "@/components/Orgwalled";
|
import { OrgWalled } from "@/components/Orgwalled";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
isDeleteable?: boolean;
|
||||||
isScriptable?: boolean;
|
isScriptable?: boolean;
|
||||||
onDelete: () => void;
|
showScriptText?: string;
|
||||||
|
onDelete?: () => void;
|
||||||
onShowScript?: () => void;
|
onShowScript?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function NodeActionMenu({
|
function NodeActionMenu({
|
||||||
|
isDeleteable = true,
|
||||||
isScriptable = false,
|
isScriptable = false,
|
||||||
|
showScriptText,
|
||||||
onDelete,
|
onDelete,
|
||||||
onShowScript,
|
onShowScript,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
if (!isDeleteable && !isScriptable) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
@@ -28,13 +36,15 @@ function NodeActionMenu({
|
|||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
<DropdownMenuLabel>Block Actions</DropdownMenuLabel>
|
<DropdownMenuLabel>Block Actions</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem
|
{isDeleteable && (
|
||||||
onSelect={() => {
|
<DropdownMenuItem
|
||||||
onDelete();
|
onSelect={() => {
|
||||||
}}
|
onDelete?.();
|
||||||
>
|
}}
|
||||||
Delete Block
|
>
|
||||||
</DropdownMenuItem>
|
Delete Block
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
{isScriptable && (
|
{isScriptable && (
|
||||||
<OrgWalled className="p-0">
|
<OrgWalled className="p-0">
|
||||||
{onShowScript && (
|
{onShowScript && (
|
||||||
@@ -43,7 +53,7 @@ function NodeActionMenu({
|
|||||||
onShowScript();
|
onShowScript();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Show Script
|
{showScriptText ?? "Show Script"}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
</OrgWalled>
|
</OrgWalled>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { getClient } from "@/api/AxiosClient";
|
import { getClient } from "@/api/AxiosClient";
|
||||||
import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react";
|
import { Handle, Node, NodeProps, Position, useReactFlow } from "@xyflow/react";
|
||||||
import type { StartNode } from "./types";
|
import type { StartNode } from "./types";
|
||||||
import {
|
import {
|
||||||
Accordion,
|
Accordion,
|
||||||
@@ -25,12 +25,25 @@ import { MAX_SCREENSHOT_SCROLLS_DEFAULT } from "../Taskv2Node/types";
|
|||||||
import { KeyValueInput } from "@/components/KeyValueInput";
|
import { KeyValueInput } from "@/components/KeyValueInput";
|
||||||
import { OrgWalled } from "@/components/Orgwalled";
|
import { OrgWalled } from "@/components/Orgwalled";
|
||||||
import { placeholders } from "@/routes/workflows/editor/helpContent";
|
import { placeholders } from "@/routes/workflows/editor/helpContent";
|
||||||
|
import { NodeActionMenu } from "@/routes/workflows/editor/nodes/NodeActionMenu";
|
||||||
import { useWorkflowSettingsStore } from "@/store/WorkflowSettingsStore";
|
import { useWorkflowSettingsStore } from "@/store/WorkflowSettingsStore";
|
||||||
|
import {
|
||||||
|
scriptableWorkflowBlockTypes,
|
||||||
|
type WorkflowBlockType,
|
||||||
|
} from "@/routes/workflows/types/workflowTypes";
|
||||||
|
// import { useToggleScriptForNodeCallback } from "@/routes/workflows/hooks/useToggleScriptForNodeCallback";
|
||||||
|
|
||||||
|
import { Flippable } from "@/components/Flippable";
|
||||||
|
import { useRerender } from "@/hooks/useRerender";
|
||||||
|
import { useBlockScriptStore } from "@/store/BlockScriptStore";
|
||||||
|
import { BlockCodeEditor } from "@/routes/workflows/components/BlockCodeEditor";
|
||||||
|
|
||||||
function StartNode({ id, data }: NodeProps<StartNode>) {
|
function StartNode({ id, data }: NodeProps<StartNode>) {
|
||||||
const workflowSettingsStore = useWorkflowSettingsStore();
|
const workflowSettingsStore = useWorkflowSettingsStore();
|
||||||
const credentialGetter = useCredentialGetter();
|
const credentialGetter = useCredentialGetter();
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
|
// const toggleScriptForNodeCallback = useToggleScriptForNodeCallback();
|
||||||
|
const reactFlowInstance = useReactFlow();
|
||||||
|
|
||||||
const { data: availableModels } = useQuery<ModelsResponse>({
|
const { data: availableModels } = useQuery<ModelsResponse>({
|
||||||
queryKey: ["models"],
|
queryKey: ["models"],
|
||||||
@@ -66,6 +79,15 @@ function StartNode({ id, data }: NodeProps<StartNode>) {
|
|||||||
scriptCacheKey: data.withWorkflowSettings ? data.scriptCacheKey : null,
|
scriptCacheKey: data.withWorkflowSettings ? data.scriptCacheKey : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [facing, setFacing] = useState<"front" | "back">("front");
|
||||||
|
const blockScriptStore = useBlockScriptStore();
|
||||||
|
const script = blockScriptStore.scripts.__start_block__;
|
||||||
|
const rerender = useRerender({ prefix: "accordion" });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFacing(data.showCode ? "back" : "front");
|
||||||
|
}, [data.showCode]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
workflowSettingsStore.setWorkflowSettings(inputs);
|
workflowSettingsStore.setWorkflowSettings(inputs);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -79,148 +101,217 @@ function StartNode({ id, data }: NodeProps<StartNode>) {
|
|||||||
updateNodeData(id, { [key]: value });
|
updateNodeData(id, { [key]: value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function nodeIsFlippable(node: Node) {
|
||||||
|
return (
|
||||||
|
scriptableWorkflowBlockTypes.has(node.type as WorkflowBlockType) ||
|
||||||
|
node.type === "start"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAllScripts() {
|
||||||
|
reactFlowInstance.setNodes((nodes) => {
|
||||||
|
return nodes.map((node) => {
|
||||||
|
if (nodeIsFlippable(node)) {
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
data: {
|
||||||
|
...node.data,
|
||||||
|
showCode: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideAllScripts() {
|
||||||
|
reactFlowInstance.setNodes((nodes) => {
|
||||||
|
return nodes.map((node) => {
|
||||||
|
if (nodeIsFlippable(node)) {
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
data: {
|
||||||
|
...node.data,
|
||||||
|
showCode: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (data.withWorkflowSettings) {
|
if (data.withWorkflowSettings) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Flippable facing={facing} preserveFrontsideHeight={true}>
|
||||||
<Handle
|
<div>
|
||||||
type="source"
|
<Handle
|
||||||
position={Position.Bottom}
|
type="source"
|
||||||
id="a"
|
position={Position.Bottom}
|
||||||
className="opacity-0"
|
id="a"
|
||||||
/>
|
className="opacity-0"
|
||||||
<div className="w-[30rem] rounded-lg bg-slate-elevation3 px-6 py-4 text-center">
|
/>
|
||||||
<div className="space-y-4">
|
<div className="w-[30rem] rounded-lg bg-slate-elevation3 px-6 py-4 text-center">
|
||||||
<header>Start</header>
|
<div className="relative">
|
||||||
<Separator />
|
<div className="absolute right-0 top-0">
|
||||||
<Accordion type="single" collapsible>
|
<div>
|
||||||
<AccordionItem value="settings" className="border-b-0">
|
<div className="rounded p-1 hover:bg-muted">
|
||||||
<AccordionTrigger className="py-2">
|
<NodeActionMenu
|
||||||
Workflow Settings
|
isDeleteable={false}
|
||||||
</AccordionTrigger>
|
isScriptable={true}
|
||||||
<AccordionContent className="pl-6 pr-1 pt-1">
|
showScriptText="Show All Scripts"
|
||||||
<div className="space-y-4">
|
onShowScript={showAllScripts}
|
||||||
<div className="space-y-2">
|
/>
|
||||||
<ModelSelector
|
</div>
|
||||||
className="nopan w-52 text-xs"
|
</div>
|
||||||
value={inputs.model}
|
</div>
|
||||||
onChange={(value) => {
|
<header className="mb-4">Start</header>
|
||||||
handleChange("model", value);
|
<Separator />
|
||||||
}}
|
<Accordion
|
||||||
/>
|
type="single"
|
||||||
</div>
|
collapsible
|
||||||
<div className="space-y-2">
|
onValueChange={() => rerender.bump()}
|
||||||
<div className="flex gap-2">
|
>
|
||||||
<Label>Webhook Callback URL</Label>
|
<AccordionItem value="settings" className="mt-4 border-b-0">
|
||||||
<HelpTooltip content="The URL of a webhook endpoint to send the workflow results" />
|
<AccordionTrigger className="py-2">
|
||||||
|
Workflow Settings
|
||||||
|
</AccordionTrigger>
|
||||||
|
<AccordionContent className="pl-6 pr-1 pt-1">
|
||||||
|
<div key={rerender.key} className="space-y-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<ModelSelector
|
||||||
|
className="nopan w-52 text-xs"
|
||||||
|
value={inputs.model}
|
||||||
|
onChange={(value) => {
|
||||||
|
handleChange("model", value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<div className="space-y-2">
|
||||||
value={inputs.webhookCallbackUrl}
|
<div className="flex gap-2">
|
||||||
placeholder="https://"
|
<Label>Webhook Callback URL</Label>
|
||||||
onChange={(event) => {
|
<HelpTooltip content="The URL of a webhook endpoint to send the workflow results" />
|
||||||
handleChange(
|
</div>
|
||||||
"webhookCallbackUrl",
|
<Input
|
||||||
event.target.value,
|
value={inputs.webhookCallbackUrl}
|
||||||
);
|
placeholder="https://"
|
||||||
}}
|
onChange={(event) => {
|
||||||
/>
|
handleChange(
|
||||||
</div>
|
"webhookCallbackUrl",
|
||||||
<div className="space-y-2">
|
event.target.value,
|
||||||
<div className="flex gap-2">
|
);
|
||||||
<Label>Proxy Location</Label>
|
}}
|
||||||
<HelpTooltip content="Route Skyvern through one of our available proxies." />
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ProxySelector
|
<div className="space-y-2">
|
||||||
value={inputs.proxyLocation}
|
<div className="flex gap-2">
|
||||||
onChange={(value) => {
|
<Label>Proxy Location</Label>
|
||||||
handleChange("proxyLocation", value);
|
<HelpTooltip content="Route Skyvern through one of our available proxies." />
|
||||||
}}
|
</div>
|
||||||
/>
|
<ProxySelector
|
||||||
</div>
|
value={inputs.proxyLocation}
|
||||||
<OrgWalled className="flex flex-col gap-4">
|
onChange={(value) => {
|
||||||
|
handleChange("proxyLocation", value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<OrgWalled className="flex flex-col gap-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Label>Generate Script</Label>
|
||||||
|
<HelpTooltip content="Generate & use cached scripts for faster execution." />
|
||||||
|
<Switch
|
||||||
|
className="ml-auto"
|
||||||
|
checked={inputs.useScriptCache}
|
||||||
|
onCheckedChange={(value) => {
|
||||||
|
handleChange("useScriptCache", value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{inputs.useScriptCache && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label>Script Key (optional)</Label>
|
||||||
|
</div>
|
||||||
|
<WorkflowBlockInputTextarea
|
||||||
|
nodeId={id}
|
||||||
|
onChange={(value) => {
|
||||||
|
const v = value.length ? value : null;
|
||||||
|
handleChange("scriptCacheKey", v);
|
||||||
|
}}
|
||||||
|
value={inputs.scriptCacheKey ?? ""}
|
||||||
|
placeholder={placeholders["scripts"]["scriptKey"]}
|
||||||
|
className="nopan text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</OrgWalled>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Label>Generate Script</Label>
|
<Label>Save & Reuse Session</Label>
|
||||||
<HelpTooltip content="Generate & use cached scripts for faster execution." />
|
<HelpTooltip content="Persist session information across workflow runs" />
|
||||||
<Switch
|
<Switch
|
||||||
className="ml-auto"
|
className="ml-auto"
|
||||||
checked={inputs.useScriptCache}
|
checked={inputs.persistBrowserSession}
|
||||||
onCheckedChange={(value) => {
|
onCheckedChange={(value) => {
|
||||||
handleChange("useScriptCache", value);
|
handleChange("persistBrowserSession", value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{inputs.useScriptCache && (
|
<div className="space-y-2">
|
||||||
<div className="space-y-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex gap-2">
|
<Label>Extra HTTP Headers</Label>
|
||||||
<Label>Script Key (optional)</Label>
|
<HelpTooltip content="Specify some self-defined HTTP requests headers" />
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<KeyValueInput
|
||||||
nodeId={id}
|
value={inputs.extraHttpHeaders ?? null}
|
||||||
onChange={(value) => {
|
onChange={(val) =>
|
||||||
const v = value.length ? value : null;
|
handleChange("extraHttpHeaders", val)
|
||||||
handleChange("scriptCacheKey", v);
|
}
|
||||||
}}
|
addButtonText="Add Header"
|
||||||
value={inputs.scriptCacheKey ?? ""}
|
/>
|
||||||
placeholder={placeholders["scripts"]["scriptKey"]}
|
</div>
|
||||||
className="nopan text-xs"
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Label>Max Screenshot Scrolls</Label>
|
||||||
|
<HelpTooltip
|
||||||
|
content={`The maximum number of scrolls for the post action screenshot. Default is ${MAX_SCREENSHOT_SCROLLS_DEFAULT}. If it's set to 0, it will take the current viewport screenshot.`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
<Input
|
||||||
</OrgWalled>
|
value={inputs.maxScreenshotScrolls ?? ""}
|
||||||
<div className="space-y-2">
|
placeholder={`Default: ${MAX_SCREENSHOT_SCROLLS_DEFAULT}`}
|
||||||
<div className="flex items-center gap-2">
|
onChange={(event) => {
|
||||||
<Label>Save & Reuse Session</Label>
|
const value =
|
||||||
<HelpTooltip content="Persist session information across workflow runs" />
|
event.target.value === ""
|
||||||
<Switch
|
? null
|
||||||
className="ml-auto"
|
: Number(event.target.value);
|
||||||
checked={inputs.persistBrowserSession}
|
|
||||||
onCheckedChange={(value) => {
|
handleChange("maxScreenshotScrolls", value);
|
||||||
handleChange("persistBrowserSession", value);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
</AccordionContent>
|
||||||
<div className="flex items-center gap-2">
|
</AccordionItem>
|
||||||
<Label>Extra HTTP Headers</Label>
|
</Accordion>
|
||||||
<HelpTooltip content="Specify some self-defined HTTP requests headers" />
|
</div>
|
||||||
</div>
|
|
||||||
<KeyValueInput
|
|
||||||
value={inputs.extraHttpHeaders ?? null}
|
|
||||||
onChange={(val) =>
|
|
||||||
handleChange("extraHttpHeaders", val)
|
|
||||||
}
|
|
||||||
addButtonText="Add Header"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Label>Max Screenshot Scrolls</Label>
|
|
||||||
<HelpTooltip
|
|
||||||
content={`The maximum number of scrolls for the post action screenshot. Default is ${MAX_SCREENSHOT_SCROLLS_DEFAULT}. If it's set to 0, it will take the current viewport screenshot.`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Input
|
|
||||||
value={inputs.maxScreenshotScrolls ?? ""}
|
|
||||||
placeholder={`Default: ${MAX_SCREENSHOT_SCROLLS_DEFAULT}`}
|
|
||||||
onChange={(event) => {
|
|
||||||
const value =
|
|
||||||
event.target.value === ""
|
|
||||||
? null
|
|
||||||
: Number(event.target.value);
|
|
||||||
|
|
||||||
handleChange("maxScreenshotScrolls", value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<BlockCodeEditor
|
||||||
|
blockLabel="__start_block__"
|
||||||
|
title="Start"
|
||||||
|
script={script}
|
||||||
|
onExit={() => {
|
||||||
|
hideAllScripts();
|
||||||
|
return false;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flippable>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,15 @@ export type WorkflowStartNodeData = {
|
|||||||
editable: boolean;
|
editable: boolean;
|
||||||
useScriptCache: boolean;
|
useScriptCache: boolean;
|
||||||
scriptCacheKey: string | null;
|
scriptCacheKey: string | null;
|
||||||
|
label: "__start_block__";
|
||||||
|
showCode: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OtherStartNodeData = {
|
export type OtherStartNodeData = {
|
||||||
withWorkflowSettings: false;
|
withWorkflowSettings: false;
|
||||||
editable: boolean;
|
editable: boolean;
|
||||||
|
label: "__start_block__";
|
||||||
|
showCode: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StartNodeData = WorkflowStartNodeData | OtherStartNodeData;
|
export type StartNodeData = WorkflowStartNodeData | OtherStartNodeData;
|
||||||
|
|||||||
@@ -703,6 +703,8 @@ function getElements(
|
|||||||
editable,
|
editable,
|
||||||
useScriptCache: settings.useScriptCache,
|
useScriptCache: settings.useScriptCache,
|
||||||
scriptCacheKey: settings.scriptCacheKey,
|
scriptCacheKey: settings.scriptCacheKey,
|
||||||
|
label: "__start_block__",
|
||||||
|
showCode: false,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -733,6 +735,8 @@ function getElements(
|
|||||||
{
|
{
|
||||||
withWorkflowSettings: false,
|
withWorkflowSettings: false,
|
||||||
editable,
|
editable,
|
||||||
|
label: "__start_block__",
|
||||||
|
showCode: false,
|
||||||
},
|
},
|
||||||
block.id,
|
block.id,
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user