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