@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,75 @@
|
|
|
1
|
+
import type { ComponentPropsWithRef } from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
import type { ComponentSize } from "./shared";
|
|
6
|
+
|
|
7
|
+
type IconButtonSize = Extract<
|
|
8
|
+
ComponentSize,
|
|
9
|
+
"6" | "7" | "8" | "9" | "10" | "11" | "12" | "16"
|
|
10
|
+
>;
|
|
11
|
+
type IconButtonVariant = "default" | "ghost";
|
|
12
|
+
|
|
13
|
+
export type IconButtonProps = ComponentPropsWithRef<"button"> & {
|
|
14
|
+
/**
|
|
15
|
+
* The size of the icon button
|
|
16
|
+
*/
|
|
17
|
+
size?: IconButtonSize;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The visual variant of the icon button
|
|
21
|
+
*/
|
|
22
|
+
variant?: IconButtonVariant;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Accessible label for screen readers
|
|
26
|
+
* Required for accessibility compliance
|
|
27
|
+
*/
|
|
28
|
+
"aria-label": string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const sizeClasses: Record<IconButtonSize, string> = {
|
|
32
|
+
"6": "h-6 min-h-6",
|
|
33
|
+
"7": "h-7 min-h-7",
|
|
34
|
+
"8": "h-8 min-h-8",
|
|
35
|
+
"9": "h-9 min-h-9",
|
|
36
|
+
"10": "h-10 min-h-10",
|
|
37
|
+
"11": "h-11 min-h-11",
|
|
38
|
+
"12": "h-12 min-h-12",
|
|
39
|
+
"16": "h-16 min-h-16",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const variantClasses: Record<IconButtonVariant, string> = {
|
|
43
|
+
default: "border border-border bg-transparent ",
|
|
44
|
+
ghost: "bg-transparent",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// TODO: Remake to call Icon component directly instead of passing children
|
|
48
|
+
export const IconButton = ({
|
|
49
|
+
children,
|
|
50
|
+
className,
|
|
51
|
+
size = "6",
|
|
52
|
+
variant = "default",
|
|
53
|
+
type = "button",
|
|
54
|
+
"aria-label": ariaLabel,
|
|
55
|
+
...rest
|
|
56
|
+
}: IconButtonProps) => {
|
|
57
|
+
return (
|
|
58
|
+
<button
|
|
59
|
+
type={type}
|
|
60
|
+
aria-label={ariaLabel}
|
|
61
|
+
className={cn(
|
|
62
|
+
className,
|
|
63
|
+
sizeClasses[size],
|
|
64
|
+
"inline-flex aspect-square shrink-0 items-center justify-center",
|
|
65
|
+
"rounded-md",
|
|
66
|
+
variantClasses[variant],
|
|
67
|
+
"text-muted-foreground ",
|
|
68
|
+
"hover:bg-gray-200 dark:hover:bg-gray-800",
|
|
69
|
+
)}
|
|
70
|
+
{...rest}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
</button>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ComponentPropsWithRef } from "react";
|
|
2
|
+
|
|
3
|
+
import { Badge, type BadgeProps } from "./Badge";
|
|
4
|
+
|
|
5
|
+
export type PriceBadgeProps = ComponentPropsWithRef<"span"> & {
|
|
6
|
+
cost: number;
|
|
7
|
+
size?: BadgeProps["size"];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const PriceBadge = ({ cost, size, ...rest }: PriceBadgeProps) => {
|
|
11
|
+
return <Badge theme="gray" size={size} {...rest} label={`$ ${cost}`} />;
|
|
12
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Search } from "lucide-react";
|
|
2
|
+
|
|
3
|
+
import { TextInput, type TextInputProps } from "./TextInput";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A simple wrapper around the TextInput component.
|
|
7
|
+
* It adds a search icon and a placeholder.
|
|
8
|
+
*/
|
|
9
|
+
export const SearchInput = ({ ...props }: TextInputProps) => {
|
|
10
|
+
return (
|
|
11
|
+
<TextInput
|
|
12
|
+
startIcon={<Search className="size-4" />}
|
|
13
|
+
placeholder="Filter..."
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
import type { TraceSpan } from "@evilmartians/agent-prism-types";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
formatDuration,
|
|
5
|
+
getTimelineData,
|
|
6
|
+
} from "@evilmartians/agent-prism-data";
|
|
7
|
+
import * as Collapsible from "@radix-ui/react-collapsible";
|
|
8
|
+
import { cn } from "@/lib/utils";
|
|
9
|
+
import {
|
|
10
|
+
type FC,
|
|
11
|
+
useCallback,
|
|
12
|
+
type KeyboardEvent,
|
|
13
|
+
type MouseEvent,
|
|
14
|
+
} from "react";
|
|
15
|
+
|
|
16
|
+
import { Avatar, type AvatarProps } from "../Avatar.tsx";
|
|
17
|
+
import { getSpanCategoryTheme } from "../shared.ts";
|
|
18
|
+
import { SpanStatus } from "../SpanStatus.tsx";
|
|
19
|
+
import { SpanCardBadges } from "./SpanCardBadges.tsx";
|
|
20
|
+
import {
|
|
21
|
+
type SpanCardConnectorType,
|
|
22
|
+
SpanCardConnector,
|
|
23
|
+
} from "./SpanCardConnector.tsx";
|
|
24
|
+
import { SpanCardTimeline } from "./SpanCardTimeline.tsx";
|
|
25
|
+
import { SpanCardToggle } from "./SpanCardToggle.tsx";
|
|
26
|
+
|
|
27
|
+
const LAYOUT_CONSTANTS = {
|
|
28
|
+
CONNECTOR_WIDTH: 20,
|
|
29
|
+
CONTENT_BASE_WIDTH: 320,
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
type ExpandButtonPlacement = "inside" | "outside";
|
|
33
|
+
|
|
34
|
+
export type SpanCardViewOptions = {
|
|
35
|
+
withStatus?: boolean;
|
|
36
|
+
expandButton?: ExpandButtonPlacement;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const DEFAULT_VIEW_OPTIONS: Required<SpanCardViewOptions> = {
|
|
40
|
+
withStatus: true,
|
|
41
|
+
expandButton: "inside",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
interface SpanCardProps {
|
|
45
|
+
data: TraceSpan;
|
|
46
|
+
level?: number;
|
|
47
|
+
selectedSpan?: TraceSpan;
|
|
48
|
+
avatar?: AvatarProps;
|
|
49
|
+
onSpanSelect?: (span: TraceSpan) => void;
|
|
50
|
+
minStart: number;
|
|
51
|
+
maxEnd: number;
|
|
52
|
+
isLastChild: boolean;
|
|
53
|
+
prevLevelConnectors?: SpanCardConnectorType[];
|
|
54
|
+
expandedSpansIds: string[];
|
|
55
|
+
onExpandSpansIdsChange: (ids: string[]) => void;
|
|
56
|
+
viewOptions?: SpanCardViewOptions;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface SpanCardState {
|
|
60
|
+
isExpanded: boolean;
|
|
61
|
+
hasChildren: boolean;
|
|
62
|
+
isSelected: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const getContentWidth = ({
|
|
66
|
+
level,
|
|
67
|
+
hasExpandButton,
|
|
68
|
+
contentPadding,
|
|
69
|
+
expandButton,
|
|
70
|
+
}: {
|
|
71
|
+
level: number;
|
|
72
|
+
hasExpandButton: boolean;
|
|
73
|
+
contentPadding: number;
|
|
74
|
+
expandButton: ExpandButtonPlacement;
|
|
75
|
+
}) => {
|
|
76
|
+
let width =
|
|
77
|
+
LAYOUT_CONSTANTS.CONTENT_BASE_WIDTH -
|
|
78
|
+
level * LAYOUT_CONSTANTS.CONNECTOR_WIDTH;
|
|
79
|
+
|
|
80
|
+
if (hasExpandButton && expandButton === "inside") {
|
|
81
|
+
width -= LAYOUT_CONSTANTS.CONNECTOR_WIDTH;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (expandButton === "outside" && level === 0) {
|
|
85
|
+
width -= LAYOUT_CONSTANTS.CONNECTOR_WIDTH;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return width - contentPadding;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const getGridTemplateColumns = ({
|
|
92
|
+
connectorsColumnWidth,
|
|
93
|
+
expandButton,
|
|
94
|
+
}: {
|
|
95
|
+
connectorsColumnWidth: number;
|
|
96
|
+
expandButton: ExpandButtonPlacement;
|
|
97
|
+
}) => {
|
|
98
|
+
if (expandButton === "inside") {
|
|
99
|
+
return `${connectorsColumnWidth}px 1fr`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return `${connectorsColumnWidth}px 1fr ${LAYOUT_CONSTANTS.CONNECTOR_WIDTH}px`;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const getContentPadding = ({
|
|
106
|
+
level,
|
|
107
|
+
hasExpandButton,
|
|
108
|
+
}: {
|
|
109
|
+
level: number;
|
|
110
|
+
hasExpandButton: boolean;
|
|
111
|
+
}) => {
|
|
112
|
+
if (level === 0) return 0;
|
|
113
|
+
|
|
114
|
+
if (hasExpandButton) return 4;
|
|
115
|
+
|
|
116
|
+
return 8;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const getConnectorsLayout = ({
|
|
120
|
+
level,
|
|
121
|
+
hasExpandButton,
|
|
122
|
+
isLastChild,
|
|
123
|
+
prevConnectors,
|
|
124
|
+
expandButton,
|
|
125
|
+
}: {
|
|
126
|
+
hasExpandButton: boolean;
|
|
127
|
+
isLastChild: boolean;
|
|
128
|
+
level: number;
|
|
129
|
+
prevConnectors: SpanCardConnectorType[];
|
|
130
|
+
expandButton: ExpandButtonPlacement;
|
|
131
|
+
}): {
|
|
132
|
+
connectors: SpanCardConnectorType[];
|
|
133
|
+
connectorsColumnWidth: number;
|
|
134
|
+
} => {
|
|
135
|
+
const connectors: SpanCardConnectorType[] = [];
|
|
136
|
+
|
|
137
|
+
if (level === 0) {
|
|
138
|
+
return {
|
|
139
|
+
connectors: expandButton === "inside" ? [] : ["vertical"],
|
|
140
|
+
connectorsColumnWidth: 20,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for (let i = 0; i < level - 1; i++) {
|
|
145
|
+
connectors.push("vertical");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!isLastChild) {
|
|
149
|
+
connectors.push("t-right");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (isLastChild) {
|
|
153
|
+
connectors.push("corner-top-right");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let connectorsColumnWidth =
|
|
157
|
+
connectors.length * LAYOUT_CONSTANTS.CONNECTOR_WIDTH;
|
|
158
|
+
|
|
159
|
+
if (hasExpandButton) {
|
|
160
|
+
connectorsColumnWidth += LAYOUT_CONSTANTS.CONNECTOR_WIDTH;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
for (let i = 0; i < prevConnectors.length; i++) {
|
|
164
|
+
if (
|
|
165
|
+
prevConnectors[i] === "empty" ||
|
|
166
|
+
prevConnectors[i] === "corner-top-right"
|
|
167
|
+
) {
|
|
168
|
+
connectors[i] = "empty";
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
connectors,
|
|
174
|
+
connectorsColumnWidth,
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const useSpanCardEventHandlers = (
|
|
179
|
+
data: TraceSpan,
|
|
180
|
+
onSpanSelect?: (span: TraceSpan) => void,
|
|
181
|
+
) => {
|
|
182
|
+
const handleCardClick = useCallback((): void => {
|
|
183
|
+
onSpanSelect?.(data);
|
|
184
|
+
}, [data, onSpanSelect]);
|
|
185
|
+
|
|
186
|
+
const handleKeyDown = useCallback(
|
|
187
|
+
(e: KeyboardEvent): void => {
|
|
188
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
189
|
+
e.preventDefault();
|
|
190
|
+
handleCardClick();
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
[handleCardClick],
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const handleToggleClick = useCallback(
|
|
197
|
+
(e: MouseEvent | KeyboardEvent): void => {
|
|
198
|
+
e.stopPropagation();
|
|
199
|
+
},
|
|
200
|
+
[],
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
handleCardClick,
|
|
205
|
+
handleKeyDown,
|
|
206
|
+
handleToggleClick,
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const SpanCardChildren: FC<{
|
|
211
|
+
data: TraceSpan;
|
|
212
|
+
level: number;
|
|
213
|
+
selectedSpan?: TraceSpan;
|
|
214
|
+
onSpanSelect?: (span: TraceSpan) => void;
|
|
215
|
+
minStart: number;
|
|
216
|
+
maxEnd: number;
|
|
217
|
+
prevLevelConnectors: SpanCardConnectorType[];
|
|
218
|
+
expandedSpansIds: string[];
|
|
219
|
+
onExpandSpansIdsChange: (ids: string[]) => void;
|
|
220
|
+
viewOptions?: SpanCardViewOptions;
|
|
221
|
+
}> = ({
|
|
222
|
+
data,
|
|
223
|
+
level,
|
|
224
|
+
selectedSpan,
|
|
225
|
+
onSpanSelect,
|
|
226
|
+
minStart,
|
|
227
|
+
maxEnd,
|
|
228
|
+
prevLevelConnectors,
|
|
229
|
+
expandedSpansIds,
|
|
230
|
+
onExpandSpansIdsChange,
|
|
231
|
+
viewOptions = DEFAULT_VIEW_OPTIONS,
|
|
232
|
+
}) => {
|
|
233
|
+
if (!data.children?.length) return null;
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<div className="relative">
|
|
237
|
+
<Collapsible.Content>
|
|
238
|
+
<ul role="group">
|
|
239
|
+
{data.children.map((child, idx) => (
|
|
240
|
+
<SpanCard
|
|
241
|
+
viewOptions={viewOptions}
|
|
242
|
+
key={child.id}
|
|
243
|
+
data={child}
|
|
244
|
+
minStart={minStart}
|
|
245
|
+
maxEnd={maxEnd}
|
|
246
|
+
level={level + 1}
|
|
247
|
+
selectedSpan={selectedSpan}
|
|
248
|
+
onSpanSelect={onSpanSelect}
|
|
249
|
+
isLastChild={idx === (data.children || []).length - 1}
|
|
250
|
+
prevLevelConnectors={prevLevelConnectors}
|
|
251
|
+
expandedSpansIds={expandedSpansIds}
|
|
252
|
+
onExpandSpansIdsChange={onExpandSpansIdsChange}
|
|
253
|
+
/>
|
|
254
|
+
))}
|
|
255
|
+
</ul>
|
|
256
|
+
</Collapsible.Content>
|
|
257
|
+
</div>
|
|
258
|
+
);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export const SpanCard: FC<SpanCardProps> = ({
|
|
262
|
+
data,
|
|
263
|
+
level = 0,
|
|
264
|
+
selectedSpan,
|
|
265
|
+
onSpanSelect,
|
|
266
|
+
viewOptions = DEFAULT_VIEW_OPTIONS,
|
|
267
|
+
avatar,
|
|
268
|
+
minStart,
|
|
269
|
+
maxEnd,
|
|
270
|
+
isLastChild,
|
|
271
|
+
prevLevelConnectors = [],
|
|
272
|
+
expandedSpansIds,
|
|
273
|
+
onExpandSpansIdsChange,
|
|
274
|
+
}) => {
|
|
275
|
+
const isExpanded = expandedSpansIds.includes(data.id);
|
|
276
|
+
|
|
277
|
+
const withStatus = viewOptions.withStatus ?? DEFAULT_VIEW_OPTIONS.withStatus;
|
|
278
|
+
const expandButton =
|
|
279
|
+
viewOptions.expandButton || DEFAULT_VIEW_OPTIONS.expandButton;
|
|
280
|
+
|
|
281
|
+
const handleToggleClick = useCallback(
|
|
282
|
+
(expanded: boolean) => {
|
|
283
|
+
const alreadyExpanded = expandedSpansIds.includes(data.id);
|
|
284
|
+
|
|
285
|
+
if (alreadyExpanded && !expanded) {
|
|
286
|
+
onExpandSpansIdsChange(expandedSpansIds.filter((id) => id !== data.id));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (!alreadyExpanded && expanded) {
|
|
290
|
+
onExpandSpansIdsChange([...expandedSpansIds, data.id]);
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
[isExpanded, expandedSpansIds, data.id, onExpandSpansIdsChange],
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
const state: SpanCardState = {
|
|
297
|
+
isExpanded,
|
|
298
|
+
hasChildren: Boolean(data.children?.length),
|
|
299
|
+
isSelected: selectedSpan?.id === data.id,
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const eventHandlers = useSpanCardEventHandlers(data, onSpanSelect);
|
|
303
|
+
|
|
304
|
+
const { durationMs } = getTimelineData({
|
|
305
|
+
spanCard: data,
|
|
306
|
+
minStart,
|
|
307
|
+
maxEnd,
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const hasExpandButtonAsFirstChild =
|
|
311
|
+
expandButton === "inside" && state.hasChildren;
|
|
312
|
+
|
|
313
|
+
const contentPadding = getContentPadding({
|
|
314
|
+
level,
|
|
315
|
+
hasExpandButton: hasExpandButtonAsFirstChild,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
const contentWidth = getContentWidth({
|
|
319
|
+
level,
|
|
320
|
+
hasExpandButton: hasExpandButtonAsFirstChild,
|
|
321
|
+
contentPadding,
|
|
322
|
+
expandButton,
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
const { connectors, connectorsColumnWidth } = getConnectorsLayout({
|
|
326
|
+
level,
|
|
327
|
+
hasExpandButton: hasExpandButtonAsFirstChild,
|
|
328
|
+
isLastChild,
|
|
329
|
+
prevConnectors: prevLevelConnectors,
|
|
330
|
+
expandButton,
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const gridTemplateColumns = getGridTemplateColumns({
|
|
334
|
+
connectorsColumnWidth,
|
|
335
|
+
expandButton,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
return (
|
|
339
|
+
<li
|
|
340
|
+
role="treeitem"
|
|
341
|
+
aria-expanded={state.hasChildren ? state.isExpanded : undefined}
|
|
342
|
+
className="list-none"
|
|
343
|
+
>
|
|
344
|
+
<Collapsible.Root
|
|
345
|
+
open={state.isExpanded}
|
|
346
|
+
onOpenChange={handleToggleClick}
|
|
347
|
+
>
|
|
348
|
+
<div
|
|
349
|
+
className={cn(
|
|
350
|
+
"relative grid w-full rounded-md transition-colors",
|
|
351
|
+
state.isSelected
|
|
352
|
+
? "bg-accent/50 border border-accent"
|
|
353
|
+
: "hover:bg-muted/30",
|
|
354
|
+
)}
|
|
355
|
+
style={{
|
|
356
|
+
gridTemplateColumns,
|
|
357
|
+
}}
|
|
358
|
+
onClick={eventHandlers.handleCardClick}
|
|
359
|
+
onKeyDown={eventHandlers.handleKeyDown}
|
|
360
|
+
tabIndex={0}
|
|
361
|
+
role="button"
|
|
362
|
+
aria-pressed={state.isSelected}
|
|
363
|
+
aria-describedby={`span-card-desc-${data.id}`}
|
|
364
|
+
aria-expanded={state.hasChildren ? state.isExpanded : undefined}
|
|
365
|
+
aria-label={`${state.isSelected ? "Selected" : "Not selected"} span card for ${data.title} at level ${level}`}
|
|
366
|
+
>
|
|
367
|
+
<div className="flex flex-nowrap">
|
|
368
|
+
{connectors.map((connector, idx) => (
|
|
369
|
+
<SpanCardConnector key={`${connector}-${idx}`} type={connector} />
|
|
370
|
+
))}
|
|
371
|
+
|
|
372
|
+
{hasExpandButtonAsFirstChild && (
|
|
373
|
+
<div className="flex w-5 flex-col items-center">
|
|
374
|
+
<SpanCardToggle
|
|
375
|
+
isExpanded={state.isExpanded}
|
|
376
|
+
title={data.title}
|
|
377
|
+
onToggleClick={eventHandlers.handleToggleClick}
|
|
378
|
+
/>
|
|
379
|
+
|
|
380
|
+
{state.isExpanded && <SpanCardConnector type="vertical" />}
|
|
381
|
+
</div>
|
|
382
|
+
)}
|
|
383
|
+
</div>
|
|
384
|
+
<div
|
|
385
|
+
className={cn(
|
|
386
|
+
"flex flex-nowrap items-center align-middle gap-x-3 gap py-3 px-2",
|
|
387
|
+
"min-h-5 w-full cursor-pointer",
|
|
388
|
+
level !== 0 && !hasExpandButtonAsFirstChild && "pl-2",
|
|
389
|
+
level !== 0 && hasExpandButtonAsFirstChild && "pl-1",
|
|
390
|
+
)}
|
|
391
|
+
>
|
|
392
|
+
<div
|
|
393
|
+
className="relative flex min-h-4 flex-shrink-0 flex-grow-0 flex-wrap items-start gap-1"
|
|
394
|
+
style={{
|
|
395
|
+
width: `min(${contentWidth}px, 100%)`,
|
|
396
|
+
minWidth: 140,
|
|
397
|
+
}}
|
|
398
|
+
>
|
|
399
|
+
{avatar && <Avatar size="4" {...avatar} />}
|
|
400
|
+
|
|
401
|
+
<h3
|
|
402
|
+
className="mr-1 h-4 max-w-32 truncate text-sm leading-[14px] text-foreground "
|
|
403
|
+
title={data.title}
|
|
404
|
+
>
|
|
405
|
+
{data.title}
|
|
406
|
+
</h3>
|
|
407
|
+
|
|
408
|
+
<SpanCardBadges data={data} />
|
|
409
|
+
</div>
|
|
410
|
+
|
|
411
|
+
<div className="flex flex-shrink flex-grow flex-nowrap items-center justify-end gap-1 min-w-0">
|
|
412
|
+
{expandButton === "outside" && withStatus && (
|
|
413
|
+
<div className="flex-shrink-0">
|
|
414
|
+
<SpanStatus status={data.status} />
|
|
415
|
+
</div>
|
|
416
|
+
)}
|
|
417
|
+
|
|
418
|
+
<SpanCardTimeline
|
|
419
|
+
theme={getSpanCategoryTheme(data.type)}
|
|
420
|
+
minStart={minStart}
|
|
421
|
+
maxEnd={maxEnd}
|
|
422
|
+
spanCard={data}
|
|
423
|
+
className="max-w-48 flex-shrink"
|
|
424
|
+
/>
|
|
425
|
+
|
|
426
|
+
<div className="flex items-center gap-2 flex-shrink-0">
|
|
427
|
+
<span className="inline-block w-14 whitespace-nowrap px-1 text-right text-xs text-foreground">
|
|
428
|
+
{formatDuration(durationMs)}
|
|
429
|
+
</span>
|
|
430
|
+
|
|
431
|
+
{expandButton === "inside" && withStatus && (
|
|
432
|
+
<div>
|
|
433
|
+
<SpanStatus status={data.status} />
|
|
434
|
+
</div>
|
|
435
|
+
)}
|
|
436
|
+
</div>
|
|
437
|
+
</div>
|
|
438
|
+
</div>
|
|
439
|
+
|
|
440
|
+
{expandButton === "outside" &&
|
|
441
|
+
(state.hasChildren ? (
|
|
442
|
+
<SpanCardToggle
|
|
443
|
+
isExpanded={state.isExpanded}
|
|
444
|
+
title={data.title}
|
|
445
|
+
onToggleClick={eventHandlers.handleToggleClick}
|
|
446
|
+
/>
|
|
447
|
+
) : (
|
|
448
|
+
<div />
|
|
449
|
+
))}
|
|
450
|
+
</div>
|
|
451
|
+
|
|
452
|
+
<SpanCardChildren
|
|
453
|
+
minStart={minStart}
|
|
454
|
+
maxEnd={maxEnd}
|
|
455
|
+
viewOptions={viewOptions}
|
|
456
|
+
data={data}
|
|
457
|
+
level={level}
|
|
458
|
+
selectedSpan={selectedSpan}
|
|
459
|
+
onSpanSelect={onSpanSelect}
|
|
460
|
+
prevLevelConnectors={connectors}
|
|
461
|
+
expandedSpansIds={expandedSpansIds}
|
|
462
|
+
onExpandSpansIdsChange={onExpandSpansIdsChange}
|
|
463
|
+
/>
|
|
464
|
+
</Collapsible.Root>
|
|
465
|
+
</li>
|
|
466
|
+
);
|
|
467
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { TraceSpan } from "@evilmartians/agent-prism-types";
|
|
2
|
+
|
|
3
|
+
import { Badge } from "../Badge.tsx";
|
|
4
|
+
import { PriceBadge } from "../PriceBadge.tsx";
|
|
5
|
+
import {
|
|
6
|
+
getSpanCategoryIcon,
|
|
7
|
+
getSpanCategoryLabel,
|
|
8
|
+
getSpanCategoryTheme,
|
|
9
|
+
} from "../shared.ts";
|
|
10
|
+
import { TokensBadge } from "../TokensBadge.tsx";
|
|
11
|
+
|
|
12
|
+
interface SpanCardBagdesProps {
|
|
13
|
+
data: TraceSpan;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const SpanCardBadges = ({ data }: SpanCardBagdesProps) => {
|
|
17
|
+
const Icon = getSpanCategoryIcon(data.type);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="flex flex-wrap items-center justify-start gap-1">
|
|
21
|
+
<Badge
|
|
22
|
+
iconStart={<Icon className="size-2.5" />}
|
|
23
|
+
theme={getSpanCategoryTheme(data.type)}
|
|
24
|
+
size="4"
|
|
25
|
+
label={getSpanCategoryLabel(data.type)}
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
{typeof data.tokensCount === "number" && (
|
|
29
|
+
<TokensBadge tokensCount={data.tokensCount} />
|
|
30
|
+
)}
|
|
31
|
+
|
|
32
|
+
{typeof data.cost === "number" && <PriceBadge cost={data.cost} />}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type SpanCardConnectorType =
|
|
2
|
+
| "horizontal"
|
|
3
|
+
| "vertical"
|
|
4
|
+
| "t-right"
|
|
5
|
+
| "corner-top-right"
|
|
6
|
+
| "empty";
|
|
7
|
+
|
|
8
|
+
interface SpanCardConnectorProps {
|
|
9
|
+
type: SpanCardConnectorType;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const SpanCardConnector = ({ type }: SpanCardConnectorProps) => {
|
|
13
|
+
if (type === "empty") return <div className="w-5 shrink-0 grow" />;
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div className="relative w-5 shrink-0 grow">
|
|
17
|
+
{(type === "vertical" || type === "t-right") && (
|
|
18
|
+
<div className="absolute bottom-0 left-1/2 top-0 w-0.5 -translate-x-1/2 bg-muted" />
|
|
19
|
+
)}
|
|
20
|
+
|
|
21
|
+
{type === "t-right" && (
|
|
22
|
+
<div className="absolute left-2.5 top-2.5 h-0.5 w-2.5 -translate-y-[3px] bg-muted" />
|
|
23
|
+
)}
|
|
24
|
+
|
|
25
|
+
{type === "corner-top-right" && (
|
|
26
|
+
<>
|
|
27
|
+
<div className="absolute left-1/2 top-2 size-0.5 -translate-x-1/2 -translate-y-px bg-muted" />
|
|
28
|
+
|
|
29
|
+
<div className="absolute left-1/2 top-2.5 h-0.5 w-2.5 -translate-y-[3px] bg-muted" />
|
|
30
|
+
|
|
31
|
+
<div className="absolute left-1/2 top-0 h-[7px] w-0.5 -translate-x-px bg-muted" />
|
|
32
|
+
</>
|
|
33
|
+
)}
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { TraceSpan } from "@evilmartians/agent-prism-types";
|
|
2
|
+
|
|
3
|
+
import { getTimelineData } from "@evilmartians/agent-prism-data";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
import type { ColorVariant } from "../shared.ts";
|
|
7
|
+
|
|
8
|
+
interface SpanCardTimelineProps {
|
|
9
|
+
spanCard: TraceSpan;
|
|
10
|
+
theme: ColorVariant;
|
|
11
|
+
minStart: number;
|
|
12
|
+
maxEnd: number;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const timelineBgColors: Record<ColorVariant, string> = {
|
|
17
|
+
purple: "bg-primary",
|
|
18
|
+
indigo: "bg-primary",
|
|
19
|
+
orange: "bg-chart-1",
|
|
20
|
+
teal: "bg-chart-2",
|
|
21
|
+
cyan: "bg-chart-3",
|
|
22
|
+
sky: "bg-chart-4",
|
|
23
|
+
yellow: "bg-chart-5",
|
|
24
|
+
emerald: "bg-accent",
|
|
25
|
+
red: "bg-destructive",
|
|
26
|
+
gray: "bg-muted-foreground",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const SpanCardTimeline = ({
|
|
30
|
+
spanCard,
|
|
31
|
+
theme,
|
|
32
|
+
minStart,
|
|
33
|
+
maxEnd,
|
|
34
|
+
className,
|
|
35
|
+
}: SpanCardTimelineProps) => {
|
|
36
|
+
const { startPercent, widthPercent } = getTimelineData({
|
|
37
|
+
spanCard,
|
|
38
|
+
minStart,
|
|
39
|
+
maxEnd,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<span
|
|
44
|
+
className={cn(
|
|
45
|
+
"relative flex h-4 min-w-20 flex-1 rounded bg-muted",
|
|
46
|
+
className,
|
|
47
|
+
)}
|
|
48
|
+
>
|
|
49
|
+
<span className="pointer-events-none absolute inset-x-1 top-1/2 h-1.5 -translate-y-1/2">
|
|
50
|
+
<span
|
|
51
|
+
className={`absolute h-full rounded-sm ${timelineBgColors[theme]}`}
|
|
52
|
+
style={{
|
|
53
|
+
left: `${startPercent}%`,
|
|
54
|
+
width: `${widthPercent}%`,
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
</span>
|
|
58
|
+
</span>
|
|
59
|
+
);
|
|
60
|
+
};
|