Jon/sky 5906 debugger show actions inputs outputs for a block run (#3202)
This commit is contained in:
@@ -226,7 +226,7 @@ type Props = {
|
||||
initialTitle: string;
|
||||
// initialParameters: ParametersState;
|
||||
workflow: WorkflowApiResponse;
|
||||
onDebuggableBlockCountChange: (count: number) => void;
|
||||
onDebuggableBlockCountChange?: (count: number) => void;
|
||||
onMouseDownCapture?: () => void;
|
||||
zIndex?: number;
|
||||
};
|
||||
@@ -305,7 +305,7 @@ function FlowRenderer({
|
||||
}
|
||||
}
|
||||
|
||||
onDebuggableBlockCountChange(debuggable.length);
|
||||
onDebuggableBlockCountChange?.(debuggable.length);
|
||||
}, [nodes, edges, onDebuggableBlockCountChange]);
|
||||
|
||||
const constructSaveData = useCallback((): WorkflowSaveData => {
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { ReactFlowProvider } from "@xyflow/react";
|
||||
|
||||
import { useWorkflowQuery } from "../hooks/useWorkflowQuery";
|
||||
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();
|
||||
const { data: workflow } = useWorkflowQuery({
|
||||
workflowPermanentId,
|
||||
});
|
||||
|
||||
const setParameters = useWorkflowParametersStore(
|
||||
(state) => state.setParameters,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (workflow) {
|
||||
const initialParameters = getInitialParameters(workflow);
|
||||
setParameters(initialParameters);
|
||||
}
|
||||
}, [workflow, setParameters]);
|
||||
|
||||
if (!workflow) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const settings: WorkflowSettings = {
|
||||
persistBrowserSession: workflow.persist_browser_session,
|
||||
proxyLocation: workflow.proxy_location,
|
||||
webhookCallbackUrl: workflow.webhook_callback_url,
|
||||
model: workflow.model,
|
||||
maxScreenshotScrolls: workflow.max_screenshot_scrolls,
|
||||
extraHttpHeaders: workflow.extra_http_headers
|
||||
? JSON.stringify(workflow.extra_http_headers)
|
||||
: null,
|
||||
useScriptCache: workflow.generate_script,
|
||||
scriptCacheKey: workflow.cache_key,
|
||||
};
|
||||
|
||||
const elements = getElements(
|
||||
workflow.workflow_definition.blocks,
|
||||
settings,
|
||||
true,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="relative flex h-screen w-full">
|
||||
<ReactFlowProvider>
|
||||
<Workspace
|
||||
initialEdges={elements.edges}
|
||||
initialNodes={elements.nodes}
|
||||
initialTitle={workflow.title}
|
||||
showBrowser={true}
|
||||
workflow={workflow}
|
||||
/>
|
||||
</ReactFlowProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { WorkflowDebugger };
|
||||
@@ -1,37 +0,0 @@
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
import { WorkflowDebuggerRunTimeline } from "./WorkflowDebuggerRunTimeline";
|
||||
|
||||
function WorkflowDebuggerRun() {
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
|
||||
const workflowFailureReason = workflowRun?.failure_reason ? (
|
||||
<div
|
||||
className="m-4 w-full rounded-md border border-red-600 p-4"
|
||||
style={{
|
||||
backgroundColor: "rgba(220, 38, 38, 0.10)",
|
||||
width: "calc(100% - 2rem)",
|
||||
}}
|
||||
>
|
||||
<div className="font-bold">Workflow Failure Reason</div>
|
||||
<div className="text-sm">{workflowRun.failure_reason}</div>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col items-center justify-start overflow-hidden overflow-y-auto">
|
||||
<div className="flex h-full w-full flex-col items-center justify-start gap-4 bg-[#0c1121]">
|
||||
{workflowFailureReason}
|
||||
<div className="h-full w-full">
|
||||
<WorkflowDebuggerRunTimeline
|
||||
activeItem="stream"
|
||||
onActionItemSelected={() => {}}
|
||||
onBlockItemSelected={() => {}}
|
||||
onObserverThoughtCardSelected={() => {}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { WorkflowDebuggerRun };
|
||||
@@ -1,129 +0,0 @@
|
||||
import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { statusIsFinalized } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "../hooks/useWorkflowRunQuery";
|
||||
import { useWorkflowRunTimelineQuery } from "../hooks/useWorkflowRunTimelineQuery";
|
||||
import {
|
||||
isBlockItem,
|
||||
isObserverThought,
|
||||
isTaskVariantBlockItem,
|
||||
isThoughtItem,
|
||||
ObserverThought,
|
||||
WorkflowRunBlock,
|
||||
} from "../types/workflowRunTypes";
|
||||
import { ThoughtCard } from "@/routes/workflows/workflowRun/ThoughtCard";
|
||||
import {
|
||||
ActionItem,
|
||||
WorkflowRunOverviewActiveElement,
|
||||
} from "@/routes/workflows/workflowRun/WorkflowRunOverview";
|
||||
import { WorkflowRunTimelineBlockItem } from "@/routes/workflows/workflowRun/WorkflowRunTimelineBlockItem";
|
||||
|
||||
type Props = {
|
||||
activeItem: WorkflowRunOverviewActiveElement;
|
||||
onObserverThoughtCardSelected: (item: ObserverThought) => void;
|
||||
onActionItemSelected: (item: ActionItem) => void;
|
||||
onBlockItemSelected: (item: WorkflowRunBlock) => void;
|
||||
};
|
||||
|
||||
function WorkflowDebuggerRunTimeline({
|
||||
activeItem,
|
||||
onObserverThoughtCardSelected,
|
||||
onActionItemSelected,
|
||||
onBlockItemSelected,
|
||||
}: Props) {
|
||||
const { data: workflowRun, isLoading: workflowRunIsLoading } =
|
||||
useWorkflowRunQuery();
|
||||
|
||||
const { data: workflowRunTimeline, isLoading: workflowRunTimelineIsLoading } =
|
||||
useWorkflowRunTimelineQuery();
|
||||
|
||||
if (workflowRunIsLoading || workflowRunTimelineIsLoading) {
|
||||
return <Skeleton className="h-full w-full" />;
|
||||
}
|
||||
|
||||
if (!workflowRun || !workflowRunTimeline) {
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center rounded-xl bg-[#020817] p-12">
|
||||
<div className="flex h-full w-full flex-col items-center justify-center gap-4">
|
||||
<div>
|
||||
Hi! 👋 We're experimenting with a new feature called debugger.
|
||||
</div>
|
||||
<div>
|
||||
This debugger allows you to see the state of your workflow in a live
|
||||
browser.
|
||||
</div>
|
||||
<div>
|
||||
You can run individual blocks, instead of the whole workflow.
|
||||
</div>
|
||||
<div>
|
||||
To get started, press the play button on a block in your workflow.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const workflowRunIsFinalized = statusIsFinalized(workflowRun);
|
||||
|
||||
const numberOfActions = workflowRunTimeline.reduce((total, current) => {
|
||||
if (isTaskVariantBlockItem(current)) {
|
||||
return total + current.block!.actions!.length;
|
||||
}
|
||||
return total + 0;
|
||||
}, 0);
|
||||
|
||||
return (
|
||||
<div className="w-full min-w-0 space-y-4 rounded bg-slate-elevation1 p-4">
|
||||
<div className="grid w-full grid-cols-2 gap-2">
|
||||
<div className="flex items-center justify-center rounded bg-slate-elevation3 px-4 py-3 text-xs">
|
||||
Actions: {numberOfActions}
|
||||
</div>
|
||||
<div className="flex items-center justify-center rounded bg-slate-elevation3 px-4 py-3 text-xs">
|
||||
Steps: {workflowRun.total_steps ?? 0}
|
||||
</div>
|
||||
</div>
|
||||
{!workflowRunIsFinalized && workflowRunTimeline.length === 0 && (
|
||||
<Skeleton className="h-full w-full" />
|
||||
)}
|
||||
<ScrollArea>
|
||||
<ScrollAreaViewport className="h-full w-full">
|
||||
<div className="w-full space-y-4">
|
||||
{workflowRunIsFinalized && workflowRunTimeline.length === 0 && (
|
||||
<div>Workflow timeline is empty</div>
|
||||
)}
|
||||
{workflowRunTimeline?.map((timelineItem) => {
|
||||
if (isBlockItem(timelineItem)) {
|
||||
return (
|
||||
<WorkflowRunTimelineBlockItem
|
||||
key={timelineItem.block.workflow_run_block_id}
|
||||
subItems={timelineItem.children}
|
||||
activeItem={activeItem}
|
||||
block={timelineItem.block}
|
||||
onActionClick={onActionItemSelected}
|
||||
onBlockItemClick={onBlockItemSelected}
|
||||
onThoughtCardClick={onObserverThoughtCardSelected}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (isThoughtItem(timelineItem)) {
|
||||
return (
|
||||
<ThoughtCard
|
||||
key={timelineItem.thought.thought_id}
|
||||
active={
|
||||
isObserverThought(activeItem) &&
|
||||
activeItem.thought_id === timelineItem.thought.thought_id
|
||||
}
|
||||
onClick={onObserverThoughtCardSelected}
|
||||
thought={timelineItem.thought}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</ScrollAreaViewport>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { WorkflowDebuggerRunTimeline };
|
||||
@@ -15,17 +15,18 @@ import {
|
||||
ReloadIcon,
|
||||
} from "@radix-ui/react-icons";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useGlobalWorkflowsQuery } from "../hooks/useGlobalWorkflowsQuery";
|
||||
import { EditableNodeTitle } from "./nodes/components/EditableNodeTitle";
|
||||
import { useCreateWorkflowMutation } from "../hooks/useCreateWorkflowMutation";
|
||||
import { convert } from "./workflowEditorUtils";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { useWorkflowTitleStore } from "@/store/WorkflowTitleStore";
|
||||
import { useWorkflowHasChangesStore } from "@/store/WorkflowHasChangesStore";
|
||||
import { cn } from "@/util/utils";
|
||||
|
||||
type Props = {
|
||||
debuggableBlockCount: number;
|
||||
parametersPanelOpen: boolean;
|
||||
onParametersClick: () => void;
|
||||
onSave: () => void;
|
||||
@@ -34,7 +35,6 @@ type Props = {
|
||||
};
|
||||
|
||||
function WorkflowHeader({
|
||||
debuggableBlockCount,
|
||||
parametersPanelOpen,
|
||||
onParametersClick,
|
||||
onSave,
|
||||
@@ -43,13 +43,14 @@ function WorkflowHeader({
|
||||
}: Props) {
|
||||
const { title, setTitle } = useWorkflowTitleStore();
|
||||
const workflowChangesStore = useWorkflowHasChangesStore();
|
||||
const { blockLabel: urlBlockLabel, workflowPermanentId } = useParams();
|
||||
const { workflowPermanentId } = useParams();
|
||||
const { data: globalWorkflows } = useGlobalWorkflowsQuery();
|
||||
const navigate = useNavigate();
|
||||
const createWorkflowMutation = useCreateWorkflowMutation();
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const debugStore = useDebugStore();
|
||||
const anyBlockIsPlaying =
|
||||
urlBlockLabel !== undefined && urlBlockLabel.length > 0;
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
|
||||
if (!globalWorkflows) {
|
||||
return null; // this should be loaded already by some other components
|
||||
@@ -105,7 +106,7 @@ function WorkflowHeader({
|
||||
<Button
|
||||
size="lg"
|
||||
variant={debugStore.isDebugMode ? "default" : "tertiary"}
|
||||
disabled={debuggableBlockCount === 0 || anyBlockIsPlaying}
|
||||
disabled={workflowRunIsRunningOrQueued}
|
||||
onClick={() => {
|
||||
if (debugStore.isDebugMode) {
|
||||
navigate(`/workflows/${workflowPermanentId}/edit`);
|
||||
|
||||
@@ -27,12 +27,15 @@ import {
|
||||
DialogTitle,
|
||||
DialogClose,
|
||||
} from "@/components/ui/dialog";
|
||||
import { SwitchBar } from "@/components/SwitchBar";
|
||||
import { toast } from "@/components/ui/use-toast";
|
||||
import { BrowserStream } from "@/components/BrowserStream";
|
||||
import { FloatingWindow } from "@/components/FloatingWindow";
|
||||
import { statusIsFinalized } from "@/routes/tasks/types.ts";
|
||||
import { WorkflowDebuggerRun } from "@/routes/workflows/editor/WorkflowDebuggerRun";
|
||||
import { DebuggerRun } from "@/routes/workflows/debugger/DebuggerRun";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
import { DebuggerRunOutput } from "@/routes/workflows/debugger/DebuggerRunOutput";
|
||||
import { DebuggerPostRunParameters } from "@/routes/workflows/debugger/DebuggerPostRunParameters";
|
||||
import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { useWorkflowPanelStore } from "@/store/WorkflowPanelStore";
|
||||
import {
|
||||
@@ -80,11 +83,11 @@ function Workspace({
|
||||
showBrowser = false,
|
||||
workflow,
|
||||
}: Props) {
|
||||
const { blockLabel, workflowPermanentId } = useParams();
|
||||
const { blockLabel, workflowPermanentId, workflowRunId } = useParams();
|
||||
const [content, setContent] = useState("actions");
|
||||
const { workflowPanelState, setWorkflowPanelState, closeWorkflowPanel } =
|
||||
useWorkflowPanelStore();
|
||||
const debugStore = useDebugStore();
|
||||
const [debuggableBlockCount, setDebuggableBlockCount] = useState(0);
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||
const saveWorkflow = useWorkflowSave();
|
||||
@@ -384,7 +387,6 @@ function Workspace({
|
||||
}}
|
||||
>
|
||||
<WorkflowHeader
|
||||
debuggableBlockCount={debuggableBlockCount}
|
||||
saving={workflowChangesStore.saveIsPending}
|
||||
parametersPanelOpen={
|
||||
workflowPanelState.active &&
|
||||
@@ -468,9 +470,38 @@ function Workspace({
|
||||
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 className="pointer-events-none absolute right-0 top-0 flex h-full w-[400px] flex-col items-end justify-end bg-slate-900">
|
||||
<div className="pointer-events-auto relative flex h-full w-full flex-col items-start overflow-hidden rounded-xl border-2 border-slate-500">
|
||||
{workflowRunId && (
|
||||
<SwitchBar
|
||||
className="m-2 border-none"
|
||||
onChange={(value) => setContent(value)}
|
||||
value={content}
|
||||
options={[
|
||||
{
|
||||
label: "Actions",
|
||||
value: "actions",
|
||||
},
|
||||
{
|
||||
label: "Inputs",
|
||||
value: "inputs",
|
||||
},
|
||||
{
|
||||
label: "Outputs",
|
||||
value: "outputs",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<div className="h-full w-full overflow-hidden overflow-y-auto">
|
||||
{(!workflowRunId || content === "actions") && <DebuggerRun />}
|
||||
{workflowRunId && content === "inputs" && (
|
||||
<DebuggerPostRunParameters />
|
||||
)}
|
||||
{workflowRunId && content === "outputs" && (
|
||||
<DebuggerRunOutput />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -487,7 +518,6 @@ function Workspace({
|
||||
initialTitle={initialTitle}
|
||||
// initialParameters={initialParameters}
|
||||
workflow={workflow}
|
||||
onDebuggableBlockCountChange={(c) => setDebuggableBlockCount(c)}
|
||||
onMouseDownCapture={() => promote("infiniteCanvas")}
|
||||
zIndex={rankedItems.infiniteCanvas}
|
||||
/>
|
||||
|
||||
@@ -38,6 +38,8 @@ import { useBlockScriptStore } from "@/store/BlockScriptStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
const urlTooltip =
|
||||
"The URL Skyvern is navigating to. Leave this field blank to pick up from where the last block left off.";
|
||||
@@ -67,8 +69,13 @@ function ActionNode({ id, data, type }: NodeProps<ActionNode>) {
|
||||
});
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const debugStore = useDebugStore();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
|
||||
const nodes = useNodes<AppNode>();
|
||||
@@ -108,8 +115,9 @@ function ActionNode({ id, data, type }: NodeProps<ActionNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -8,6 +8,8 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function CodeBlockNode({ id, data }: NodeProps<CodeBlockNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -15,8 +17,13 @@ function CodeBlockNode({ id, data }: NodeProps<CodeBlockNode>) {
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const [inputs, setInputs] = useState({
|
||||
code: data.code,
|
||||
parameterKeys: data.parameterKeys,
|
||||
@@ -40,8 +47,9 @@ function CodeBlockNode({ id, data }: NodeProps<CodeBlockNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -8,14 +8,21 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function DownloadNode({ id, data }: NodeProps<DownloadNode>) {
|
||||
const { debuggable, editable, label } = data;
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -35,8 +42,9 @@ function DownloadNode({ id, data }: NodeProps<DownloadNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -38,6 +38,8 @@ import { useBlockScriptStore } from "@/store/BlockScriptStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function ExtractionNode({ id, data, type }: NodeProps<ExtractionNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -48,8 +50,13 @@ function ExtractionNode({ id, data, type }: NodeProps<ExtractionNode>) {
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const [inputs, setInputs] = useState({
|
||||
url: data.url,
|
||||
dataExtractionGoal: data.dataExtractionGoal,
|
||||
@@ -97,8 +104,9 @@ function ExtractionNode({ id, data, type }: NodeProps<ExtractionNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -38,6 +38,8 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
const urlTooltip =
|
||||
"The URL Skyvern is navigating to. Leave this field blank to pick up from where the last block left off.";
|
||||
@@ -54,8 +56,13 @@ function FileDownloadNode({ id, data }: NodeProps<FileDownloadNode>) {
|
||||
const script = blockScriptStore.scripts[label];
|
||||
const debugStore = useDebugStore();
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const [inputs, setInputs] = useState({
|
||||
url: data.url,
|
||||
@@ -105,8 +112,9 @@ function FileDownloadNode({ id, data }: NodeProps<FileDownloadNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -12,6 +12,8 @@ import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { WorkflowDataSchemaInputGroup } from "@/components/DataSchemaInputGroup/WorkflowDataSchemaInputGroup";
|
||||
import { dataSchemaExampleForFileExtraction } from "../types";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function FileParserNode({ id, data }: NodeProps<FileParserNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -19,8 +21,13 @@ function FileParserNode({ id, data }: NodeProps<FileParserNode>) {
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const [inputs, setInputs] = useState({
|
||||
fileUrl: data.fileUrl,
|
||||
jsonSchema: data.jsonSchema,
|
||||
@@ -54,8 +61,9 @@ function FileParserNode({ id, data }: NodeProps<FileParserNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function FileUploadNode({ id, data }: NodeProps<FileUploadNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -24,8 +26,13 @@ function FileUploadNode({ id, data }: NodeProps<FileUploadNode>) {
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
|
||||
const [inputs, setInputs] = useState({
|
||||
storageType: data.storageType,
|
||||
@@ -65,8 +72,9 @@ function FileUploadNode({ id, data }: NodeProps<FileUploadNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -39,6 +39,8 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function LoginNode({ id, data, type }: NodeProps<LoginNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -49,8 +51,13 @@ function LoginNode({ id, data, type }: NodeProps<LoginNode>) {
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const [inputs, setInputs] = useState({
|
||||
url: data.url,
|
||||
navigationGoal: data.navigationGoal,
|
||||
@@ -102,8 +109,9 @@ function LoginNode({ id, data, type }: NodeProps<LoginNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -20,6 +20,8 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function LoopNode({ id, data }: NodeProps<LoopNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -32,8 +34,13 @@ function LoopNode({ id, data }: NodeProps<LoopNode>) {
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const [inputs, setInputs] = useState({
|
||||
loopVariableReference: data.loopVariableReference,
|
||||
});
|
||||
@@ -94,8 +101,9 @@ function LoopNode({ id, data }: NodeProps<LoopNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -39,6 +39,8 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function NavigationNode({ id, data, type }: NodeProps<NavigationNode>) {
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
@@ -48,8 +50,13 @@ function NavigationNode({ id, data, type }: NodeProps<NavigationNode>) {
|
||||
const blockScriptStore = useBlockScriptStore();
|
||||
const { editable, debuggable, label } = data;
|
||||
const script = blockScriptStore.scripts[label];
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const [inputs, setInputs] = useState({
|
||||
allowDownloads: data.allowDownloads,
|
||||
@@ -107,8 +114,9 @@ function NavigationNode({ id, data, type }: NodeProps<NavigationNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -12,6 +12,8 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function PDFParserNode({ id, data }: NodeProps<PDFParserNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -19,8 +21,13 @@ function PDFParserNode({ id, data }: NodeProps<PDFParserNode>) {
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const [inputs, setInputs] = useState({
|
||||
fileUrl: data.fileUrl,
|
||||
jsonSchema: data.jsonSchema,
|
||||
@@ -54,8 +61,9 @@ function PDFParserNode({ id, data }: NodeProps<PDFParserNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -12,6 +12,8 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function SendEmailNode({ id, data }: NodeProps<SendEmailNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -19,8 +21,13 @@ function SendEmailNode({ id, data }: NodeProps<SendEmailNode>) {
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const [inputs, setInputs] = useState({
|
||||
recipients: data.recipients,
|
||||
subject: data.subject,
|
||||
@@ -56,8 +63,9 @@ function SendEmailNode({ id, data }: NodeProps<SendEmailNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -40,6 +40,8 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function TaskNode({ id, data, type }: NodeProps<TaskNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -50,8 +52,13 @@ function TaskNode({ id, data, type }: NodeProps<TaskNode>) {
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const nodes = useNodes<AppNode>();
|
||||
const edges = useEdges();
|
||||
const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id);
|
||||
@@ -108,8 +115,9 @@ function TaskNode({ id, data, type }: NodeProps<TaskNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -19,14 +19,21 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function Taskv2Node({ id, data, type }: NodeProps<Taskv2Node>) {
|
||||
const { debuggable, editable, label } = data;
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const { updateNodeData } = useReactFlow();
|
||||
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||
|
||||
@@ -65,8 +72,9 @@ function Taskv2Node({ id, data, type }: NodeProps<Taskv2Node>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -14,6 +14,8 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function TextPromptNode({ id, data }: NodeProps<TextPromptNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -21,8 +23,13 @@ function TextPromptNode({ id, data }: NodeProps<TextPromptNode>) {
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const [inputs, setInputs] = useState({
|
||||
prompt: data.prompt,
|
||||
jsonSchema: data.jsonSchema,
|
||||
@@ -57,8 +64,9 @@ function TextPromptNode({ id, data }: NodeProps<TextPromptNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -13,6 +13,8 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function URLNode({ id, data, type }: NodeProps<URLNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -23,8 +25,13 @@ function URLNode({ id, data, type }: NodeProps<URLNode>) {
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||
|
||||
const [inputs, setInputs] = useState({
|
||||
@@ -62,8 +69,9 @@ function URLNode({ id, data, type }: NodeProps<URLNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -8,14 +8,20 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
function UploadNode({ id, data }: NodeProps<UploadNode>) {
|
||||
const { debuggable, editable, label } = data;
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -35,8 +41,9 @@ function UploadNode({ id, data }: NodeProps<UploadNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -36,6 +36,8 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function ValidationNode({ id, data, type }: NodeProps<ValidationNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -46,8 +48,13 @@ function ValidationNode({ id, data, type }: NodeProps<ValidationNode>) {
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const [inputs, setInputs] = useState({
|
||||
completeCriterion: data.completeCriterion,
|
||||
terminateCriterion: data.terminateCriterion,
|
||||
@@ -91,8 +98,9 @@ function ValidationNode({ id, data, type }: NodeProps<ValidationNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -10,6 +10,8 @@ import { useDebugStore } from "@/store/useDebugStore";
|
||||
import { cn } from "@/util/utils";
|
||||
import { NodeHeader } from "../components/NodeHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { statusIsRunningOrQueued } from "@/routes/tasks/types";
|
||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
||||
|
||||
function WaitNode({ id, data, type }: NodeProps<WaitNode>) {
|
||||
const { updateNodeData } = useReactFlow();
|
||||
@@ -17,8 +19,13 @@ function WaitNode({ id, data, type }: NodeProps<WaitNode>) {
|
||||
const debugStore = useDebugStore();
|
||||
const elideFromDebugging = debugStore.isDebugMode && !debuggable;
|
||||
const { blockLabel: urlBlockLabel } = useParams();
|
||||
const thisBlockIsPlaying =
|
||||
const { data: workflowRun } = useWorkflowRunQuery();
|
||||
const workflowRunIsRunningOrQueued =
|
||||
workflowRun && statusIsRunningOrQueued(workflowRun);
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === label;
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued && thisBlockIsTargetted;
|
||||
const [inputs, setInputs] = useState({
|
||||
waitInSeconds: data.waitInSeconds,
|
||||
});
|
||||
@@ -51,8 +58,9 @@ function WaitNode({ id, data, type }: NodeProps<WaitNode>) {
|
||||
className={cn(
|
||||
"transform-origin-center w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4 transition-all",
|
||||
{
|
||||
"pointer-events-none bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsPlaying,
|
||||
"pointer-events-none": thisBlockIsPlaying,
|
||||
"bg-slate-950 outline outline-2 outline-slate-300":
|
||||
thisBlockIsTargetted,
|
||||
},
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -138,10 +138,6 @@ function NodeHeader({
|
||||
} = useParams();
|
||||
const debugStore = useDebugStore();
|
||||
const { closeWorkflowPanel } = useWorkflowPanelStore();
|
||||
const thisBlockIsPlaying =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === blockLabel;
|
||||
const anyBlockIsPlaying =
|
||||
urlBlockLabel !== undefined && urlBlockLabel.length > 0;
|
||||
const workflowSettingsStore = useWorkflowSettingsStore();
|
||||
const [label, setLabel] = useNodeLabelChangeHandler({
|
||||
id: nodeId,
|
||||
@@ -164,6 +160,21 @@ function NodeHeader({
|
||||
});
|
||||
const saveWorkflow = useWorkflowSave();
|
||||
|
||||
const thisBlockIsPlaying =
|
||||
workflowRunIsRunningOrQueued &&
|
||||
urlBlockLabel !== undefined &&
|
||||
urlBlockLabel === blockLabel;
|
||||
|
||||
const thisBlockIsTargetted =
|
||||
urlBlockLabel !== undefined && urlBlockLabel === blockLabel;
|
||||
|
||||
const timerDurationOverride =
|
||||
workflowRun && workflowRun.finished_at
|
||||
? new Date(workflowRun.finished_at).getTime() -
|
||||
new Date(workflowRun.created_at).getTime() +
|
||||
3500
|
||||
: null;
|
||||
|
||||
useEffect(() => {
|
||||
if (!workflowRun || !workflowPermanentId || !workflowRunId) {
|
||||
return;
|
||||
@@ -173,7 +184,7 @@ function NodeHeader({
|
||||
workflowRunId === workflowRun?.workflow_run_id &&
|
||||
statusIsFinalized(workflowRun)
|
||||
) {
|
||||
navigate(`/workflows/${workflowPermanentId}/debug`);
|
||||
// navigate(`/workflows/${workflowPermanentId}/debug`);
|
||||
|
||||
if (statusIsAFailureType(workflowRun)) {
|
||||
toast({
|
||||
@@ -340,10 +351,10 @@ function NodeHeader({
|
||||
|
||||
return (
|
||||
<>
|
||||
{thisBlockIsPlaying && (
|
||||
{thisBlockIsTargetted && (
|
||||
<div className="flex w-full animate-[auto-height_1s_ease-in-out_forwards] items-center justify-between overflow-hidden">
|
||||
<div className="pb-4">
|
||||
<Timer />
|
||||
<Timer override={timerDurationOverride ?? undefined} />
|
||||
</div>
|
||||
<div className="pb-4">{workflowRun?.status ?? "pending"}</div>
|
||||
</div>
|
||||
@@ -370,7 +381,7 @@ function NodeHeader({
|
||||
</div>
|
||||
</div>
|
||||
<div className="pointer-events-auto ml-auto flex items-center gap-2">
|
||||
{thisBlockIsPlaying && workflowRunIsRunningOrQueued && (
|
||||
{thisBlockIsPlaying && (
|
||||
<div className="ml-auto">
|
||||
<button className="rounded p-1 hover:bg-red-500 hover:text-black disabled:opacity-50">
|
||||
{cancelBlock.isPending ? (
|
||||
@@ -388,9 +399,9 @@ function NodeHeader({
|
||||
)}
|
||||
{debugStore.isDebugMode && isDebuggable && (
|
||||
<button
|
||||
disabled={anyBlockIsPlaying}
|
||||
disabled={workflowRunIsRunningOrQueued}
|
||||
className={cn("rounded p-1 disabled:opacity-50", {
|
||||
"hover:bg-muted": anyBlockIsPlaying,
|
||||
"hover:bg-muted": workflowRunIsRunningOrQueued,
|
||||
})}
|
||||
>
|
||||
{runBlock.isPending ? (
|
||||
@@ -399,7 +410,7 @@ function NodeHeader({
|
||||
<PlayIcon
|
||||
className={cn("size-6", {
|
||||
"fill-gray-500 text-gray-500":
|
||||
anyBlockIsPlaying || !workflowPermanentId,
|
||||
workflowRunIsRunningOrQueued || !workflowPermanentId,
|
||||
})}
|
||||
onClick={() => {
|
||||
handleOnPlay();
|
||||
@@ -412,7 +423,8 @@ function NodeHeader({
|
||||
<div>
|
||||
<div
|
||||
className={cn("rounded p-1 hover:bg-muted", {
|
||||
"pointer-events-none opacity-50": anyBlockIsPlaying,
|
||||
"pointer-events-none opacity-50":
|
||||
workflowRunIsRunningOrQueued,
|
||||
})}
|
||||
>
|
||||
<NodeActionMenu
|
||||
|
||||
Reference in New Issue
Block a user