@tonyclaw/agent-inspector 2.1.15 → 2.1.17
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/.output/cli.js +57 -13
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-BcEd6V-V.js → CompareDrawer-B9sLBHw5.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-DbWjc_BE.js +106 -0
- package/.output/public/assets/{ReplayDialog-BNpC0548.js → ReplayDialog-D9I9W9n3.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-Ds1uRLVB.js → RequestAnatomy-DGXK_Rii.js} +1 -1
- package/.output/public/assets/{ResponseView-7KPVqKl5.js → ResponseView-df_JzwsS.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-BHQT261s.js → StreamingChunkSequence-i3DM5IKL.js} +1 -1
- package/.output/public/assets/_sessionId-CTpCHdFh.js +1 -0
- package/.output/public/assets/index-Blqvndcn.js +1 -0
- package/.output/public/assets/index-Cs79WRQj.css +1 -0
- package/.output/public/assets/{index-DjKt8XKe.js → index-Dqc2r1ea.js} +1 -1
- package/.output/public/assets/{json-viewer-CkCu-rka.js → json-viewer-D3QWQlSB.js} +1 -1
- package/.output/public/assets/{main-DpD1N0S8.js → main-ZhxhOCmF.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +255 -201
- package/.output/server/{_sessionId-DF9Sy8cP.mjs → _sessionId-YFrP0lZP.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-BoxztaO7.mjs → CompareDrawer-qC3adEeq.mjs} +3 -3
- package/.output/server/_ssr/{ProxyViewerContainer-CRBkqFlJ.mjs → ProxyViewerContainer-DCZIJC2b.mjs} +2732 -1849
- package/.output/server/_ssr/{ReplayDialog-Cc1dyDuK.mjs → ReplayDialog-CvX4XYCi.mjs} +4 -4
- package/.output/server/_ssr/{RequestAnatomy-CMGSsz5Z.mjs → RequestAnatomy-HQfrxjO1.mjs} +3 -3
- package/.output/server/_ssr/{ResponseView-Cp10DM1D.mjs → ResponseView-QSlSD11e.mjs} +3 -3
- package/.output/server/_ssr/{StreamingChunkSequence-B1VGxy3A.mjs → StreamingChunkSequence-DTAiwlnI.mjs} +3 -3
- package/.output/server/_ssr/{index-47XVPghS.mjs → index-D_EzfXaN.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-zDE2rrmJ.mjs → json-viewer-CrIX0e0Q.mjs} +3 -3
- package/.output/server/_ssr/{router-DVeuZFqI.mjs → router-__o2wrfO.mjs} +505 -136
- package/.output/server/{_tanstack-start-manifest_v-Bp8JxtPW.mjs → _tanstack-start-manifest_v-DbqyLpsI.mjs} +1 -1
- package/.output/server/index.mjs +63 -63
- package/README.md +57 -2
- package/package.json +1 -1
- package/src/cli/startupOutput.ts +16 -0
- package/src/cli.ts +61 -15
- package/src/components/ProxyViewer.tsx +535 -163
- package/src/components/ProxyViewerContainer.tsx +77 -67
- package/src/components/alerts/AlertsDialog.tsx +2 -1
- package/src/components/clients/ClientLogo.tsx +1 -1
- package/src/components/groups/GroupsDialog.tsx +19 -1
- package/src/components/providers/ProviderCard.tsx +133 -32
- package/src/components/providers/ProviderForm.tsx +61 -29
- package/src/components/providers/ProviderLogo.tsx +6 -1
- package/src/components/providers/ProvidersPanel.tsx +7 -7
- package/src/components/providers/SettingsDialog.tsx +13 -6
- package/src/components/proxy-viewer/AgentTraceSummary.tsx +6 -6
- package/src/components/proxy-viewer/ConversationGroup.tsx +43 -32
- package/src/components/proxy-viewer/ConversationGroupList.tsx +452 -89
- package/src/components/proxy-viewer/ConversationHeader.tsx +153 -120
- package/src/components/proxy-viewer/LogEntry.tsx +130 -43
- package/src/components/proxy-viewer/LogEntryHeader.tsx +357 -354
- package/src/components/proxy-viewer/ProviderLogoStack.tsx +2 -2
- package/src/components/proxy-viewer/RequestToolsPanel.tsx +9 -9
- package/src/components/proxy-viewer/ThreadConnector.tsx +102 -83
- package/src/components/proxy-viewer/TurnGroup.tsx +33 -69
- package/src/components/proxy-viewer/TurnGroupList.tsx +3 -0
- package/src/components/proxy-viewer/bodyHydration.ts +56 -0
- package/src/components/proxy-viewer/viewerState.ts +0 -4
- package/src/components/ui/icon-trigger.ts +2 -0
- package/src/components/ui/transient-toast.tsx +104 -0
- package/src/lib/providerTestContract.ts +56 -10
- package/src/lib/providerTestPrompt.ts +23 -1
- package/src/lib/sessionInfoContract.ts +4 -0
- package/src/lib/upstreamUrl.ts +105 -1
- package/src/mcp/server.ts +71 -27
- package/src/mcp/toolHandlers.ts +24 -5
- package/src/proxy/formats/openai/alibabaProvider.ts +0 -4
- package/src/proxy/sessionInfo.ts +31 -2
- package/src/proxy/sessionSupervisor.ts +20 -1
- package/src/proxy/store.ts +1 -1
- package/src/routes/api/providers.$providerId.test.log.ts +93 -14
- package/styles/globals.css +180 -180
- package/.output/public/assets/ProxyViewerContainer-h851qWNp.js +0 -106
- package/.output/public/assets/_sessionId-DWePGjnS.js +0 -1
- package/.output/public/assets/index-CI1-G8ua.js +0 -1
- package/.output/public/assets/index-DdhFqPsI.css +0 -1
|
@@ -32,10 +32,9 @@ import { Badge } from "../ui/badge";
|
|
|
32
32
|
import { Button } from "../ui/button";
|
|
33
33
|
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/tooltip";
|
|
34
34
|
import type { CacheTrend } from "./cacheTrend";
|
|
35
|
-
import { ROW_CHEVRON_SLOT_CLASS } from "./ProviderLogoStack";
|
|
36
35
|
|
|
37
36
|
const STATUS_BADGE_CLASSES: Record<StatusCategory, string> = {
|
|
38
|
-
success: "bg-
|
|
37
|
+
success: "bg-slate-300/[0.08] text-slate-100 border-slate-300/20",
|
|
39
38
|
client_error: "bg-amber-300/[0.10] text-amber-100 border-amber-300/30",
|
|
40
39
|
server_error: "bg-rose-400/[0.10] text-rose-100 border-rose-300/30",
|
|
41
40
|
pending: "bg-cyan-300/[0.08] text-cyan-100 border-cyan-300/25",
|
|
@@ -46,36 +45,31 @@ const STATUS_CUE_CLASSES: Record<
|
|
|
46
45
|
{
|
|
47
46
|
frame: string;
|
|
48
47
|
dot: string;
|
|
49
|
-
rail: string;
|
|
50
48
|
}
|
|
51
49
|
> = {
|
|
52
50
|
success: {
|
|
53
|
-
frame: "border-
|
|
54
|
-
dot: "bg-
|
|
55
|
-
rail: "bg-emerald-300/45 shadow-[0_0_22px_rgba(110,231,183,0.28)]",
|
|
51
|
+
frame: "border-cyan-200/15 bg-white/[0.04] text-slate-100",
|
|
52
|
+
dot: "bg-cyan-200/80 shadow-[0_0_14px_rgba(125,211,252,0.65)]",
|
|
56
53
|
},
|
|
57
54
|
client_error: {
|
|
58
55
|
frame: "border-amber-300/25 bg-amber-300/[0.08] text-amber-100",
|
|
59
56
|
dot: "bg-amber-200 shadow-[0_0_14px_rgba(252,211,77,0.8)]",
|
|
60
|
-
rail: "bg-amber-300/50 shadow-[0_0_22px_rgba(252,211,77,0.28)]",
|
|
61
57
|
},
|
|
62
58
|
server_error: {
|
|
63
59
|
frame: "border-rose-300/25 bg-rose-300/[0.08] text-rose-100",
|
|
64
60
|
dot: "bg-rose-300 shadow-[0_0_14px_rgba(253,164,175,0.8)]",
|
|
65
|
-
rail: "bg-rose-300/50 shadow-[0_0_22px_rgba(253,164,175,0.28)]",
|
|
66
61
|
},
|
|
67
62
|
pending: {
|
|
68
63
|
frame: "border-cyan-300/25 bg-cyan-300/[0.08] text-cyan-100",
|
|
69
64
|
dot: "bg-cyan-200 shadow-[0_0_14px_rgba(103,232,249,0.8)]",
|
|
70
|
-
rail: "bg-cyan-300/50 shadow-[0_0_22px_rgba(103,232,249,0.28)]",
|
|
71
65
|
},
|
|
72
66
|
};
|
|
73
67
|
|
|
74
68
|
const HEADER_ACTION_BUTTON_CLASS =
|
|
75
|
-
"
|
|
69
|
+
"border border-input bg-background hover:bg-accent hover:text-accent-foreground size-8 text-muted-foreground shadow-none hover:text-cyan-100";
|
|
76
70
|
|
|
77
71
|
const TELEMETRY_CHIP_CLASS =
|
|
78
|
-
"flex h-7 items-center gap-1 rounded-md
|
|
72
|
+
"border border-border bg-muted/40 flex h-7 items-center gap-1 rounded-md px-2 text-xs";
|
|
79
73
|
|
|
80
74
|
function formatElapsed(ms: number): string {
|
|
81
75
|
if (ms < 1000) return `${ms}ms`;
|
|
@@ -101,7 +95,7 @@ function statusCueLabel(category: StatusCategory, status: number | null): string
|
|
|
101
95
|
}
|
|
102
96
|
|
|
103
97
|
/**
|
|
104
|
-
* Inline trend indicator: small arrow (
|
|
98
|
+
* Inline trend indicator: small arrow (up / down) plus the absolute
|
|
105
99
|
* delta in compact form. Returns `null` when there is no trend to display.
|
|
106
100
|
*/
|
|
107
101
|
function CacheTrendIndicator({ trend }: { trend: CacheTrend | null }): JSX.Element | null {
|
|
@@ -111,7 +105,7 @@ function CacheTrendIndicator({ trend }: { trend: CacheTrend | null }): JSX.Eleme
|
|
|
111
105
|
const sign = isUp ? "+" : "-";
|
|
112
106
|
return (
|
|
113
107
|
<span className="flex items-center gap-0.5 text-muted-foreground tabular-nums">
|
|
114
|
-
<Icon className={isUp ? "size-3 text-
|
|
108
|
+
<Icon className={isUp ? "size-3 text-cyan-300" : "size-3 text-rose-400"} />
|
|
115
109
|
<span className="font-mono">
|
|
116
110
|
{sign}
|
|
117
111
|
{formatTokens(trend.delta)}
|
|
@@ -122,12 +116,12 @@ function CacheTrendIndicator({ trend }: { trend: CacheTrend | null }): JSX.Eleme
|
|
|
122
116
|
|
|
123
117
|
/**
|
|
124
118
|
* Per-tab action bundle surfaced in the header when the tab is active.
|
|
125
|
-
* - `copyText === null`
|
|
126
|
-
* - `expansion === null`
|
|
119
|
+
* - `copyText === null` - Copy button hidden (e.g. tab has no body to copy)
|
|
120
|
+
* - `expansion === null` - Expand-all button hidden (e.g. flat dict, or
|
|
127
121
|
* not valid JSON). When present, the button reflects the JSON viewer's
|
|
128
122
|
* bulk-expansion state and the caller is responsible for forwarding the
|
|
129
123
|
* matching `bulkDepth` / `bulkRevision` to the underlying viewer.
|
|
130
|
-
* - `diffWithRaw` / `diffWithPrevious`
|
|
124
|
+
* - `diffWithRaw` / `diffWithPrevious` - optional Diff buttons. Undefined
|
|
131
125
|
* means "not applicable" (hidden). Today only the Request tab sets them.
|
|
132
126
|
*/
|
|
133
127
|
export type HeaderTabAction = {
|
|
@@ -181,6 +175,8 @@ export type LogEntryHeaderProps = {
|
|
|
181
175
|
onReplay?: () => void;
|
|
182
176
|
/** Slow-response threshold in seconds. `0` disables the warning indicator. */
|
|
183
177
|
slowResponseThresholdSeconds?: number;
|
|
178
|
+
/** Show client PID/project metadata on each log row. Parent process rows can disable this. */
|
|
179
|
+
showClientMetadata?: boolean;
|
|
184
180
|
/** Controls whether timestamps render as compact local time or full ISO strings. */
|
|
185
181
|
timeDisplayFormat: TimeDisplayFormat;
|
|
186
182
|
};
|
|
@@ -197,6 +193,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
197
193
|
tabActions,
|
|
198
194
|
onReplay,
|
|
199
195
|
slowResponseThresholdSeconds = 0,
|
|
196
|
+
showClientMetadata = true,
|
|
200
197
|
timeDisplayFormat,
|
|
201
198
|
}: LogEntryHeaderProps): JSX.Element {
|
|
202
199
|
const statusCategory = getStatusCategory(log.responseStatus);
|
|
@@ -221,6 +218,9 @@ export const LogEntryHeader = memo(function ({
|
|
|
221
218
|
log.clientCwd !== null && log.clientCwd !== undefined && log.clientCwd !== "";
|
|
222
219
|
const statusCue = STATUS_CUE_CLASSES[statusCategory];
|
|
223
220
|
const statusLabel = statusCueLabel(statusCategory, log.responseStatus);
|
|
221
|
+
const showStatusBadge = statusCategory !== "success" || log.responseStatus !== 200;
|
|
222
|
+
const showStandaloneStreamingIndicator =
|
|
223
|
+
log.streaming && firstChunkLabel === null && tokenRateLabel === null;
|
|
224
224
|
|
|
225
225
|
return (
|
|
226
226
|
<TooltipProvider>
|
|
@@ -231,11 +231,11 @@ export const LogEntryHeader = memo(function ({
|
|
|
231
231
|
data-nav-id={`log-${log.id}`}
|
|
232
232
|
data-nav-action={expanded ? "collapse" : "expand"}
|
|
233
233
|
className={cn(
|
|
234
|
-
"group/header relative isolate flex min-w-0
|
|
234
|
+
"group/header relative isolate flex min-w-0 cursor-pointer items-start gap-2 overflow-hidden px-3 py-2 transition-[background-color,border-color,box-shadow]",
|
|
235
235
|
expanded
|
|
236
|
-
? "bg-[
|
|
237
|
-
: "bg-
|
|
238
|
-
"select-none before:pointer-events-none before:absolute before:inset-x-
|
|
236
|
+
? "bg-white/[0.035] shadow-[inset_0_1px_0_rgba(255,255,255,0.10)]"
|
|
237
|
+
: "bg-transparent hover:bg-white/[0.035]",
|
|
238
|
+
"select-none before:pointer-events-none before:absolute before:inset-x-3 before:top-0 before:h-px before:bg-white/12",
|
|
239
239
|
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none",
|
|
240
240
|
)}
|
|
241
241
|
onClick={onToggle}
|
|
@@ -246,375 +246,378 @@ export const LogEntryHeader = memo(function ({
|
|
|
246
246
|
}
|
|
247
247
|
}}
|
|
248
248
|
>
|
|
249
|
-
<span
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
<
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
)}
|
|
262
|
-
</span>
|
|
263
|
-
|
|
264
|
-
{/* Request ID */}
|
|
265
|
-
<span
|
|
266
|
-
className={cn(
|
|
267
|
-
"inline-flex h-8 shrink-0 items-center gap-2 rounded-md border px-2 font-mono shadow-[inset_0_1px_0_rgba(255,255,255,0.05)]",
|
|
268
|
-
statusCue.frame,
|
|
269
|
-
)}
|
|
270
|
-
>
|
|
271
|
-
<span className={cn("size-1.5 rounded-full", statusCue.dot)} aria-hidden="true" />
|
|
272
|
-
<span className="text-xs font-semibold tabular-nums" title={`Log ID ${String(log.id)}`}>
|
|
273
|
-
#{displayNumber}
|
|
249
|
+
<span className="flex min-w-0 flex-1 flex-wrap items-center gap-x-2 gap-y-1">
|
|
250
|
+
{/* Request ID */}
|
|
251
|
+
<span
|
|
252
|
+
className={cn(
|
|
253
|
+
"border border-border bg-muted/40 inline-flex h-8 shrink-0 items-center gap-2 rounded-md px-2 font-mono",
|
|
254
|
+
statusCue.frame,
|
|
255
|
+
)}
|
|
256
|
+
>
|
|
257
|
+
<span className={cn("size-1.5 rounded-full", statusCue.dot)} aria-hidden="true" />
|
|
258
|
+
<span className="text-xs font-semibold tabular-nums" title={`Log ID ${String(log.id)}`}>
|
|
259
|
+
#{displayNumber}
|
|
260
|
+
</span>
|
|
274
261
|
</span>
|
|
275
|
-
</span>
|
|
276
262
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
263
|
+
{/* Request start time */}
|
|
264
|
+
<span className="border border-border bg-muted/40 flex h-7 shrink-0 items-center gap-1 rounded-md px-2 text-xs text-muted-foreground">
|
|
265
|
+
<Clock className="size-3" />
|
|
266
|
+
<span className="font-mono tabular-nums" title={log.timestamp}>
|
|
267
|
+
{formatTimestamp(log.timestamp, timeDisplayFormat)}
|
|
268
|
+
</span>
|
|
282
269
|
</span>
|
|
283
|
-
</span>
|
|
284
270
|
|
|
285
|
-
|
|
271
|
+
{/* Response Status - only shown for non-200 or pending. Each category
|
|
286
272
|
carries a distinct icon in addition to color so the meaning is
|
|
287
273
|
legible without color perception. */}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
274
|
+
{showStatusBadge && (
|
|
275
|
+
<Badge
|
|
276
|
+
variant="outline"
|
|
277
|
+
className={cn(
|
|
278
|
+
"h-6 gap-1 rounded-md px-2 py-0 font-mono text-[10px] font-semibold uppercase tabular-nums shadow-[inset_0_1px_0_rgba(255,255,255,0.05)]",
|
|
279
|
+
STATUS_BADGE_CLASSES[statusCategory],
|
|
280
|
+
)}
|
|
281
|
+
>
|
|
282
|
+
{statusCategory === "success" ? (
|
|
283
|
+
<Check className="size-3" aria-label="Success" />
|
|
284
|
+
) : statusCategory === "server_error" ? (
|
|
285
|
+
<OctagonAlert className="size-3" aria-label="Server error" />
|
|
286
|
+
) : statusCategory === "client_error" ? (
|
|
287
|
+
<AlertTriangle className="size-3" aria-label="Client error" />
|
|
288
|
+
) : (
|
|
289
|
+
<Loader2 className="size-3 animate-spin" aria-label="Pending" />
|
|
290
|
+
)}
|
|
291
|
+
{statusLabel}
|
|
292
|
+
</Badge>
|
|
293
293
|
)}
|
|
294
|
-
>
|
|
295
|
-
{statusCategory === "success" ? (
|
|
296
|
-
<Check className="size-3" aria-label="Success" />
|
|
297
|
-
) : statusCategory === "server_error" ? (
|
|
298
|
-
<OctagonAlert className="size-3" aria-label="Server error" />
|
|
299
|
-
) : statusCategory === "client_error" ? (
|
|
300
|
-
<AlertTriangle className="size-3" aria-label="Client error" />
|
|
301
|
-
) : (
|
|
302
|
-
<Loader2 className="size-3 animate-spin" aria-label="Pending" />
|
|
303
|
-
)}
|
|
304
|
-
{statusLabel}
|
|
305
|
-
</Badge>
|
|
306
|
-
|
|
307
|
-
{warningCount > 0 && (
|
|
308
|
-
<Tooltip>
|
|
309
|
-
<TooltipTrigger asChild>
|
|
310
|
-
<Badge
|
|
311
|
-
variant="outline"
|
|
312
|
-
className="h-6 gap-1 rounded-md border-amber-300/30 bg-amber-300/[0.10] px-2 py-0 font-mono text-[10px] tabular-nums text-amber-100"
|
|
313
|
-
>
|
|
314
|
-
<AlertTriangle className="size-3" aria-label="Tool schema warnings" />
|
|
315
|
-
{warningCount}
|
|
316
|
-
</Badge>
|
|
317
|
-
</TooltipTrigger>
|
|
318
|
-
<TooltipContent>
|
|
319
|
-
{warningCount === 1
|
|
320
|
-
? "1 tool schema warning"
|
|
321
|
-
: `${warningCount} tool schema warnings`}
|
|
322
|
-
</TooltipContent>
|
|
323
|
-
</Tooltip>
|
|
324
|
-
)}
|
|
325
294
|
|
|
326
|
-
|
|
327
|
-
response threshold — for normal-speed logs the elapsed value is
|
|
328
|
-
self-explanatory, so we skip the tooltip wrapper to keep the row
|
|
329
|
-
quiet. */}
|
|
330
|
-
{log.elapsedMs !== null &&
|
|
331
|
-
(isSlowResponse ? (
|
|
295
|
+
{warningCount > 0 && (
|
|
332
296
|
<Tooltip>
|
|
333
297
|
<TooltipTrigger asChild>
|
|
334
|
-
<
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
"shrink-0 border-amber-300/25 text-amber-200",
|
|
338
|
-
)}
|
|
298
|
+
<Badge
|
|
299
|
+
variant="outline"
|
|
300
|
+
className="h-6 gap-1 rounded-md border-amber-300/30 bg-amber-300/[0.10] px-2 py-0 font-mono text-[10px] tabular-nums text-amber-100"
|
|
339
301
|
>
|
|
340
|
-
<
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
</span>
|
|
302
|
+
<AlertTriangle className="size-3" aria-label="Tool schema warnings" />
|
|
303
|
+
{warningCount}
|
|
304
|
+
</Badge>
|
|
344
305
|
</TooltipTrigger>
|
|
345
306
|
<TooltipContent>
|
|
346
|
-
|
|
347
|
-
|
|
307
|
+
{warningCount === 1
|
|
308
|
+
? "1 tool schema warning"
|
|
309
|
+
: `${warningCount} tool schema warnings`}
|
|
348
310
|
</TooltipContent>
|
|
349
311
|
</Tooltip>
|
|
350
|
-
)
|
|
351
|
-
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-muted-foreground")}>
|
|
352
|
-
<Clock className="size-3" />
|
|
353
|
-
<span className="font-mono tabular-nums">{formatElapsed(log.elapsedMs)}</span>
|
|
354
|
-
</span>
|
|
355
|
-
))}
|
|
312
|
+
)}
|
|
356
313
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
314
|
+
{/* Elapsed time. Tooltip is only shown when this log exceeded the slow-
|
|
315
|
+
response threshold - for normal-speed logs the elapsed value is
|
|
316
|
+
self-explanatory, so we skip the tooltip wrapper to keep the row
|
|
317
|
+
quiet. */}
|
|
318
|
+
{log.elapsedMs !== null &&
|
|
319
|
+
(isSlowResponse ? (
|
|
320
|
+
<Tooltip>
|
|
321
|
+
<TooltipTrigger asChild>
|
|
322
|
+
<span
|
|
323
|
+
className={cn(
|
|
324
|
+
TELEMETRY_CHIP_CLASS,
|
|
325
|
+
"shrink-0 border-amber-300/25 text-amber-200",
|
|
326
|
+
)}
|
|
327
|
+
>
|
|
328
|
+
<Clock className="size-3" />
|
|
329
|
+
<span className="font-mono tabular-nums">{formatElapsed(log.elapsedMs)}</span>
|
|
330
|
+
<AlertTriangle className="size-3" aria-label="Slow response" />
|
|
331
|
+
</span>
|
|
332
|
+
</TooltipTrigger>
|
|
333
|
+
<TooltipContent>
|
|
334
|
+
Slow response: {formatElapsed(log.elapsedMs)} exceeds{" "}
|
|
335
|
+
{formatElapsed(slowResponseThresholdSeconds * 1000)}
|
|
336
|
+
</TooltipContent>
|
|
337
|
+
</Tooltip>
|
|
338
|
+
) : (
|
|
339
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-muted-foreground")}>
|
|
340
|
+
<Clock className="size-3" />
|
|
341
|
+
<span className="font-mono tabular-nums">{formatElapsed(log.elapsedMs)}</span>
|
|
375
342
|
</span>
|
|
376
|
-
|
|
377
|
-
<TooltipContent>Output token throughput after first chunk</TooltipContent>
|
|
378
|
-
</Tooltip>
|
|
379
|
-
)}
|
|
343
|
+
))}
|
|
380
344
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
<
|
|
396
|
-
className={cn(
|
|
397
|
-
"
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
>
|
|
401
|
-
|
|
345
|
+
{/* Streaming timing */}
|
|
346
|
+
{log.streaming && firstChunkLabel !== null && (
|
|
347
|
+
<Tooltip>
|
|
348
|
+
<TooltipTrigger asChild>
|
|
349
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-cyan-300")}>
|
|
350
|
+
<Radio className="size-3" />
|
|
351
|
+
<span className="font-mono tabular-nums">first {firstChunkLabel}</span>
|
|
352
|
+
</span>
|
|
353
|
+
</TooltipTrigger>
|
|
354
|
+
<TooltipContent>Time to first streaming chunk</TooltipContent>
|
|
355
|
+
</Tooltip>
|
|
356
|
+
)}
|
|
357
|
+
{log.streaming && tokenRateLabel !== null && (
|
|
358
|
+
<Tooltip>
|
|
359
|
+
<TooltipTrigger asChild>
|
|
360
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-cyan-300")}>
|
|
361
|
+
<Gauge className="size-3" />
|
|
362
|
+
<span className="font-mono tabular-nums">{tokenRateLabel}</span>
|
|
363
|
+
</span>
|
|
364
|
+
</TooltipTrigger>
|
|
365
|
+
<TooltipContent>Output token throughput after first chunk</TooltipContent>
|
|
366
|
+
</Tooltip>
|
|
367
|
+
)}
|
|
368
|
+
|
|
369
|
+
{/* Token counts */}
|
|
370
|
+
{hasTokens && (
|
|
371
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "min-w-0 sm:shrink-0")}>
|
|
372
|
+
<Zap className="size-3 text-muted-foreground" />
|
|
373
|
+
<span className="flex min-w-0 flex-wrap items-center gap-x-1 font-mono tabular-nums">
|
|
374
|
+
<span
|
|
375
|
+
className={cn(
|
|
376
|
+
"whitespace-nowrap",
|
|
377
|
+
log.inputTokens !== null ? "text-blue-400" : "text-muted-foreground",
|
|
378
|
+
)}
|
|
379
|
+
>
|
|
380
|
+
IN {log.inputTokens !== null ? formatTokens(log.inputTokens) : "n/a"}
|
|
381
|
+
</span>
|
|
382
|
+
<span className="text-muted-foreground">/</span>
|
|
383
|
+
<span
|
|
384
|
+
className={cn(
|
|
385
|
+
"whitespace-nowrap",
|
|
386
|
+
log.outputTokens !== null ? "text-amber-400" : "text-muted-foreground",
|
|
387
|
+
)}
|
|
388
|
+
>
|
|
389
|
+
OUT {log.outputTokens !== null ? formatTokens(log.outputTokens) : "n/a"}
|
|
390
|
+
</span>
|
|
402
391
|
</span>
|
|
403
392
|
</span>
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
393
|
+
)}
|
|
394
|
+
{/* Cache tokens */}
|
|
395
|
+
{log.cacheCreationInputTokens !== null && log.cacheCreationInputTokens > 0 && (
|
|
396
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0")}>
|
|
397
|
+
<CacheTrendIndicator trend={cacheTrend?.creation ?? null} />
|
|
398
|
+
<span className="font-mono tabular-nums text-cyan-300">
|
|
399
|
+
KV Cache +{formatTokens(log.cacheCreationInputTokens)}
|
|
400
|
+
</span>
|
|
412
401
|
</span>
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
402
|
+
)}
|
|
403
|
+
{log.cacheReadInputTokens !== null && log.cacheReadInputTokens > 0 && (
|
|
404
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0")}>
|
|
405
|
+
<CacheTrendIndicator trend={cacheTrend?.read ?? null} />
|
|
406
|
+
<span className="font-mono tabular-nums text-purple-400">
|
|
407
|
+
KV Cache ~{formatTokens(log.cacheReadInputTokens)}
|
|
408
|
+
</span>
|
|
420
409
|
</span>
|
|
421
|
-
|
|
422
|
-
)}
|
|
423
|
-
|
|
424
|
-
{/* Message count */}
|
|
425
|
-
{messageCount !== null && (
|
|
426
|
-
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-muted-foreground")}>
|
|
427
|
-
<MessageSquare className="size-3" />
|
|
428
|
-
<span className="font-mono tabular-nums">{messageCount}</span>
|
|
429
|
-
</span>
|
|
430
|
-
)}
|
|
410
|
+
)}
|
|
431
411
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
412
|
+
{/* Message count */}
|
|
413
|
+
{messageCount !== null && (
|
|
414
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-muted-foreground")}>
|
|
415
|
+
<MessageSquare className="size-3" />
|
|
416
|
+
<span className="font-mono tabular-nums">{messageCount}</span>
|
|
417
|
+
</span>
|
|
418
|
+
)}
|
|
439
419
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
>
|
|
446
|
-
<Globe className="size-3" />
|
|
447
|
-
<span className="font-mono tabular-nums truncate max-w-[120px]" title={log.origin}>
|
|
448
|
-
{log.origin}
|
|
420
|
+
{/* Tool count */}
|
|
421
|
+
{toolCount !== null && (
|
|
422
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-muted-foreground")}>
|
|
423
|
+
<Wrench className="size-3" />
|
|
424
|
+
<span className="font-mono tabular-nums">{toolCount}</span>
|
|
449
425
|
</span>
|
|
450
|
-
|
|
451
|
-
)}
|
|
426
|
+
)}
|
|
452
427
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
<span className="font-mono tabular-nums">PID {log.clientPid}</span>
|
|
463
|
-
)}
|
|
428
|
+
{/* Origin */}
|
|
429
|
+
{log.origin !== null && (
|
|
430
|
+
<span
|
|
431
|
+
className={cn(TELEMETRY_CHIP_CLASS, "hidden shrink-0 text-muted-foreground xl:flex")}
|
|
432
|
+
title={`Origin: ${log.origin}`}
|
|
433
|
+
>
|
|
434
|
+
<Globe className="size-3" />
|
|
435
|
+
<span className="font-mono tabular-nums truncate max-w-[120px]" title={log.origin}>
|
|
436
|
+
{log.origin}
|
|
464
437
|
</span>
|
|
465
|
-
</
|
|
466
|
-
|
|
467
|
-
{hasClientCwd
|
|
468
|
-
? `PID: ${log.clientPid ?? "?"} CWD: ${log.clientCwd}`
|
|
469
|
-
: `Process ID: ${log.clientPid ?? "?"}`}
|
|
470
|
-
</TooltipContent>
|
|
471
|
-
</Tooltip>
|
|
472
|
-
)}
|
|
438
|
+
</span>
|
|
439
|
+
)}
|
|
473
440
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
441
|
+
{/* Client info (PID + project folder) */}
|
|
442
|
+
{showClientMetadata && (hasClientPid || hasClientProjectFolder) && (
|
|
443
|
+
<Tooltip>
|
|
444
|
+
<TooltipTrigger asChild>
|
|
445
|
+
<span
|
|
446
|
+
className={cn(TELEMETRY_CHIP_CLASS, "hidden shrink-0 text-purple-300 xl:flex")}
|
|
447
|
+
>
|
|
448
|
+
<FileTerminal className="size-3" />
|
|
449
|
+
{hasClientProjectFolder ? (
|
|
450
|
+
<span className="font-mono tabular-nums">{log.clientProjectFolder}</span>
|
|
451
|
+
) : (
|
|
452
|
+
<span className="font-mono tabular-nums">PID {log.clientPid}</span>
|
|
453
|
+
)}
|
|
454
|
+
</span>
|
|
455
|
+
</TooltipTrigger>
|
|
456
|
+
<TooltipContent>
|
|
457
|
+
{hasClientCwd
|
|
458
|
+
? `PID: ${log.clientPid ?? "?"} CWD: ${log.clientCwd}`
|
|
459
|
+
: `Process ID: ${log.clientPid ?? "?"}`}
|
|
460
|
+
</TooltipContent>
|
|
461
|
+
</Tooltip>
|
|
462
|
+
)}
|
|
483
463
|
|
|
484
|
-
|
|
485
|
-
|
|
464
|
+
{/* Streaming indicator */}
|
|
465
|
+
{showStandaloneStreamingIndicator && (
|
|
466
|
+
<Tooltip>
|
|
467
|
+
<TooltipTrigger asChild>
|
|
468
|
+
<Radio className="size-3 text-muted-foreground/60 shrink-0" />
|
|
469
|
+
</TooltipTrigger>
|
|
470
|
+
<TooltipContent>Request used SSE streaming</TooltipContent>
|
|
471
|
+
</Tooltip>
|
|
472
|
+
)}
|
|
473
|
+
</span>
|
|
486
474
|
|
|
487
|
-
|
|
475
|
+
<span className="flex shrink-0 items-start gap-1.5">
|
|
476
|
+
{/* Header actions - only when expanded, so the collapsed view stays
|
|
488
477
|
compact. Buttons stop propagation so they don't toggle the log. */}
|
|
489
|
-
|
|
478
|
+
{expanded && (
|
|
479
|
+
<span
|
|
480
|
+
className="flex items-center gap-1.5 shrink-0"
|
|
481
|
+
onClick={(e) => e.stopPropagation()}
|
|
482
|
+
onKeyDown={(e) => e.stopPropagation()}
|
|
483
|
+
>
|
|
484
|
+
{tabActions !== undefined &&
|
|
485
|
+
activeTab !== undefined &&
|
|
486
|
+
(() => {
|
|
487
|
+
const action = tabActions[activeTab];
|
|
488
|
+
if (action === undefined) return null;
|
|
489
|
+
return (
|
|
490
|
+
<>
|
|
491
|
+
{action.expansion !== null && (
|
|
492
|
+
<Tooltip>
|
|
493
|
+
<TooltipTrigger asChild>
|
|
494
|
+
<Button
|
|
495
|
+
variant="outline"
|
|
496
|
+
size="icon"
|
|
497
|
+
className={HEADER_ACTION_BUTTON_CLASS}
|
|
498
|
+
onClick={action.expansion.onToggle}
|
|
499
|
+
disabled={action.expansion.isPending}
|
|
500
|
+
aria-pressed={action.expansion.isExpanded}
|
|
501
|
+
aria-label={
|
|
502
|
+
action.expansion.isExpanded
|
|
503
|
+
? "Collapse all JSON"
|
|
504
|
+
: "Expand all JSON"
|
|
505
|
+
}
|
|
506
|
+
>
|
|
507
|
+
{action.expansion.isExpanded ? (
|
|
508
|
+
<ChevronsUp className="size-3.5" />
|
|
509
|
+
) : (
|
|
510
|
+
<ChevronsDown className="size-3.5" />
|
|
511
|
+
)}
|
|
512
|
+
</Button>
|
|
513
|
+
</TooltipTrigger>
|
|
514
|
+
<TooltipContent>
|
|
515
|
+
{action.expansion.isExpanded
|
|
516
|
+
? "Collapse all JSON nodes"
|
|
517
|
+
: "Expand all JSON nodes"}
|
|
518
|
+
</TooltipContent>
|
|
519
|
+
</Tooltip>
|
|
520
|
+
)}
|
|
521
|
+
{action.diffWithRaw !== undefined && (
|
|
522
|
+
<Tooltip>
|
|
523
|
+
<TooltipTrigger asChild>
|
|
524
|
+
<Button
|
|
525
|
+
variant="outline"
|
|
526
|
+
size="icon"
|
|
527
|
+
className={cn(
|
|
528
|
+
HEADER_ACTION_BUTTON_CLASS,
|
|
529
|
+
action.diffWithRaw.active &&
|
|
530
|
+
"border-cyan-300/35 bg-cyan-400/15 text-cyan-100",
|
|
531
|
+
)}
|
|
532
|
+
onClick={action.diffWithRaw.onToggle}
|
|
533
|
+
aria-pressed={action.diffWithRaw.active}
|
|
534
|
+
aria-label={
|
|
535
|
+
action.diffWithRaw.active ? "Hide raw diff" : "Diff with raw"
|
|
536
|
+
}
|
|
537
|
+
>
|
|
538
|
+
<FileDiff className="size-3.5" />
|
|
539
|
+
</Button>
|
|
540
|
+
</TooltipTrigger>
|
|
541
|
+
<TooltipContent>
|
|
542
|
+
{action.diffWithRaw.active
|
|
543
|
+
? "Hide diff with raw request"
|
|
544
|
+
: "Show diff between displayed and raw request body"}
|
|
545
|
+
</TooltipContent>
|
|
546
|
+
</Tooltip>
|
|
547
|
+
)}
|
|
548
|
+
{action.diffWithPrevious !== undefined && (
|
|
549
|
+
<Tooltip>
|
|
550
|
+
<TooltipTrigger asChild>
|
|
551
|
+
<Button
|
|
552
|
+
variant="outline"
|
|
553
|
+
size="icon"
|
|
554
|
+
className={HEADER_ACTION_BUTTON_CLASS}
|
|
555
|
+
onClick={action.diffWithPrevious}
|
|
556
|
+
aria-label="Diff with previous"
|
|
557
|
+
>
|
|
558
|
+
<History className="size-3.5" />
|
|
559
|
+
</Button>
|
|
560
|
+
</TooltipTrigger>
|
|
561
|
+
<TooltipContent>
|
|
562
|
+
Compare this request with the immediately preceding one
|
|
563
|
+
</TooltipContent>
|
|
564
|
+
</Tooltip>
|
|
565
|
+
)}
|
|
566
|
+
{action.copyText !== null && (
|
|
567
|
+
<Tooltip>
|
|
568
|
+
<TooltipTrigger asChild>
|
|
569
|
+
<Button
|
|
570
|
+
variant="outline"
|
|
571
|
+
size="icon"
|
|
572
|
+
className={HEADER_ACTION_BUTTON_CLASS}
|
|
573
|
+
onClick={action.onCopy}
|
|
574
|
+
aria-label={action.copyCopied ? "Copied" : action.copyLabel}
|
|
575
|
+
>
|
|
576
|
+
{action.copyCopied ? (
|
|
577
|
+
<Check className="size-3.5 text-cyan-300" />
|
|
578
|
+
) : (
|
|
579
|
+
<Copy className="size-3.5" />
|
|
580
|
+
)}
|
|
581
|
+
</Button>
|
|
582
|
+
</TooltipTrigger>
|
|
583
|
+
<TooltipContent>
|
|
584
|
+
{action.copyCopied ? "Copied to clipboard" : action.copyLabel}
|
|
585
|
+
</TooltipContent>
|
|
586
|
+
</Tooltip>
|
|
587
|
+
)}
|
|
588
|
+
</>
|
|
589
|
+
);
|
|
590
|
+
})()}
|
|
591
|
+
{onReplay !== undefined && (
|
|
592
|
+
<Tooltip>
|
|
593
|
+
<TooltipTrigger asChild>
|
|
594
|
+
<Button
|
|
595
|
+
variant="outline"
|
|
596
|
+
size="icon"
|
|
597
|
+
className={HEADER_ACTION_BUTTON_CLASS}
|
|
598
|
+
onClick={onReplay}
|
|
599
|
+
aria-label="Replay request"
|
|
600
|
+
>
|
|
601
|
+
<RotateCcw className="size-3.5" />
|
|
602
|
+
</Button>
|
|
603
|
+
</TooltipTrigger>
|
|
604
|
+
<TooltipContent>Re-send this request to the provider</TooltipContent>
|
|
605
|
+
</Tooltip>
|
|
606
|
+
)}
|
|
607
|
+
</span>
|
|
608
|
+
)}
|
|
609
|
+
|
|
490
610
|
<span
|
|
491
|
-
className="flex items-center
|
|
492
|
-
|
|
493
|
-
onKeyDown={(e) => e.stopPropagation()}
|
|
611
|
+
className="border border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex size-8 shrink-0 items-center justify-center rounded-md text-slate-200"
|
|
612
|
+
aria-hidden="true"
|
|
494
613
|
>
|
|
495
|
-
{
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
if (action === undefined) return null;
|
|
500
|
-
return (
|
|
501
|
-
<>
|
|
502
|
-
{action.expansion !== null && (
|
|
503
|
-
<Tooltip>
|
|
504
|
-
<TooltipTrigger asChild>
|
|
505
|
-
<Button
|
|
506
|
-
variant="outline"
|
|
507
|
-
size="icon"
|
|
508
|
-
className={HEADER_ACTION_BUTTON_CLASS}
|
|
509
|
-
onClick={action.expansion.onToggle}
|
|
510
|
-
disabled={action.expansion.isPending}
|
|
511
|
-
aria-pressed={action.expansion.isExpanded}
|
|
512
|
-
aria-label={
|
|
513
|
-
action.expansion.isExpanded ? "Collapse all JSON" : "Expand all JSON"
|
|
514
|
-
}
|
|
515
|
-
>
|
|
516
|
-
{action.expansion.isExpanded ? (
|
|
517
|
-
<ChevronsUp className="size-3.5" />
|
|
518
|
-
) : (
|
|
519
|
-
<ChevronsDown className="size-3.5" />
|
|
520
|
-
)}
|
|
521
|
-
</Button>
|
|
522
|
-
</TooltipTrigger>
|
|
523
|
-
<TooltipContent>
|
|
524
|
-
{action.expansion.isExpanded
|
|
525
|
-
? "Collapse all JSON nodes"
|
|
526
|
-
: "Expand all JSON nodes"}
|
|
527
|
-
</TooltipContent>
|
|
528
|
-
</Tooltip>
|
|
529
|
-
)}
|
|
530
|
-
{action.diffWithRaw !== undefined && (
|
|
531
|
-
<Tooltip>
|
|
532
|
-
<TooltipTrigger asChild>
|
|
533
|
-
<Button
|
|
534
|
-
variant="outline"
|
|
535
|
-
size="icon"
|
|
536
|
-
className={cn(
|
|
537
|
-
HEADER_ACTION_BUTTON_CLASS,
|
|
538
|
-
action.diffWithRaw.active &&
|
|
539
|
-
"border-cyan-300/35 bg-cyan-400/15 text-cyan-100",
|
|
540
|
-
)}
|
|
541
|
-
onClick={action.diffWithRaw.onToggle}
|
|
542
|
-
aria-pressed={action.diffWithRaw.active}
|
|
543
|
-
aria-label={
|
|
544
|
-
action.diffWithRaw.active ? "Hide raw diff" : "Diff with raw"
|
|
545
|
-
}
|
|
546
|
-
>
|
|
547
|
-
<FileDiff className="size-3.5" />
|
|
548
|
-
</Button>
|
|
549
|
-
</TooltipTrigger>
|
|
550
|
-
<TooltipContent>
|
|
551
|
-
{action.diffWithRaw.active
|
|
552
|
-
? "Hide diff with raw request"
|
|
553
|
-
: "Show diff between displayed and raw request body"}
|
|
554
|
-
</TooltipContent>
|
|
555
|
-
</Tooltip>
|
|
556
|
-
)}
|
|
557
|
-
{action.diffWithPrevious !== undefined && (
|
|
558
|
-
<Tooltip>
|
|
559
|
-
<TooltipTrigger asChild>
|
|
560
|
-
<Button
|
|
561
|
-
variant="outline"
|
|
562
|
-
size="icon"
|
|
563
|
-
className={HEADER_ACTION_BUTTON_CLASS}
|
|
564
|
-
onClick={action.diffWithPrevious}
|
|
565
|
-
aria-label="Diff with previous"
|
|
566
|
-
>
|
|
567
|
-
<History className="size-3.5" />
|
|
568
|
-
</Button>
|
|
569
|
-
</TooltipTrigger>
|
|
570
|
-
<TooltipContent>
|
|
571
|
-
Compare this request with the immediately preceding one
|
|
572
|
-
</TooltipContent>
|
|
573
|
-
</Tooltip>
|
|
574
|
-
)}
|
|
575
|
-
{action.copyText !== null && (
|
|
576
|
-
<Tooltip>
|
|
577
|
-
<TooltipTrigger asChild>
|
|
578
|
-
<Button
|
|
579
|
-
variant="outline"
|
|
580
|
-
size="icon"
|
|
581
|
-
className={HEADER_ACTION_BUTTON_CLASS}
|
|
582
|
-
onClick={action.onCopy}
|
|
583
|
-
aria-label={action.copyCopied ? "Copied" : action.copyLabel}
|
|
584
|
-
>
|
|
585
|
-
{action.copyCopied ? (
|
|
586
|
-
<Check className="size-3.5 text-emerald-500" />
|
|
587
|
-
) : (
|
|
588
|
-
<Copy className="size-3.5" />
|
|
589
|
-
)}
|
|
590
|
-
</Button>
|
|
591
|
-
</TooltipTrigger>
|
|
592
|
-
<TooltipContent>
|
|
593
|
-
{action.copyCopied ? "Copied to clipboard" : action.copyLabel}
|
|
594
|
-
</TooltipContent>
|
|
595
|
-
</Tooltip>
|
|
596
|
-
)}
|
|
597
|
-
</>
|
|
598
|
-
);
|
|
599
|
-
})()}
|
|
600
|
-
{onReplay !== undefined && (
|
|
601
|
-
<Tooltip>
|
|
602
|
-
<TooltipTrigger asChild>
|
|
603
|
-
<Button
|
|
604
|
-
variant="outline"
|
|
605
|
-
size="icon"
|
|
606
|
-
className={HEADER_ACTION_BUTTON_CLASS}
|
|
607
|
-
onClick={onReplay}
|
|
608
|
-
aria-label="Replay request"
|
|
609
|
-
>
|
|
610
|
-
<RotateCcw className="size-3.5" />
|
|
611
|
-
</Button>
|
|
612
|
-
</TooltipTrigger>
|
|
613
|
-
<TooltipContent>Re-send this request to the provider</TooltipContent>
|
|
614
|
-
</Tooltip>
|
|
614
|
+
{expanded ? (
|
|
615
|
+
<ChevronDown className="size-4 shrink-0" />
|
|
616
|
+
) : (
|
|
617
|
+
<ChevronRight className="size-4 shrink-0" />
|
|
615
618
|
)}
|
|
616
619
|
</span>
|
|
617
|
-
|
|
620
|
+
</span>
|
|
618
621
|
</div>
|
|
619
622
|
</TooltipProvider>
|
|
620
623
|
);
|