@tonyclaw/agent-inspector 2.1.0 → 2.1.1
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/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-VDpcSNGM.js → CompareDrawer-DkDpl_Sg.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-D_-e1qn7.js +117 -0
- package/.output/public/assets/{ReplayDialog-CoDgTzHR.js → ReplayDialog-CKqYfD0A.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-t3plgkPE.js → RequestAnatomy-CQZl-YIx.js} +1 -1
- package/.output/public/assets/{ResponseView-BYuKiNeG.js → ResponseView-DW9tFPi0.js} +1 -1
- package/.output/public/assets/{StreamingChunkSequence-DOdU38Is.js → StreamingChunkSequence-m8zKc3L1.js} +1 -1
- package/.output/public/assets/_sessionId-DyfzgUXv.js +1 -0
- package/.output/public/assets/index-B6bobhTz.js +1 -0
- package/.output/public/assets/index-CLQKZ5A5.css +1 -0
- package/.output/public/assets/{main-DmzLt6ti.js → main-BjXd3DR-.js} +2 -2
- package/.output/server/{_sessionId-CCN1UoEf.mjs → _sessionId-LjzdyWF4.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-DkSY3vxP.mjs → CompareDrawer-YX2uOwh9.mjs} +2 -2
- package/.output/server/_ssr/{ProxyViewerContainer-OTh1V_Kh.mjs → ProxyViewerContainer-BcKHl2E3.mjs} +268 -238
- package/.output/server/_ssr/{ReplayDialog-DhI6yxrR.mjs → ReplayDialog-B9AxvV2j.mjs} +3 -3
- package/.output/server/_ssr/{RequestAnatomy-DN1SJVVL.mjs → RequestAnatomy-DxmUOcz8.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-DW-kpJL5.mjs → ResponseView-B73PHDQ0.mjs} +2 -2
- package/.output/server/_ssr/{StreamingChunkSequence-BAE57Fae.mjs → StreamingChunkSequence-Bkwxu5Ev.mjs} +2 -2
- package/.output/server/_ssr/{index-C9ryJEna.mjs → index-Dfl6uxit.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-DIIJGyML.mjs → router-B5cpU5Hi.mjs} +4 -4
- package/.output/server/{_tanstack-start-manifest_v-C_v9-E5d.mjs → _tanstack-start-manifest_v-CHhxRQY_.mjs} +1 -1
- package/.output/server/index.mjs +60 -60
- package/package.json +1 -1
- package/src/components/OnboardingBanner.tsx +6 -22
- package/src/components/ProxyViewer.tsx +0 -34
- package/src/components/ProxyViewerContainer.tsx +1 -2
- package/src/components/providers/SettingsDialog.tsx +0 -13
- package/src/components/proxy-viewer/LogEntry.tsx +198 -89
- package/src/components/proxy-viewer/LogEntryHeader.tsx +11 -7
- package/src/components/proxy-viewer/RequestToolsPanel.tsx +8 -8
- package/src/components/ui/json-viewer.tsx +6 -0
- package/styles/globals.css +53 -8
- package/.output/public/assets/ProxyViewerContainer-fsXSjqtm.js +0 -117
- package/.output/public/assets/_sessionId-ew5QlyZ2.js +0 -1
- package/.output/public/assets/index-DFICWD6o.js +0 -1
- package/.output/public/assets/index-zLLCkAnF.css +0 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { AlertTriangle, GitCompareArrows } from "lucide-react";
|
|
2
|
-
import { Suspense, type JSX } from "react";
|
|
2
|
+
import { Suspense, type JSX, type ReactNode } from "react";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState, memo } from "react";
|
|
4
4
|
import { CapturedLogSchema, type CapturedLog } from "../../contracts";
|
|
5
5
|
import { fetchJson } from "../../lib/apiClient";
|
|
6
6
|
import type { TimeDisplayFormat } from "../../lib/runtimeConfig";
|
|
7
|
+
import { cn } from "../../lib/utils";
|
|
7
8
|
import { stripClaudeCodeBillingHeader } from "../../proxy/claudeCodeStrip";
|
|
8
9
|
import { Button } from "../ui/button";
|
|
9
10
|
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/tooltip";
|
|
@@ -53,10 +54,111 @@ function resolveFocusedTab(tab: LogFocusTab, anatomySegments: AnatomySegment[] |
|
|
|
53
54
|
|
|
54
55
|
type BodyHydrationState = "idle" | "loading" | "failed";
|
|
55
56
|
|
|
57
|
+
const TAB_TRIGGER_CLASS =
|
|
58
|
+
"h-9 flex-none rounded-none border-0 border-b-2 border-transparent bg-transparent px-3 text-xs font-semibold text-muted-foreground shadow-none hover:bg-cyan-400/[0.04] hover:text-foreground data-[state=active]:border-cyan-300 data-[state=active]:bg-transparent data-[state=active]:text-cyan-100 data-[state=active]:shadow-none";
|
|
59
|
+
|
|
56
60
|
function shouldHydrateLogBody(log: CapturedLog): boolean {
|
|
57
61
|
return log.bodyContentMode === "compact" || log.bodyContentMode === "truncated";
|
|
58
62
|
}
|
|
59
63
|
|
|
64
|
+
function ExpandedPanel({
|
|
65
|
+
children,
|
|
66
|
+
className,
|
|
67
|
+
}: {
|
|
68
|
+
children: ReactNode;
|
|
69
|
+
className?: string;
|
|
70
|
+
}): JSX.Element {
|
|
71
|
+
return (
|
|
72
|
+
<div
|
|
73
|
+
className={cn(
|
|
74
|
+
"inspector-scrollbar mx-3 mb-3 max-h-[72vh] overflow-auto rounded-md border border-white/10 bg-[#06080b] p-3 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]",
|
|
75
|
+
className,
|
|
76
|
+
)}
|
|
77
|
+
>
|
|
78
|
+
{children}
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function EmptyState({ children }: { children: ReactNode }): JSX.Element {
|
|
84
|
+
return (
|
|
85
|
+
<div className="rounded-md border border-dashed border-white/10 bg-white/[0.02] px-3 py-5 text-center text-xs italic text-muted-foreground">
|
|
86
|
+
{children}
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function RawTextBlock({ children }: { children: ReactNode }): JSX.Element {
|
|
92
|
+
return (
|
|
93
|
+
<pre className="inspector-scrollbar max-h-[64vh] overflow-auto whitespace-pre-wrap break-words rounded-md border border-white/10 bg-black/30 p-3 font-mono text-xs leading-relaxed text-muted-foreground">
|
|
94
|
+
{children}
|
|
95
|
+
</pre>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
type NoticeTone = "loading" | "warning" | "danger";
|
|
100
|
+
|
|
101
|
+
const NOTICE_TONE_CLASSES: Record<NoticeTone, string> = {
|
|
102
|
+
loading: "border-cyan-400/25 bg-cyan-400/[0.06] text-cyan-100",
|
|
103
|
+
warning: "border-amber-400/25 bg-amber-400/[0.08] text-amber-100",
|
|
104
|
+
danger: "border-rose-400/25 bg-rose-400/[0.08] text-rose-100",
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
function NoticeBlock({
|
|
108
|
+
tone,
|
|
109
|
+
title,
|
|
110
|
+
children,
|
|
111
|
+
}: {
|
|
112
|
+
tone: NoticeTone;
|
|
113
|
+
title?: string;
|
|
114
|
+
children: ReactNode;
|
|
115
|
+
}): JSX.Element {
|
|
116
|
+
return (
|
|
117
|
+
<div
|
|
118
|
+
className={cn(
|
|
119
|
+
"mb-3 rounded-md border px-3 py-2 text-xs shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]",
|
|
120
|
+
NOTICE_TONE_CLASSES[tone],
|
|
121
|
+
)}
|
|
122
|
+
>
|
|
123
|
+
{title !== undefined && <div className="mb-1 font-semibold">{title}</div>}
|
|
124
|
+
<div className="leading-relaxed text-muted-foreground">{children}</div>
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function HeaderRows({
|
|
130
|
+
headers,
|
|
131
|
+
emptyLabel,
|
|
132
|
+
}: {
|
|
133
|
+
headers: Record<string, string> | undefined;
|
|
134
|
+
emptyLabel: string;
|
|
135
|
+
}): JSX.Element {
|
|
136
|
+
const entries =
|
|
137
|
+
headers === undefined
|
|
138
|
+
? []
|
|
139
|
+
: Object.entries(headers).sort(([left], [right]) => left.localeCompare(right));
|
|
140
|
+
|
|
141
|
+
if (entries.length === 0) return <EmptyState>{emptyLabel}</EmptyState>;
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<div className="overflow-hidden rounded-md border border-white/10 bg-black/20 font-mono text-xs">
|
|
145
|
+
{entries.map(([key, value]) => (
|
|
146
|
+
<div
|
|
147
|
+
key={key}
|
|
148
|
+
className="grid min-w-0 grid-cols-[minmax(140px,0.32fr)_minmax(0,1fr)] border-b border-white/5 last:border-b-0"
|
|
149
|
+
>
|
|
150
|
+
<div className="min-w-0 truncate border-r border-white/5 bg-white/[0.03] px-2.5 py-2 font-semibold text-cyan-200">
|
|
151
|
+
{key}
|
|
152
|
+
</div>
|
|
153
|
+
<div className="min-w-0 truncate px-2.5 py-2 text-muted-foreground" title={value}>
|
|
154
|
+
{value}
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
))}
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
60
162
|
export type LogEntryProps = {
|
|
61
163
|
log: CapturedLog;
|
|
62
164
|
viewMode?: "simple" | "full";
|
|
@@ -92,7 +194,12 @@ function DiffToggleButton({
|
|
|
92
194
|
<Button
|
|
93
195
|
variant={active ? "default" : "outline"}
|
|
94
196
|
size="sm"
|
|
95
|
-
className=
|
|
197
|
+
className={cn(
|
|
198
|
+
"h-8 border-white/10 text-xs shadow-none",
|
|
199
|
+
active
|
|
200
|
+
? "bg-cyan-300 text-black hover:bg-cyan-200"
|
|
201
|
+
: "bg-[#0a0d11] text-muted-foreground hover:border-cyan-300/30 hover:bg-cyan-400/10 hover:text-cyan-100",
|
|
202
|
+
)}
|
|
96
203
|
onClick={onClick}
|
|
97
204
|
aria-pressed={active}
|
|
98
205
|
>
|
|
@@ -372,7 +479,14 @@ export const LogEntry = memo(function ({
|
|
|
372
479
|
|
|
373
480
|
return (
|
|
374
481
|
<TooltipProvider>
|
|
375
|
-
<div
|
|
482
|
+
<div
|
|
483
|
+
className={cn(
|
|
484
|
+
"mb-2 overflow-hidden rounded-md border bg-[#090a0d] transition-[background-color,border-color,box-shadow]",
|
|
485
|
+
expanded
|
|
486
|
+
? "border-cyan-300/20 shadow-[0_18px_60px_rgba(0,0,0,0.28),inset_0_1px_0_rgba(255,255,255,0.05)]"
|
|
487
|
+
: "border-border/70 hover:border-border",
|
|
488
|
+
)}
|
|
489
|
+
>
|
|
376
490
|
<LogEntryHeader
|
|
377
491
|
log={displayLog}
|
|
378
492
|
messageCount={requestAnalysis.messageCount}
|
|
@@ -394,26 +508,55 @@ export const LogEntry = memo(function ({
|
|
|
394
508
|
/>
|
|
395
509
|
|
|
396
510
|
{expanded && (
|
|
397
|
-
<div
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
511
|
+
<div
|
|
512
|
+
className="border-t border-white/10 bg-[#050607]/80 pt-2"
|
|
513
|
+
onClick={(e) => e.stopPropagation()}
|
|
514
|
+
onKeyDown={(e) => e.stopPropagation()}
|
|
515
|
+
>
|
|
516
|
+
<Tabs value={activeTab} onValueChange={setActiveTab} className="gap-2">
|
|
517
|
+
<TabsList
|
|
518
|
+
variant="line"
|
|
519
|
+
className="inspector-scrollbar mx-3 h-auto w-[calc(100%-1.5rem)] justify-start overflow-x-auto rounded-none border-b border-white/10 bg-transparent p-0"
|
|
520
|
+
>
|
|
521
|
+
{viewMode === "full" && (
|
|
522
|
+
<TabsTrigger className={TAB_TRIGGER_CLASS} value="raw-headers">
|
|
523
|
+
Raw Headers
|
|
524
|
+
</TabsTrigger>
|
|
525
|
+
)}
|
|
526
|
+
{viewMode === "full" && (
|
|
527
|
+
<TabsTrigger className={TAB_TRIGGER_CLASS} value="headers">
|
|
528
|
+
Headers
|
|
529
|
+
</TabsTrigger>
|
|
530
|
+
)}
|
|
531
|
+
{anatomySegments !== null && (
|
|
532
|
+
<TabsTrigger className={TAB_TRIGGER_CLASS} value="anatomy">
|
|
533
|
+
Context
|
|
534
|
+
</TabsTrigger>
|
|
535
|
+
)}
|
|
403
536
|
{shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && (
|
|
404
|
-
<TabsTrigger value="raw-request">
|
|
537
|
+
<TabsTrigger className={TAB_TRIGGER_CLASS} value="raw-request">
|
|
538
|
+
Raw Request
|
|
539
|
+
</TabsTrigger>
|
|
540
|
+
)}
|
|
541
|
+
<TabsTrigger className={TAB_TRIGGER_CLASS} value="request">
|
|
542
|
+
Request
|
|
543
|
+
</TabsTrigger>
|
|
544
|
+
{viewMode === "full" && (
|
|
545
|
+
<TabsTrigger className={TAB_TRIGGER_CLASS} value="raw">
|
|
546
|
+
Raw Response
|
|
547
|
+
</TabsTrigger>
|
|
405
548
|
)}
|
|
406
|
-
<TabsTrigger value="
|
|
407
|
-
|
|
408
|
-
|
|
549
|
+
<TabsTrigger className={TAB_TRIGGER_CLASS} value="parsed">
|
|
550
|
+
Response
|
|
551
|
+
</TabsTrigger>
|
|
409
552
|
</TabsList>
|
|
410
553
|
|
|
411
554
|
{shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && (
|
|
412
555
|
<TabsContent value="raw-request">
|
|
413
556
|
{activeTab === "raw-request" && (
|
|
414
|
-
<
|
|
557
|
+
<ExpandedPanel>
|
|
415
558
|
{displayLog.rawRequestBody === null ? (
|
|
416
|
-
<
|
|
559
|
+
<EmptyState>No request body</EmptyState>
|
|
417
560
|
) : rawRequestExpansion.parsedData !== null ? (
|
|
418
561
|
<Suspense fallback={<TabFallback />}>
|
|
419
562
|
<LazyJsonViewer
|
|
@@ -423,31 +566,29 @@ export const LogEntry = memo(function ({
|
|
|
423
566
|
/>
|
|
424
567
|
</Suspense>
|
|
425
568
|
) : (
|
|
426
|
-
<
|
|
427
|
-
{displayLog.rawRequestBody}
|
|
428
|
-
</pre>
|
|
569
|
+
<RawTextBlock>{displayLog.rawRequestBody}</RawTextBlock>
|
|
429
570
|
)}
|
|
430
|
-
</
|
|
571
|
+
</ExpandedPanel>
|
|
431
572
|
)}
|
|
432
573
|
</TabsContent>
|
|
433
574
|
)}
|
|
434
575
|
|
|
435
576
|
<TabsContent value="request">
|
|
436
577
|
{activeTab === "request" && (
|
|
437
|
-
<
|
|
578
|
+
<ExpandedPanel>
|
|
438
579
|
{bodyHydrationState === "loading" && (
|
|
439
|
-
<
|
|
580
|
+
<NoticeBlock tone="loading">
|
|
440
581
|
Loading full request and response bodies...
|
|
441
|
-
</
|
|
582
|
+
</NoticeBlock>
|
|
442
583
|
)}
|
|
443
584
|
{bodyHydrationState === "failed" && (
|
|
444
|
-
<
|
|
585
|
+
<NoticeBlock tone="warning">
|
|
445
586
|
Full request and response bodies could not be loaded. Compact metadata is
|
|
446
587
|
still available.
|
|
447
|
-
</
|
|
588
|
+
</NoticeBlock>
|
|
448
589
|
)}
|
|
449
590
|
{warnings.length > 0 && (
|
|
450
|
-
<
|
|
591
|
+
<NoticeBlock tone="warning">
|
|
451
592
|
<div className="flex items-center gap-2 font-semibold text-amber-300">
|
|
452
593
|
<AlertTriangle className="size-3.5" />
|
|
453
594
|
Tool schema warnings
|
|
@@ -457,7 +598,7 @@ export const LogEntry = memo(function ({
|
|
|
457
598
|
<li key={warning}>{warning}</li>
|
|
458
599
|
))}
|
|
459
600
|
</ul>
|
|
460
|
-
</
|
|
601
|
+
</NoticeBlock>
|
|
461
602
|
)}
|
|
462
603
|
<RequestTools
|
|
463
604
|
summary={requestTools}
|
|
@@ -475,7 +616,7 @@ export const LogEntry = memo(function ({
|
|
|
475
616
|
) : (
|
|
476
617
|
<div ref={requestJsonRef}>
|
|
477
618
|
{displayedRequestBody === null ? (
|
|
478
|
-
<
|
|
619
|
+
<EmptyState>No request body</EmptyState>
|
|
479
620
|
) : requestExpansion.parsedData !== null ? (
|
|
480
621
|
<Suspense fallback={<TabFallback />}>
|
|
481
622
|
<LazyJsonViewer
|
|
@@ -487,28 +628,28 @@ export const LogEntry = memo(function ({
|
|
|
487
628
|
/>
|
|
488
629
|
</Suspense>
|
|
489
630
|
) : (
|
|
490
|
-
<
|
|
491
|
-
{displayedRequestBody}
|
|
492
|
-
</pre>
|
|
631
|
+
<RawTextBlock>{displayedRequestBody}</RawTextBlock>
|
|
493
632
|
)}
|
|
494
633
|
</div>
|
|
495
634
|
)}
|
|
496
|
-
</
|
|
635
|
+
</ExpandedPanel>
|
|
497
636
|
)}
|
|
498
637
|
</TabsContent>
|
|
499
638
|
|
|
500
639
|
{anatomySegments !== null && (
|
|
501
640
|
<TabsContent value="anatomy">
|
|
502
641
|
{activeTab === "anatomy" && (
|
|
503
|
-
<
|
|
504
|
-
<
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
642
|
+
<ExpandedPanel className="p-0">
|
|
643
|
+
<Suspense fallback={<TabFallback />}>
|
|
644
|
+
<LazyRequestAnatomy
|
|
645
|
+
parsed={requestExpansion.parsedData}
|
|
646
|
+
inputTokens={displayLog.inputTokens ?? null}
|
|
647
|
+
model={displayLog.model}
|
|
648
|
+
segments={anatomySegments}
|
|
649
|
+
onSegmentActivate={jumpToAnatomySegment}
|
|
650
|
+
/>
|
|
651
|
+
</Suspense>
|
|
652
|
+
</ExpandedPanel>
|
|
512
653
|
)}
|
|
513
654
|
</TabsContent>
|
|
514
655
|
)}
|
|
@@ -516,7 +657,7 @@ export const LogEntry = memo(function ({
|
|
|
516
657
|
{viewMode === "full" && (
|
|
517
658
|
<TabsContent value="headers">
|
|
518
659
|
{activeTab === "headers" && (
|
|
519
|
-
<
|
|
660
|
+
<ExpandedPanel>
|
|
520
661
|
{/* Copy lives in the log header. */}
|
|
521
662
|
<div className="flex justify-end gap-2 mb-2">
|
|
522
663
|
{shouldShowHeadersDiffButton(
|
|
@@ -539,25 +680,10 @@ export const LogEntry = memo(function ({
|
|
|
539
680
|
headers={displayLog.headers}
|
|
540
681
|
emptyLabel="No transformation applied; raw and processed headers are identical."
|
|
541
682
|
/>
|
|
542
|
-
) : displayLog.headers && Object.keys(displayLog.headers).length > 0 ? (
|
|
543
|
-
<div className="space-y-1 font-mono text-xs">
|
|
544
|
-
{Object.entries(displayLog.headers)
|
|
545
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
546
|
-
.map(([key, value]) => (
|
|
547
|
-
<div key={key} className="flex gap-2">
|
|
548
|
-
<span className="text-blue-600 dark:text-blue-400 font-semibold shrink-0">
|
|
549
|
-
{key}:
|
|
550
|
-
</span>
|
|
551
|
-
<span className="text-muted-foreground truncate" title={value}>
|
|
552
|
-
{value}
|
|
553
|
-
</span>
|
|
554
|
-
</div>
|
|
555
|
-
))}
|
|
556
|
-
</div>
|
|
557
683
|
) : (
|
|
558
|
-
<
|
|
684
|
+
<HeaderRows headers={displayLog.headers} emptyLabel="No headers captured" />
|
|
559
685
|
)}
|
|
560
|
-
</
|
|
686
|
+
</ExpandedPanel>
|
|
561
687
|
)}
|
|
562
688
|
</TabsContent>
|
|
563
689
|
)}
|
|
@@ -565,41 +691,24 @@ export const LogEntry = memo(function ({
|
|
|
565
691
|
{viewMode === "full" && (
|
|
566
692
|
<TabsContent value="raw-headers">
|
|
567
693
|
{activeTab === "raw-headers" && (
|
|
568
|
-
<
|
|
694
|
+
<ExpandedPanel>
|
|
569
695
|
{/* Copy lives in the log header. */}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
<div key={key} className="flex gap-2">
|
|
576
|
-
<span className="text-blue-600 dark:text-blue-400 font-semibold shrink-0">
|
|
577
|
-
{key}:
|
|
578
|
-
</span>
|
|
579
|
-
<span className="text-muted-foreground truncate" title={value}>
|
|
580
|
-
{value}
|
|
581
|
-
</span>
|
|
582
|
-
</div>
|
|
583
|
-
))}
|
|
584
|
-
</div>
|
|
585
|
-
) : (
|
|
586
|
-
<p className="text-xs text-muted-foreground italic">
|
|
587
|
-
No raw headers captured
|
|
588
|
-
</p>
|
|
589
|
-
)}
|
|
590
|
-
</div>
|
|
696
|
+
<HeaderRows
|
|
697
|
+
headers={displayLog.rawHeaders}
|
|
698
|
+
emptyLabel="No raw headers captured"
|
|
699
|
+
/>
|
|
700
|
+
</ExpandedPanel>
|
|
591
701
|
)}
|
|
592
702
|
</TabsContent>
|
|
593
703
|
)}
|
|
594
704
|
|
|
595
705
|
<TabsContent value="raw">
|
|
596
706
|
{activeTab === "raw" && (
|
|
597
|
-
<
|
|
707
|
+
<ExpandedPanel className="space-y-3">
|
|
598
708
|
{displayLog.error !== undefined && displayLog.error !== null && (
|
|
599
|
-
<
|
|
600
|
-
<
|
|
601
|
-
|
|
602
|
-
</div>
|
|
709
|
+
<NoticeBlock tone="danger" title="SSE Error">
|
|
710
|
+
<span className="font-mono">{displayLog.error}</span>
|
|
711
|
+
</NoticeBlock>
|
|
603
712
|
)}
|
|
604
713
|
{displayLog.responseText !== null ? (
|
|
605
714
|
<Suspense fallback={<TabFallback />}>
|
|
@@ -611,7 +720,7 @@ export const LogEntry = memo(function ({
|
|
|
611
720
|
/>
|
|
612
721
|
</Suspense>
|
|
613
722
|
) : (
|
|
614
|
-
<
|
|
723
|
+
<EmptyState>No response</EmptyState>
|
|
615
724
|
)}
|
|
616
725
|
{displayLog.streaming === true && (
|
|
617
726
|
<Suspense fallback={<TabFallback />}>
|
|
@@ -621,13 +730,13 @@ export const LogEntry = memo(function ({
|
|
|
621
730
|
/>
|
|
622
731
|
</Suspense>
|
|
623
732
|
)}
|
|
624
|
-
</
|
|
733
|
+
</ExpandedPanel>
|
|
625
734
|
)}
|
|
626
735
|
</TabsContent>
|
|
627
736
|
|
|
628
737
|
<TabsContent value="parsed">
|
|
629
738
|
{activeTab === "parsed" && (
|
|
630
|
-
<
|
|
739
|
+
<ExpandedPanel>
|
|
631
740
|
<Suspense fallback={<TabFallback />}>
|
|
632
741
|
<LazyResponseView
|
|
633
742
|
responseText={displayLog.responseText}
|
|
@@ -641,7 +750,7 @@ export const LogEntry = memo(function ({
|
|
|
641
750
|
error={displayLog.error}
|
|
642
751
|
/>
|
|
643
752
|
</Suspense>
|
|
644
|
-
</
|
|
753
|
+
</ExpandedPanel>
|
|
645
754
|
)}
|
|
646
755
|
</TabsContent>
|
|
647
756
|
</Tabs>
|
|
@@ -41,6 +41,9 @@ const STATUS_BADGE_CLASSES: Record<StatusCategory, string> = {
|
|
|
41
41
|
pending: "bg-muted text-muted-foreground border-border",
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
const HEADER_ACTION_BUTTON_CLASS =
|
|
45
|
+
"size-8 border-white/10 bg-[#0a0d11] text-muted-foreground shadow-none hover:border-cyan-300/30 hover:bg-cyan-400/10 hover:text-cyan-100";
|
|
46
|
+
|
|
44
47
|
function formatElapsed(ms: number): string {
|
|
45
48
|
if (ms < 1000) return `${ms}ms`;
|
|
46
49
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
@@ -171,7 +174,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
171
174
|
data-nav-action={expanded ? "collapse" : "expand"}
|
|
172
175
|
className={cn(
|
|
173
176
|
"flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 px-3 py-1.5 cursor-pointer transition-colors",
|
|
174
|
-
"hover:bg-muted/
|
|
177
|
+
expanded ? "bg-[#0c1015]" : "hover:bg-muted/30",
|
|
175
178
|
"select-none",
|
|
176
179
|
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none",
|
|
177
180
|
)}
|
|
@@ -427,7 +430,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
427
430
|
<Button
|
|
428
431
|
variant="outline"
|
|
429
432
|
size="icon"
|
|
430
|
-
className=
|
|
433
|
+
className={HEADER_ACTION_BUTTON_CLASS}
|
|
431
434
|
onClick={action.expansion.onToggle}
|
|
432
435
|
disabled={action.expansion.isPending}
|
|
433
436
|
aria-pressed={action.expansion.isExpanded}
|
|
@@ -456,8 +459,9 @@ export const LogEntryHeader = memo(function ({
|
|
|
456
459
|
variant="outline"
|
|
457
460
|
size="icon"
|
|
458
461
|
className={cn(
|
|
459
|
-
|
|
460
|
-
action.diffWithRaw.active &&
|
|
462
|
+
HEADER_ACTION_BUTTON_CLASS,
|
|
463
|
+
action.diffWithRaw.active &&
|
|
464
|
+
"border-cyan-300/35 bg-cyan-400/15 text-cyan-100",
|
|
461
465
|
)}
|
|
462
466
|
onClick={action.diffWithRaw.onToggle}
|
|
463
467
|
aria-pressed={action.diffWithRaw.active}
|
|
@@ -481,7 +485,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
481
485
|
<Button
|
|
482
486
|
variant="outline"
|
|
483
487
|
size="icon"
|
|
484
|
-
className=
|
|
488
|
+
className={HEADER_ACTION_BUTTON_CLASS}
|
|
485
489
|
onClick={action.diffWithPrevious}
|
|
486
490
|
aria-label="Diff with previous"
|
|
487
491
|
>
|
|
@@ -499,7 +503,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
499
503
|
<Button
|
|
500
504
|
variant="outline"
|
|
501
505
|
size="icon"
|
|
502
|
-
className=
|
|
506
|
+
className={HEADER_ACTION_BUTTON_CLASS}
|
|
503
507
|
onClick={action.onCopy}
|
|
504
508
|
aria-label={action.copyCopied ? "Copied" : action.copyLabel}
|
|
505
509
|
>
|
|
@@ -524,7 +528,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
524
528
|
<Button
|
|
525
529
|
variant="outline"
|
|
526
530
|
size="icon"
|
|
527
|
-
className=
|
|
531
|
+
className={HEADER_ACTION_BUTTON_CLASS}
|
|
528
532
|
onClick={onReplay}
|
|
529
533
|
aria-label="Replay request"
|
|
530
534
|
>
|
|
@@ -101,7 +101,7 @@ function ToolRow({ tool, called }: { tool: RequestToolDefinition; called: boolea
|
|
|
101
101
|
|
|
102
102
|
return (
|
|
103
103
|
<Collapsible open={open} onOpenChange={setOpen}>
|
|
104
|
-
<div className="rounded-md border border-
|
|
104
|
+
<div className="rounded-md border border-white/10 bg-black/20 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]">
|
|
105
105
|
<div className="flex min-w-0 items-center gap-2 px-2.5 py-2">
|
|
106
106
|
<button
|
|
107
107
|
type="button"
|
|
@@ -152,13 +152,13 @@ function ToolRow({ tool, called }: { tool: RequestToolDefinition; called: boolea
|
|
|
152
152
|
</button>
|
|
153
153
|
</div>
|
|
154
154
|
<CollapsibleContent>
|
|
155
|
-
<div className="space-y-2 border-t border-
|
|
155
|
+
<div className="space-y-2 border-t border-white/10 bg-white/[0.02] px-2.5 py-2">
|
|
156
156
|
{tool.description !== null && (
|
|
157
157
|
<p className="text-xs leading-relaxed text-muted-foreground">{tool.description}</p>
|
|
158
158
|
)}
|
|
159
159
|
<RequiredParameters names={tool.requiredParameters} />
|
|
160
160
|
{tool.schema !== null && (
|
|
161
|
-
<ScrollArea className="max-h-64 rounded-md border border-
|
|
161
|
+
<ScrollArea className="max-h-64 rounded-md border border-white/10 bg-[#06080b] p-2">
|
|
162
162
|
<JsonViewer data={safeJsonValue(tool.schema)} defaultExpandDepth={0} />
|
|
163
163
|
</ScrollArea>
|
|
164
164
|
)}
|
|
@@ -197,7 +197,7 @@ export const RequestTools = memo(function RequestTools({
|
|
|
197
197
|
const hiddenPreviewCount = summary.tools.length - previewTools.length;
|
|
198
198
|
|
|
199
199
|
return (
|
|
200
|
-
<section className="mb-3 rounded-
|
|
200
|
+
<section className="mb-3 overflow-hidden rounded-md border border-white/10 bg-[#080b0f] shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]">
|
|
201
201
|
<div className="flex min-w-0 flex-wrap items-center gap-2 px-3 py-2">
|
|
202
202
|
<div className="flex min-w-0 flex-1 items-center gap-2">
|
|
203
203
|
<Wrench className="size-4 shrink-0 text-sky-300/80" />
|
|
@@ -236,7 +236,7 @@ export const RequestTools = memo(function RequestTools({
|
|
|
236
236
|
type="button"
|
|
237
237
|
variant="ghost"
|
|
238
238
|
size="sm"
|
|
239
|
-
className="h-7 px-2"
|
|
239
|
+
className="h-7 border border-white/10 bg-black/20 px-2 text-muted-foreground hover:border-cyan-300/30 hover:bg-cyan-400/10 hover:text-cyan-100"
|
|
240
240
|
onClick={() => setExpanded((value) => !value)}
|
|
241
241
|
aria-expanded={expanded}
|
|
242
242
|
>
|
|
@@ -245,7 +245,7 @@ export const RequestTools = memo(function RequestTools({
|
|
|
245
245
|
</Button>
|
|
246
246
|
</div>
|
|
247
247
|
|
|
248
|
-
<div className="flex min-w-0 flex-wrap gap-1.5 border-t border-
|
|
248
|
+
<div className="flex min-w-0 flex-wrap gap-1.5 border-t border-white/10 bg-black/10 px-3 py-2">
|
|
249
249
|
{previewTools.map((tool) => (
|
|
250
250
|
<Badge
|
|
251
251
|
key={tool.name}
|
|
@@ -266,8 +266,8 @@ export const RequestTools = memo(function RequestTools({
|
|
|
266
266
|
</div>
|
|
267
267
|
|
|
268
268
|
{expanded && (
|
|
269
|
-
<div className="space-y-2 border-t border-
|
|
270
|
-
<label className="flex h-8 items-center gap-2 rounded-md border border-
|
|
269
|
+
<div className="space-y-2 border-t border-white/10 bg-black/15 px-3 py-3">
|
|
270
|
+
<label className="flex h-8 items-center gap-2 rounded-md border border-white/10 bg-[#06080b] px-2 text-xs">
|
|
271
271
|
<Search className="size-3.5 shrink-0 text-muted-foreground" />
|
|
272
272
|
<input
|
|
273
273
|
value={query}
|
|
@@ -438,6 +438,12 @@ export function JsonViewer({
|
|
|
438
438
|
<CopyValueButton value={data} />
|
|
439
439
|
</div>
|
|
440
440
|
)}
|
|
441
|
+
{entries.length === 0 && (
|
|
442
|
+
<div className="rounded-md border border-white/10 bg-black/20 px-3 py-2 text-muted-foreground">
|
|
443
|
+
<span className="text-cyan-300">{isArray ? "[]" : "{}"}</span>
|
|
444
|
+
<span className="ml-2 text-[10px] uppercase">0 items</span>
|
|
445
|
+
</div>
|
|
446
|
+
)}
|
|
441
447
|
<div key={bulkRevision}>
|
|
442
448
|
{entries.map(([key, childValue]) => (
|
|
443
449
|
<JsonNode
|
package/styles/globals.css
CHANGED
|
@@ -178,11 +178,56 @@
|
|
|
178
178
|
0 0 0 3px color-mix(in oklch, var(--chart-2) 20%, transparent);
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
@layer base {
|
|
182
|
-
* {
|
|
183
|
-
@apply border-border outline-ring/50;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
181
|
+
@layer base {
|
|
182
|
+
* {
|
|
183
|
+
@apply border-border outline-ring/50;
|
|
184
|
+
}
|
|
185
|
+
html {
|
|
186
|
+
scrollbar-color: color-mix(in srgb, var(--muted-foreground) 32%, transparent) transparent;
|
|
187
|
+
scrollbar-width: thin;
|
|
188
|
+
}
|
|
189
|
+
html::-webkit-scrollbar {
|
|
190
|
+
height: 8px;
|
|
191
|
+
width: 8px;
|
|
192
|
+
}
|
|
193
|
+
html::-webkit-scrollbar-track {
|
|
194
|
+
background: transparent;
|
|
195
|
+
}
|
|
196
|
+
html::-webkit-scrollbar-thumb {
|
|
197
|
+
background: color-mix(in srgb, var(--muted-foreground) 26%, transparent);
|
|
198
|
+
border: 2px solid transparent;
|
|
199
|
+
border-radius: 999px;
|
|
200
|
+
background-clip: content-box;
|
|
201
|
+
}
|
|
202
|
+
body {
|
|
203
|
+
@apply bg-background text-foreground;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
@layer utilities {
|
|
208
|
+
.inspector-scrollbar {
|
|
209
|
+
scrollbar-color: color-mix(in srgb, var(--muted-foreground) 32%, transparent) transparent;
|
|
210
|
+
scrollbar-width: thin;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.inspector-scrollbar::-webkit-scrollbar {
|
|
214
|
+
height: 8px;
|
|
215
|
+
width: 8px;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.inspector-scrollbar::-webkit-scrollbar-track {
|
|
219
|
+
background: transparent;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.inspector-scrollbar::-webkit-scrollbar-thumb {
|
|
223
|
+
background: color-mix(in srgb, var(--muted-foreground) 26%, transparent);
|
|
224
|
+
border: 2px solid transparent;
|
|
225
|
+
border-radius: 999px;
|
|
226
|
+
background-clip: content-box;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.inspector-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
230
|
+
background: color-mix(in srgb, var(--muted-foreground) 42%, transparent);
|
|
231
|
+
background-clip: content-box;
|
|
232
|
+
}
|
|
233
|
+
}
|