Skip to content

Commit 6f3f595

Browse files
committed
refactor(frontend): replace TraceView tab with compact AgentFlowSummary
Remove the TraceView and TabBar components in favor of a lightweight AgentFlowSummary strip below the chat panel. RightPanel now renders only LearnView, and totalUsage display is removed from Chat. This simplifies the layout and removes ~650 lines of unused trace UI code.
1 parent b13b4a7 commit 6f3f595

File tree

7 files changed

+148
-795
lines changed

7 files changed

+148
-795
lines changed

frontend/src/App.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useCallback, useEffect, useRef, useState } from "react";
2+
import { AgentFlowSummary } from "./components/AgentFlowSummary.tsx";
23
import { Chat } from "./components/Chat.tsx";
34
import { PatternSelector } from "./components/PatternSelector.tsx";
45
import { RightPanel } from "./components/RightPanel.tsx";
@@ -14,7 +15,6 @@ export function App() {
1415
traceEdges,
1516
isStreaming,
1617
error,
17-
totalUsage,
1818
send,
1919
} = useStream(selectedPattern);
2020

@@ -77,20 +77,26 @@ export function App() {
7777
<div className="flex-[3] min-h-0 glass rounded-2xl overflow-hidden">
7878
<RightPanel
7979
selectedPattern={selectedPattern}
80-
traceNodes={traceNodes}
81-
traceEdges={traceEdges}
82-
totalUsage={totalUsage}
83-
isStreaming={isStreaming}
8480
onTryPrompt={handleTryPrompt}
8581
/>
8682
</div>
87-
<div className="flex-[2] min-h-0 glass-strong rounded-2xl overflow-hidden">
88-
<Chat
89-
messages={messages}
90-
isStreaming={isStreaming}
91-
error={error}
92-
totalUsage={totalUsage}
93-
/>
83+
<div className="flex-[2] min-h-0 flex flex-col gap-2">
84+
<div className="flex-1 min-h-0 glass-strong rounded-2xl overflow-hidden">
85+
<Chat
86+
messages={messages}
87+
isStreaming={isStreaming}
88+
error={error}
89+
/>
90+
</div>
91+
{traceNodes.length > 0 && (
92+
<div className="shrink-0 glass-strong rounded-2xl overflow-hidden">
93+
<AgentFlowSummary
94+
traceNodes={traceNodes}
95+
traceEdges={traceEdges}
96+
isStreaming={isStreaming}
97+
/>
98+
</div>
99+
)}
94100
</div>
95101
</main>
96102

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import type { TraceEdge, TraceNode } from "../types.ts";
2+
import { AgentAvatar } from "./AgentAvatar.tsx";
3+
4+
/* ── Status dot colors ─────────────────────────────────────── */
5+
6+
const STATUS_DOT: Record<TraceNode["status"], string> = {
7+
running: "bg-blue-500 animate-pulse",
8+
done: "bg-emerald-500",
9+
error: "bg-red-500",
10+
};
11+
12+
/* ── Flow Node ─────────────────────────────────────────────── */
13+
14+
function FlowNode({ node }: { node: TraceNode }) {
15+
return (
16+
<div className="flex flex-col items-center gap-1 min-w-0">
17+
<div className="relative">
18+
<AgentAvatar name={node.agent} role={node.role} size="sm" />
19+
<span
20+
className={`absolute -bottom-0.5 -right-0.5 h-2 w-2 rounded-full border border-white ${STATUS_DOT[node.status]}`}
21+
/>
22+
</div>
23+
<span className="text-[10px] font-medium text-[var(--color-text-primary)] truncate max-w-[72px] text-center leading-tight">
24+
{node.agent}
25+
</span>
26+
</div>
27+
);
28+
}
29+
30+
/* ── Flow Arrow ────────────────────────────────────────────── */
31+
32+
function FlowArrow({ edge }: { edge: TraceEdge | undefined }) {
33+
return (
34+
<div className="flex flex-col items-center justify-center gap-0.5 px-1 shrink-0">
35+
<svg
36+
width="20"
37+
height="10"
38+
viewBox="0 0 20 10"
39+
className="text-[var(--color-text-tertiary)]"
40+
>
41+
<path
42+
d="M0 5h16M12 1l4 4-4 4"
43+
stroke="currentColor"
44+
fill="none"
45+
strokeWidth="1.5"
46+
strokeLinecap="round"
47+
strokeLinejoin="round"
48+
/>
49+
</svg>
50+
{edge?.reason && (
51+
<span className="text-[9px] text-[var(--color-accent)] max-w-[64px] truncate text-center leading-tight">
52+
{edge.reason}
53+
</span>
54+
)}
55+
</div>
56+
);
57+
}
58+
59+
/* ── Agent Flow Summary ────────────────────────────────────── */
60+
61+
interface AgentFlowSummaryProps {
62+
traceNodes: TraceNode[];
63+
traceEdges: TraceEdge[];
64+
isStreaming: boolean;
65+
}
66+
67+
export function AgentFlowSummary({
68+
traceNodes,
69+
traceEdges,
70+
isStreaming,
71+
}: AgentFlowSummaryProps) {
72+
if (traceNodes.length === 0) return null;
73+
74+
// Find edge between consecutive nodes
75+
function findEdge(fromAgent: string, toAgent: string): TraceEdge | undefined {
76+
return traceEdges.find((e) => e.from === fromAgent && e.to === toAgent);
77+
}
78+
79+
return (
80+
<div
81+
className={`animate-fade-in px-4 py-3 ${
82+
isStreaming ? "animate-summary-pulse" : ""
83+
}`}
84+
>
85+
{/* Header */}
86+
<div className="flex items-center gap-2 mb-3">
87+
<svg
88+
width="14"
89+
height="14"
90+
viewBox="0 0 24 24"
91+
fill="none"
92+
stroke="currentColor"
93+
strokeWidth="2"
94+
strokeLinecap="round"
95+
strokeLinejoin="round"
96+
className="text-[var(--color-text-tertiary)]"
97+
>
98+
<path d="M7.5 21 3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5" />
99+
</svg>
100+
<span className="text-[11px] font-semibold uppercase tracking-wider text-[var(--color-text-tertiary)]">
101+
Agent Flow
102+
</span>
103+
{isStreaming && (
104+
<span className="inline-flex items-center gap-1 rounded-full bg-blue-50 px-2 py-0.5 text-[10px] font-medium text-blue-600">
105+
<span className="h-1 w-1 rounded-full bg-blue-500 animate-pulse" />
106+
Live
107+
</span>
108+
)}
109+
</div>
110+
111+
{/* Flow chain */}
112+
<div className="flex items-center flex-wrap gap-y-3">
113+
{traceNodes.map((node, i) => (
114+
<div key={`${node.agent}-${i}`} className="flex items-center">
115+
{i > 0 && (
116+
<FlowArrow
117+
edge={findEdge(traceNodes[i - 1]?.agent ?? "", node.agent)}
118+
/>
119+
)}
120+
<FlowNode node={node} />
121+
</div>
122+
))}
123+
</div>
124+
</div>
125+
);
126+
}

frontend/src/components/Chat.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type {
33
AgentMessage,
44
ChatMessage,
55
HandoffMessage,
6-
TokenUsage,
76
UserMessage,
87
} from "../types.ts";
98
import { MessageBubble } from "./MessageBubble.tsx";
@@ -24,14 +23,12 @@ interface ChatProps {
2423
messages: ChatMessage[];
2524
isStreaming: boolean;
2625
error: string | null;
27-
totalUsage: TokenUsage | null;
2826
}
2927

3028
export function Chat({
3129
messages,
3230
isStreaming,
3331
error,
34-
totalUsage,
3532
}: ChatProps) {
3633
const scrollRef = useRef<HTMLDivElement>(null);
3734

@@ -127,11 +124,6 @@ export function Chat({
127124
</div>
128125
)}
129126

130-
{!isStreaming && totalUsage && (
131-
<div className="px-5 py-1 text-[11px] text-[var(--color-text-tertiary)] text-right animate-fade-in">
132-
Total: {totalUsage.inputTokens + totalUsage.outputTokens} tokens
133-
</div>
134-
)}
135127
</div>
136128
);
137129
}
Lines changed: 4 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,20 @@
1-
import { useCallback, useEffect, useRef, useState } from "react";
2-
import type { TokenUsage, TraceEdge, TraceNode } from "../types.ts";
31
import { LearnView } from "./LearnView.tsx";
4-
import { TabBar } from "./TabBar.tsx";
5-
import type { TabId } from "./TabBar.tsx";
6-
import { TraceView } from "./TraceView.tsx";
72

