Skip to content

Commit 41b341f

Browse files
authored
Jon/debugger layout (#3340)
1 parent aee181c commit 41b341f

16 files changed

+604
-249
lines changed

skyvern-frontend/src/components/BrowserStream.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ function BrowserStream({
328328

329329
return (
330330
<div
331-
className={cn("browser-stream", {
331+
className={cn("browser-stream flex items-center justify-center", {
332332
"user-is-controlling": theUserIsControlling,
333333
})}
334334
ref={setCanvasContainerRef}
@@ -369,7 +369,7 @@ function BrowserStream({
369369
</div>
370370
)}
371371
{!isVncConnected && (
372-
<div className="flex h-full w-full flex-col items-center justify-center gap-2 pb-2 pt-4 text-sm text-slate-400">
372+
<div className="flex aspect-video w-full flex-col items-center justify-center gap-2 rounded-md border border-slate-800 text-sm text-slate-400">
373373
<RotateThrough interval={7 * 1000}>
374374
<span>Hm, working on the connection...</span>
375375
<span>Hang tight, we're almost there...</span>

skyvern-frontend/src/components/FloatingWindow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -756,4 +756,4 @@ function FloatingWindow({
756756
);
757757
}
758758

759-
export { FloatingWindow };
759+
export { BreakoutButton, FloatingWindow, PowerButton, ReloadButton };
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import {
2+
Tooltip,
3+
TooltipContent,
4+
TooltipProvider,
5+
TooltipTrigger,
6+
} from "@/components/ui/tooltip";
7+
8+
function Tip({
9+
asChild = true,
10+
children,
11+
content,
12+
}: {
13+
asChild?: boolean;
14+
children: React.ReactNode;
15+
content: string | null;
16+
}) {
17+
if (content === null) {
18+
return children;
19+
}
20+
21+
return (
22+
<TooltipProvider>
23+
<Tooltip delayDuration={300}>
24+
<TooltipTrigger asChild={asChild}>{children}</TooltipTrigger>
25+
<TooltipContent className="max-w-[250px]">{content}</TooltipContent>
26+
</Tooltip>
27+
</TooltipProvider>
28+
);
29+
}
30+
31+
export { Tip };

skyvern-frontend/src/components/browser-stream.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
width: 100%;
44
height: 100%;
55
min-height: 0;
6-
padding: 0.5rem;
76
overflow: visible;
87

98
transition: padding 0.2s ease-in-out;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { ActionType, ReadableActionTypes } from "@/api/types";
2+
import {
3+
CheckCircledIcon,
4+
CursorArrowIcon,
5+
InputIcon,
6+
QuestionMarkIcon,
7+
} from "@radix-ui/react-icons";
8+
import { Tip } from "@/components/Tip";
9+
10+
type Props = {
11+
actionType: ActionType;
12+
};
13+
14+
const icons: Partial<Record<ActionType, React.ReactNode>> = {
15+
click: <CursorArrowIcon className="h-4 w-4" />,
16+
complete: <CheckCircledIcon className="h-4 w-4" />,
17+
input_text: <InputIcon className="h-4 w-4" />,
18+
};
19+
20+
function ActionTypePillMinimal({ actionType }: Props) {
21+
const icon = icons[actionType] ?? <QuestionMarkIcon className="h-4 w-4" />;
22+
23+
if (!icon) {
24+
return null;
25+
}
26+
27+
return (
28+
<Tip content={ReadableActionTypes[actionType]}>
29+
<div className="flex w-full items-center justify-center gap-2">
30+
{icon}
31+
</div>
32+
</Tip>
33+
);
34+
}
35+
36+
export { ActionTypePillMinimal };

skyvern-frontend/src/routes/workflows/debugger/DebuggerRun.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ function DebuggerRun() {
66

77
const workflowFailureReason = workflowRun?.failure_reason ? (
88
<div
9-
className="align-self-start h-[8rem] w-full overflow-y-auto rounded-md border border-red-600 p-4"
9+
className="align-self-start h-[8rem] min-h-[8rem] w-full overflow-y-auto rounded-md border border-red-600 p-4"
1010
style={{
1111
backgroundColor: "rgba(220, 38, 38, 0.10)",
1212
width: "calc(100% - 2rem)",
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { CrossCircledIcon } from "@radix-ui/react-icons";
2+
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
3+
import { DebuggerRunTimelineMinimal } from "./DebuggerRunTimelineMinimal";
4+
import { Tip } from "@/components/Tip";
5+
6+
function DebuggerRunMinimal() {
7+
const { data: workflowRun } = useWorkflowRunQuery();
8+
9+
const workflowFailureReason = workflowRun?.failure_reason ? (
10+
<Tip content={workflowRun.failure_reason}>
11+
<div className="items-center-justify-center flex text-destructive">
12+
<CrossCircledIcon />
13+
</div>
14+
</Tip>
15+
) : null;
16+
17+
return (
18+
<div className="relative flex h-full w-full flex-col items-center justify-start gap-4 overflow-hidden overflow-y-auto pb-12 pt-4">
19+
{workflowFailureReason}
20+
<div className="flex h-full w-full items-start justify-center">
21+
<DebuggerRunTimelineMinimal />
22+
</div>
23+
</div>
24+
);
25+
}
26+
27+
export { DebuggerRunMinimal };

skyvern-frontend/src/routes/workflows/debugger/DebuggerRunTimeline.tsx

Lines changed: 2 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { useParams } from "react-router-dom";
21
import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area";
32
import { Skeleton } from "@/components/ui/skeleton";
43
import { statusIsFinalized } from "@/routes/tasks/types";
@@ -18,7 +17,6 @@ import {
1817
WorkflowRunOverviewActiveElement,
1918
} from "@/routes/workflows/workflowRun/WorkflowRunOverview";
2019
import { WorkflowRunTimelineBlockItem } from "@/routes/workflows/workflowRun/WorkflowRunTimelineBlockItem";
21-
import { useWorkflowQuery } from "../hooks/useWorkflowQuery";
2220

2321
type Props = {
2422
activeItem: WorkflowRunOverviewActiveElement;
@@ -27,28 +25,12 @@ type Props = {
2725
onBlockItemSelected: (item: WorkflowRunBlock) => void;
2826
};
2927

30-
function Step({ n, children }: { n: number; children: React.ReactNode }) {
31-
return (
32-
<div className="relative flex items-center justify-center gap-2 rounded-lg border border-slate-600 p-4">
33-
<div className="absolute right-[-1.22rem] top-[-1.22rem] flex h-[3rem] w-[3rem] items-center justify-center rounded-full border border-slate-600 bg-slate-elevation3 px-4 py-3 text-xl font-bold">
34-
{n}
35-
</div>
36-
<div className="absolute right-[-1.25rem] top-[-1.25rem] flex h-[3rem] w-[3rem] items-center justify-center rounded-full bg-slate-elevation3 px-4 py-3 text-xl font-bold text-slate-100">
37-
{n}
38-
</div>
39-
<div className="flex-1">{children}</div>
40-
</div>
41-
);
42-
}
43-
4428
function DebuggerRunTimeline({
4529
activeItem,
4630
onObserverThoughtCardSelected,
4731
onActionItemSelected,
4832
onBlockItemSelected,
4933
}: Props) {
50-
const { workflowPermanentId } = useParams();
51-
const { data: workflow } = useWorkflowQuery({ workflowPermanentId }!);
5234
const { data: workflowRun, isLoading: workflowRunIsLoading } =
5335
useWorkflowRunQuery();
5436

@@ -59,67 +41,8 @@ function DebuggerRunTimeline({
5941
return <Skeleton className="h-full w-full" />;
6042
}
6143

62-
const blocks = workflow?.workflow_definition.blocks ?? [];
63-
64-
const getStarted =
65-
blocks.length === 0 ? (
66-
<div>
67-
Hi! 👋 To get started, add a block to your workflow. You can do that by
68-
clicking the round plus button beneath the Start block, on the left
69-
</div>
70-
) : null;
71-
72-
const runABlock = (
73-
<div>
74-
To run a single block, click the play button on that block. Skyvern will
75-
run the block in the browser, live!
76-
</div>
77-
);
78-
79-
const adjustBrowser = (
80-
<div>
81-
Need to adjust the browser to test your block again? You can click around
82-
in the browser to bring Skyvern to any page (manually!)
83-
</div>
84-
);
85-
86-
const parameters = (
87-
<div>
88-
Want Skyvern to do different things based on your inputs? Use Parameters
89-
to specify them and reference them using <code>{"{{ }}"}</code> syntax!
90-
</div>
91-
);
92-
93-
const addBlocks = (
94-
<div>
95-
Not finished? Add a block to your workflow by clicking the round plus
96-
button before or after any other block.
97-
</div>
98-
);
99-
100-
const steps = [
101-
getStarted,
102-
runABlock,
103-
adjustBrowser,
104-
getStarted === null ? parameters : null,
105-
getStarted === null ? addBlocks : null,
106-
].filter((step) => step);
107-
10844
if (!workflowRun || !workflowRunTimeline) {
109-
return (
110-
<div className="flex h-full w-full flex-col items-center justify-center overflow-y-auto rounded-xl bg-[#020817] p-8 text-slate-300">
111-
<div className="flex h-full w-full flex-col items-center justify-around gap-4">
112-
<div className="text-center text-xl">
113-
Build & Debug Complex Browser Automations
114-
</div>
115-
{steps.map((step, index) => (
116-
<Step key={index} n={index + 1}>
117-
{step}
118-
</Step>
119-
))}
120-
</div>
121-
</div>
122-
);
45+
return null;
12346
}
12447

12548
const workflowRunIsFinalized = statusIsFinalized(workflowRun);
@@ -132,7 +55,7 @@ function DebuggerRunTimeline({
13255
}, 0);
13356

13457
return (
135-
<div className="w-full min-w-0 space-y-4 rounded bg-slate-elevation1 p-4">
58+
<div className="w-full min-w-0 space-y-4 rounded p-4">
13659
<div className="grid w-full grid-cols-2 gap-2">
13760
<div className="flex items-center justify-center rounded bg-slate-elevation3 px-4 py-3 text-xs">
13861
Actions: {numberOfActions}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area";
2+
import { Skeleton } from "@/components/ui/skeleton";
3+
import { statusIsFinalized } from "@/routes/tasks/types";
4+
import { useWorkflowRunQuery } from "../hooks/useWorkflowRunQuery";
5+
import { useWorkflowRunTimelineQuery } from "../hooks/useWorkflowRunTimelineQuery";
6+
import { isBlockItem, isThoughtItem } from "../types/workflowRunTypes";
7+
import { ThoughtCardMinimal } from "@/routes/workflows/workflowRun/ThoughtCardMinimal";
8+
import { WorkflowRunTimelineBlockItemMinimal } from "@/routes/workflows/workflowRun/WorkflowRunTimelineBlockItemMinimal";
9+
10+
function DebuggerRunTimelineMinimal() {
11+
const { data: workflowRun, isLoading: workflowRunIsLoading } =
12+
useWorkflowRunQuery();
13+
14+
const { data: workflowRunTimeline, isLoading: workflowRunTimelineIsLoading } =
15+
useWorkflowRunTimelineQuery();
16+
17+
if (workflowRunIsLoading || workflowRunTimelineIsLoading) {
18+
return <Skeleton className="h-full w-full" />;
19+
}
20+
21+
if (!workflowRun || !workflowRunTimeline) {
22+
return null;
23+
}
24+
25+
const workflowRunIsFinalized = statusIsFinalized(workflowRun);
26+
27+
return (
28+
<div className="h-full w-full">
29+
{!workflowRunIsFinalized && workflowRunTimeline.length === 0 && (
30+
<Skeleton className="h-full w-full" />
31+
)}
32+
<ScrollArea className="h-full w-full">
33+
<ScrollAreaViewport className="h-full w-full">
34+
<div className="flex w-full flex-col items-center justify-center gap-4 pt-2">
35+
{workflowRunIsFinalized && workflowRunTimeline.length === 0 && (
36+
<div>-</div>
37+
)}
38+
{workflowRunTimeline?.map((timelineItem) => {
39+
if (isBlockItem(timelineItem)) {
40+
return (
41+
<WorkflowRunTimelineBlockItemMinimal
42+
key={timelineItem.block.workflow_run_block_id}
43+
subItems={timelineItem.children}
44+
block={timelineItem.block}
45+
/>
46+
);
47+
}
48+
if (isThoughtItem(timelineItem)) {
49+
return (
50+
<ThoughtCardMinimal
51+
key={timelineItem.thought.thought_id}
52+
thought={timelineItem.thought}
53+
/>
54+
);
55+
}
56+
})}
57+
</div>
58+
</ScrollAreaViewport>
59+
</ScrollArea>
60+
</div>
61+
);
62+
}
63+
64+
export { DebuggerRunTimelineMinimal };

skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ function convertToParametersYAML(
220220
}
221221

222222
type Props = {
223+
hideBackground?: boolean;
223224
nodes: Array<AppNode>;
224225
edges: Array<Edge>;
225226
setNodes: (nodes: Array<AppNode>) => void;
@@ -235,6 +236,7 @@ type Props = {
235236
};
236237

237238
function FlowRenderer({
239+
hideBackground = false,
238240
nodes,
239241
edges,
240242
setNodes,
@@ -648,7 +650,7 @@ function FlowRenderer({
648650
onEdgesChange={onEdgesChange}
649651
nodeTypes={nodeTypes}
650652
edgeTypes={edgeTypes}
651-
colorMode="dark"
653+
// colorMode="dark"
652654
fitView={true}
653655
fitViewOptions={{
654656
maxZoom: 1,
@@ -668,7 +670,9 @@ function FlowRenderer({
668670
zoomOnPinch={!flowIsConstrained}
669671
zoomOnScroll={!flowIsConstrained}
670672
>
671-
<Background variant={BackgroundVariant.Dots} bgColor="#020617" />
673+
{!hideBackground && (
674+
<Background variant={BackgroundVariant.Dots} bgColor="#020617" />
675+
)}
672676
<Controls position="bottom-left" />
673677
</ReactFlow>
674678
</BlockActionContext.Provider>

0 commit comments

Comments
 (0)