Spaces:
Sleeping
Sleeping
Your Name commited on
Commit ·
1448ed8
1
Parent(s): e9b8ed2
Add animated glowing radar, live pulse, and thinking indicators for agent
Browse files
frontend/src/components/TaskGraph.tsx
CHANGED
|
@@ -20,7 +20,7 @@ export default function TaskGraph({
|
|
| 20 |
return (
|
| 21 |
<div className="task-graph">
|
| 22 |
{tasks.map((task) => (
|
| 23 |
-
<div className=
|
| 24 |
<div><strong>{task.task_key}</strong></div>
|
| 25 |
<div>{task.task_type}</div>
|
| 26 |
<span className={`status ${task.status}`}>{task.status}</span>
|
|
@@ -36,7 +36,7 @@ export default function TaskGraph({
|
|
| 36 |
const live = taskMap.get(pt.id);
|
| 37 |
const status = live?.status || "PENDING";
|
| 38 |
return (
|
| 39 |
-
<div className=
|
| 40 |
<div><strong>{pt.id}</strong></div>
|
| 41 |
<div>{pt.type}</div>
|
| 42 |
<div style={{ fontSize: "0.8rem", color: "#7f92ab" }}>
|
|
|
|
| 20 |
return (
|
| 21 |
<div className="task-graph">
|
| 22 |
{tasks.map((task) => (
|
| 23 |
+
<div className={`task-node ${task.status.toLowerCase()}`} key={task.task_key}>
|
| 24 |
<div><strong>{task.task_key}</strong></div>
|
| 25 |
<div>{task.task_type}</div>
|
| 26 |
<span className={`status ${task.status}`}>{task.status}</span>
|
|
|
|
| 36 |
const live = taskMap.get(pt.id);
|
| 37 |
const status = live?.status || "PENDING";
|
| 38 |
return (
|
| 39 |
+
<div className={`task-node ${status.toLowerCase()}`} key={pt.id}>
|
| 40 |
<div><strong>{pt.id}</strong></div>
|
| 41 |
<div>{pt.type}</div>
|
| 42 |
<div style={{ fontSize: "0.8rem", color: "#7f92ab" }}>
|
frontend/src/components/WorkflowTimeline.tsx
CHANGED
|
@@ -42,9 +42,11 @@ export default function WorkflowTimeline({ events }: { events: WorkflowEvent[] }
|
|
| 42 |
style={{
|
| 43 |
background: "var(--bg-elevated)",
|
| 44 |
borderRadius: "var(--radius-md)",
|
| 45 |
-
border: "1px solid var(--border-subtle)",
|
|
|
|
| 46 |
marginBottom: "2rem",
|
| 47 |
-
overflow: "hidden"
|
|
|
|
| 48 |
}}
|
| 49 |
>
|
| 50 |
<summary style={{
|
|
@@ -53,16 +55,26 @@ export default function WorkflowTimeline({ events }: { events: WorkflowEvent[] }
|
|
| 53 |
fontWeight: 600,
|
| 54 |
display: "flex",
|
| 55 |
alignItems: "center",
|
| 56 |
-
|
|
|
|
| 57 |
listStyle: "none",
|
| 58 |
borderBottom: isOpen ? "1px solid var(--border-subtle)" : "none",
|
| 59 |
-
userSelect: "none"
|
|
|
|
| 60 |
}}>
|
| 61 |
-
<
|
| 62 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
</summary>
|
| 64 |
|
| 65 |
-
<div style={{ padding: "1.5rem", maxHeight: "
|
| 66 |
<ul className="timeline">
|
| 67 |
{events.map((event) => (
|
| 68 |
<li key={event.id} style={{ marginBottom: "1rem", display: "flex", gap: "1rem", alignItems: "flex-start" }}>
|
|
@@ -87,7 +99,27 @@ export default function WorkflowTimeline({ events }: { events: WorkflowEvent[] }
|
|
| 87 |
</div>
|
| 88 |
</li>
|
| 89 |
))}
|
| 90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
</ul>
|
| 92 |
</div>
|
| 93 |
</details>
|
|
|
|
| 42 |
style={{
|
| 43 |
background: "var(--bg-elevated)",
|
| 44 |
borderRadius: "var(--radius-md)",
|
| 45 |
+
border: isComplete ? "1px solid var(--border-subtle)" : "1px solid rgba(99, 102, 241, 0.4)",
|
| 46 |
+
boxShadow: isComplete ? "none" : "0 0 20px rgba(99, 102, 241, 0.25)",
|
| 47 |
marginBottom: "2rem",
|
| 48 |
+
overflow: "hidden",
|
| 49 |
+
transition: "all 0.3s ease"
|
| 50 |
}}
|
| 51 |
>
|
| 52 |
<summary style={{
|
|
|
|
| 55 |
fontWeight: 600,
|
| 56 |
display: "flex",
|
| 57 |
alignItems: "center",
|
| 58 |
+
justifyContent: "space-between",
|
| 59 |
+
gap: "0.75rem",
|
| 60 |
listStyle: "none",
|
| 61 |
borderBottom: isOpen ? "1px solid var(--border-subtle)" : "none",
|
| 62 |
+
userSelect: "none",
|
| 63 |
+
background: isComplete ? "transparent" : "linear-gradient(90deg, rgba(99, 102, 241, 0.05) 0%, transparent 100%)"
|
| 64 |
}}>
|
| 65 |
+
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem" }}>
|
| 66 |
+
<span style={{ transition: "transform 0.2s", transform: isOpen ? "rotate(90deg)" : "rotate(0deg)", display: "inline-block" }}>▶</span>
|
| 67 |
+
{!isComplete && <span className="radar-dot green"></span>}
|
| 68 |
+
<span>{isComplete ? "Agent completed execution" : "Agent is thinking & executing..."}</span>
|
| 69 |
+
</div>
|
| 70 |
+
{!isComplete && (
|
| 71 |
+
<span style={{ fontSize: "0.8rem", color: "#6366f1", display: "flex", alignItems: "center", gap: "0.3rem" }}>
|
| 72 |
+
LIVE <span className="thinking-dots"><span></span><span></span><span></span></span>
|
| 73 |
+
</span>
|
| 74 |
+
)}
|
| 75 |
</summary>
|
| 76 |
|
| 77 |
+
<div style={{ padding: "1.5rem", maxHeight: "450px", overflowY: "auto" }}>
|
| 78 |
<ul className="timeline">
|
| 79 |
{events.map((event) => (
|
| 80 |
<li key={event.id} style={{ marginBottom: "1rem", display: "flex", gap: "1rem", alignItems: "flex-start" }}>
|
|
|
|
| 99 |
</div>
|
| 100 |
</li>
|
| 101 |
))}
|
| 102 |
+
|
| 103 |
+
{!isComplete && (
|
| 104 |
+
<li style={{ marginTop: "1rem", listStyle: "none" }}>
|
| 105 |
+
<div className="live-thinking-pulse">
|
| 106 |
+
<span className="radar-dot"></span>
|
| 107 |
+
<div style={{ flex: 1 }}>
|
| 108 |
+
<div style={{ display: "flex", alignItems: "center", gap: "0.4rem" }}>
|
| 109 |
+
<span>Agent is working on the autonomous loop</span>
|
| 110 |
+
<span className="thinking-dots"><span></span><span></span><span></span></span>
|
| 111 |
+
</div>
|
| 112 |
+
<div style={{ fontSize: "0.8rem", color: "var(--text-muted)", marginTop: "0.2rem" }}>
|
| 113 |
+
Generating code, compiling DAG graph, executing tasks & verifying...
|
| 114 |
+
</div>
|
| 115 |
+
<div style={{ marginTop: "0.6rem" }}>
|
| 116 |
+
<div className="thinking-shimmer-progress"></div>
|
| 117 |
+
</div>
|
| 118 |
+
</div>
|
| 119 |
+
</div>
|
| 120 |
+
</li>
|
| 121 |
+
)}
|
| 122 |
+
{events.length === 0 && isComplete && <li>No events recorded.</li>}
|
| 123 |
</ul>
|
| 124 |
</div>
|
| 125 |
</details>
|
frontend/src/index.css
CHANGED
|
@@ -1039,3 +1039,165 @@ button.secondary:hover,
|
|
| 1039 |
padding: 1.5rem;
|
| 1040 |
}
|
| 1041 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1039 |
padding: 1.5rem;
|
| 1040 |
}
|
| 1041 |
}
|
| 1042 |
+
|
| 1043 |
+
/* ============================================================
|
| 1044 |
+
LIVE AGENT GLOWING & WORKING ANIMATIONS
|
| 1045 |
+
============================================================ */
|
| 1046 |
+
|
| 1047 |
+
@keyframes pulse-ring {
|
| 1048 |
+
0% {
|
| 1049 |
+
transform: scale(0.95);
|
| 1050 |
+
box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.8);
|
| 1051 |
+
}
|
| 1052 |
+
70% {
|
| 1053 |
+
transform: scale(1.1);
|
| 1054 |
+
box-shadow: 0 0 0 12px rgba(99, 102, 241, 0);
|
| 1055 |
+
}
|
| 1056 |
+
100% {
|
| 1057 |
+
transform: scale(0.95);
|
| 1058 |
+
box-shadow: 0 0 0 0 rgba(99, 102, 241, 0);
|
| 1059 |
+
}
|
| 1060 |
+
}
|
| 1061 |
+
|
| 1062 |
+
@keyframes pulse-ring-green {
|
| 1063 |
+
0% {
|
| 1064 |
+
transform: scale(0.95);
|
| 1065 |
+
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.8);
|
| 1066 |
+
}
|
| 1067 |
+
70% {
|
| 1068 |
+
transform: scale(1.1);
|
| 1069 |
+
box-shadow: 0 0 0 12px rgba(16, 185, 129, 0);
|
| 1070 |
+
}
|
| 1071 |
+
100% {
|
| 1072 |
+
transform: scale(0.95);
|
| 1073 |
+
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0);
|
| 1074 |
+
}
|
| 1075 |
+
}
|
| 1076 |
+
|
| 1077 |
+
@keyframes live-glow {
|
| 1078 |
+
0%, 100% {
|
| 1079 |
+
box-shadow: 0 0 16px rgba(99, 102, 241, 0.4), 0 0 32px rgba(168, 85, 247, 0.2);
|
| 1080 |
+
border-color: rgba(99, 102, 241, 0.7);
|
| 1081 |
+
}
|
| 1082 |
+
50% {
|
| 1083 |
+
box-shadow: 0 0 30px rgba(99, 102, 241, 0.7), 0 0 60px rgba(168, 85, 247, 0.45);
|
| 1084 |
+
border-color: rgba(168, 85, 247, 1);
|
| 1085 |
+
}
|
| 1086 |
+
}
|
| 1087 |
+
|
| 1088 |
+
@keyframes live-neon-sweep {
|
| 1089 |
+
0% {
|
| 1090 |
+
background-position: 0% 50%;
|
| 1091 |
+
}
|
| 1092 |
+
50% {
|
| 1093 |
+
background-position: 100% 50%;
|
| 1094 |
+
}
|
| 1095 |
+
100% {
|
| 1096 |
+
background-position: 0% 50%;
|
| 1097 |
+
}
|
| 1098 |
+
}
|
| 1099 |
+
|
| 1100 |
+
@keyframes shimmer-bar {
|
| 1101 |
+
0% {
|
| 1102 |
+
background-position: -200% 0;
|
| 1103 |
+
}
|
| 1104 |
+
100% {
|
| 1105 |
+
background-position: 200% 0;
|
| 1106 |
+
}
|
| 1107 |
+
}
|
| 1108 |
+
|
| 1109 |
+
@keyframes bounce-dot {
|
| 1110 |
+
0%, 80%, 100% {
|
| 1111 |
+
transform: scale(0.4);
|
| 1112 |
+
opacity: 0.3;
|
| 1113 |
+
}
|
| 1114 |
+
40% {
|
| 1115 |
+
transform: scale(1.1);
|
| 1116 |
+
opacity: 1;
|
| 1117 |
+
}
|
| 1118 |
+
}
|
| 1119 |
+
|
| 1120 |
+
.radar-dot {
|
| 1121 |
+
width: 10px;
|
| 1122 |
+
height: 10px;
|
| 1123 |
+
background: #6366f1;
|
| 1124 |
+
border-radius: 50%;
|
| 1125 |
+
display: inline-block;
|
| 1126 |
+
animation: pulse-ring 1.6s cubic-bezier(0.45, 0, 0.55, 1) infinite;
|
| 1127 |
+
}
|
| 1128 |
+
|
| 1129 |
+
.radar-dot.green {
|
| 1130 |
+
background: #10b981;
|
| 1131 |
+
animation: pulse-ring-green 1.6s cubic-bezier(0.45, 0, 0.55, 1) infinite;
|
| 1132 |
+
}
|
| 1133 |
+
|
| 1134 |
+
.agent-live-banner {
|
| 1135 |
+
background: linear-gradient(135deg, rgba(99, 102, 241, 0.12) 0%, rgba(168, 85, 247, 0.12) 100%);
|
| 1136 |
+
border: 1px solid rgba(99, 102, 241, 0.4);
|
| 1137 |
+
border-radius: var(--radius-lg);
|
| 1138 |
+
padding: 1.25rem 1.5rem;
|
| 1139 |
+
margin-bottom: 1.5rem;
|
| 1140 |
+
animation: live-glow 2.5s ease-in-out infinite;
|
| 1141 |
+
display: flex;
|
| 1142 |
+
flex-direction: column;
|
| 1143 |
+
gap: 0.75rem;
|
| 1144 |
+
}
|
| 1145 |
+
|
| 1146 |
+
.agent-live-header {
|
| 1147 |
+
display: flex;
|
| 1148 |
+
align-items: center;
|
| 1149 |
+
justify-content: space-between;
|
| 1150 |
+
font-weight: 600;
|
| 1151 |
+
color: var(--text-primary);
|
| 1152 |
+
}
|
| 1153 |
+
|
| 1154 |
+
.thinking-shimmer-progress {
|
| 1155 |
+
height: 6px;
|
| 1156 |
+
width: 100%;
|
| 1157 |
+
background: linear-gradient(90deg, rgba(99, 102, 241, 0.2) 0%, #6366f1 30%, #ec4899 70%, rgba(168, 85, 247, 0.2) 100%);
|
| 1158 |
+
background-size: 200% 100%;
|
| 1159 |
+
animation: shimmer-bar 1.8s linear infinite;
|
| 1160 |
+
border-radius: 999px;
|
| 1161 |
+
}
|
| 1162 |
+
|
| 1163 |
+
.thinking-dots span {
|
| 1164 |
+
display: inline-block;
|
| 1165 |
+
width: 6px;
|
| 1166 |
+
height: 6px;
|
| 1167 |
+
background: currentColor;
|
| 1168 |
+
border-radius: 50%;
|
| 1169 |
+
margin: 0 2px;
|
| 1170 |
+
animation: bounce-dot 1.4s infinite ease-in-out both;
|
| 1171 |
+
}
|
| 1172 |
+
.thinking-dots span:nth-child(1) { animation-delay: -0.32s; }
|
| 1173 |
+
.thinking-dots span:nth-child(2) { animation-delay: -0.16s; }
|
| 1174 |
+
|
| 1175 |
+
.task-node.running {
|
| 1176 |
+
border-color: #6366f1 !important;
|
| 1177 |
+
animation: live-glow 2s ease-in-out infinite;
|
| 1178 |
+
background: linear-gradient(145deg, var(--bg-surface), rgba(99, 102, 241, 0.1)) !important;
|
| 1179 |
+
}
|
| 1180 |
+
|
| 1181 |
+
.status.RUNNING {
|
| 1182 |
+
background: rgba(99, 102, 241, 0.18) !important;
|
| 1183 |
+
color: #6366f1 !important;
|
| 1184 |
+
box-shadow: 0 0 12px rgba(99, 102, 241, 0.5);
|
| 1185 |
+
font-weight: 700;
|
| 1186 |
+
animation: live-glow 2s infinite;
|
| 1187 |
+
}
|
| 1188 |
+
|
| 1189 |
+
.live-thinking-pulse {
|
| 1190 |
+
display: flex;
|
| 1191 |
+
align-items: center;
|
| 1192 |
+
gap: 0.75rem;
|
| 1193 |
+
padding: 0.85rem 1.25rem;
|
| 1194 |
+
background: linear-gradient(90deg, rgba(99, 102, 241, 0.08) 0%, rgba(168, 85, 247, 0.08) 100%);
|
| 1195 |
+
border: 1px dashed rgba(99, 102, 241, 0.5);
|
| 1196 |
+
border-radius: var(--radius-md);
|
| 1197 |
+
margin-top: 1rem;
|
| 1198 |
+
color: #6366f1;
|
| 1199 |
+
font-weight: 500;
|
| 1200 |
+
font-size: 0.9rem;
|
| 1201 |
+
animation: live-glow 2.5s ease-in-out infinite;
|
| 1202 |
+
}
|
| 1203 |
+
|
frontend/src/pages/WorkflowPage.tsx
CHANGED
|
@@ -64,11 +64,29 @@ export default function WorkflowPage() {
|
|
| 64 |
<div>
|
| 65 |
<h2>Workflow {workflow.workflow_id}</h2>
|
| 66 |
{error && <p className="error">{error}</p>}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
<div className="card">
|
| 68 |
-
<p>{workflow.user_prompt}</p>
|
| 69 |
-
<p>
|
| 70 |
<span className={`status ${workflow.status}`}>{workflow.status}</span>
|
| 71 |
-
{"
|
| 72 |
</p>
|
| 73 |
</div>
|
| 74 |
|
|
|
|
| 64 |
<div>
|
| 65 |
<h2>Workflow {workflow.workflow_id}</h2>
|
| 66 |
{error && <p className="error">{error}</p>}
|
| 67 |
+
{isLive && (
|
| 68 |
+
<div className="agent-live-banner">
|
| 69 |
+
<div className="agent-live-header">
|
| 70 |
+
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem" }}>
|
| 71 |
+
<span className="radar-dot green"></span>
|
| 72 |
+
<span style={{ fontSize: "1.05rem", fontWeight: 600 }}>Agent is Actively Working</span>
|
| 73 |
+
</div>
|
| 74 |
+
<span style={{ fontSize: "0.85rem", color: "#6366f1", fontWeight: 600, display: "flex", alignItems: "center", gap: "0.25rem" }}>
|
| 75 |
+
AUTONOMOUS LOOP <span className="thinking-dots"><span></span><span></span><span></span></span>
|
| 76 |
+
</span>
|
| 77 |
+
</div>
|
| 78 |
+
<p style={{ margin: 0, color: "var(--text-secondary)", fontSize: "0.9rem" }}>
|
| 79 |
+
The AI agent is planning architecture, generating code & tests, and orchestrating verification tasks in the background.
|
| 80 |
+
</p>
|
| 81 |
+
<div className="thinking-shimmer-progress"></div>
|
| 82 |
+
</div>
|
| 83 |
+
)}
|
| 84 |
+
|
| 85 |
<div className="card">
|
| 86 |
+
<p style={{ fontSize: "1.1rem", marginBottom: "0.75rem", fontWeight: 500 }}>{workflow.user_prompt}</p>
|
| 87 |
+
<p style={{ display: "flex", alignItems: "center", gap: "0.75rem", margin: 0 }}>
|
| 88 |
<span className={`status ${workflow.status}`}>{workflow.status}</span>
|
| 89 |
+
<span style={{ color: "var(--text-muted)", fontSize: "0.9rem" }}>{workflow.llm_provider} / {workflow.llm_model}</span>
|
| 90 |
</p>
|
| 91 |
</div>
|
| 92 |
|