Jon/sky 5906 debugger show actions inputs outputs for a block run (#3202)

This commit is contained in:
Jonathan Dobson
2025-08-15 07:25:04 -04:00
committed by GitHub
parent 654cdb14e4
commit 40d104c193
33 changed files with 898 additions and 121 deletions

View File

@@ -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 => {

View File

@@ -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 };

View File

@@ -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 };

View File

@@ -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 };

View File

@@ -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`);

View File

@@ -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}
/>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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,
},
)}
>

View File

@@ -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