@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AlertTriangle, GitCompareArrows } from "lucide-react";
|
|
1
|
+
import { AlertTriangle, GitCompareArrows, Loader2, RefreshCw } from "lucide-react";
|
|
2
2
|
import { Suspense, type JSX, type ReactNode } from "react";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState, memo } from "react";
|
|
4
4
|
import {
|
|
@@ -27,6 +27,12 @@ import {
|
|
|
27
27
|
} from "./lazy";
|
|
28
28
|
import type { AnatomySegment } from "./anatomy/types";
|
|
29
29
|
import { useAnatomyJump } from "./anatomy/useAnatomyJump";
|
|
30
|
+
import {
|
|
31
|
+
describeFullBodyHydrationError,
|
|
32
|
+
formatBodyHydrationFootprint,
|
|
33
|
+
formatBytes,
|
|
34
|
+
type FullBodyHydrationError,
|
|
35
|
+
} from "./bodyHydration";
|
|
30
36
|
import { computeHeadersDiff, computeRequestDiff, DiffView } from "./diff";
|
|
31
37
|
import { LogEntryHeader } from "./LogEntryHeader";
|
|
32
38
|
import { RequestTools } from "./RequestToolsPanel";
|
|
@@ -57,7 +63,7 @@ function resolveFocusedTab(tab: LogFocusTab, anatomySegments: AnatomySegment[] |
|
|
|
57
63
|
}
|
|
58
64
|
}
|
|
59
65
|
|
|
60
|
-
type
|
|
66
|
+
type BodyHydrationStatus = "idle" | "loading" | "failed";
|
|
61
67
|
type BodyChunkState = "idle" | "loading" | "success" | "failed";
|
|
62
68
|
|
|
63
69
|
type BodyPreviewState = {
|
|
@@ -75,7 +81,7 @@ const BODY_CHUNK_BYTES = 256 * 1024;
|
|
|
75
81
|
const CHUNKED_BODY_THRESHOLD_BYTES = 1024 * 1024;
|
|
76
82
|
|
|
77
83
|
const TAB_TRIGGER_CLASS =
|
|
78
|
-
"h-9 flex-none rounded-
|
|
84
|
+
"h-9 flex-none rounded-md border border-transparent bg-transparent px-3 text-xs font-semibold text-muted-foreground shadow-none transition-colors hover:border-white/10 hover:bg-white/[0.045] hover:text-foreground data-[state=active]:border-cyan-200/25 data-[state=active]:bg-cyan-300/[0.08] data-[state=active]:text-cyan-100 data-[state=active]:shadow-none";
|
|
79
85
|
|
|
80
86
|
function createEmptyBodyPreviewState(totalBytes: number | null = null): BodyPreviewState {
|
|
81
87
|
return {
|
|
@@ -108,13 +114,6 @@ function shouldHydrateLogBody(log: CapturedLog): boolean {
|
|
|
108
114
|
return isCompactBody(log) && !shouldUseChunkedBodyLoading(log);
|
|
109
115
|
}
|
|
110
116
|
|
|
111
|
-
function formatBytes(bytes: number): string {
|
|
112
|
-
if (bytes < 1024) return `${bytes.toLocaleString()} B`;
|
|
113
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KiB`;
|
|
114
|
-
if (bytes < 1024 * 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MiB`;
|
|
115
|
-
return `${(bytes / 1024 / 1024 / 1024).toFixed(1)} GiB`;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
117
|
function bodyPreviewErrorMessage(error: unknown): string {
|
|
119
118
|
if (error instanceof ApiTimeoutError) {
|
|
120
119
|
return "Body preview timed out. The log is still available on disk; try loading again.";
|
|
@@ -123,6 +122,57 @@ function bodyPreviewErrorMessage(error: unknown): string {
|
|
|
123
122
|
return "Body preview could not be loaded.";
|
|
124
123
|
}
|
|
125
124
|
|
|
125
|
+
function FullBodyHydrationNotice({
|
|
126
|
+
status,
|
|
127
|
+
error,
|
|
128
|
+
footprint,
|
|
129
|
+
onRetry,
|
|
130
|
+
}: {
|
|
131
|
+
status: BodyHydrationStatus;
|
|
132
|
+
error: FullBodyHydrationError | null;
|
|
133
|
+
footprint: string | null;
|
|
134
|
+
onRetry: () => void;
|
|
135
|
+
}): JSX.Element | null {
|
|
136
|
+
switch (status) {
|
|
137
|
+
case "idle":
|
|
138
|
+
return null;
|
|
139
|
+
case "loading":
|
|
140
|
+
return (
|
|
141
|
+
<NoticeBlock tone="loading" title="Loading full bodies">
|
|
142
|
+
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
143
|
+
<span className="flex min-w-0 items-center gap-2">
|
|
144
|
+
<Loader2 className="size-3.5 shrink-0 animate-spin text-cyan-200" />
|
|
145
|
+
<span>Loading full request and response bodies from the log store.</span>
|
|
146
|
+
</span>
|
|
147
|
+
{footprint !== null && (
|
|
148
|
+
<span className="rounded border border-cyan-300/15 bg-cyan-300/[0.06] px-2 py-0.5 font-mono text-[11px] text-cyan-100">
|
|
149
|
+
{footprint}
|
|
150
|
+
</span>
|
|
151
|
+
)}
|
|
152
|
+
</div>
|
|
153
|
+
</NoticeBlock>
|
|
154
|
+
);
|
|
155
|
+
case "failed":
|
|
156
|
+
return (
|
|
157
|
+
<NoticeBlock tone="warning" title={error?.title ?? "Full bodies could not be loaded"}>
|
|
158
|
+
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
159
|
+
<span>{error?.detail ?? "Compact metadata is still available."}</span>
|
|
160
|
+
<Button
|
|
161
|
+
type="button"
|
|
162
|
+
variant="outline"
|
|
163
|
+
size="sm"
|
|
164
|
+
className="border border-input bg-background hover:bg-accent hover:text-accent-foreground h-7 gap-1.5 text-xs text-amber-100"
|
|
165
|
+
onClick={onRetry}
|
|
166
|
+
>
|
|
167
|
+
<RefreshCw className="size-3" />
|
|
168
|
+
Retry
|
|
169
|
+
</Button>
|
|
170
|
+
</div>
|
|
171
|
+
</NoticeBlock>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
126
176
|
function bodyPreviewTextForView(enabled: boolean, state: BodyPreviewState): string | null {
|
|
127
177
|
if (!enabled) return null;
|
|
128
178
|
if (state.text !== "") return state.text;
|
|
@@ -140,7 +190,7 @@ function ExpandedPanel({
|
|
|
140
190
|
return (
|
|
141
191
|
<div
|
|
142
192
|
className={cn(
|
|
143
|
-
"inspector-scrollbar mx-3 mb-3 max-h-[72vh] overflow-auto rounded-
|
|
193
|
+
"border border-border bg-card shadow-sm inspector-scrollbar mx-3 mb-3 max-h-[72vh] overflow-auto rounded-[8px] p-3",
|
|
144
194
|
className,
|
|
145
195
|
)}
|
|
146
196
|
>
|
|
@@ -151,7 +201,7 @@ function ExpandedPanel({
|
|
|
151
201
|
|
|
152
202
|
function EmptyState({ children }: { children: ReactNode }): JSX.Element {
|
|
153
203
|
return (
|
|
154
|
-
<div className="
|
|
204
|
+
<div className="border border-border bg-muted/40 rounded-md border-dashed px-3 py-5 text-center text-xs italic text-muted-foreground">
|
|
155
205
|
{children}
|
|
156
206
|
</div>
|
|
157
207
|
);
|
|
@@ -159,7 +209,7 @@ function EmptyState({ children }: { children: ReactNode }): JSX.Element {
|
|
|
159
209
|
|
|
160
210
|
function RawTextBlock({ children }: { children: ReactNode }): JSX.Element {
|
|
161
211
|
return (
|
|
162
|
-
<pre className="inspector-scrollbar max-h-[64vh] overflow-auto whitespace-pre-wrap break-words rounded-
|
|
212
|
+
<pre className="border border-border bg-card shadow-sm inspector-scrollbar max-h-[64vh] overflow-auto whitespace-pre-wrap break-words rounded-[8px] p-3 font-mono text-xs leading-relaxed text-muted-foreground">
|
|
163
213
|
{children}
|
|
164
214
|
</pre>
|
|
165
215
|
);
|
|
@@ -231,7 +281,7 @@ function BodyPreviewNotice({
|
|
|
231
281
|
type="button"
|
|
232
282
|
variant="outline"
|
|
233
283
|
size="sm"
|
|
234
|
-
className="
|
|
284
|
+
className="border border-input bg-background hover:bg-accent hover:text-accent-foreground h-7 text-xs text-cyan-100"
|
|
235
285
|
onClick={onLoadMore}
|
|
236
286
|
>
|
|
237
287
|
{actionLabel}
|
|
@@ -257,13 +307,13 @@ function HeaderRows({
|
|
|
257
307
|
if (entries.length === 0) return <EmptyState>{emptyLabel}</EmptyState>;
|
|
258
308
|
|
|
259
309
|
return (
|
|
260
|
-
<div className="
|
|
310
|
+
<div className="border border-border bg-card shadow-sm overflow-hidden rounded-[8px] font-mono text-xs">
|
|
261
311
|
{entries.map(([key, value]) => (
|
|
262
312
|
<div
|
|
263
313
|
key={key}
|
|
264
|
-
className="grid min-w-0 grid-cols-[minmax(140px,0.32fr)_minmax(0,1fr)] border-b border-white/
|
|
314
|
+
className="grid min-w-0 grid-cols-[minmax(140px,0.32fr)_minmax(0,1fr)] border-b border-white/10 last:border-b-0"
|
|
265
315
|
>
|
|
266
|
-
<div className="min-w-0 truncate border-r border-white/
|
|
316
|
+
<div className="min-w-0 truncate border-r border-white/10 bg-white/[0.035] px-2.5 py-2 font-semibold text-cyan-200">
|
|
267
317
|
{key}
|
|
268
318
|
</div>
|
|
269
319
|
<div className="min-w-0 truncate px-2.5 py-2 text-muted-foreground" title={value}>
|
|
@@ -297,6 +347,8 @@ export type LogEntryProps = {
|
|
|
297
347
|
cacheTrend?: CacheTrendEntry | null;
|
|
298
348
|
/** Callback to open CompareDrawer with this log and its immediate predecessor. */
|
|
299
349
|
onCompareWithPrevious?: (log: CapturedLog) => void;
|
|
350
|
+
/** Show client PID/project metadata on the log header row. */
|
|
351
|
+
showClientMetadata?: boolean;
|
|
300
352
|
};
|
|
301
353
|
|
|
302
354
|
function DiffToggleButton({
|
|
@@ -316,7 +368,7 @@ function DiffToggleButton({
|
|
|
316
368
|
"h-8 border-white/10 text-xs shadow-none",
|
|
317
369
|
active
|
|
318
370
|
? "bg-cyan-300 text-black hover:bg-cyan-200"
|
|
319
|
-
: "
|
|
371
|
+
: "border border-input bg-background hover:bg-accent hover:text-accent-foreground text-muted-foreground hover:text-cyan-100",
|
|
320
372
|
)}
|
|
321
373
|
onClick={onClick}
|
|
322
374
|
aria-pressed={active}
|
|
@@ -370,6 +422,7 @@ export const LogEntry = memo(function ({
|
|
|
370
422
|
timeDisplayFormat,
|
|
371
423
|
cacheTrend = null,
|
|
372
424
|
onCompareWithPrevious,
|
|
425
|
+
showClientMetadata = true,
|
|
373
426
|
}: LogEntryProps): JSX.Element {
|
|
374
427
|
const [expanded, setExpanded] = useState<boolean>(false);
|
|
375
428
|
const [replayOpen, setReplayOpen] = useState<boolean>(false);
|
|
@@ -378,7 +431,9 @@ export const LogEntry = memo(function ({
|
|
|
378
431
|
const [activeTab, setActiveTab] = useState("request");
|
|
379
432
|
const [expandToPath, setExpandToPath] = useState<string | null>(null);
|
|
380
433
|
const [hydratedLog, setHydratedLog] = useState<CapturedLog | null>(null);
|
|
381
|
-
const [
|
|
434
|
+
const [bodyHydrationStatus, setBodyHydrationStatus] = useState<BodyHydrationStatus>("idle");
|
|
435
|
+
const [bodyHydrationError, setBodyHydrationError] = useState<FullBodyHydrationError | null>(null);
|
|
436
|
+
const [bodyHydrationRetryNonce, setBodyHydrationRetryNonce] = useState(0);
|
|
382
437
|
const [requestPreview, setRequestPreview] = useState<BodyPreviewState>(() =>
|
|
383
438
|
createEmptyBodyPreviewState(log.rawRequestBodyBytes ?? null),
|
|
384
439
|
);
|
|
@@ -390,6 +445,10 @@ export const LogEntry = memo(function ({
|
|
|
390
445
|
const bodyHydrationInFlightRef = useRef(false);
|
|
391
446
|
const displayLog = hydratedLog ?? log;
|
|
392
447
|
const useChunkedBody = hydratedLog === null && shouldUseChunkedBodyLoading(log);
|
|
448
|
+
const fullBodyHydrationFootprint = useMemo(
|
|
449
|
+
() => formatBodyHydrationFootprint(log.rawRequestBodyBytes, log.responseTextBytes),
|
|
450
|
+
[log.rawRequestBodyBytes, log.responseTextBytes],
|
|
451
|
+
);
|
|
393
452
|
const requestPreviewBody = bodyPreviewTextForView(useChunkedBody, requestPreview);
|
|
394
453
|
const responsePreviewBody = bodyPreviewTextForView(useChunkedBody, responsePreview);
|
|
395
454
|
const rawRequestBodyForView = displayLog.rawRequestBody ?? requestPreviewBody;
|
|
@@ -432,7 +491,7 @@ export const LogEntry = memo(function ({
|
|
|
432
491
|
);
|
|
433
492
|
|
|
434
493
|
// Headers are rendered as a flat list, so we copy them as pretty-printed JSON.
|
|
435
|
-
// Only build the string when there's at least one entry
|
|
494
|
+
// Only build the string when there's at least one entry; empty headers would
|
|
436
495
|
// otherwise copy "{}", which is misleading.
|
|
437
496
|
const headersText = useMemo(
|
|
438
497
|
() =>
|
|
@@ -628,6 +687,14 @@ export const LogEntry = memo(function ({
|
|
|
628
687
|
[log.id],
|
|
629
688
|
);
|
|
630
689
|
|
|
690
|
+
const retryFullBodyHydration = useCallback(() => {
|
|
691
|
+
if (bodyHydrationInFlightRef.current) return;
|
|
692
|
+
setHydratedLog(null);
|
|
693
|
+
setBodyHydrationStatus("idle");
|
|
694
|
+
setBodyHydrationError(null);
|
|
695
|
+
setBodyHydrationRetryNonce((current) => current + 1);
|
|
696
|
+
}, []);
|
|
697
|
+
|
|
631
698
|
const handleToggleExpanded = useCallback(() => {
|
|
632
699
|
const nextExpanded = !expanded;
|
|
633
700
|
if (nextExpanded) {
|
|
@@ -640,7 +707,9 @@ export const LogEntry = memo(function ({
|
|
|
640
707
|
logIdRef.current = log.id;
|
|
641
708
|
bodyHydrationInFlightRef.current = false;
|
|
642
709
|
setHydratedLog(null);
|
|
643
|
-
|
|
710
|
+
setBodyHydrationStatus("idle");
|
|
711
|
+
setBodyHydrationError(null);
|
|
712
|
+
setBodyHydrationRetryNonce(0);
|
|
644
713
|
setRequestPreview(createEmptyBodyPreviewState(log.rawRequestBodyBytes ?? null));
|
|
645
714
|
setResponsePreview(createEmptyBodyPreviewState(log.responseTextBytes ?? null));
|
|
646
715
|
}, [log.id, log.rawRequestBodyBytes, log.responseTextBytes]);
|
|
@@ -652,7 +721,8 @@ export const LogEntry = memo(function ({
|
|
|
652
721
|
|
|
653
722
|
let cancelled = false;
|
|
654
723
|
bodyHydrationInFlightRef.current = true;
|
|
655
|
-
|
|
724
|
+
setBodyHydrationStatus("loading");
|
|
725
|
+
setBodyHydrationError(null);
|
|
656
726
|
void fetchJsonWithTimeout(
|
|
657
727
|
`/api/logs/${String(log.id)}`,
|
|
658
728
|
CapturedLogSchema,
|
|
@@ -662,12 +732,14 @@ export const LogEntry = memo(function ({
|
|
|
662
732
|
if (cancelled) return;
|
|
663
733
|
bodyHydrationInFlightRef.current = false;
|
|
664
734
|
setHydratedLog(fullLog);
|
|
665
|
-
|
|
735
|
+
setBodyHydrationStatus("idle");
|
|
736
|
+
setBodyHydrationError(null);
|
|
666
737
|
})
|
|
667
|
-
.catch(() => {
|
|
738
|
+
.catch((error: unknown) => {
|
|
668
739
|
if (cancelled) return;
|
|
669
740
|
bodyHydrationInFlightRef.current = false;
|
|
670
|
-
|
|
741
|
+
setBodyHydrationStatus("failed");
|
|
742
|
+
setBodyHydrationError(describeFullBodyHydrationError(error));
|
|
671
743
|
});
|
|
672
744
|
|
|
673
745
|
return () => {
|
|
@@ -681,6 +753,7 @@ export const LogEntry = memo(function ({
|
|
|
681
753
|
log.id,
|
|
682
754
|
log.rawRequestBodyBytes,
|
|
683
755
|
log.responseTextBytes,
|
|
756
|
+
bodyHydrationRetryNonce,
|
|
684
757
|
]);
|
|
685
758
|
|
|
686
759
|
useEffect(() => {
|
|
@@ -718,16 +791,16 @@ export const LogEntry = memo(function ({
|
|
|
718
791
|
<TooltipProvider>
|
|
719
792
|
<div
|
|
720
793
|
className={cn(
|
|
721
|
-
"relative mb-2 overflow-hidden rounded-
|
|
794
|
+
"border border-border bg-card shadow-sm relative mb-2 overflow-hidden rounded-[8px] transition-[background-color,border-color,box-shadow]",
|
|
722
795
|
expanded
|
|
723
|
-
? "border-cyan-
|
|
724
|
-
: "
|
|
796
|
+
? "border-cyan-200/25 shadow-[0_24px_80px_rgba(0,0,0,0.36),0_0_0_1px_rgba(103,232,249,0.05),inset_0_1px_0_rgba(255,255,255,0.14)]"
|
|
797
|
+
: "hover:border-cyan-200/18",
|
|
725
798
|
)}
|
|
726
799
|
>
|
|
727
800
|
<div
|
|
728
801
|
className={cn(
|
|
729
802
|
"pointer-events-none absolute inset-y-3 left-1 w-px rounded-full",
|
|
730
|
-
expanded ? "bg-cyan-
|
|
803
|
+
expanded ? "bg-cyan-200/55 shadow-[0_0_20px_rgba(103,232,249,0.26)]" : "bg-white/12",
|
|
731
804
|
)}
|
|
732
805
|
aria-hidden="true"
|
|
733
806
|
/>
|
|
@@ -744,6 +817,7 @@ export const LogEntry = memo(function ({
|
|
|
744
817
|
onToggle={handleToggleExpanded}
|
|
745
818
|
cacheTrend={cacheTrend}
|
|
746
819
|
slowResponseThresholdSeconds={slowResponseThresholdSeconds}
|
|
820
|
+
showClientMetadata={showClientMetadata}
|
|
747
821
|
timeDisplayFormat={timeDisplayFormat}
|
|
748
822
|
activeTab={activeTab}
|
|
749
823
|
tabActions={tabActions}
|
|
@@ -758,18 +832,18 @@ export const LogEntry = memo(function ({
|
|
|
758
832
|
|
|
759
833
|
{expanded && (
|
|
760
834
|
<div
|
|
761
|
-
className="relative border-t border-
|
|
835
|
+
className="relative border-t border-white/10 bg-black/[0.18] pt-2"
|
|
762
836
|
onClick={(e) => e.stopPropagation()}
|
|
763
837
|
onKeyDown={(e) => e.stopPropagation()}
|
|
764
838
|
>
|
|
765
839
|
<div
|
|
766
|
-
className="pointer-events-none absolute inset-x-0 top-0 h-12 bg-gradient-to-b from-
|
|
840
|
+
className="pointer-events-none absolute inset-x-0 top-0 h-12 bg-gradient-to-b from-white/[0.05] to-transparent"
|
|
767
841
|
aria-hidden="true"
|
|
768
842
|
/>
|
|
769
843
|
<Tabs value={activeTab} onValueChange={setActiveTab} className="gap-2">
|
|
770
844
|
<TabsList
|
|
771
845
|
variant="line"
|
|
772
|
-
className="inspector-scrollbar relative mx-3 h-auto w-[calc(100%-1.5rem)] justify-start overflow-x-auto rounded-
|
|
846
|
+
className="border border-border bg-muted/40 inspector-scrollbar relative mx-3 h-auto w-[calc(100%-1.5rem)] justify-start overflow-x-auto rounded-[8px] p-1"
|
|
773
847
|
>
|
|
774
848
|
{viewMode === "full" && (
|
|
775
849
|
<TabsTrigger className={TAB_TRIGGER_CLASS} value="raw-headers">
|
|
@@ -808,6 +882,12 @@ export const LogEntry = memo(function ({
|
|
|
808
882
|
<TabsContent value="raw-request">
|
|
809
883
|
{activeTab === "raw-request" && (
|
|
810
884
|
<ExpandedPanel>
|
|
885
|
+
<FullBodyHydrationNotice
|
|
886
|
+
status={bodyHydrationStatus}
|
|
887
|
+
error={bodyHydrationError}
|
|
888
|
+
footprint={fullBodyHydrationFootprint}
|
|
889
|
+
onRetry={retryFullBodyHydration}
|
|
890
|
+
/>
|
|
811
891
|
{useChunkedBody && (
|
|
812
892
|
<BodyPreviewNotice
|
|
813
893
|
label="Request"
|
|
@@ -839,17 +919,12 @@ export const LogEntry = memo(function ({
|
|
|
839
919
|
<TabsContent value="request">
|
|
840
920
|
{activeTab === "request" && (
|
|
841
921
|
<ExpandedPanel>
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
<NoticeBlock tone="warning">
|
|
849
|
-
Full request and response bodies could not be loaded. Compact metadata is
|
|
850
|
-
still available.
|
|
851
|
-
</NoticeBlock>
|
|
852
|
-
)}
|
|
922
|
+
<FullBodyHydrationNotice
|
|
923
|
+
status={bodyHydrationStatus}
|
|
924
|
+
error={bodyHydrationError}
|
|
925
|
+
footprint={fullBodyHydrationFootprint}
|
|
926
|
+
onRetry={retryFullBodyHydration}
|
|
927
|
+
/>
|
|
853
928
|
{useChunkedBody && (
|
|
854
929
|
<BodyPreviewNotice
|
|
855
930
|
label="Request"
|
|
@@ -978,6 +1053,12 @@ export const LogEntry = memo(function ({
|
|
|
978
1053
|
<TabsContent value="raw">
|
|
979
1054
|
{activeTab === "raw" && (
|
|
980
1055
|
<ExpandedPanel className="space-y-3">
|
|
1056
|
+
<FullBodyHydrationNotice
|
|
1057
|
+
status={bodyHydrationStatus}
|
|
1058
|
+
error={bodyHydrationError}
|
|
1059
|
+
footprint={fullBodyHydrationFootprint}
|
|
1060
|
+
onRetry={retryFullBodyHydration}
|
|
1061
|
+
/>
|
|
981
1062
|
{useChunkedBody && (
|
|
982
1063
|
<BodyPreviewNotice
|
|
983
1064
|
label="Response"
|
|
@@ -1022,6 +1103,12 @@ export const LogEntry = memo(function ({
|
|
|
1022
1103
|
<TabsContent value="parsed">
|
|
1023
1104
|
{activeTab === "parsed" && (
|
|
1024
1105
|
<ExpandedPanel>
|
|
1106
|
+
<FullBodyHydrationNotice
|
|
1107
|
+
status={bodyHydrationStatus}
|
|
1108
|
+
error={bodyHydrationError}
|
|
1109
|
+
footprint={fullBodyHydrationFootprint}
|
|
1110
|
+
onRetry={retryFullBodyHydration}
|
|
1111
|
+
/>
|
|
1025
1112
|
{useChunkedBody && (
|
|
1026
1113
|
<BodyPreviewNotice
|
|
1027
1114
|
label="Response"
|