83
interface RightPanelProps {
94
selectedPattern: string | null;
10-
traceNodes: TraceNode[];
11-
traceEdges: TraceEdge[];
12-
totalUsage: TokenUsage | null;
13-
isStreaming: boolean;
145
onTryPrompt: (prompt: string) => void;
156
}
167

178
export function RightPanel({
189
selectedPattern,
19-
traceNodes,
20-
traceEdges,
21-
totalUsage,
22-
isStreaming,
2310
onTryPrompt,
2411
}: RightPanelProps) {
25-
/* Per-pattern tab state */
26-
const tabMapRef = useRef(new Map<string, TabId>());
27-
28-
const getTab = useCallback((): TabId => {
29-
if (!selectedPattern) return "learn";
30-
return tabMapRef.current.get(selectedPattern) ?? "learn";
31-
}, [selectedPattern]);
32-
33-
const [activeTab, setActiveTab] = useState<TabId>(getTab);
34-
35-
/* Restore tab state on pattern switch */
36-
useEffect(() => {
37-
setActiveTab(getTab());
38-
}, [getTab]);
39-
40-
const handleTabChange = useCallback(
41-
(tab: TabId) => {
42-
setActiveTab(tab);
43-
if (selectedPattern) {
44-
tabMapRef.current.set(selectedPattern, tab);
45-
}
46-
},
47-
[selectedPattern],
48-
);
49-
50-
/* Auto-switch to trace when streaming starts */
51-
const wasStreamingRef = useRef(false);
52-
useEffect(() => {
53-
if (isStreaming && !wasStreamingRef.current) {
54-
handleTabChange("trace");
55-
}
56-
wasStreamingRef.current = isStreaming;
57-
}, [isStreaming, handleTabChange]);
58-
5912
return (
6013
<div className="flex flex-col h-full">
61-
<TabBar activeTab={activeTab} onTabChange={handleTabChange} />
62-
63-
<div className="flex-1 min-h-0">
64-
{activeTab === "trace" ? (
65-
<div
66-
role="tabpanel"
67-
id="tabpanel-trace"
68-
aria-labelledby="tab-trace"
69-
className="h-full"
70-
>
71-
<TraceView
72-
traceNodes={traceNodes}
73-
traceEdges={traceEdges}
74-
totalUsage={totalUsage}
75-
isStreaming={isStreaming}
76-
patternName={selectedPattern}
77-
/>
78-
</div>
79-
) : (
80-
<div
81-
role="tabpanel"
82-
id="tabpanel-learn"
83-
aria-labelledby="tab-learn"
84-
className="h-full"
85-
>
86-
<LearnView
87-
selectedPattern={selectedPattern}
88-
onTryPrompt={onTryPrompt}
89-
/>
90-
</div>
91-
)}
92-
</div>
14+
<LearnView
15+
selectedPattern={selectedPattern}
16+
onTryPrompt={onTryPrompt}
17+
/>
9318
</div>
9419
);
9520
}

0 commit comments

Comments
 (0)