Skip to content

Commit b750f88

Browse files
cristipufuclaude
andcommitted
feat: add runtime state phase to events panel and green glow for executing nodes
Pass phase (started/updated/completed/faulted) from UiPathRuntimeStateEvent through backend and WebSocket to the frontend. Events panel shows colored bullet icons per phase with grey labels. Graph nodes glow green when executing (phase=started), distinct from accent glow for breakpoints. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6d47002 commit b750f88

19 files changed

Lines changed: 200 additions & 133 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
[project]
22
name = "uipath-dev"
3-
version = "0.0.47"
3+
version = "0.0.48"
44
description = "UiPath Developer Console"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"
77
dependencies = [
8-
"uipath-runtime>=0.8.2, <0.9.0",
8+
"uipath-runtime>=0.8.6, <0.9.0",
99
"textual>=7.5.0, <8.0.0",
1010
"pyperclip>=1.11.0, <2.0.0",
1111
"fastapi>=0.128.8",

src/uipath/dev/models/data.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class StateData:
3939
run_id: str
4040
node_name: str
4141
qualified_node_name: str | None = None
42+
phase: str | None = None
4243
payload: dict[str, Any] | None = None
4344
timestamp: datetime = field(default_factory=datetime.now)
4445

src/uipath/dev/server/frontend/src/App.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export default function App() {
8989
detail.states.map((s) => ({
9090
node_name: s.node_name,
9191
qualified_node_name: s.qualified_node_name,
92+
phase: s.phase,
9293
timestamp: new Date(s.timestamp).getTime(),
9394
payload: s.payload,
9495
})),

src/uipath/dev/server/frontend/src/components/graph/GraphPanel.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -575,19 +575,21 @@ export default function GraphPanel({ entrypoint, runId, breakpointNode, breakpoi
575575
// 3) Mark nodes as active
576576
// - Running: targets of highlighted edges + __start__/__end__ when matched
577577
// - Paused: next_nodes get isActiveNode (about to execute)
578+
// - Executing: matchIds get isExecutingNode (currently running node, green glow)
578579
setNodes((nds) =>
579580
nds.map((n) => {
581+
const executing = !isPaused && matchIds.has(n.id);
580582
if (n.type === "startNode" || n.type === "endNode") {
581583
const active = activeTargetIds.has(n.id) || (!isPaused && matchIds.has(n.id));
582-
return active !== !!n.data?.isActiveNode
583-
? { ...n, data: { ...n.data, isActiveNode: active } }
584+
return active !== !!n.data?.isActiveNode || executing !== !!n.data?.isExecutingNode
585+
? { ...n, data: { ...n.data, isActiveNode: active, isExecutingNode: executing } }
584586
: n;
585587
}
586588
const active = isPaused
587589
? nextNodeIds.has(n.id)
588590
: activeTargetIds.has(n.id);
589-
return active !== !!n.data?.isActiveNode
590-
? { ...n, data: { ...n.data, isActiveNode: active } }
591+
return active !== !!n.data?.isActiveNode || executing !== !!n.data?.isExecutingNode
592+
? { ...n, data: { ...n.data, isActiveNode: active, isExecutingNode: executing } }
591593
: n;
592594
}),
593595
);
@@ -821,10 +823,14 @@ export default function GraphPanel({ entrypoint, runId, breakpointNode, breakpoi
821823
@keyframes edge-flow {
822824
to { stroke-dashoffset: -12; }
823825
}
824-
@keyframes node-pulse {
826+
@keyframes node-pulse-accent {
825827
0%, 100% { box-shadow: 0 0 4px var(--accent); }
826828
50% { box-shadow: 0 0 10px var(--accent); }
827829
}
830+
@keyframes node-pulse-green {
831+
0%, 100% { box-shadow: 0 0 4px var(--success); }
832+
50% { box-shadow: 0 0 10px var(--success); }
833+
}
828834
`}</style>
829835
<ReactFlow
830836
nodes={nodes}

src/uipath/dev/server/frontend/src/components/graph/nodes/DefaultNode.tsx

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,23 @@ export default function DefaultNode({ data }: NodeProps) {
77
const hasBreakpoint = data.hasBreakpoint as boolean | undefined;
88
const isPausedHere = data.isPausedHere as boolean | undefined;
99
const isActiveNode = data.isActiveNode as boolean | undefined;
10+
const isExecutingNode = data.isExecutingNode as boolean | undefined;
1011

1112
const borderColor = isPausedHere
1213
? "var(--accent)"
13-
: isActiveNode
14-
? "var(--accent)"
15-
: status === "completed"
14+
: isExecutingNode
1615
? "var(--success)"
17-
: status === "running"
18-
? "var(--warning)"
19-
: status === "failed"
20-
? "var(--error)"
21-
: "var(--node-border)";
16+
: isActiveNode
17+
? "var(--accent)"
18+
: status === "completed"
19+
? "var(--success)"
20+
: status === "running"
21+
? "var(--warning)"
22+
: status === "failed"
23+
? "var(--error)"
24+
: "var(--node-border)";
25+
26+
const glowColor = isExecutingNode ? "var(--success)" : "var(--accent)";
2227

2328
return (
2429
<div
@@ -28,8 +33,8 @@ export default function DefaultNode({ data }: NodeProps) {
2833
background: "var(--node-bg)",
2934
color: "var(--text-primary)",
3035
border: `2px solid ${borderColor}`,
31-
boxShadow: isPausedHere || isActiveNode ? "0 0 4px var(--accent)" : undefined,
32-
animation: isActiveNode && !isPausedHere ? "node-pulse 1.5s ease-in-out infinite" : undefined,
36+
boxShadow: isPausedHere || isActiveNode || isExecutingNode ? `0 0 4px ${glowColor}` : undefined,
37+
animation: (isActiveNode || isExecutingNode) && !isPausedHere ? `node-pulse-${isExecutingNode ? "green" : "accent"} 1.5s ease-in-out infinite` : undefined,
3338
}}
3439
title={label}
3540
>

src/uipath/dev/server/frontend/src/components/graph/nodes/EndNode.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,21 @@ export default function EndNode({ data }: NodeProps) {
1717
const hasBreakpoint = data.hasBreakpoint as boolean | undefined;
1818
const isPausedHere = data.isPausedHere as boolean | undefined;
1919
const isActiveNode = data.isActiveNode as boolean | undefined;
20+
const isExecutingNode = data.isExecutingNode as boolean | undefined;
2021

2122
const borderColor = isPausedHere
2223
? "var(--accent)"
23-
: isActiveNode
24-
? "var(--accent)"
25-
: status === "completed"
26-
? "var(--success)"
27-
: status === "failed"
28-
? "var(--error)"
29-
: "var(--node-border)";
24+
: isExecutingNode
25+
? "var(--success)"
26+
: isActiveNode
27+
? "var(--accent)"
28+
: status === "completed"
29+
? "var(--success)"
30+
: status === "failed"
31+
? "var(--error)"
32+
: "var(--node-border)";
33+
34+
const glowColor = isExecutingNode ? "var(--success)" : "var(--accent)";
3035

3136
return (
3237
<div
@@ -36,8 +41,8 @@ export default function EndNode({ data }: NodeProps) {
3641
background: "var(--node-bg)",
3742
color: "var(--text-primary)",
3843
border: `2px solid ${borderColor}`,
39-
boxShadow: isPausedHere || isActiveNode ? "0 0 4px var(--accent)" : undefined,
40-
animation: isActiveNode && !isPausedHere ? "node-pulse 1.5s ease-in-out infinite" : undefined,
44+
boxShadow: isPausedHere || isActiveNode || isExecutingNode ? `0 0 4px ${glowColor}` : undefined,
45+
animation: (isActiveNode || isExecutingNode) && !isPausedHere ? `node-pulse-${isExecutingNode ? "green" : "accent"} 1.5s ease-in-out infinite` : undefined,
4146
}}
4247
title={label}
4348
>

src/uipath/dev/server/frontend/src/components/graph/nodes/GroupNode.tsx

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,23 @@ export default function GroupNode({ data }: NodeProps) {
1717
const hasBreakpoint = data.hasBreakpoint as boolean | undefined;
1818
const isPausedHere = data.isPausedHere as boolean | undefined;
1919
const isActiveNode = data.isActiveNode as boolean | undefined;
20+
const isExecutingNode = data.isExecutingNode as boolean | undefined;
2021

21-
const borderColor = isPausedHere || isActiveNode
22+
const borderColor = isPausedHere
2223
? "var(--accent)"
23-
: status === "completed"
24+
: isExecutingNode
2425
? "var(--success)"
25-
: status === "running"
26-
? "var(--warning)"
27-
: status === "failed"
28-
? "var(--error)"
29-
: "var(--bg-tertiary)";
26+
: isActiveNode
27+
? "var(--accent)"
28+
: status === "completed"
29+
? "var(--success)"
30+
: status === "running"
31+
? "var(--warning)"
32+
: status === "failed"
33+
? "var(--error)"
34+
: "var(--bg-tertiary)";
35+
36+
const glowColor = isExecutingNode ? "var(--success)" : "var(--accent)";
3037

3138
return (
3239
<div
@@ -35,10 +42,10 @@ export default function GroupNode({ data }: NodeProps) {
3542
width: "100%",
3643
height: "100%",
3744
background: "var(--bg-secondary)",
38-
border: `1.5px ${isPausedHere || isActiveNode ? "solid" : "dashed"} ${borderColor}`,
45+
border: `1.5px ${isPausedHere || isActiveNode || isExecutingNode ? "solid" : "dashed"} ${borderColor}`,
3946
borderRadius: 8,
40-
boxShadow: isPausedHere || isActiveNode ? "0 0 4px var(--accent)" : undefined,
41-
animation: isActiveNode && !isPausedHere ? "node-pulse 1.5s ease-in-out infinite" : undefined,
47+
boxShadow: isPausedHere || isActiveNode || isExecutingNode ? `0 0 4px ${glowColor}` : undefined,
48+
animation: (isActiveNode || isExecutingNode) && !isPausedHere ? `node-pulse-${isExecutingNode ? "green" : "accent"} 1.5s ease-in-out infinite` : undefined,
4249
}}
4350
>
4451
{hasBreakpoint && (

src/uipath/dev/server/frontend/src/components/graph/nodes/ModelNode.tsx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,23 @@ export default function ModelNode({ data }: NodeProps) {
1818
const hasBreakpoint = data.hasBreakpoint as boolean | undefined;
1919
const isPausedHere = data.isPausedHere as boolean | undefined;
2020
const isActiveNode = data.isActiveNode as boolean | undefined;
21+
const isExecutingNode = data.isExecutingNode as boolean | undefined;
2122

2223
const borderColor = isPausedHere
2324
? "var(--accent)"
24-
: isActiveNode
25-
? "var(--accent)"
26-
: status === "completed"
27-
? "var(--success)"
28-
: status === "running"
29-
? "var(--warning)"
30-
: status === "failed"
31-
? "var(--error)"
32-
: "var(--node-border)";
25+
: isExecutingNode
26+
? "var(--success)"
27+
: isActiveNode
28+
? "var(--accent)"
29+
: status === "completed"
30+
? "var(--success)"
31+
: status === "running"
32+
? "var(--warning)"
33+
: status === "failed"
34+
? "var(--error)"
35+
: "var(--node-border)";
36+
37+
const glowColor = isExecutingNode ? "var(--success)" : "var(--accent)";
3338

3439
return (
3540
<div
@@ -39,8 +44,8 @@ export default function ModelNode({ data }: NodeProps) {
3944
background: "var(--node-bg)",
4045
color: "var(--text-primary)",
4146
border: `2px solid ${borderColor}`,
42-
boxShadow: isPausedHere || isActiveNode ? "0 0 4px var(--accent)" : undefined,
43-
animation: isActiveNode && !isPausedHere ? "node-pulse 1.5s ease-in-out infinite" : undefined,
47+
boxShadow: isPausedHere || isActiveNode || isExecutingNode ? `0 0 4px ${glowColor}` : undefined,
48+
animation: (isActiveNode || isExecutingNode) && !isPausedHere ? `node-pulse-${isExecutingNode ? "green" : "accent"} 1.5s ease-in-out infinite` : undefined,
4449
}}
4550
title={modelName ? `${label}\n${modelName}` : label}
4651
>

src/uipath/dev/server/frontend/src/components/graph/nodes/StartNode.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,21 @@ export default function StartNode({ data }: NodeProps) {
1717
const hasBreakpoint = data.hasBreakpoint as boolean | undefined;
1818
const isPausedHere = data.isPausedHere as boolean | undefined;
1919
const isActiveNode = data.isActiveNode as boolean | undefined;
20+
const isExecutingNode = data.isExecutingNode as boolean | undefined;
2021

2122
const borderColor = isPausedHere
2223
? "var(--accent)"
23-
: isActiveNode
24-
? "var(--accent)"
25-
: status === "completed"
26-
? "var(--success)"
27-
: status === "running"
28-
? "var(--warning)"
29-
: "var(--node-border)";
24+
: isExecutingNode
25+
? "var(--success)"
26+
: isActiveNode
27+
? "var(--accent)"
28+
: status === "completed"
29+
? "var(--success)"
30+
: status === "running"
31+
? "var(--warning)"
32+
: "var(--node-border)";
33+
34+
const glowColor = isExecutingNode ? "var(--success)" : "var(--accent)";
3035

3136
return (
3237
<div
@@ -36,8 +41,8 @@ export default function StartNode({ data }: NodeProps) {
3641
background: "var(--node-bg)",
3742
color: "var(--text-primary)",
3843
border: `2px solid ${borderColor}`,
39-
boxShadow: isPausedHere || isActiveNode ? "0 0 4px var(--accent)" : undefined,
40-
animation: isActiveNode && !isPausedHere ? "node-pulse 1.5s ease-in-out infinite" : undefined,
44+
boxShadow: isPausedHere || isActiveNode || isExecutingNode ? `0 0 4px ${glowColor}` : undefined,
45+
animation: (isActiveNode || isExecutingNode) && !isPausedHere ? `node-pulse-${isExecutingNode ? "green" : "accent"} 1.5s ease-in-out infinite` : undefined,
4146
}}
4247
title={label}
4348
>

src/uipath/dev/server/frontend/src/components/graph/nodes/ToolNode.tsx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,23 @@ export default function ToolNode({ data }: NodeProps) {
2121
const hasBreakpoint = data.hasBreakpoint as boolean | undefined;
2222
const isPausedHere = data.isPausedHere as boolean | undefined;
2323
const isActiveNode = data.isActiveNode as boolean | undefined;
24+
const isExecutingNode = data.isExecutingNode as boolean | undefined;
2425

2526
const borderColor = isPausedHere
2627
? "var(--accent)"
27-
: isActiveNode
28-
? "var(--accent)"
29-
: status === "completed"
30-
? "var(--success)"
31-
: status === "running"
32-
? "var(--warning)"
33-
: status === "failed"
34-
? "var(--error)"
35-
: "var(--node-border)";
28+
: isExecutingNode
29+
? "var(--success)"
30+
: isActiveNode
31+
? "var(--accent)"
32+
: status === "completed"
33+
? "var(--success)"
34+
: status === "running"
35+
? "var(--warning)"
36+
: status === "failed"
37+
? "var(--error)"
38+
: "var(--node-border)";
39+
40+
const glowColor = isExecutingNode ? "var(--success)" : "var(--accent)";
3641

3742
const visibleTools = toolNames?.slice(0, MAX_VISIBLE_TOOLS) ?? [];
3843
const remaining = (toolCount ?? toolNames?.length ?? 0) - visibleTools.length;
@@ -45,8 +50,8 @@ export default function ToolNode({ data }: NodeProps) {
4550
background: "var(--node-bg)",
4651
color: "var(--text-primary)",
4752
border: `2px solid ${borderColor}`,
48-
boxShadow: isPausedHere || isActiveNode ? "0 0 4px var(--accent)" : undefined,
49-
animation: isActiveNode && !isPausedHere ? "node-pulse 1.5s ease-in-out infinite" : undefined,
53+
boxShadow: isPausedHere || isActiveNode || isExecutingNode ? `0 0 4px ${glowColor}` : undefined,
54+
animation: (isActiveNode || isExecutingNode) && !isPausedHere ? `node-pulse-${isExecutingNode ? "green" : "accent"} 1.5s ease-in-out infinite` : undefined,
5055
}}
5156
title={toolNames?.length ? `${label}\n\n${toolNames.join("\n")}` : label}
5257
>

0 commit comments

Comments
 (0)