Jon/sky 6375 alter how show all code works (#3445)
This commit is contained in:
@@ -341,30 +341,21 @@ function Splitter({
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
useMountEffect(() => {
|
||||
// small delay here, to allow for arbitrary layout thrashing to settle;
|
||||
// otherwise we have to rely on an observer for the container size, and
|
||||
// resetting whenever the container resizes it likely incorrect behaviour
|
||||
setTimeout(() => {
|
||||
if (containerRef.current) {
|
||||
const newPosition = normalizeUnitsToPercent(
|
||||
containerRef,
|
||||
direction,
|
||||
firstSizingTarget,
|
||||
firstSizing,
|
||||
storageKey,
|
||||
);
|
||||
if (containerRef.current) {
|
||||
const newPosition = normalizeUnitsToPercent(
|
||||
containerRef,
|
||||
direction,
|
||||
firstSizingTarget,
|
||||
firstSizing,
|
||||
storageKey,
|
||||
);
|
||||
|
||||
setSplitPosition(newPosition);
|
||||
setSplitPosition(newPosition);
|
||||
|
||||
if (storageKey) {
|
||||
setStoredSizing(
|
||||
firstSizingTarget,
|
||||
storageKey,
|
||||
newPosition.toString(),
|
||||
);
|
||||
}
|
||||
if (storageKey) {
|
||||
setStoredSizing(firstSizingTarget, storageKey, newPosition.toString());
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
useOnChange(isDragging, (newValue, oldValue) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
CodeIcon,
|
||||
CopyIcon,
|
||||
PlayIcon,
|
||||
ReloadIcon,
|
||||
@@ -41,12 +42,14 @@ type Props = {
|
||||
cacheKeyValuesPanelOpen: boolean;
|
||||
parametersPanelOpen: boolean;
|
||||
saving: boolean;
|
||||
showAllCode: boolean;
|
||||
workflow: WorkflowApiResponse;
|
||||
onCacheKeyValueAccept: (cacheKeyValue: string | null) => void;
|
||||
onCacheKeyValuesBlurred: (cacheKeyValue: string | null) => void;
|
||||
onCacheKeyValuesFilter: (cacheKeyValue: string) => void;
|
||||
onCacheKeyValuesKeydown: (e: React.KeyboardEvent<HTMLInputElement>) => void;
|
||||
onParametersClick: () => void;
|
||||
onShowAllCodeClick?: () => void;
|
||||
onCacheKeyValuesClick: () => void;
|
||||
onSave: () => void;
|
||||
onRun?: () => void;
|
||||
@@ -58,12 +61,14 @@ function WorkflowHeader({
|
||||
cacheKeyValuesPanelOpen,
|
||||
parametersPanelOpen,
|
||||
saving,
|
||||
showAllCode,
|
||||
workflow,
|
||||
onCacheKeyValueAccept,
|
||||
onCacheKeyValuesBlurred,
|
||||
onCacheKeyValuesFilter,
|
||||
onCacheKeyValuesKeydown,
|
||||
onParametersClick,
|
||||
onShowAllCodeClick,
|
||||
onCacheKeyValuesClick,
|
||||
onSave,
|
||||
onRun,
|
||||
@@ -87,6 +92,10 @@ function WorkflowHeader({
|
||||
input: useRef<HTMLInputElement>(null),
|
||||
};
|
||||
|
||||
const handleShowAllCode = () => {
|
||||
onShowAllCodeClick?.();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (cacheKeyValue === chosenCacheKeyValue) {
|
||||
return;
|
||||
@@ -125,62 +134,75 @@ function WorkflowHeader({
|
||||
<div className="flex h-full items-center justify-end gap-4">
|
||||
{user && workflow.generate_script && (
|
||||
// (cacheKeyValues?.total_count ?? 0) > 0 && (
|
||||
<div
|
||||
tabIndex={1}
|
||||
className="flex max-w-[15rem] items-center justify-center gap-1 rounded-md border border-input pr-1 focus-within:ring-1 focus-within:ring-ring"
|
||||
>
|
||||
<Input
|
||||
ref={dom.input}
|
||||
className="focus-visible:transparent focus-visible:none h-[2.75rem] text-ellipsis whitespace-nowrap border-none focus-visible:outline-none focus-visible:ring-0"
|
||||
onChange={(e) => {
|
||||
setChosenCacheKeyValue(e.target.value);
|
||||
onCacheKeyValuesFilter(e.target.value);
|
||||
}}
|
||||
onMouseDown={() => {
|
||||
if (!cacheKeyValuesPanelOpen) {
|
||||
onCacheKeyValuesClick();
|
||||
}
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
const numFiltered = cacheKeyValues?.values?.length ?? 0;
|
||||
|
||||
if (numFiltered === 1) {
|
||||
const first = cacheKeyValues?.values?.[0];
|
||||
if (first) {
|
||||
setChosenCacheKeyValue(first);
|
||||
onCacheKeyValueAccept(first);
|
||||
}
|
||||
return;
|
||||
<>
|
||||
{debugStore.isDebugMode && (
|
||||
<Button
|
||||
className="pl-2 pr-3"
|
||||
size="lg"
|
||||
variant={!showAllCode ? "tertiary" : "default"}
|
||||
onClick={handleShowAllCode}
|
||||
>
|
||||
<CodeIcon className="mr-2 h-6 w-6" />
|
||||
Show Code
|
||||
</Button>
|
||||
)}
|
||||
<div
|
||||
tabIndex={1}
|
||||
className="flex max-w-[10rem] items-center justify-center gap-1 rounded-md border border-input pr-1 focus-within:ring-1 focus-within:ring-ring"
|
||||
>
|
||||
<Input
|
||||
ref={dom.input}
|
||||
className="focus-visible:transparent focus-visible:none h-[2.75rem] text-ellipsis whitespace-nowrap border-none focus-visible:outline-none focus-visible:ring-0"
|
||||
onChange={(e) => {
|
||||
setChosenCacheKeyValue(e.target.value);
|
||||
onCacheKeyValuesFilter(e.target.value);
|
||||
}}
|
||||
onMouseDown={() => {
|
||||
if (!cacheKeyValuesPanelOpen) {
|
||||
onCacheKeyValuesClick();
|
||||
}
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
const numFiltered = cacheKeyValues?.values?.length ?? 0;
|
||||
|
||||
setChosenCacheKeyValue(chosenCacheKeyValue);
|
||||
onCacheKeyValueAccept(chosenCacheKeyValue);
|
||||
}
|
||||
onCacheKeyValuesKeydown(e);
|
||||
}}
|
||||
placeholder="Code Key Value"
|
||||
value={chosenCacheKeyValue ?? undefined}
|
||||
onBlur={(e) => {
|
||||
onCacheKeyValuesBlurred(e.target.value);
|
||||
setChosenCacheKeyValue(e.target.value);
|
||||
}}
|
||||
/>
|
||||
{cacheKeyValuesPanelOpen ? (
|
||||
<ChevronUpIcon
|
||||
className="h-6 w-6 cursor-pointer"
|
||||
onClick={onCacheKeyValuesClick}
|
||||
/>
|
||||
) : (
|
||||
<ChevronDownIcon
|
||||
className="h-6 w-6 cursor-pointer"
|
||||
onClick={() => {
|
||||
dom.input.current?.focus();
|
||||
onCacheKeyValuesClick();
|
||||
if (numFiltered === 1) {
|
||||
const first = cacheKeyValues?.values?.[0];
|
||||
if (first) {
|
||||
setChosenCacheKeyValue(first);
|
||||
onCacheKeyValueAccept(first);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setChosenCacheKeyValue(chosenCacheKeyValue);
|
||||
onCacheKeyValueAccept(chosenCacheKeyValue);
|
||||
}
|
||||
onCacheKeyValuesKeydown(e);
|
||||
}}
|
||||
placeholder="Code Key Value"
|
||||
value={chosenCacheKeyValue ?? undefined}
|
||||
onBlur={(e) => {
|
||||
onCacheKeyValuesBlurred(e.target.value);
|
||||
setChosenCacheKeyValue(e.target.value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{cacheKeyValuesPanelOpen ? (
|
||||
<ChevronUpIcon
|
||||
className="h-6 w-6 cursor-pointer"
|
||||
onClick={onCacheKeyValuesClick}
|
||||
/>
|
||||
) : (
|
||||
<ChevronDownIcon
|
||||
className="h-6 w-6 cursor-pointer"
|
||||
onClick={() => {
|
||||
dom.input.current?.focus();
|
||||
onCacheKeyValuesClick();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{isGlobalWorkflow ? (
|
||||
<Button
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { AxiosError } from "axios";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useEffect, useRef, useState, MutableRefObject } from "react";
|
||||
import { nanoid } from "nanoid";
|
||||
import {
|
||||
ChevronRightIcon,
|
||||
ChevronLeftIcon,
|
||||
GlobeIcon,
|
||||
ReloadIcon,
|
||||
CheckIcon,
|
||||
CopyIcon,
|
||||
} from "@radix-ui/react-icons";
|
||||
import { useParams, useSearchParams } from "react-router-dom";
|
||||
import { useEdgesState, useNodesState, Edge } from "@xyflow/react";
|
||||
@@ -42,6 +44,7 @@ import {
|
||||
import { toast } from "@/components/ui/use-toast";
|
||||
import { BrowserStream } from "@/components/BrowserStream";
|
||||
import { statusIsFinalized } from "@/routes/tasks/types.ts";
|
||||
import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
|
||||
import { DebuggerRun } from "@/routes/workflows/debugger/DebuggerRun";
|
||||
import { DebuggerRunMinimal } from "@/routes/workflows/debugger/DebuggerRunMinimal";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
@@ -50,6 +53,7 @@ import {
|
||||
useWorkflowHasChangesStore,
|
||||
useWorkflowSave,
|
||||
} from "@/store/WorkflowHasChangesStore";
|
||||
import { getCode, getOrderedBlockLabels } from "@/routes/workflows/utils";
|
||||
|
||||
import { cn } from "@/util/utils";
|
||||
|
||||
@@ -69,7 +73,6 @@ import {
|
||||
startNode,
|
||||
} from "./workflowEditorUtils";
|
||||
import { constructCacheKeyValue } from "./utils";
|
||||
|
||||
import "./workspace-styles.css";
|
||||
|
||||
const Constants = {
|
||||
@@ -90,6 +93,26 @@ export type AddNodeProps = {
|
||||
connectingEdgeType: string;
|
||||
};
|
||||
|
||||
interface Dom {
|
||||
splitLeft: MutableRefObject<HTMLInputElement | null>;
|
||||
}
|
||||
|
||||
function CopyText({ text }: { text: string }) {
|
||||
const [wasCopied, setWasCopied] = useState(false);
|
||||
|
||||
function handleCopy(code: string) {
|
||||
navigator.clipboard.writeText(code);
|
||||
setWasCopied(true);
|
||||
setTimeout(() => setWasCopied(false), 2000);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button size="icon" variant="link" onClick={() => handleCopy(text)}>
|
||||
{wasCopied ? <CheckIcon /> : <CopyIcon />}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
function Workspace({
|
||||
initialNodes,
|
||||
initialEdges,
|
||||
@@ -146,6 +169,15 @@ function Workspace({
|
||||
: constructCacheKeyValue({ codeKey: cacheKey, workflow }),
|
||||
);
|
||||
|
||||
const [showAllCode, setShowAllCode] = useState(false);
|
||||
const [leftSideLayoutMode, setLeftSideLayoutMode] = useState<
|
||||
"single" | "side-by-side"
|
||||
>("single");
|
||||
|
||||
const dom: Dom = {
|
||||
splitLeft: useRef<HTMLInputElement>(null),
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") {
|
||||
@@ -400,6 +432,32 @@ function Workspace({
|
||||
};
|
||||
}, [debugSession, shouldFetchDebugSession, workflowPermanentId, queryClient]);
|
||||
|
||||
useEffect(() => {
|
||||
const splitLeft = dom.splitLeft.current;
|
||||
|
||||
if (!splitLeft) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parent = splitLeft.parentElement;
|
||||
|
||||
if (!parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = new ResizeObserver(() => {
|
||||
setLeftSideLayoutMode(
|
||||
parent.offsetWidth < 1100 ? "single" : "side-by-side",
|
||||
);
|
||||
});
|
||||
|
||||
observer.observe(parent);
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [dom.splitLeft]);
|
||||
|
||||
function doLayout(nodes: Array<AppNode>, edges: Array<Edge>) {
|
||||
const layoutedElements = layout(nodes, edges);
|
||||
setNodes(layoutedElements.nodes);
|
||||
@@ -508,6 +566,9 @@ function Workspace({
|
||||
}
|
||||
}
|
||||
|
||||
const orderedBlockLabels = getOrderedBlockLabels(workflow);
|
||||
const code = getCode(orderedBlockLabels, blockScripts).join("");
|
||||
|
||||
return (
|
||||
<div className="relative h-full w-full">
|
||||
{/* cycle browser dialog */}
|
||||
@@ -631,6 +692,7 @@ function Workspace({
|
||||
workflowPanelState.active &&
|
||||
workflowPanelState.content === "parameters"
|
||||
}
|
||||
showAllCode={showAllCode}
|
||||
workflow={workflow}
|
||||
onCacheKeyValueAccept={(v) => {
|
||||
setCacheKeyValue(v ?? "");
|
||||
@@ -696,6 +758,9 @@ function Workspace({
|
||||
onRun={() => {
|
||||
closeWorkflowPanel();
|
||||
}}
|
||||
onShowAllCodeClick={() => {
|
||||
setShowAllCode(!showAllCode);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -806,7 +871,7 @@ function Workspace({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* infinite canvas, browser, and timeline when in debug mode */}
|
||||
{/* code, infinite canvas, browser, and timeline when in debug mode */}
|
||||
{showBrowser && (
|
||||
<div className="relative flex h-full w-full overflow-hidden overflow-x-hidden">
|
||||
<Splitter
|
||||
@@ -816,21 +881,64 @@ function Workspace({
|
||||
split={{ left: workflowWidth }}
|
||||
onResize={() => setContainerResizeTrigger((prev) => prev + 1)}
|
||||
>
|
||||
{/* infinite canvas */}
|
||||
<div className="skyvern-split-left h-full w-full">
|
||||
<FlowRenderer
|
||||
hideBackground={true}
|
||||
hideControls={true}
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
setNodes={setNodes}
|
||||
setEdges={setEdges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
initialTitle={initialTitle}
|
||||
workflow={workflow}
|
||||
onContainerResize={containerResizeTrigger}
|
||||
/>
|
||||
{/* code and infinite canvas */}
|
||||
<div className="relative h-full w-full">
|
||||
<div
|
||||
className={cn(
|
||||
"skyvern-split-left flex h-full w-[200%] translate-x-[-50%] transition-none duration-300",
|
||||
{
|
||||
"w-[100%] translate-x-0":
|
||||
leftSideLayoutMode === "side-by-side",
|
||||
},
|
||||
{
|
||||
"translate-x-0": showAllCode,
|
||||
},
|
||||
)}
|
||||
ref={dom.splitLeft}
|
||||
>
|
||||
{/* code */}
|
||||
<div
|
||||
className={cn("h-full w-[50%]", {
|
||||
"w-[0%]":
|
||||
leftSideLayoutMode === "side-by-side" && !showAllCode,
|
||||
})}
|
||||
>
|
||||
<div className="relative mt-[8.5rem] w-full p-6 pr-5 pt-0">
|
||||
<div className="absolute right-[1.25rem] top-0 z-20">
|
||||
<CopyText text={code} />
|
||||
</div>
|
||||
<CodeEditor
|
||||
className="w-full overflow-y-scroll"
|
||||
language="python"
|
||||
value={code}
|
||||
lineWrap={false}
|
||||
readOnly
|
||||
fontSize={10}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* infinite canvas */}
|
||||
<div
|
||||
className={cn("h-full w-[50%]", {
|
||||
"w-[100%]":
|
||||
leftSideLayoutMode === "side-by-side" && !showAllCode,
|
||||
})}
|
||||
>
|
||||
<FlowRenderer
|
||||
hideBackground={true}
|
||||
hideControls={true}
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
setNodes={setNodes}
|
||||
setEdges={setEdges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
initialTitle={initialTitle}
|
||||
workflow={workflow}
|
||||
onContainerResize={containerResizeTrigger}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* browser & timeline */}
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import { getClient } from "@/api/AxiosClient";
|
||||
import { Handle, Node, NodeProps, Position, useReactFlow } from "@xyflow/react";
|
||||
import type { StartNode } from "./types";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
@@ -42,7 +35,6 @@ import { useRerender } from "@/hooks/useRerender";
|
||||
import { useBlockScriptStore } from "@/store/BlockScriptStore";
|
||||
import { BlockCodeEditor } from "@/routes/workflows/components/BlockCodeEditor";
|
||||
import { cn } from "@/util/utils";
|
||||
import { LightningBoltIcon } from "@radix-ui/react-icons";
|
||||
|
||||
function StartNode({ id, data }: NodeProps<StartNode>) {
|
||||
const workflowSettingsStore = useWorkflowSettingsStore();
|
||||
@@ -115,19 +107,20 @@ function StartNode({ id, data }: NodeProps<StartNode>) {
|
||||
);
|
||||
}
|
||||
|
||||
function showAllScripts() {
|
||||
for (const node of reactFlowInstance.getNodes()) {
|
||||
const label = node.data.label;
|
||||
// NOTE(jdo): keeping for reference; we seem to revert stuff all the time
|
||||
// function showAllScripts() {
|
||||
// for (const node of reactFlowInstance.getNodes()) {
|
||||
// const label = node.data.label;
|
||||
|
||||
label &&
|
||||
nodeIsFlippable(node) &&
|
||||
typeof label === "string" &&
|
||||
toggleScriptForNodeCallback({
|
||||
label,
|
||||
show: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
// label &&
|
||||
// nodeIsFlippable(node) &&
|
||||
// typeof label === "string" &&
|
||||
// toggleScriptForNodeCallback({
|
||||
// label,
|
||||
// show: true,
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
function hideAllScripts() {
|
||||
for (const node of reactFlowInstance.getNodes()) {
|
||||
@@ -160,20 +153,6 @@ function StartNode({ id, data }: NodeProps<StartNode>) {
|
||||
)}
|
||||
>
|
||||
<div className="relative">
|
||||
<div className="absolute right-[-0.5rem] top-[-0.25rem]">
|
||||
<div>
|
||||
<Button variant="link" size="icon" onClick={showAllScripts}>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<LightningBoltIcon className="h-4 w-4 text-[gold]" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Show all code</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<header className="mb-6 mt-2">Start</header>
|
||||
<Separator />
|
||||
<Accordion
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useLocation } from "react-router-dom";
|
||||
import type { WorkflowParameter } from "./types/workflowTypes";
|
||||
import { WorkflowApiResponse } from "@/routes/workflows/types/workflowTypes";
|
||||
|
||||
type Location = ReturnType<typeof useLocation>;
|
||||
|
||||
@@ -77,3 +78,49 @@ export const formatDuration = (duration: Duration): string => {
|
||||
return `${duration.second}s`;
|
||||
}
|
||||
};
|
||||
|
||||
export const getOrderedBlockLabels = (workflow?: WorkflowApiResponse) => {
|
||||
if (!workflow) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const blockLabels = workflow.workflow_definition.blocks.map(
|
||||
(block) => block.label,
|
||||
);
|
||||
|
||||
return blockLabels;
|
||||
};
|
||||
|
||||
const getCommentForBlockWithoutCode = (blockLabel: string) => {
|
||||
return `
|
||||
# block '${blockLabel}' code goes here
|
||||
`;
|
||||
};
|
||||
|
||||
export const getCode = (
|
||||
orderedBlockLabels: string[],
|
||||
blockScripts?: {
|
||||
[blockName: string]: string;
|
||||
},
|
||||
): string[] => {
|
||||
const blockCode: string[] = [];
|
||||
const startBlockCode = blockScripts?.__start_block__;
|
||||
|
||||
if (startBlockCode) {
|
||||
blockCode.push(startBlockCode);
|
||||
}
|
||||
|
||||
for (const blockLabel of orderedBlockLabels) {
|
||||
const code = blockScripts?.[blockLabel];
|
||||
|
||||
if (!code) {
|
||||
blockCode.push(getCommentForBlockWithoutCode(blockLabel));
|
||||
continue;
|
||||
}
|
||||
|
||||
blockCode.push(`${code}
|
||||
`);
|
||||
}
|
||||
|
||||
return blockCode;
|
||||
};
|
||||
|
||||
@@ -18,58 +18,12 @@ import { useCacheKeyValuesQuery } from "@/routes/workflows/hooks/useCacheKeyValu
|
||||
import { useWorkflowQuery } from "@/routes/workflows/hooks/useWorkflowQuery";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
import { constructCacheKeyValue } from "@/routes/workflows/editor/utils";
|
||||
import { WorkflowApiResponse } from "@/routes/workflows/types/workflowTypes";
|
||||
import { getCode, getOrderedBlockLabels } from "@/routes/workflows/utils";
|
||||
|
||||
interface Props {
|
||||
showCacheKeyValueSelector?: boolean;
|
||||
}
|
||||
|
||||
const getOrderedBlockLabels = (workflow?: WorkflowApiResponse) => {
|
||||
if (!workflow) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const blockLabels = workflow.workflow_definition.blocks.map(
|
||||
(block) => block.label,
|
||||
);
|
||||
|
||||
return blockLabels;
|
||||
};
|
||||
|
||||
const getCommentForBlockWithoutCode = (blockLabel: string) => {
|
||||
return `
|
||||
# If the "Generate Code" option is turned on for this workflow when it runs, AI will execute block '${blockLabel}', and generate code for it.
|
||||
`;
|
||||
};
|
||||
|
||||
const getCode = (
|
||||
orderedBlockLabels: string[],
|
||||
blockScripts?: {
|
||||
[blockName: string]: string;
|
||||
},
|
||||
): string[] => {
|
||||
const blockCode: string[] = [];
|
||||
const startBlockCode = blockScripts?.__start_block__;
|
||||
|
||||
if (startBlockCode) {
|
||||
blockCode.push(startBlockCode);
|
||||
}
|
||||
|
||||
for (const blockLabel of orderedBlockLabels) {
|
||||
const code = blockScripts?.[blockLabel];
|
||||
|
||||
if (!code) {
|
||||
blockCode.push(getCommentForBlockWithoutCode(blockLabel));
|
||||
continue;
|
||||
}
|
||||
|
||||
blockCode.push(`${code}
|
||||
`);
|
||||
}
|
||||
|
||||
return blockCode;
|
||||
};
|
||||
|
||||
function WorkflowRunCode(props?: Props) {
|
||||
const showCacheKeyValueSelector = props?.showCacheKeyValueSelector ?? false;
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
Reference in New Issue
Block a user