@elizaos/client 1.6.1-alpha.4 → 1.6.1-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/main-BM2lpId8.js +155 -0
- package/dist/assets/main-BM2lpId8.js.map +1 -0
- package/dist/assets/{main-BOBWcKWW.css → main-CNv6B3RZ.css} +597 -71
- package/dist/assets/{main-C4q5_rtN.js → main-CQAV8tyh.js} +4 -4
- package/dist/assets/main-CQAV8tyh.js.map +1 -0
- package/dist/assets/react-vendor-C1OK-nqm.js +611 -0
- package/dist/assets/react-vendor-C1OK-nqm.js.map +1 -0
- package/dist/index.html +1 -1
- package/package.json +29 -25
- package/src/components/agent-prism/Avatar.tsx +164 -0
- package/src/components/agent-prism/Badge.tsx +109 -0
- package/src/components/agent-prism/Button.tsx +138 -0
- package/src/components/agent-prism/CollapseAndExpandControls.tsx +45 -0
- package/src/components/agent-prism/CollapsibleSection.tsx +121 -0
- package/src/components/agent-prism/DetailsView/DetailsView.tsx +141 -0
- package/src/components/agent-prism/DetailsView/DetailsViewAttributesTab.tsx +45 -0
- package/src/components/agent-prism/DetailsView/DetailsViewHeader.tsx +77 -0
- package/src/components/agent-prism/DetailsView/DetailsViewHeaderActions.tsx +21 -0
- package/src/components/agent-prism/DetailsView/DetailsViewInputOutputTab.tsx +210 -0
- package/src/components/agent-prism/DetailsView/DetailsViewMetrics.tsx +53 -0
- package/src/components/agent-prism/DetailsView/DetailsViewRawDataTab.tsx +24 -0
- package/src/components/agent-prism/IconButton.tsx +75 -0
- package/src/components/agent-prism/PriceBadge.tsx +12 -0
- package/src/components/agent-prism/SearchInput.tsx +17 -0
- package/src/components/agent-prism/SpanCard/SpanCard.tsx +467 -0
- package/src/components/agent-prism/SpanCard/SpanCardBadges.tsx +35 -0
- package/src/components/agent-prism/SpanCard/SpanCardConnector.tsx +36 -0
- package/src/components/agent-prism/SpanCard/SpanCardTimeline.tsx +60 -0
- package/src/components/agent-prism/SpanCard/SpanCardToggle.tsx +32 -0
- package/src/components/agent-prism/SpanStatus.tsx +79 -0
- package/src/components/agent-prism/Tabs.tsx +141 -0
- package/src/components/agent-prism/TextInput.tsx +142 -0
- package/src/components/agent-prism/TimestampBadge.tsx +28 -0
- package/src/components/agent-prism/TokensBadge.tsx +26 -0
- package/src/components/agent-prism/TraceList/TraceList.tsx +80 -0
- package/src/components/agent-prism/TraceList/TraceListItem.tsx +79 -0
- package/src/components/agent-prism/TraceList/TraceListItemHeader.tsx +46 -0
- package/src/components/agent-prism/TraceViewer.tsx +476 -0
- package/src/components/agent-prism/TreeView.tsx +57 -0
- package/src/components/agent-prism/shared.ts +210 -0
- package/src/components/agent-runs/AgentRunTimeline.tsx +64 -673
- package/src/components/agent-sidebar.tsx +2 -2
- package/src/components/chat.tsx +8 -8
- package/src/lib/agent-prism-utils.ts +46 -0
- package/src/lib/eliza-span-adapter.ts +487 -0
- package/dist/assets/main-BNtEiK3o.js +0 -141
- package/dist/assets/main-BNtEiK3o.js.map +0 -1
- package/dist/assets/main-C4q5_rtN.js.map +0 -1
- package/dist/assets/react-vendor-pe76PXQl.js +0 -546
- package/dist/assets/react-vendor-pe76PXQl.js.map +0 -1
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
import {
|
|
2
|
+
flattenSpans,
|
|
3
|
+
formatDuration,
|
|
4
|
+
} from "@evilmartians/agent-prism-data";
|
|
5
|
+
import {
|
|
6
|
+
type TraceRecord,
|
|
7
|
+
type TraceSpan,
|
|
8
|
+
} from "@evilmartians/agent-prism-types";
|
|
9
|
+
import { filterSpansRecursively } from "@/lib/agent-prism-utils";
|
|
10
|
+
import { cn } from "@/lib/utils";
|
|
11
|
+
import { ArrowLeft } from "lucide-react";
|
|
12
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
13
|
+
|
|
14
|
+
import type { BadgeProps } from "./Badge";
|
|
15
|
+
import type { SpanCardViewOptions } from "./SpanCard/SpanCard";
|
|
16
|
+
|
|
17
|
+
import { Button } from "./Button";
|
|
18
|
+
import {
|
|
19
|
+
CollapseAllButton,
|
|
20
|
+
ExpandAllButton,
|
|
21
|
+
} from "./CollapseAndExpandControls";
|
|
22
|
+
import { DetailsView } from "./DetailsView/DetailsView";
|
|
23
|
+
import { SearchInput } from "./SearchInput";
|
|
24
|
+
import { TraceList } from "./TraceList/TraceList";
|
|
25
|
+
import { TraceListItemHeader } from "./TraceList/TraceListItemHeader";
|
|
26
|
+
import { TreeView } from "./TreeView";
|
|
27
|
+
import {
|
|
28
|
+
Select,
|
|
29
|
+
SelectContent,
|
|
30
|
+
SelectItem,
|
|
31
|
+
SelectTrigger,
|
|
32
|
+
SelectValue,
|
|
33
|
+
} from "@/components/ui/select";
|
|
34
|
+
import { Badge } from "./Badge";
|
|
35
|
+
import { PriceBadge } from "./PriceBadge";
|
|
36
|
+
import { TimestampBadge } from "./TimestampBadge";
|
|
37
|
+
import { TokensBadge } from "./TokensBadge";
|
|
38
|
+
|
|
39
|
+
export interface TraceViewerData {
|
|
40
|
+
traceRecord: TraceRecord;
|
|
41
|
+
badges?: Array<BadgeProps>;
|
|
42
|
+
spans: TraceSpan[];
|
|
43
|
+
spanCardViewOptions?: SpanCardViewOptions;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface TraceViewerProps {
|
|
47
|
+
data: Array<TraceViewerData>;
|
|
48
|
+
spanCardViewOptions?: SpanCardViewOptions;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const TraceViewer = ({ data }: TraceViewerProps) => {
|
|
52
|
+
const [selectedTrace, setSelectedTrace] = useState<
|
|
53
|
+
TraceRecordWithDisplayData | undefined
|
|
54
|
+
>(data[0]?.traceRecord);
|
|
55
|
+
const [selectedTraceSpans, setSelectedTraceSpans] = useState<TraceSpan[]>(
|
|
56
|
+
data[0]?.spans ?? [],
|
|
57
|
+
);
|
|
58
|
+
const [selectedSpan, setSelectedSpan] = useState<TraceSpan | undefined>(
|
|
59
|
+
data[0]?.spans?.[0]?.children?.[0],
|
|
60
|
+
);
|
|
61
|
+
const [searchValue, setSearchValue] = useState("");
|
|
62
|
+
|
|
63
|
+
const [traceListExpanded, setTraceListExpanded] = useState(true);
|
|
64
|
+
|
|
65
|
+
const traceRecords: TraceRecordWithDisplayData[] = useMemo(() => {
|
|
66
|
+
return data.map((item) => ({
|
|
67
|
+
...item.traceRecord,
|
|
68
|
+
badges: item.badges,
|
|
69
|
+
spanCardViewOptions: item.spanCardViewOptions,
|
|
70
|
+
}));
|
|
71
|
+
}, [data]);
|
|
72
|
+
|
|
73
|
+
const filteredSpans = useMemo(() => {
|
|
74
|
+
if (!searchValue.trim()) {
|
|
75
|
+
return selectedTraceSpans;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return filterSpansRecursively(selectedTraceSpans, searchValue);
|
|
79
|
+
}, [selectedTraceSpans, searchValue]);
|
|
80
|
+
|
|
81
|
+
const allIds = useMemo(() => {
|
|
82
|
+
return flattenSpans(selectedTraceSpans).map((span) => span.id);
|
|
83
|
+
}, [selectedTraceSpans]);
|
|
84
|
+
|
|
85
|
+
const [expandedSpansIds, setExpandedSpansIds] = useState<string[]>(allIds);
|
|
86
|
+
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
setExpandedSpansIds(allIds);
|
|
89
|
+
}, [allIds]);
|
|
90
|
+
|
|
91
|
+
const handleExpandAll = useCallback(() => {
|
|
92
|
+
setExpandedSpansIds(allIds);
|
|
93
|
+
}, [allIds]);
|
|
94
|
+
|
|
95
|
+
const handleCollapseAll = useCallback(() => {
|
|
96
|
+
setExpandedSpansIds([]);
|
|
97
|
+
}, []);
|
|
98
|
+
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
setSelectedSpan(selectedTraceSpans[0]);
|
|
101
|
+
}, [selectedTraceSpans]);
|
|
102
|
+
|
|
103
|
+
const handleTraceSelect = useCallback(
|
|
104
|
+
(trace: TraceRecord) => {
|
|
105
|
+
setSelectedTrace(trace);
|
|
106
|
+
setSelectedTraceSpans(
|
|
107
|
+
data.find((item) => item.traceRecord.id === trace.id)?.spans ?? [],
|
|
108
|
+
);
|
|
109
|
+
},
|
|
110
|
+
[data],
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const props: LayoutProps = {
|
|
114
|
+
traceRecords,
|
|
115
|
+
traceListExpanded,
|
|
116
|
+
setTraceListExpanded,
|
|
117
|
+
selectedTrace,
|
|
118
|
+
setSelectedTrace,
|
|
119
|
+
selectedTraceSpans,
|
|
120
|
+
setSelectedTraceSpans,
|
|
121
|
+
selectedSpan,
|
|
122
|
+
setSelectedSpan,
|
|
123
|
+
searchValue,
|
|
124
|
+
setSearchValue,
|
|
125
|
+
filteredSpans,
|
|
126
|
+
expandedSpansIds,
|
|
127
|
+
setExpandedSpansIds,
|
|
128
|
+
handleExpandAll,
|
|
129
|
+
handleCollapseAll,
|
|
130
|
+
handleTraceSelect,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div className="h-full w-full p-4 lg:p-8">
|
|
135
|
+
<div className="hidden lg:block">
|
|
136
|
+
<DesktopLayout {...props} />
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<div className="lg:hidden">
|
|
140
|
+
<MobileLayout {...props} />
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
interface TraceRecordWithDisplayData extends TraceRecord {
|
|
147
|
+
spanCardViewOptions?: SpanCardViewOptions;
|
|
148
|
+
badges?: BadgeProps[];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
interface LayoutProps {
|
|
152
|
+
traceRecords: TraceRecordWithDisplayData[];
|
|
153
|
+
traceListExpanded: boolean;
|
|
154
|
+
setTraceListExpanded: (expanded: boolean) => void;
|
|
155
|
+
selectedTrace: TraceRecordWithDisplayData | undefined;
|
|
156
|
+
setSelectedTrace: (trace: TraceRecordWithDisplayData | undefined) => void;
|
|
157
|
+
selectedTraceSpans: TraceSpan[];
|
|
158
|
+
setSelectedTraceSpans: (spans: TraceSpan[]) => void;
|
|
159
|
+
selectedSpan: TraceSpan | undefined;
|
|
160
|
+
setSelectedSpan: (span: TraceSpan | undefined) => void;
|
|
161
|
+
searchValue: string;
|
|
162
|
+
setSearchValue: (value: string) => void;
|
|
163
|
+
filteredSpans: TraceSpan[];
|
|
164
|
+
expandedSpansIds: string[];
|
|
165
|
+
setExpandedSpansIds: (ids: string[]) => void;
|
|
166
|
+
handleExpandAll: () => void;
|
|
167
|
+
handleCollapseAll: () => void;
|
|
168
|
+
handleTraceSelect: (trace: TraceRecord) => void;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const DesktopLayout = ({
|
|
172
|
+
traceRecords,
|
|
173
|
+
traceListExpanded,
|
|
174
|
+
setTraceListExpanded,
|
|
175
|
+
selectedTrace,
|
|
176
|
+
selectedSpan,
|
|
177
|
+
setSelectedSpan,
|
|
178
|
+
searchValue,
|
|
179
|
+
setSearchValue,
|
|
180
|
+
filteredSpans,
|
|
181
|
+
expandedSpansIds,
|
|
182
|
+
setExpandedSpansIds,
|
|
183
|
+
handleExpandAll,
|
|
184
|
+
handleCollapseAll,
|
|
185
|
+
handleTraceSelect,
|
|
186
|
+
}: LayoutProps) => {
|
|
187
|
+
return (
|
|
188
|
+
<div className="flex flex-col gap-4">
|
|
189
|
+
{selectedTrace ? (
|
|
190
|
+
<div className="flex flex-col gap-4">
|
|
191
|
+
{/* Trace Selector Dropdown */}
|
|
192
|
+
<Select
|
|
193
|
+
value={selectedTrace.id}
|
|
194
|
+
onValueChange={(value) => {
|
|
195
|
+
const trace = traceRecords.find((t) => t.id === value);
|
|
196
|
+
if (trace) handleTraceSelect(trace);
|
|
197
|
+
}}
|
|
198
|
+
>
|
|
199
|
+
<SelectTrigger className="w-full h-auto py-3">
|
|
200
|
+
<SelectValue>
|
|
201
|
+
<div className="flex items-center gap-4 w-full">
|
|
202
|
+
<span className="font-semibold text-base">{selectedTrace.name}</span>
|
|
203
|
+
<div className="flex items-center gap-2 ml-auto">
|
|
204
|
+
{typeof selectedTrace.startTime === "number" && (
|
|
205
|
+
<span className="text-xs text-muted-foreground font-mono">
|
|
206
|
+
{new Date(selectedTrace.startTime).toLocaleTimeString()}
|
|
207
|
+
</span>
|
|
208
|
+
)}
|
|
209
|
+
<Badge
|
|
210
|
+
size="4"
|
|
211
|
+
theme="gray"
|
|
212
|
+
variant="outline"
|
|
213
|
+
label={`${selectedTrace.spansCount} span${selectedTrace.spansCount === 1 ? '' : 's'}`}
|
|
214
|
+
/>
|
|
215
|
+
<Badge
|
|
216
|
+
size="4"
|
|
217
|
+
theme="gray"
|
|
218
|
+
variant="outline"
|
|
219
|
+
label={formatDuration(selectedTrace.durationMs)}
|
|
220
|
+
/>
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
</SelectValue>
|
|
224
|
+
</SelectTrigger>
|
|
225
|
+
<SelectContent className="w-full max-w-2xl max-h-[400px]">
|
|
226
|
+
{traceRecords.map((trace) => (
|
|
227
|
+
<SelectItem key={trace.id} value={trace.id} className="py-2.5 px-4 cursor-pointer">
|
|
228
|
+
<div className="flex items-center justify-between gap-4 w-full pr-6">
|
|
229
|
+
<div className="flex flex-col gap-1 min-w-0">
|
|
230
|
+
<span className="font-semibold text-base text-foreground">{trace.name}</span>
|
|
231
|
+
{trace.agentDescription && (
|
|
232
|
+
<span className="text-xs text-muted-foreground">
|
|
233
|
+
{trace.agentDescription}
|
|
234
|
+
</span>
|
|
235
|
+
)}
|
|
236
|
+
</div>
|
|
237
|
+
<div className="flex items-center gap-2 flex-shrink-0">
|
|
238
|
+
{typeof trace.startTime === "number" && (
|
|
239
|
+
<span className="text-xs text-muted-foreground font-mono">
|
|
240
|
+
{new Date(trace.startTime).toLocaleTimeString()}
|
|
241
|
+
</span>
|
|
242
|
+
)}
|
|
243
|
+
<Badge
|
|
244
|
+
size="4"
|
|
245
|
+
theme="gray"
|
|
246
|
+
variant="outline"
|
|
247
|
+
label={`${trace.spansCount} span${trace.spansCount === 1 ? '' : 's'}`}
|
|
248
|
+
/>
|
|
249
|
+
<Badge
|
|
250
|
+
size="4"
|
|
251
|
+
theme="gray"
|
|
252
|
+
variant="outline"
|
|
253
|
+
label={formatDuration(trace.durationMs)}
|
|
254
|
+
/>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
</SelectItem>
|
|
258
|
+
))}
|
|
259
|
+
</SelectContent>
|
|
260
|
+
</Select>
|
|
261
|
+
|
|
262
|
+
<div className="rounded-lg border border-border bg-card shadow-sm">
|
|
263
|
+
<div className="flex items-center justify-between gap-2 border-b border-border p-4">
|
|
264
|
+
<SearchInput
|
|
265
|
+
id="span-search-desktop"
|
|
266
|
+
name="search"
|
|
267
|
+
onClear={() => setSearchValue("")}
|
|
268
|
+
value={searchValue}
|
|
269
|
+
onValueChange={setSearchValue}
|
|
270
|
+
className="max-w-60 grow"
|
|
271
|
+
/>
|
|
272
|
+
|
|
273
|
+
<div className="flex items-center gap-2">
|
|
274
|
+
<div className="ml-auto flex items-center gap-3">
|
|
275
|
+
<ExpandAllButton onExpandAll={handleExpandAll} />
|
|
276
|
+
<CollapseAllButton onCollapseAll={handleCollapseAll} />
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
|
|
281
|
+
{filteredSpans.length === 0 ? (
|
|
282
|
+
<div className="p-3 text-center text-muted-foreground">
|
|
283
|
+
No spans found
|
|
284
|
+
</div>
|
|
285
|
+
) : (
|
|
286
|
+
<TreeView
|
|
287
|
+
spans={filteredSpans}
|
|
288
|
+
onSpanSelect={setSelectedSpan}
|
|
289
|
+
selectedSpan={selectedSpan}
|
|
290
|
+
expandedSpansIds={expandedSpansIds}
|
|
291
|
+
onExpandSpansIdsChange={setExpandedSpansIds}
|
|
292
|
+
spanCardViewOptions={selectedTrace.spanCardViewOptions}
|
|
293
|
+
/>
|
|
294
|
+
)}
|
|
295
|
+
</div>
|
|
296
|
+
|
|
297
|
+
{selectedSpan ? (
|
|
298
|
+
<DetailsView data={selectedSpan} />
|
|
299
|
+
) : (
|
|
300
|
+
<Placeholder title="Select a span to see the details" />
|
|
301
|
+
)}
|
|
302
|
+
</div>
|
|
303
|
+
) : (
|
|
304
|
+
<div className="flex flex-col gap-4">
|
|
305
|
+
<Select
|
|
306
|
+
value=""
|
|
307
|
+
onValueChange={(value) => {
|
|
308
|
+
const trace = traceRecords.find((t) => t.id === value);
|
|
309
|
+
if (trace) handleTraceSelect(trace);
|
|
310
|
+
}}
|
|
311
|
+
>
|
|
312
|
+
<SelectTrigger className="w-full">
|
|
313
|
+
<SelectValue placeholder="Select a trace to view details..." />
|
|
314
|
+
</SelectTrigger>
|
|
315
|
+
<SelectContent className="w-full max-w-2xl max-h-[400px]">
|
|
316
|
+
{traceRecords.map((trace) => (
|
|
317
|
+
<SelectItem key={trace.id} value={trace.id} className="py-2.5 px-4 cursor-pointer">
|
|
318
|
+
<div className="flex items-center justify-between gap-4 w-full pr-6">
|
|
319
|
+
<div className="flex flex-col gap-1 min-w-0">
|
|
320
|
+
<span className="font-semibold text-base text-foreground">{trace.name}</span>
|
|
321
|
+
{trace.agentDescription && (
|
|
322
|
+
<span className="text-xs text-muted-foreground">
|
|
323
|
+
{trace.agentDescription}
|
|
324
|
+
</span>
|
|
325
|
+
)}
|
|
326
|
+
</div>
|
|
327
|
+
<div className="flex items-center gap-2 flex-shrink-0">
|
|
328
|
+
{typeof trace.startTime === "number" && (
|
|
329
|
+
<span className="text-xs text-muted-foreground font-mono">
|
|
330
|
+
{new Date(trace.startTime).toLocaleTimeString()}
|
|
331
|
+
</span>
|
|
332
|
+
)}
|
|
333
|
+
<Badge
|
|
334
|
+
size="4"
|
|
335
|
+
theme="gray"
|
|
336
|
+
variant="outline"
|
|
337
|
+
label={`${trace.spansCount} span${trace.spansCount === 1 ? '' : 's'}`}
|
|
338
|
+
/>
|
|
339
|
+
<Badge
|
|
340
|
+
size="4"
|
|
341
|
+
theme="gray"
|
|
342
|
+
variant="outline"
|
|
343
|
+
label={formatDuration(trace.durationMs)}
|
|
344
|
+
/>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
</SelectItem>
|
|
348
|
+
))}
|
|
349
|
+
</SelectContent>
|
|
350
|
+
</Select>
|
|
351
|
+
<Placeholder title="Select a trace to see the details" />
|
|
352
|
+
</div>
|
|
353
|
+
)}
|
|
354
|
+
</div>
|
|
355
|
+
);
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const MobileLayout = ({
|
|
359
|
+
traceRecords,
|
|
360
|
+
traceListExpanded,
|
|
361
|
+
setTraceListExpanded,
|
|
362
|
+
selectedTrace,
|
|
363
|
+
setSelectedTrace,
|
|
364
|
+
selectedTraceSpans,
|
|
365
|
+
setSelectedTraceSpans,
|
|
366
|
+
selectedSpan,
|
|
367
|
+
setSelectedSpan,
|
|
368
|
+
searchValue,
|
|
369
|
+
setSearchValue,
|
|
370
|
+
filteredSpans,
|
|
371
|
+
expandedSpansIds,
|
|
372
|
+
setExpandedSpansIds,
|
|
373
|
+
handleExpandAll,
|
|
374
|
+
handleCollapseAll,
|
|
375
|
+
handleTraceSelect,
|
|
376
|
+
}: LayoutProps) => {
|
|
377
|
+
if (!selectedTrace) {
|
|
378
|
+
return (
|
|
379
|
+
<TraceList
|
|
380
|
+
traces={traceRecords}
|
|
381
|
+
expanded={traceListExpanded}
|
|
382
|
+
onExpandStateChange={setTraceListExpanded}
|
|
383
|
+
onTraceSelect={handleTraceSelect}
|
|
384
|
+
selectedTrace={selectedTrace}
|
|
385
|
+
/>
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (selectedTrace && selectedTraceSpans.length && !selectedSpan) {
|
|
390
|
+
return (
|
|
391
|
+
<div className="flex flex-col gap-4">
|
|
392
|
+
<Button
|
|
393
|
+
onClick={() => {
|
|
394
|
+
setSelectedTrace(undefined);
|
|
395
|
+
setSelectedTraceSpans([]);
|
|
396
|
+
}}
|
|
397
|
+
iconStart={<ArrowLeft className="size-3" />}
|
|
398
|
+
variant="ghost"
|
|
399
|
+
className="self-start"
|
|
400
|
+
>
|
|
401
|
+
Traces list
|
|
402
|
+
</Button>
|
|
403
|
+
|
|
404
|
+
<TraceListItemHeader trace={selectedTrace} />
|
|
405
|
+
|
|
406
|
+
<div className="rounded border border-border bg-card">
|
|
407
|
+
<div className="flex items-center justify-between gap-2 border-b border-border p-3">
|
|
408
|
+
<SearchInput
|
|
409
|
+
id="span-search-mobile"
|
|
410
|
+
name="search"
|
|
411
|
+
onClear={() => setSearchValue("")}
|
|
412
|
+
value={searchValue}
|
|
413
|
+
onValueChange={setSearchValue}
|
|
414
|
+
className="max-w-60 grow"
|
|
415
|
+
/>
|
|
416
|
+
|
|
417
|
+
<div className="flex items-center gap-2">
|
|
418
|
+
<div className="ml-auto flex items-center gap-3">
|
|
419
|
+
<ExpandAllButton onExpandAll={handleExpandAll} />
|
|
420
|
+
<CollapseAllButton onCollapseAll={handleCollapseAll} />
|
|
421
|
+
</div>
|
|
422
|
+
</div>
|
|
423
|
+
</div>
|
|
424
|
+
|
|
425
|
+
{filteredSpans.length === 0 ? (
|
|
426
|
+
<div className="p-3 text-center text-muted-foreground">
|
|
427
|
+
No spans found
|
|
428
|
+
</div>
|
|
429
|
+
) : (
|
|
430
|
+
<TreeView
|
|
431
|
+
spans={filteredSpans}
|
|
432
|
+
spanCardViewOptions={selectedTrace.spanCardViewOptions}
|
|
433
|
+
onSpanSelect={setSelectedSpan}
|
|
434
|
+
selectedSpan={selectedSpan}
|
|
435
|
+
expandedSpansIds={expandedSpansIds}
|
|
436
|
+
onExpandSpansIdsChange={setExpandedSpansIds}
|
|
437
|
+
/>
|
|
438
|
+
)}
|
|
439
|
+
</div>
|
|
440
|
+
</div>
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (selectedTrace && selectedTraceSpans.length && selectedSpan) {
|
|
445
|
+
return (
|
|
446
|
+
<div className="flex flex-col gap-4">
|
|
447
|
+
<Button
|
|
448
|
+
onClick={() => {
|
|
449
|
+
setSelectedSpan(undefined);
|
|
450
|
+
}}
|
|
451
|
+
iconStart={<ArrowLeft className="size-3" />}
|
|
452
|
+
variant="ghost"
|
|
453
|
+
className="self-start"
|
|
454
|
+
>
|
|
455
|
+
Tree View
|
|
456
|
+
</Button>
|
|
457
|
+
|
|
458
|
+
<DetailsView data={selectedSpan} />
|
|
459
|
+
</div>
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return null;
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
interface PlaceholderProps {
|
|
467
|
+
title: string;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const Placeholder = ({ title }: PlaceholderProps) => {
|
|
471
|
+
return (
|
|
472
|
+
<p className="hidden items-center justify-center rounded-lg bg-muted p-4 text-center text-muted-foreground lg:flex">
|
|
473
|
+
{title}
|
|
474
|
+
</p>
|
|
475
|
+
);
|
|
476
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { TraceSpan } from "@evilmartians/agent-prism-types";
|
|
2
|
+
|
|
3
|
+
import { findTimeRange, flattenSpans } from "@evilmartians/agent-prism-data";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
import { type FC } from "react";
|
|
6
|
+
|
|
7
|
+
import { SpanCard, type SpanCardViewOptions } from "./SpanCard/SpanCard";
|
|
8
|
+
|
|
9
|
+
interface TreeViewProps {
|
|
10
|
+
spans: TraceSpan[];
|
|
11
|
+
className?: string;
|
|
12
|
+
selectedSpan?: TraceSpan;
|
|
13
|
+
onSpanSelect?: (span: TraceSpan) => void;
|
|
14
|
+
expandedSpansIds: string[];
|
|
15
|
+
onExpandSpansIdsChange: (ids: string[]) => void;
|
|
16
|
+
spanCardViewOptions?: SpanCardViewOptions;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const TreeView: FC<TreeViewProps> = ({
|
|
20
|
+
spans,
|
|
21
|
+
onSpanSelect,
|
|
22
|
+
className = "",
|
|
23
|
+
selectedSpan,
|
|
24
|
+
expandedSpansIds,
|
|
25
|
+
onExpandSpansIdsChange,
|
|
26
|
+
spanCardViewOptions,
|
|
27
|
+
}) => {
|
|
28
|
+
const allCards = flattenSpans(spans);
|
|
29
|
+
|
|
30
|
+
const { minStart, maxEnd } = findTimeRange(allCards);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className="w-full min-w-0 p-4">
|
|
34
|
+
<ul
|
|
35
|
+
className={cn(className, "overflow-x-auto space-y-1")}
|
|
36
|
+
role="tree"
|
|
37
|
+
aria-label="Hierarchical card list"
|
|
38
|
+
>
|
|
39
|
+
{spans.map((span, idx) => (
|
|
40
|
+
<SpanCard
|
|
41
|
+
key={span.id}
|
|
42
|
+
data={span}
|
|
43
|
+
level={0}
|
|
44
|
+
selectedSpan={selectedSpan}
|
|
45
|
+
onSpanSelect={onSpanSelect}
|
|
46
|
+
minStart={minStart}
|
|
47
|
+
maxEnd={maxEnd}
|
|
48
|
+
isLastChild={idx === spans.length - 1}
|
|
49
|
+
expandedSpansIds={expandedSpansIds}
|
|
50
|
+
onExpandSpansIdsChange={onExpandSpansIdsChange}
|
|
51
|
+
viewOptions={spanCardViewOptions}
|
|
52
|
+
/>
|
|
53
|
+
))}
|
|
54
|
+
</ul>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
};
|