@tonyclaw/llm-inspector 1.16.4 → 1.17.0
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-C4fie5g5.js +1 -0
- package/.output/public/assets/ReplayDialog-Dme5uOR9.js +1 -0
- package/.output/public/assets/RequestAnatomy-ChBLDNFH.js +1 -0
- package/.output/public/assets/ResponseView-wGeqBzVU.js +1 -0
- package/.output/public/assets/StreamingChunkSequence-zeJZQLqT.js +1 -0
- package/.output/public/assets/index-DoGvsnbA.css +1 -0
- package/.output/public/assets/index-DpbutOvo.js +101 -0
- package/.output/public/assets/json-viewer-BV-WUszW.js +14 -0
- package/.output/public/assets/{main-DbWwVQFh.js → main-DRu10KNQ.js} +1 -1
- package/.output/server/_libs/lucide-react.mjs +105 -85
- package/.output/server/_ssr/CompareDrawer-C4-CQL5w.mjs +1040 -0
- package/.output/server/_ssr/ReplayDialog-BTb1Bam8.mjs +321 -0
- package/.output/server/_ssr/RequestAnatomy-CZFV1IvL.mjs +351 -0
- package/.output/server/_ssr/ResponseView-CTZekh65.mjs +601 -0
- package/.output/server/_ssr/StreamingChunkSequence-C38Ynabd.mjs +301 -0
- package/.output/server/_ssr/{index-C-z-fZtq.mjs → index-Cnu-QzAy.mjs} +1141 -2443
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/json-viewer-DROqpjS9.mjs +510 -0
- package/.output/server/_ssr/{router-CNM9Kbi0.mjs → router-pP4GCTQx.mjs} +42 -18
- package/.output/server/{_tanstack-start-manifest_v-BWfLeIsC.mjs → _tanstack-start-manifest_v-CphS4rZd.mjs} +1 -1
- package/.output/server/index.mjs +69 -27
- package/package.json +1 -1
- package/src/components/OnboardingBanner.tsx +2 -2
- package/src/components/ProxyViewer.tsx +44 -27
- package/src/components/ProxyViewerContainer.tsx +5 -25
- package/src/components/providers/SettingsDialog.tsx +52 -1
- package/src/components/proxy-viewer/ConversationGroup.tsx +5 -1
- package/src/components/proxy-viewer/ConversationHeader.tsx +4 -1
- package/src/components/proxy-viewer/LogEntry.tsx +217 -181
- package/src/components/proxy-viewer/LogEntryHeader.tsx +181 -40
- package/src/components/proxy-viewer/ThreadConnector.tsx +17 -2
- package/src/components/proxy-viewer/TurnGroup.tsx +124 -72
- package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +98 -0
- package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +196 -0
- package/src/components/proxy-viewer/anatomy/tokenEstimate.ts +53 -0
- package/src/components/proxy-viewer/anatomy/types.ts +39 -0
- package/src/components/proxy-viewer/anatomy/useAnatomyJump.ts +114 -0
- package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +3 -23
- package/src/components/proxy-viewer/formats/anthropic/thinkingExtract.ts +21 -0
- package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +5 -3
- package/src/components/proxy-viewer/lazy.ts +37 -0
- package/src/components/proxy-viewer/log-formats/anthropic.ts +146 -0
- package/src/components/proxy-viewer/log-formats/openai.ts +127 -0
- package/src/components/proxy-viewer/log-formats/types.ts +7 -0
- package/src/components/proxy-viewer/log-formats/unknown.ts +4 -0
- package/src/components/proxy-viewer/logEntryVisibility.ts +39 -0
- package/src/components/proxy-viewer/useKeyboardNavigation.ts +190 -0
- package/src/components/proxy-viewer/viewerState.ts +8 -0
- package/src/components/ui/crab-variants.tsx +11 -0
- package/src/components/ui/json-expansion-button.tsx +56 -0
- package/src/components/ui/json-viewer-bulk.ts +97 -0
- package/src/components/ui/json-viewer.tsx +58 -183
- package/src/lib/runtimeConfig.ts +9 -0
- package/src/lib/useOnboarding.ts +7 -1
- package/src/lib/useStripConfig.ts +33 -2
- package/src/lib/utils.ts +2 -3
- package/src/proxy/config.ts +17 -7
- package/src/routes/api/config.ts +7 -0
- package/src/routes/api/logs.stream.ts +26 -16
- package/.output/public/assets/index-DRRCmu5p.css +0 -1
- package/.output/public/assets/index-X7CHS7fS.js +0 -107
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.mjs";
|
|
2
|
-
import { C as CapturedLogSchema, R as RuntimeConfigSchema, r as requestFormatForPath, c as createPendingProviderTestResults, P as ProviderTestResultsSchema, b as createFailedProviderTestResults, d as ProviderConfigSchema, s as stripClaudeCodeBillingHeader, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, a as AnthropicRequestSchema } from "./router-
|
|
2
|
+
import { C as CapturedLogSchema, D as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, R as RuntimeConfigSchema, r as requestFormatForPath, c as createPendingProviderTestResults, P as ProviderTestResultsSchema, b as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, d as ProviderConfigSchema, s as stripClaudeCodeBillingHeader, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, a as AnthropicRequestSchema } from "./router-pP4GCTQx.mjs";
|
|
3
3
|
import { u as useSWR, a as useSWRConfig } from "../_libs/swr.mjs";
|
|
4
4
|
import { J as JSZip } from "../_libs/jszip.mjs";
|
|
5
5
|
import { c as clsx } from "../_libs/clsx.mjs";
|
|
@@ -9,16 +9,11 @@ import { R as Root, T as Trigger$1, C as Content, a as Close, b as Title, P as P
|
|
|
9
9
|
import { d as diffJson, a as diffLines } from "../_libs/diff.mjs";
|
|
10
10
|
import { u as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
|
|
11
11
|
import { R as Root2, T as Trigger, I as Icon, V as Value, P as Portal, C as Content2, a as Viewport, b as Item, c as ItemIndicator, d as ItemText, S as ScrollUpButton, e as ScrollDownButton } from "../_libs/radix-ui__react-select.mjs";
|
|
12
|
-
import "../_libs/
|
|
13
|
-
import { C as Check, X, D as Download, S as Settings, a as ChevronDown, U as Upload, b as Scan, P as Plus, c as Copy, d as CircleAlert, e as ChevronUp, L as LoaderCircle, f as ChevronRight, g as User, h as Clock, M as MessageSquare, Z as Zap, T as Trash2, R as Rows3, i as Columns2, j as Minus, k as Pencil, E as Equal, l as EyeOff, m as Eye, n as ExternalLink, o as RotateCw, G as GitCompareArrows, p as RotateCcw, q as CircleCheckBig, W as Wrench, r as Globe, F as FileTerminal, s as Radio, t as ChevronsUp, u as ChevronsDown, v as CircleQuestionMark, w as Server, x as Gauge, y as Lock, z as Wifi, A as WifiOff, B as ArrowUp, H as ArrowDown, I as TriangleAlert, J as CircleStop, K as Brain, N as Terminal } from "../_libs/lucide-react.mjs";
|
|
14
|
-
import { M as Markdown } from "../_libs/react-markdown.mjs";
|
|
12
|
+
import { C as Check, X, D as Download, S as Settings, a as ChevronDown, U as Upload, b as Scan, P as Plus, c as Copy, d as CircleAlert, e as ChevronUp, L as LoaderCircle, f as ChevronRight, g as User, h as Clock, M as MessageSquare, Z as Zap, T as Trash2, i as TriangleAlert, E as EyeOff, j as Eye, k as ExternalLink, R as RotateCw, l as Pencil, G as GitCompareArrows, m as Minus, n as CircleCheckBig, O as OctagonAlert, W as Wrench, o as Globe, F as FileTerminal, p as Radio, q as ChevronsUp, r as ChevronsDown, s as RotateCcw, t as CircleQuestionMark, u as Server, v as Gauge, w as Lock, x as Wifi, y as WifiOff, A as ArrowUp, z as ArrowDown, B as Rows3, H as Columns2 } from "../_libs/lucide-react.mjs";
|
|
15
13
|
import { u as union, d as object, a as array, l as literal, b as string, n as number, c as boolean, _ as _enum } from "../_libs/zod.mjs";
|
|
16
14
|
import { R as Root2$1, L as List, T as Trigger$2, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
|
|
17
15
|
import { P as Provider, R as Root3, T as Trigger$3, a as Portal$2, C as Content2$1, A as Arrow2 } from "../_libs/radix-ui__react-tooltip.mjs";
|
|
18
16
|
import { S as Slot } from "../_libs/radix-ui__react-slot.mjs";
|
|
19
|
-
import { R as Root$1 } from "../_libs/radix-ui__react-separator.mjs";
|
|
20
|
-
import { R as Root$2, C as CollapsibleTrigger$1, a as CollapsibleContent$1 } from "../_libs/radix-ui__react-collapsible.mjs";
|
|
21
|
-
import { R as Root$3, V as Viewport$1, C as Corner, S as ScrollAreaScrollbar, a as ScrollAreaThumb } from "../_libs/radix-ui__react-scroll-area.mjs";
|
|
22
17
|
import "../_libs/tanstack__react-router.mjs";
|
|
23
18
|
import "../_libs/tiny-warning.mjs";
|
|
24
19
|
import "../_libs/tanstack__router-core.mjs";
|
|
@@ -61,6 +56,7 @@ import "../_libs/mimic-function.mjs";
|
|
|
61
56
|
import "../_libs/semver.mjs";
|
|
62
57
|
import "../_libs/uint8array-extras.mjs";
|
|
63
58
|
import "node:child_process";
|
|
59
|
+
import "../_libs/modelcontextprotocol__server.mjs";
|
|
64
60
|
import "../_libs/use-sync-external-store.mjs";
|
|
65
61
|
import "../_libs/dequal.mjs";
|
|
66
62
|
import "../_libs/readable-stream.mjs";
|
|
@@ -112,57 +108,6 @@ import "../_libs/radix-ui__react-arrow.mjs";
|
|
|
112
108
|
import "../_libs/radix-ui__react-use-size.mjs";
|
|
113
109
|
import "../_libs/radix-ui__react-use-previous.mjs";
|
|
114
110
|
import "../_libs/@radix-ui/react-visually-hidden+[...].mjs";
|
|
115
|
-
import "../_libs/devlop.mjs";
|
|
116
|
-
import "../_libs/unified.mjs";
|
|
117
|
-
import "../_libs/bail.mjs";
|
|
118
|
-
import "../_libs/extend.mjs";
|
|
119
|
-
import "../_libs/is-plain-obj.mjs";
|
|
120
|
-
import "../_libs/trough.mjs";
|
|
121
|
-
import "../_libs/vfile.mjs";
|
|
122
|
-
import "../_libs/vfile-message.mjs";
|
|
123
|
-
import "../_libs/unist-util-stringify-position.mjs";
|
|
124
|
-
import "node:url";
|
|
125
|
-
import "../_libs/remark-parse.mjs";
|
|
126
|
-
import "../_libs/mdast-util-from-markdown.mjs";
|
|
127
|
-
import "../_libs/micromark-util-decode-numeric-character-reference+[...].mjs";
|
|
128
|
-
import "../_libs/micromark-util-decode-string.mjs";
|
|
129
|
-
import "../_libs/decode-named-character-reference+[...].mjs";
|
|
130
|
-
import "../_libs/character-entities.mjs";
|
|
131
|
-
import "../_libs/micromark-util-normalize-identifier+[...].mjs";
|
|
132
|
-
import "../_libs/micromark.mjs";
|
|
133
|
-
import "../_libs/micromark-util-combine-extensions+[...].mjs";
|
|
134
|
-
import "../_libs/micromark-util-chunked.mjs";
|
|
135
|
-
import "../_libs/micromark-factory-space.mjs";
|
|
136
|
-
import "../_libs/micromark-util-character.mjs";
|
|
137
|
-
import "../_libs/micromark-core-commonmark.mjs";
|
|
138
|
-
import "../_libs/micromark-util-classify-character+[...].mjs";
|
|
139
|
-
import "../_libs/micromark-util-resolve-all.mjs";
|
|
140
|
-
import "../_libs/micromark-util-subtokenize.mjs";
|
|
141
|
-
import "../_libs/micromark-factory-destination.mjs";
|
|
142
|
-
import "../_libs/micromark-factory-label.mjs";
|
|
143
|
-
import "../_libs/micromark-factory-title.mjs";
|
|
144
|
-
import "../_libs/micromark-factory-whitespace.mjs";
|
|
145
|
-
import "../_libs/micromark-util-html-tag-name.mjs";
|
|
146
|
-
import "../_libs/mdast-util-to-string.mjs";
|
|
147
|
-
import "../_libs/remark-rehype.mjs";
|
|
148
|
-
import "../_libs/mdast-util-to-hast.mjs";
|
|
149
|
-
import "../_libs/ungap__structured-clone.mjs";
|
|
150
|
-
import "../_libs/micromark-util-sanitize-uri.mjs";
|
|
151
|
-
import "../_libs/unist-util-position.mjs";
|
|
152
|
-
import "../_libs/trim-lines.mjs";
|
|
153
|
-
import "../_libs/unist-util-visit.mjs";
|
|
154
|
-
import "../_libs/unist-util-visit-parents.mjs";
|
|
155
|
-
import "../_libs/unist-util-is.mjs";
|
|
156
|
-
import "../_libs/hast-util-to-jsx-runtime.mjs";
|
|
157
|
-
import "../_libs/comma-separated-tokens.mjs";
|
|
158
|
-
import "../_libs/property-information.mjs";
|
|
159
|
-
import "../_libs/space-separated-tokens.mjs";
|
|
160
|
-
import "../_libs/style-to-js.mjs";
|
|
161
|
-
import "../_libs/style-to-object.mjs";
|
|
162
|
-
import "../_libs/inline-style-parser.mjs";
|
|
163
|
-
import "../_libs/hast-util-whitespace.mjs";
|
|
164
|
-
import "../_libs/estree-util-is-identifier-name.mjs";
|
|
165
|
-
import "../_libs/html-url-attributes.mjs";
|
|
166
111
|
import "../_libs/radix-ui__react-roving-focus.mjs";
|
|
167
112
|
const ApiErrorSchema = object({
|
|
168
113
|
error: string()
|
|
@@ -220,12 +165,30 @@ function useStripConfig() {
|
|
|
220
165
|
);
|
|
221
166
|
const { mutate: globalMutate } = useSWRConfig();
|
|
222
167
|
const strip = response.data?.stripClaudeCodeBillingHeader ?? false;
|
|
168
|
+
const slowResponseThresholdSeconds = response.data?.slowResponseThresholdSeconds ?? DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS;
|
|
169
|
+
const optimisticConfig = (patch) => ({
|
|
170
|
+
stripClaudeCodeBillingHeader: response.data?.stripClaudeCodeBillingHeader ?? false,
|
|
171
|
+
hasSeenOnboarding: response.data?.hasSeenOnboarding ?? false,
|
|
172
|
+
slowResponseThresholdSeconds: response.data?.slowResponseThresholdSeconds ?? DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS,
|
|
173
|
+
...patch
|
|
174
|
+
});
|
|
223
175
|
const setStrip = async (next) => {
|
|
224
176
|
await globalMutate(
|
|
225
177
|
STRIP_CONFIG_SWR_KEY,
|
|
226
178
|
setRuntimeConfig({ stripClaudeCodeBillingHeader: next }),
|
|
227
179
|
{
|
|
228
|
-
optimisticData: { stripClaudeCodeBillingHeader: next },
|
|
180
|
+
optimisticData: optimisticConfig({ stripClaudeCodeBillingHeader: next }),
|
|
181
|
+
rollbackOnError: true,
|
|
182
|
+
revalidate: false
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
};
|
|
186
|
+
const setSlowResponseThresholdSeconds = async (next) => {
|
|
187
|
+
await globalMutate(
|
|
188
|
+
STRIP_CONFIG_SWR_KEY,
|
|
189
|
+
setRuntimeConfig({ slowResponseThresholdSeconds: next }),
|
|
190
|
+
{
|
|
191
|
+
optimisticData: optimisticConfig({ slowResponseThresholdSeconds: next }),
|
|
229
192
|
rollbackOnError: true,
|
|
230
193
|
revalidate: false
|
|
231
194
|
}
|
|
@@ -233,9 +196,11 @@ function useStripConfig() {
|
|
|
233
196
|
};
|
|
234
197
|
return {
|
|
235
198
|
strip,
|
|
199
|
+
slowResponseThresholdSeconds,
|
|
236
200
|
isLoading: response.isLoading,
|
|
237
201
|
isError: response.error !== void 0,
|
|
238
|
-
setStrip
|
|
202
|
+
setStrip,
|
|
203
|
+
setSlowResponseThresholdSeconds
|
|
239
204
|
};
|
|
240
205
|
}
|
|
241
206
|
const ONBOARDING_SWR_KEY = "/api/config";
|
|
@@ -274,7 +239,8 @@ function useOnboarding() {
|
|
|
274
239
|
await globalMutate(ONBOARDING_SWR_KEY, patchRuntimeConfig({ hasSeenOnboarding: true }), {
|
|
275
240
|
optimisticData: {
|
|
276
241
|
stripClaudeCodeBillingHeader: response.data?.stripClaudeCodeBillingHeader ?? false,
|
|
277
|
-
hasSeenOnboarding: true
|
|
242
|
+
hasSeenOnboarding: true,
|
|
243
|
+
slowResponseThresholdSeconds: response.data?.slowResponseThresholdSeconds ?? DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS
|
|
278
244
|
},
|
|
279
245
|
rollbackOnError: true,
|
|
280
246
|
revalidate: false
|
|
@@ -327,10 +293,10 @@ function OnboardingBanner() {
|
|
|
327
293
|
onClick: () => {
|
|
328
294
|
void markSeen();
|
|
329
295
|
},
|
|
330
|
-
className: "inline-flex items-center gap-1.5 text-xs
|
|
296
|
+
className: "inline-flex items-center gap-1.5 text-xs h-8 px-3 rounded-md border border-amber-500/40 text-amber-700 dark:text-amber-300 hover:bg-amber-500/10 transition-colors shrink-0",
|
|
331
297
|
"aria-label": "Dismiss onboarding tip",
|
|
332
298
|
children: [
|
|
333
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3" }),
|
|
299
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" }),
|
|
334
300
|
"Got it"
|
|
335
301
|
]
|
|
336
302
|
}
|
|
@@ -390,17 +356,12 @@ async function exportLogsAsZip(logs) {
|
|
|
390
356
|
document.body.removeChild(anchor);
|
|
391
357
|
URL.revokeObjectURL(url);
|
|
392
358
|
}
|
|
393
|
-
const version = "1.16.4";
|
|
394
|
-
const packageJson = {
|
|
395
|
-
version
|
|
396
|
-
};
|
|
397
359
|
function cn(...inputs) {
|
|
398
360
|
return twMerge(clsx(inputs));
|
|
399
361
|
}
|
|
400
362
|
function formatTokens(count) {
|
|
401
|
-
if (count >=
|
|
402
|
-
|
|
403
|
-
return count.toString();
|
|
363
|
+
if (count >= 1e6) return (count / 1e6).toFixed(1).replace(/\.0$/, "") + "M";
|
|
364
|
+
return (count / 1e3).toFixed(1).replace(/\.0$/, "") + "K";
|
|
404
365
|
}
|
|
405
366
|
function getStatusCategory(status) {
|
|
406
367
|
if (status === null) return "pending";
|
|
@@ -409,6 +370,10 @@ function getStatusCategory(status) {
|
|
|
409
370
|
if (status >= 500) return "server_error";
|
|
410
371
|
return "pending";
|
|
411
372
|
}
|
|
373
|
+
const version = "1.17.0";
|
|
374
|
+
const packageJson = {
|
|
375
|
+
version
|
|
376
|
+
};
|
|
412
377
|
const badgeVariants = cva(
|
|
413
378
|
"inline-flex items-center justify-center rounded-full border border-transparent px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
|
414
379
|
{
|
|
@@ -599,7 +564,7 @@ const API_FORMAT_LABELS = {
|
|
|
599
564
|
openai: "OpenAI",
|
|
600
565
|
unknown: "Unknown"
|
|
601
566
|
};
|
|
602
|
-
function formatTimestamp(iso) {
|
|
567
|
+
function formatTimestamp$1(iso) {
|
|
603
568
|
const date = new Date(iso);
|
|
604
569
|
return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
|
605
570
|
}
|
|
@@ -629,11 +594,14 @@ function ConversationHeader({
|
|
|
629
594
|
{
|
|
630
595
|
role: "button",
|
|
631
596
|
tabIndex: 0,
|
|
597
|
+
"data-nav-id": `conv-${conversationId}`,
|
|
598
|
+
"data-nav-action": expanded ? "collapse" : "expand",
|
|
632
599
|
className: cn(
|
|
633
600
|
"flex items-center gap-3 px-3 py-2 cursor-pointer transition-colors",
|
|
634
601
|
"hover:bg-muted/50",
|
|
635
602
|
"select-none",
|
|
636
|
-
"border border-border rounded-lg mb-2 bg-background sticky top-0 z-10"
|
|
603
|
+
"border border-border rounded-lg mb-2 bg-background sticky top-0 z-10",
|
|
604
|
+
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none"
|
|
637
605
|
),
|
|
638
606
|
onClick: onToggle,
|
|
639
607
|
onKeyDown: (e) => {
|
|
@@ -680,9 +648,9 @@ function ConversationHeader({
|
|
|
680
648
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
|
|
681
649
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
|
|
682
650
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
|
|
683
|
-
formatTimestamp(startTime),
|
|
651
|
+
formatTimestamp$1(startTime),
|
|
684
652
|
" - ",
|
|
685
|
-
formatTimestamp(endTime)
|
|
653
|
+
formatTimestamp$1(endTime)
|
|
686
654
|
] })
|
|
687
655
|
] }),
|
|
688
656
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
|
|
@@ -709,7 +677,7 @@ function ConversationHeader({
|
|
|
709
677
|
onClick: handleClearClick,
|
|
710
678
|
"aria-label": `Clear group (${totalCalls} request${totalCalls !== 1 ? "s" : ""})`,
|
|
711
679
|
title: "Clear this group",
|
|
712
|
-
className: "text-muted-foreground hover:text-foreground transition-colors shrink-0 inline-flex items-center justify-center size-
|
|
680
|
+
className: "text-muted-foreground hover:text-foreground transition-colors shrink-0 inline-flex items-center justify-center size-8 rounded hover:bg-muted cursor-pointer",
|
|
713
681
|
children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "size-3.5" })
|
|
714
682
|
}
|
|
715
683
|
),
|
|
@@ -783,7 +751,7 @@ function groupLogsByConversation(logs) {
|
|
|
783
751
|
result.sort((a, b) => (a.logs[0]?.timestamp ?? "").localeCompare(b.logs[0]?.timestamp ?? ""));
|
|
784
752
|
return result;
|
|
785
753
|
}
|
|
786
|
-
function isRecord
|
|
754
|
+
function isRecord(value) {
|
|
787
755
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
788
756
|
}
|
|
789
757
|
function extractStopReason(log) {
|
|
@@ -793,14 +761,14 @@ function extractStopReason(log) {
|
|
|
793
761
|
if (typeof json === "string") {
|
|
794
762
|
json = JSON.parse(json);
|
|
795
763
|
}
|
|
796
|
-
if (!isRecord
|
|
764
|
+
if (!isRecord(json)) return null;
|
|
797
765
|
if (typeof json.stop_reason === "string") {
|
|
798
766
|
if (json.stop_reason === "end_turn" || json.stop_reason === "tool_use") {
|
|
799
767
|
return json.stop_reason;
|
|
800
768
|
}
|
|
801
769
|
return null;
|
|
802
770
|
}
|
|
803
|
-
if (Array.isArray(json.choices) && json.choices.length > 0 && isRecord
|
|
771
|
+
if (Array.isArray(json.choices) && json.choices.length > 0 && isRecord(json.choices[0]) && typeof json.choices[0].finish_reason === "string" && json.choices[0].finish_reason === "stop") {
|
|
804
772
|
return "stop";
|
|
805
773
|
}
|
|
806
774
|
return null;
|
|
@@ -1151,8 +1119,17 @@ const crabVariants = [
|
|
|
1151
1119
|
Crab11,
|
|
1152
1120
|
Crab12
|
|
1153
1121
|
];
|
|
1154
|
-
function getCrabVariant(
|
|
1155
|
-
return crabVariants[Math.abs(
|
|
1122
|
+
function getCrabVariant(index2) {
|
|
1123
|
+
return crabVariants[Math.abs(index2) % crabVariants.length] ?? Crab1;
|
|
1124
|
+
}
|
|
1125
|
+
function getInteriorCrabVariantIndex(seed) {
|
|
1126
|
+
const interiorCount = crabVariants.length - 2;
|
|
1127
|
+
if (interiorCount <= 0) return 0;
|
|
1128
|
+
const normalizedSeed = Number.isFinite(seed) ? Math.trunc(seed) : 0;
|
|
1129
|
+
return 1 + (normalizedSeed % interiorCount + interiorCount) % interiorCount;
|
|
1130
|
+
}
|
|
1131
|
+
function getInteriorCrabVariant(seed) {
|
|
1132
|
+
return getCrabVariant(getInteriorCrabVariantIndex(seed));
|
|
1156
1133
|
}
|
|
1157
1134
|
const AnthropicLogoSvg = "data:image/svg+xml,%3csvg%20height='2500'%20viewBox='0%206.603%201192.672%201193.397'%20width='2500'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='m233.96%20800.215%20234.684-131.678%203.947-11.436-3.947-6.363h-11.436l-39.221-2.416-134.094-3.624-116.296-4.832-112.67-6.04-28.35-6.04-26.577-35.035%202.738-17.477%2023.84-16.027%2034.147%202.98%2075.463%205.155%20113.235%207.812%2082.147%204.832%20121.692%2012.644h19.329l2.738-7.812-6.604-4.832-5.154-4.832-117.182-79.41-126.845-83.92-66.443-48.321-35.92-24.484-18.12-22.953-7.813-50.093%2032.618-35.92%2043.812%202.98%2011.195%202.98%2044.375%2034.147%2094.792%2073.37%20123.786%2091.167%2018.12%2015.06%207.249-5.154.886-3.624-8.135-13.61-67.329-121.692-71.838-123.785-31.974-51.302-8.456-30.765c-2.98-12.645-5.154-23.275-5.154-36.242l37.127-50.416%2020.537-6.604%2049.53%206.604%2020.86%2018.121%2030.765%2070.39%2049.852%20110.818%2077.315%20150.684%2022.631%2044.698%2012.08%2041.396%204.51%2012.645h7.813v-7.248l6.362-84.886%2011.759-104.215%2011.436-134.094%203.946-37.772%2018.685-45.262%2037.127-24.482%2028.994%2013.852%2023.839%2034.148-3.303%2022.067-14.174%2092.134-27.785%20144.323-18.121%2096.644h10.55l12.08-12.08%2048.887-64.913%2082.147-102.685%2036.242-40.752%2042.282-45.02%2027.14-21.423h51.303l37.772%2056.135-16.913%2057.986-52.832%2067.007-43.812%2056.779-62.82%2084.563-39.22%2067.651%203.623%205.396%209.343-.886%20141.906-30.201%2076.671-13.852%2091.49-15.705%2041.396%2019.329%204.51%2019.65-16.269%2040.189-97.852%2024.16-114.764%2022.954-170.9%2040.43-2.093%201.53%202.416%202.98%2076.993%207.248%2032.94%201.771h80.617l150.12%2011.195%2039.222%2025.933%2023.517%2031.732-3.946%2024.16-60.403%2030.766-81.503-19.33-190.228-45.26-65.235-16.27h-9.02v5.397l54.362%2053.154%2099.624%2089.96%20124.752%20115.973%206.362%2028.671-16.027%2022.63-16.912-2.415-109.611-82.47-42.282-37.127-95.758-80.618h-6.363v8.456l22.067%2032.296%20116.537%20175.167%206.04%2053.719-8.456%2017.476-30.201%2010.55-33.181-6.04-68.215-95.758-70.39-107.84-56.778-96.644-6.926%203.947-33.503%20360.886-15.705%2018.443-36.243%2013.852-30.201-22.953-16.027-37.127%2016.027-73.37%2019.329-95.758%2015.704-76.107%2014.175-94.55%208.456-31.41-.563-2.094-6.927.886-71.275%2097.852-108.402%20146.497-85.772%2091.812-20.537%208.134-35.597-18.443%203.301-32.94%2019.893-29.315%20118.712-151.007%2071.597-93.583%2046.228-54.04-.322-7.813h-2.738l-315.302%20204.725-56.135%207.248-24.16-22.63%202.98-37.128%2011.435-12.08%2094.792-65.236-.322.323z'%20fill='%23d97757'/%3e%3c/svg%3e";
|
|
1158
1135
|
const OpenAILogoSvg = "data:image/svg+xml,%3csvg%20height='2500'%20viewBox='-1%20-.1%20949.1%20959.8'%20width='2474'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='m925.8%20456.3c10.4%2023.2%2017%2048%2019.7%2073.3%202.6%2025.3%201.3%2050.9-4.1%2075.8-5.3%2024.9-14.5%2048.8-27.3%2070.8-8.4%2014.7-18.3%2028.5-29.7%2041.2-11.3%2012.6-23.9%2024-37.6%2034-13.8%2010-28.5%2018.4-44.1%2025.3-15.5%206.8-31.7%2012-48.3%2015.4-7.8%2024.2-19.4%2047.1-34.4%2067.7-14.9%2020.6-33%2038.7-53.6%2053.6-20.6%2015-43.4%2026.6-67.6%2034.4-24.2%207.9-49.5%2011.8-75%2011.8-16.9.1-33.9-1.7-50.5-5.1-16.5-3.5-32.7-8.8-48.2-15.7s-30.2-15.5-43.9-25.5c-13.6-10-26.2-21.5-37.4-34.2-25%205.4-50.6%206.7-75.9%204.1-25.3-2.7-50.1-9.3-73.4-19.7-23.2-10.3-44.7-24.3-63.6-41.4s-35-37.1-47.7-59.1c-8.5-14.7-15.5-30.2-20.8-46.3s-8.8-32.7-10.6-49.6c-1.8-16.8-1.7-33.8.1-50.7%201.8-16.8%205.5-33.4%2010.8-49.5-17-18.9-31-40.4-41.4-63.6-10.3-23.3-17-48-19.6-73.3-2.7-25.3-1.3-50.9%204-75.8s14.5-48.8%2027.3-70.8c8.4-14.7%2018.3-28.6%2029.6-41.2s24-24%2037.7-34%2028.5-18.5%2044-25.3c15.6-6.9%2031.8-12%2048.4-15.4%207.8-24.3%2019.4-47.1%2034.3-67.7%2015-20.6%2033.1-38.7%2053.7-53.7%2020.6-14.9%2043.4-26.5%2067.6-34.4%2024.2-7.8%2049.5-11.8%2075-11.7%2016.9-.1%2033.9%201.6%2050.5%205.1s32.8%208.7%2048.3%2015.6c15.5%207%2030.2%2015.5%2043.9%2025.5%2013.7%2010.1%2026.3%2021.5%2037.5%2034.2%2024.9-5.3%2050.5-6.6%2075.8-4s50%209.3%2073.3%2019.6c23.2%2010.4%2044.7%2024.3%2063.6%2041.4%2018.9%2017%2035%2036.9%2047.7%2059%208.5%2014.6%2015.5%2030.1%2020.8%2046.3%205.3%2016.1%208.9%2032.7%2010.6%2049.6%201.8%2016.9%201.8%2033.9-.1%2050.8-1.8%2016.9-5.5%2033.5-10.8%2049.6%2017.1%2018.9%2031%2040.3%2041.4%2063.6zm-333.2%20426.9c21.8-9%2041.6-22.3%2058.3-39s30-36.5%2039-58.4c9-21.8%2013.7-45.2%2013.7-68.8v-223q-.1-.3-.2-.7-.1-.3-.3-.6-.2-.3-.5-.5-.3-.3-.6-.4l-80.7-46.6v269.4c0%202.7-.4%205.5-1.1%208.1-.7%202.7-1.7%205.2-3.1%207.6s-3%204.6-5%206.5a32.1%2032.1%200%200%201%20-6.5%205l-191.1%20110.3c-1.6%201-4.3%202.4-5.7%203.2%207.9%206.7%2016.5%2012.6%2025.5%2017.8%209.1%205.2%2018.5%209.6%2028.3%2013.2%209.8%203.5%2019.9%206.2%2030.1%208%2010.3%201.8%2020.7%202.7%2031.1%202.7%2023.6%200%2047-4.7%2068.8-13.8zm-455.1-151.4c11.9%2020.5%2027.6%2038.3%2046.3%2052.7%2018.8%2014.4%2040.1%2024.9%2062.9%2031s46.6%207.7%2070%204.6%2045.9-10.7%2066.4-22.5l193.2-111.5.5-.5q.2-.2.3-.6.2-.3.3-.6v-94l-233.2%20134.9c-2.4%201.4-4.9%202.4-7.5%203.2-2.7.7-5.4%201-8.2%201-2.7%200-5.4-.3-8.1-1-2.6-.8-5.2-1.8-7.6-3.2l-191.1-110.4c-1.7-1-4.2-2.5-5.6-3.4-1.8%2010.3-2.7%2020.7-2.7%2031.1s1%2020.8%202.8%2031.1c1.8%2010.2%204.6%2020.3%208.1%2030.1%203.6%209.8%208%2019.2%2013.2%2028.2zm-50.2-417c-11.8%2020.5-19.4%2043.1-22.5%2066.5s-1.5%2047.1%204.6%2070c6.1%2022.8%2016.6%2044.1%2031%2062.9%2014.4%2018.7%2032.3%2034.4%2052.7%2046.2l193.1%20111.6q.3.1.7.2h.7q.4%200%20.7-.2.3-.1.6-.3l81-46.8-233.2-134.6c-2.3-1.4-4.5-3.1-6.5-5a32.1%2032.1%200%200%201%20-5-6.5c-1.3-2.4-2.4-4.9-3.1-7.6-.7-2.6-1.1-5.3-1-8.1v-227.1c-9.8%203.6-19.3%208-28.3%2013.2-9%205.3-17.5%2011.3-25.5%2018-7.9%206.7-15.3%2014.1-22%2022.1-6.7%207.9-12.6%2016.5-17.8%2025.5zm663.3%20154.4c2.4%201.4%204.6%203%206.6%205%201.9%201.9%203.6%204.1%205%206.5%201.3%202.4%202.4%205%203.1%207.6.6%202.7%201%205.4.9%208.2v227.1c32.1-11.8%2060.1-32.5%2080.8-59.7%2020.8-27.2%2033.3-59.7%2036.2-93.7s-3.9-68.2-19.7-98.5-39.9-55.5-69.5-72.5l-193.1-111.6q-.3-.1-.7-.2h-.7q-.3.1-.7.2-.3.1-.6.3l-80.6%2046.6%20233.2%20134.7zm80.5-121h-.1v.1zm-.1-.1c5.8-33.6%201.9-68.2-11.3-99.7-13.1-31.5-35-58.6-63-78.2-28-19.5-61-30.7-95.1-32.2-34.2-1.4-68%206.9-97.6%2023.9l-193.1%20111.5q-.3.2-.5.5l-.4.6q-.1.3-.2.7-.1.3-.1.7v93.2l233.2-134.7c2.4-1.4%205-2.4%207.6-3.2%202.7-.7%205.4-1%208.1-1%202.8%200%205.5.3%208.2%201%202.6.8%205.1%201.8%207.5%203.2l191.1%20110.4c1.7%201%204.2%202.4%205.6%203.3zm-505.3-103.2c0-2.7.4-5.4%201.1-8.1.7-2.6%201.7-5.2%203.1-7.6%201.4-2.3%203-4.5%205-6.5%201.9-1.9%204.1-3.6%206.5-4.9l191.1-110.3c1.8-1.1%204.3-2.5%205.7-3.2-26.2-21.9-58.2-35.9-92.1-40.2-33.9-4.4-68.3%201-99.2%2015.5-31%2014.5-57.2%2037.6-75.5%2066.4-18.3%2028.9-28%2062.3-28%2096.5v223q.1.4.2.7.1.3.3.6.2.3.5.6.2.2.6.4l80.7%2046.6zm43.8%20294.7%20103.9%2060%20103.9-60v-119.9l-103.8-60-103.9%2060z'/%3e%3c/svg%3e";
|
|
@@ -1276,85 +1253,6 @@ function TooltipContent({
|
|
|
1276
1253
|
}
|
|
1277
1254
|
) });
|
|
1278
1255
|
}
|
|
1279
|
-
function classifyValue(value) {
|
|
1280
|
-
if (value === null) return "null";
|
|
1281
|
-
if (Array.isArray(value)) return "array";
|
|
1282
|
-
switch (typeof value) {
|
|
1283
|
-
case "string":
|
|
1284
|
-
return "string";
|
|
1285
|
-
case "number":
|
|
1286
|
-
return "number";
|
|
1287
|
-
case "boolean":
|
|
1288
|
-
return "boolean";
|
|
1289
|
-
case "object":
|
|
1290
|
-
return "object";
|
|
1291
|
-
case "bigint":
|
|
1292
|
-
case "symbol":
|
|
1293
|
-
case "undefined":
|
|
1294
|
-
case "function":
|
|
1295
|
-
return "object";
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
function isExpandable(value) {
|
|
1299
|
-
return value !== null && (Array.isArray(value) || typeof value === "object");
|
|
1300
|
-
}
|
|
1301
|
-
function getEntries(value) {
|
|
1302
|
-
if (Array.isArray(value)) {
|
|
1303
|
-
return value.map((item, index) => [String(index), item]);
|
|
1304
|
-
}
|
|
1305
|
-
if (typeof value === "object" && value !== null) {
|
|
1306
|
-
return Object.entries(value);
|
|
1307
|
-
}
|
|
1308
|
-
return [];
|
|
1309
|
-
}
|
|
1310
|
-
const JSON_FULL_EXPANSION_NODE_LIMIT = 1e3;
|
|
1311
|
-
const JSON_LARGE_EXPANSION_DEPTH = 2;
|
|
1312
|
-
function countJsonNodes(value, limit = Number.POSITIVE_INFINITY) {
|
|
1313
|
-
let count = 0;
|
|
1314
|
-
const pending = [value];
|
|
1315
|
-
while (pending.length > 0) {
|
|
1316
|
-
const current = pending.pop();
|
|
1317
|
-
if (current === void 0) continue;
|
|
1318
|
-
count += 1;
|
|
1319
|
-
if (count > limit) return count;
|
|
1320
|
-
if (!isExpandable(current)) continue;
|
|
1321
|
-
for (const [, child] of getEntries(current)) {
|
|
1322
|
-
pending.push(child);
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
return count;
|
|
1326
|
-
}
|
|
1327
|
-
function getJsonExpansionPolicy(value) {
|
|
1328
|
-
const countedNodes = countJsonNodes(value, JSON_FULL_EXPANSION_NODE_LIMIT);
|
|
1329
|
-
const limited = countedNodes > JSON_FULL_EXPANSION_NODE_LIMIT;
|
|
1330
|
-
return {
|
|
1331
|
-
depth: limited ? JSON_LARGE_EXPANSION_DEPTH : Number.POSITIVE_INFINITY,
|
|
1332
|
-
limited,
|
|
1333
|
-
countedNodes
|
|
1334
|
-
};
|
|
1335
|
-
}
|
|
1336
|
-
function useJsonBulkExpansion(text) {
|
|
1337
|
-
const parsed = reactExports.useMemo(() => text === null ? null : parseJsonText(text), [text]);
|
|
1338
|
-
const parsedData = parsed?.kind === "json" ? parsed.data : null;
|
|
1339
|
-
const policy = reactExports.useMemo(
|
|
1340
|
-
() => parsedData === null ? null : getJsonExpansionPolicy(parsedData),
|
|
1341
|
-
[parsedData]
|
|
1342
|
-
);
|
|
1343
|
-
const [isExpanded, setIsExpanded] = reactExports.useState(false);
|
|
1344
|
-
const [bulkDepth, setBulkDepth] = reactExports.useState(0);
|
|
1345
|
-
const [bulkRevision, setBulkRevision] = reactExports.useState(0);
|
|
1346
|
-
const [isPending, startTransition] = reactExports.useTransition();
|
|
1347
|
-
const toggle = reactExports.useCallback(() => {
|
|
1348
|
-
const nextExpanded = !isExpanded;
|
|
1349
|
-
const targetDepth = nextExpanded && policy !== null ? policy.depth : 0;
|
|
1350
|
-
startTransition(() => {
|
|
1351
|
-
setIsExpanded(nextExpanded);
|
|
1352
|
-
setBulkDepth(targetDepth);
|
|
1353
|
-
setBulkRevision((current) => current + 1);
|
|
1354
|
-
});
|
|
1355
|
-
}, [isExpanded, policy]);
|
|
1356
|
-
return { parsedData, policy, isExpanded, toggle, isPending, bulkDepth, bulkRevision };
|
|
1357
|
-
}
|
|
1358
1256
|
function JsonExpansionButton({
|
|
1359
1257
|
policy,
|
|
1360
1258
|
isExpanded,
|
|
@@ -1362,15 +1260,15 @@ function JsonExpansionButton({
|
|
|
1362
1260
|
onToggle
|
|
1363
1261
|
}) {
|
|
1364
1262
|
if (policy === null) return null;
|
|
1365
|
-
const label = isPending ? "Updating..." : isExpanded ? "Collapse all" :
|
|
1366
|
-
const tooltip =
|
|
1367
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
1263
|
+
const label = isPending ? "Updating..." : isExpanded ? "Collapse all" : "Expand all";
|
|
1264
|
+
const tooltip = isExpanded ? "Collapse all JSON nodes" : "Expand all JSON nodes";
|
|
1265
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
1368
1266
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1369
1267
|
Button,
|
|
1370
1268
|
{
|
|
1371
1269
|
variant: "outline",
|
|
1372
1270
|
size: "sm",
|
|
1373
|
-
className: "h-
|
|
1271
|
+
className: "h-8 text-xs",
|
|
1374
1272
|
onClick: (e) => {
|
|
1375
1273
|
e.stopPropagation();
|
|
1376
1274
|
onToggle();
|
|
@@ -1378,283 +1276,17 @@ function JsonExpansionButton({
|
|
|
1378
1276
|
disabled: isPending,
|
|
1379
1277
|
"aria-pressed": isExpanded,
|
|
1380
1278
|
children: [
|
|
1381
|
-
isExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsUp, { className: "size-3 mr-1" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsDown, { className: "size-3 mr-1" }),
|
|
1279
|
+
isExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsUp, { className: "size-3.5 mr-1" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsDown, { className: "size-3.5 mr-1" }),
|
|
1382
1280
|
label
|
|
1383
1281
|
]
|
|
1384
1282
|
}
|
|
1385
1283
|
) }),
|
|
1386
1284
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: tooltip })
|
|
1387
|
-
] });
|
|
1388
|
-
}
|
|
1389
|
-
const STRING_TRUNCATE_LIMIT = 120;
|
|
1390
|
-
function StringValue({ text }) {
|
|
1391
|
-
const [expanded, setExpanded] = reactExports.useState(false);
|
|
1392
|
-
const isLong = text.length > STRING_TRUNCATE_LIMIT;
|
|
1393
|
-
if (!isLong) {
|
|
1394
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-400 break-all", children: [
|
|
1395
|
-
'"',
|
|
1396
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "prose prose-sm dark:prose-invert inline max-w-none [&_p]:inline [&_p]:my-0 [&_code]:text-emerald-300 [&_a]:text-emerald-300", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: text }) }),
|
|
1397
|
-
'"'
|
|
1398
|
-
] });
|
|
1399
|
-
}
|
|
1400
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-400 break-all", children: [
|
|
1401
|
-
'"',
|
|
1402
|
-
expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1403
|
-
"span",
|
|
1404
|
-
{
|
|
1405
|
-
className: "cursor-pointer prose prose-sm dark:prose-invert inline max-w-none [&_p]:inline [&_p]:my-0 [&_code]:text-emerald-300 [&_a]:text-emerald-300",
|
|
1406
|
-
onClick: (e) => {
|
|
1407
|
-
e.stopPropagation();
|
|
1408
|
-
setExpanded(false);
|
|
1409
|
-
},
|
|
1410
|
-
onKeyDown: (e) => {
|
|
1411
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
1412
|
-
e.stopPropagation();
|
|
1413
|
-
setExpanded(false);
|
|
1414
|
-
}
|
|
1415
|
-
},
|
|
1416
|
-
role: "button",
|
|
1417
|
-
tabIndex: 0,
|
|
1418
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: text })
|
|
1419
|
-
}
|
|
1420
|
-
) : /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { delayDuration: 300, children: [
|
|
1421
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1422
|
-
TooltipTrigger,
|
|
1423
|
-
{
|
|
1424
|
-
onClick: (e) => {
|
|
1425
|
-
e.stopPropagation();
|
|
1426
|
-
setExpanded(true);
|
|
1427
|
-
},
|
|
1428
|
-
className: "text-left cursor-pointer",
|
|
1429
|
-
children: [
|
|
1430
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: text.slice(0, STRING_TRUNCATE_LIMIT) }),
|
|
1431
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400/50", children: "…" })
|
|
1432
|
-
]
|
|
1433
|
-
}
|
|
1434
|
-
),
|
|
1435
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1436
|
-
TooltipContent,
|
|
1437
|
-
{
|
|
1438
|
-
side: "bottom",
|
|
1439
|
-
className: "max-w-md text-xs p-2 break-words whitespace-pre-wrap",
|
|
1440
|
-
children: [
|
|
1441
|
-
text.slice(0, 500),
|
|
1442
|
-
text.length > 500 ? "…" : ""
|
|
1443
|
-
]
|
|
1444
|
-
}
|
|
1445
|
-
)
|
|
1446
|
-
] }),
|
|
1447
|
-
'"'
|
|
1448
|
-
] });
|
|
1449
|
-
}
|
|
1450
|
-
function PrimitiveValue({ value }) {
|
|
1451
|
-
if (value === null) {
|
|
1452
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-rose-400 italic", children: "null" });
|
|
1453
|
-
}
|
|
1454
|
-
switch (typeof value) {
|
|
1455
|
-
case "string":
|
|
1456
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(StringValue, { text: value });
|
|
1457
|
-
case "number":
|
|
1458
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-amber-400", children: value });
|
|
1459
|
-
case "boolean":
|
|
1460
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-blue-400", children: value ? "true" : "false" });
|
|
1461
|
-
case "object":
|
|
1462
|
-
case "bigint":
|
|
1463
|
-
case "symbol":
|
|
1464
|
-
case "undefined":
|
|
1465
|
-
case "function":
|
|
1466
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground", children: JSON.stringify(value) });
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
function CopyValueButton({ value }) {
|
|
1470
|
-
const [copied, setCopied] = reactExports.useState(false);
|
|
1471
|
-
function handleCopy(e) {
|
|
1472
|
-
e.stopPropagation();
|
|
1473
|
-
void window.navigator.clipboard.writeText(JSON.stringify(value, null, 2)).then(() => {
|
|
1474
|
-
setCopied(true);
|
|
1475
|
-
setTimeout(() => {
|
|
1476
|
-
setCopied(false);
|
|
1477
|
-
}, 2e3);
|
|
1478
|
-
});
|
|
1479
|
-
}
|
|
1480
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1481
|
-
"button",
|
|
1482
|
-
{
|
|
1483
|
-
type: "button",
|
|
1484
|
-
onClick: handleCopy,
|
|
1485
|
-
className: "inline-flex items-center gap-1 text-[10px] text-muted-foreground hover:text-foreground transition-colors",
|
|
1486
|
-
title: "Copy JSON",
|
|
1487
|
-
children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3 text-green-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3" })
|
|
1488
|
-
}
|
|
1489
|
-
);
|
|
1490
|
-
}
|
|
1491
|
-
function CopyButton$1({ value }) {
|
|
1492
|
-
const [copied, setCopied] = reactExports.useState(false);
|
|
1493
|
-
function handleCopy(e) {
|
|
1494
|
-
e.stopPropagation();
|
|
1495
|
-
void window.navigator.clipboard.writeText(JSON.stringify(value, null, 2)).then(() => {
|
|
1496
|
-
setCopied(true);
|
|
1497
|
-
setTimeout(() => {
|
|
1498
|
-
setCopied(false);
|
|
1499
|
-
}, 2e3);
|
|
1500
|
-
});
|
|
1501
|
-
}
|
|
1502
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1503
|
-
"button",
|
|
1504
|
-
{
|
|
1505
|
-
type: "button",
|
|
1506
|
-
onClick: handleCopy,
|
|
1507
|
-
className: "opacity-0 group-hover/row:opacity-100 hover:bg-muted p-0.5 rounded transition-opacity shrink-0",
|
|
1508
|
-
title: "Copy to clipboard",
|
|
1509
|
-
children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3 text-green-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3 text-muted-foreground" })
|
|
1510
|
-
}
|
|
1511
|
-
);
|
|
1512
|
-
}
|
|
1513
|
-
function ExpandCollapseButton({
|
|
1514
|
-
onClick
|
|
1515
|
-
}) {
|
|
1516
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1517
|
-
"button",
|
|
1518
|
-
{
|
|
1519
|
-
type: "button",
|
|
1520
|
-
onClick,
|
|
1521
|
-
className: "opacity-0 group-hover/row:opacity-100 hover:bg-muted p-0.5 rounded transition-opacity shrink-0",
|
|
1522
|
-
title: "Expand all descendants",
|
|
1523
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsDown, { className: "size-3.5 text-muted-foreground" })
|
|
1524
|
-
}
|
|
1525
|
-
);
|
|
1526
|
-
}
|
|
1527
|
-
const JsonNode = reactExports.memo(function JsonNode2({
|
|
1528
|
-
name,
|
|
1529
|
-
value,
|
|
1530
|
-
level,
|
|
1531
|
-
defaultExpandDepth,
|
|
1532
|
-
isArrayItem
|
|
1533
|
-
}) {
|
|
1534
|
-
const [expanded, setExpanded] = reactExports.useState(level < defaultExpandDepth);
|
|
1535
|
-
const [childResetKey, setChildResetKey] = reactExports.useState(0);
|
|
1536
|
-
const [childDepthOverride, setChildDepthOverride] = reactExports.useState(null);
|
|
1537
|
-
const expandable = isExpandable(value);
|
|
1538
|
-
const fullyExpanded = childDepthOverride === Number.POSITIVE_INFINITY;
|
|
1539
|
-
const entries = reactExports.useMemo(() => getEntries(value), [value]);
|
|
1540
|
-
const hasExpandableChild = reactExports.useMemo(
|
|
1541
|
-
() => entries.some(([, child]) => isExpandable(child)),
|
|
1542
|
-
[entries]
|
|
1543
|
-
);
|
|
1544
|
-
const dataType = classifyValue(value);
|
|
1545
|
-
const openBracket = dataType === "array" ? "[" : "{";
|
|
1546
|
-
const closeBracket = dataType === "array" ? "]" : "}";
|
|
1547
|
-
function expandAllDeep() {
|
|
1548
|
-
setExpanded(true);
|
|
1549
|
-
setChildDepthOverride(Number.POSITIVE_INFINITY);
|
|
1550
|
-
setChildResetKey((k) => k + 1);
|
|
1551
|
-
}
|
|
1552
|
-
function collapseToDefault() {
|
|
1553
|
-
setExpanded(false);
|
|
1554
|
-
setChildDepthOverride(0);
|
|
1555
|
-
setChildResetKey((k) => k + 1);
|
|
1556
|
-
}
|
|
1557
|
-
function handleRowToggle() {
|
|
1558
|
-
if (fullyExpanded) {
|
|
1559
|
-
collapseToDefault();
|
|
1560
|
-
} else {
|
|
1561
|
-
setExpanded(!expanded);
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
1564
|
-
function handleExpandAll(e) {
|
|
1565
|
-
e.stopPropagation();
|
|
1566
|
-
expandAllDeep();
|
|
1567
|
-
}
|
|
1568
|
-
const effectiveChildDepth = childDepthOverride ?? defaultExpandDepth;
|
|
1569
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn(level > 0 && "border-l border-border/50 ml-2"), children: [
|
|
1570
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1571
|
-
"div",
|
|
1572
|
-
{
|
|
1573
|
-
className: cn(
|
|
1574
|
-
"flex items-start gap-1 py-0.5 px-1 -ml-1 rounded-sm group/row",
|
|
1575
|
-
expandable && "cursor-pointer hover:bg-muted/50"
|
|
1576
|
-
),
|
|
1577
|
-
onClick: expandable ? handleRowToggle : void 0,
|
|
1578
|
-
onKeyDown: expandable ? (e) => {
|
|
1579
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
1580
|
-
e.preventDefault();
|
|
1581
|
-
handleRowToggle();
|
|
1582
|
-
}
|
|
1583
|
-
} : void 0,
|
|
1584
|
-
onDoubleClick: expandable && hasExpandableChild ? () => expandAllDeep() : void 0,
|
|
1585
|
-
role: expandable ? "button" : void 0,
|
|
1586
|
-
tabIndex: expandable ? 0 : void 0,
|
|
1587
|
-
children: [
|
|
1588
|
-
expandable ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-4 h-5 flex items-center justify-center shrink-0", children: expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 text-muted-foreground" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-4 shrink-0" }),
|
|
1589
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("shrink-0", isArrayItem ? "text-muted-foreground" : "text-cyan-400"), children: isArrayItem ? name : `"${name}"` }),
|
|
1590
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground shrink-0", children: expandable ? "" : ":" }),
|
|
1591
|
-
expandable ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground", children: [
|
|
1592
|
-
openBracket,
|
|
1593
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground/60 text-xs", children: [
|
|
1594
|
-
" ",
|
|
1595
|
-
entries.length,
|
|
1596
|
-
" ",
|
|
1597
|
-
entries.length === 1 ? "item" : "items",
|
|
1598
|
-
" ",
|
|
1599
|
-
closeBracket
|
|
1600
|
-
] })
|
|
1601
|
-
] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(PrimitiveValue, { value }) }),
|
|
1602
|
-
expandable && hasExpandableChild && !fullyExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx(ExpandCollapseButton, { onClick: handleExpandAll }),
|
|
1603
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(CopyButton$1, { value })
|
|
1604
|
-
]
|
|
1605
|
-
}
|
|
1606
|
-
),
|
|
1607
|
-
expandable && expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pl-4", children: [
|
|
1608
|
-
entries.map(([key, childValue]) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1609
|
-
JsonNode2,
|
|
1610
|
-
{
|
|
1611
|
-
name: key,
|
|
1612
|
-
value: childValue,
|
|
1613
|
-
level: level + 1,
|
|
1614
|
-
defaultExpandDepth: effectiveChildDepth,
|
|
1615
|
-
isArrayItem: dataType === "array"
|
|
1616
|
-
},
|
|
1617
|
-
key
|
|
1618
|
-
)),
|
|
1619
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground py-0.5 px-1", children: closeBracket })
|
|
1620
|
-
] }, childResetKey)
|
|
1621
|
-
] });
|
|
1622
|
-
});
|
|
1623
|
-
function JsonViewer({
|
|
1624
|
-
data,
|
|
1625
|
-
defaultExpandDepth = 0,
|
|
1626
|
-
className,
|
|
1627
|
-
showCopy = false,
|
|
1628
|
-
bulkDepth: controlledBulkDepth,
|
|
1629
|
-
bulkRevision: controlledBulkRevision
|
|
1630
|
-
}) {
|
|
1631
|
-
const expandable = isExpandable(data);
|
|
1632
|
-
const entries = reactExports.useMemo(() => getEntries(data), [data]);
|
|
1633
|
-
const bulkDepth = controlledBulkDepth ?? defaultExpandDepth;
|
|
1634
|
-
const bulkRevision = controlledBulkRevision ?? 0;
|
|
1635
|
-
if (!expandable) {
|
|
1636
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("font-mono text-xs leading-relaxed", className), children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1637
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(PrimitiveValue, { value: data }),
|
|
1638
|
-
showCopy && /* @__PURE__ */ jsxRuntimeExports.jsx(CopyValueButton, { value: data })
|
|
1639
|
-
] }) }) });
|
|
1640
|
-
}
|
|
1641
|
-
const dataType = classifyValue(data);
|
|
1642
|
-
const isArray = dataType === "array";
|
|
1643
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("font-mono text-xs leading-relaxed", className), children: [
|
|
1644
|
-
showCopy && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-2 flex items-center justify-end gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyValueButton, { value: data }) }),
|
|
1645
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: entries.map(([key, childValue]) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1646
|
-
JsonNode,
|
|
1647
|
-
{
|
|
1648
|
-
name: key,
|
|
1649
|
-
value: childValue,
|
|
1650
|
-
level: 0,
|
|
1651
|
-
defaultExpandDepth: bulkDepth,
|
|
1652
|
-
isArrayItem: isArray
|
|
1653
|
-
},
|
|
1654
|
-
key
|
|
1655
|
-
)) }, bulkRevision)
|
|
1656
1285
|
] }) });
|
|
1657
1286
|
}
|
|
1287
|
+
function getJsonExpansionPolicy(_value) {
|
|
1288
|
+
return { depth: Number.POSITIVE_INFINITY };
|
|
1289
|
+
}
|
|
1658
1290
|
function parseJsonText(text) {
|
|
1659
1291
|
try {
|
|
1660
1292
|
const parsed = JSON.parse(text);
|
|
@@ -1663,24 +1295,28 @@ function parseJsonText(text) {
|
|
|
1663
1295
|
return { kind: "text" };
|
|
1664
1296
|
}
|
|
1665
1297
|
}
|
|
1666
|
-
|
|
1667
|
-
text,
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
)
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1298
|
+
function useJsonBulkExpansion(text) {
|
|
1299
|
+
const parsed = reactExports.useMemo(() => text === null ? null : parseJsonText(text), [text]);
|
|
1300
|
+
const parsedData = parsed?.kind === "json" ? parsed.data : null;
|
|
1301
|
+
const policy = reactExports.useMemo(
|
|
1302
|
+
() => parsedData === null ? null : getJsonExpansionPolicy(),
|
|
1303
|
+
[parsedData]
|
|
1304
|
+
);
|
|
1305
|
+
const [isExpanded, setIsExpanded] = reactExports.useState(false);
|
|
1306
|
+
const [bulkDepth, setBulkDepth] = reactExports.useState(0);
|
|
1307
|
+
const [bulkRevision, setBulkRevision] = reactExports.useState(0);
|
|
1308
|
+
const [isPending, startTransition] = reactExports.useTransition();
|
|
1309
|
+
const toggle = reactExports.useCallback(() => {
|
|
1310
|
+
const nextExpanded = !isExpanded;
|
|
1311
|
+
const targetDepth = nextExpanded && policy !== null ? policy.depth : 0;
|
|
1312
|
+
startTransition(() => {
|
|
1313
|
+
setIsExpanded(nextExpanded);
|
|
1314
|
+
setBulkDepth(targetDepth);
|
|
1315
|
+
setBulkRevision((current) => current + 1);
|
|
1316
|
+
});
|
|
1317
|
+
}, [isExpanded, policy]);
|
|
1318
|
+
return { parsedData, policy, isExpanded, toggle, isPending, bulkDepth, bulkRevision };
|
|
1319
|
+
}
|
|
1684
1320
|
function safeJsonValue(value) {
|
|
1685
1321
|
if (value === null || value === void 0) return null;
|
|
1686
1322
|
switch (typeof value) {
|
|
@@ -1786,6 +1422,88 @@ function TabsContent({
|
|
|
1786
1422
|
}
|
|
1787
1423
|
);
|
|
1788
1424
|
}
|
|
1425
|
+
const LazyCompareDrawer = reactExports.lazy(
|
|
1426
|
+
() => import("./CompareDrawer-C4-CQL5w.mjs").then((m) => ({ default: m.CompareDrawer }))
|
|
1427
|
+
);
|
|
1428
|
+
const LazyReplayDialog = reactExports.lazy(
|
|
1429
|
+
() => import("./ReplayDialog-BTb1Bam8.mjs").then((m) => ({ default: m.ReplayDialog }))
|
|
1430
|
+
);
|
|
1431
|
+
const LazyRequestAnatomy = reactExports.lazy(
|
|
1432
|
+
() => import("./RequestAnatomy-CZFV1IvL.mjs").then((m) => ({ default: m.RequestAnatomy }))
|
|
1433
|
+
);
|
|
1434
|
+
const LazyResponseView = reactExports.lazy(
|
|
1435
|
+
() => import("./ResponseView-CTZekh65.mjs").then((m) => ({ default: m.ResponseView }))
|
|
1436
|
+
);
|
|
1437
|
+
const LazyStreamingChunkSequence = reactExports.lazy(
|
|
1438
|
+
() => import("./StreamingChunkSequence-C38Ynabd.mjs").then((m) => ({
|
|
1439
|
+
default: m.StreamingChunkSequence
|
|
1440
|
+
}))
|
|
1441
|
+
);
|
|
1442
|
+
const LazyJsonViewer = reactExports.lazy(
|
|
1443
|
+
() => import("./json-viewer-DROqpjS9.mjs").then((m) => ({ default: m.JsonViewer }))
|
|
1444
|
+
);
|
|
1445
|
+
const LazyJsonViewerFromString = reactExports.lazy(
|
|
1446
|
+
() => import("./json-viewer-DROqpjS9.mjs").then((m) => ({ default: m.JsonViewerFromString }))
|
|
1447
|
+
);
|
|
1448
|
+
const HIGHLIGHT_DURATION_MS = 1200;
|
|
1449
|
+
const MAX_HIGHLIGHT_ATTEMPTS = 12;
|
|
1450
|
+
function useAnatomyJump(options) {
|
|
1451
|
+
const { containerRef, setExpandToPath, ensureTabActive, highlightMs } = options;
|
|
1452
|
+
const highlightTimer = reactExports.useRef(null);
|
|
1453
|
+
return reactExports.useCallback(
|
|
1454
|
+
(segment2) => {
|
|
1455
|
+
ensureTabActive?.();
|
|
1456
|
+
setExpandToPath(segment2.path);
|
|
1457
|
+
const applyHighlight = (target) => {
|
|
1458
|
+
target.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
1459
|
+
target.classList.add(
|
|
1460
|
+
"ring-2",
|
|
1461
|
+
"ring-primary/60",
|
|
1462
|
+
"ring-offset-1",
|
|
1463
|
+
"ring-offset-background",
|
|
1464
|
+
"rounded-sm",
|
|
1465
|
+
"transition-shadow"
|
|
1466
|
+
);
|
|
1467
|
+
if (highlightTimer.current !== null) clearTimeout(highlightTimer.current);
|
|
1468
|
+
highlightTimer.current = setTimeout(() => {
|
|
1469
|
+
target.classList.remove(
|
|
1470
|
+
"ring-2",
|
|
1471
|
+
"ring-primary/60",
|
|
1472
|
+
"ring-offset-1",
|
|
1473
|
+
"ring-offset-background",
|
|
1474
|
+
"rounded-sm",
|
|
1475
|
+
"transition-shadow"
|
|
1476
|
+
);
|
|
1477
|
+
setExpandToPath(null);
|
|
1478
|
+
}, highlightMs ?? HIGHLIGHT_DURATION_MS);
|
|
1479
|
+
};
|
|
1480
|
+
const escape = (value) => {
|
|
1481
|
+
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
|
|
1482
|
+
return CSS.escape(value);
|
|
1483
|
+
}
|
|
1484
|
+
return value.replace(/(["'\\[\](){}])/g, "\\$1");
|
|
1485
|
+
};
|
|
1486
|
+
const selector = `[data-anatomy-path="${escape(segment2.path)}"]`;
|
|
1487
|
+
const tryFindTarget = (attemptsLeft) => {
|
|
1488
|
+
const container = containerRef.current;
|
|
1489
|
+
if (container !== null) {
|
|
1490
|
+
const target = container.querySelector(selector);
|
|
1491
|
+
if (target !== null) {
|
|
1492
|
+
applyHighlight(target);
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
if (attemptsLeft > 0) {
|
|
1497
|
+
window.requestAnimationFrame(() => tryFindTarget(attemptsLeft - 1));
|
|
1498
|
+
} else {
|
|
1499
|
+
setExpandToPath(null);
|
|
1500
|
+
}
|
|
1501
|
+
};
|
|
1502
|
+
window.requestAnimationFrame(() => tryFindTarget(MAX_HIGHLIGHT_ATTEMPTS));
|
|
1503
|
+
},
|
|
1504
|
+
[containerRef, ensureTabActive, highlightMs, setExpandToPath]
|
|
1505
|
+
);
|
|
1506
|
+
}
|
|
1789
1507
|
function normalizeHeaders(headers) {
|
|
1790
1508
|
if (headers === void 0) return [];
|
|
1791
1509
|
return Object.entries(headers).map((entry) => [entry[0].toLowerCase(), entry[1]]).sort(([a], [b]) => a.localeCompare(b));
|
|
@@ -2113,16 +1831,23 @@ function SplitRows({ lines }) {
|
|
|
2113
1831
|
)) });
|
|
2114
1832
|
}
|
|
2115
1833
|
const DiffView = reactExports.memo(DiffViewInner);
|
|
2116
|
-
function formatElapsed$1(ms) {
|
|
2117
|
-
if (ms < 1e3) return `${ms}ms`;
|
|
2118
|
-
return `${(ms / 1e3).toFixed(1)}s`;
|
|
2119
|
-
}
|
|
2120
1834
|
const STATUS_BADGE_CLASSES = {
|
|
2121
1835
|
success: "bg-emerald-500/15 text-emerald-400 border-emerald-500/25",
|
|
2122
1836
|
client_error: "bg-amber-500/15 text-amber-400 border-amber-500/25",
|
|
2123
|
-
server_error: "",
|
|
1837
|
+
server_error: "bg-rose-500/15 text-rose-400 border-rose-500/25",
|
|
2124
1838
|
pending: "bg-muted text-muted-foreground border-border"
|
|
2125
1839
|
};
|
|
1840
|
+
function formatElapsed$1(ms) {
|
|
1841
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
1842
|
+
return `${(ms / 1e3).toFixed(1)}s`;
|
|
1843
|
+
}
|
|
1844
|
+
function formatTimestamp(iso) {
|
|
1845
|
+
const d = new Date(iso);
|
|
1846
|
+
const hh = String(d.getHours()).padStart(2, "0");
|
|
1847
|
+
const mm = String(d.getMinutes()).padStart(2, "0");
|
|
1848
|
+
const ss = String(d.getSeconds()).padStart(2, "0");
|
|
1849
|
+
return `${hh}:${mm}:${ss}`;
|
|
1850
|
+
}
|
|
2126
1851
|
function CacheTrendIndicator({ trend }) {
|
|
2127
1852
|
if (trend === null) return null;
|
|
2128
1853
|
const isUp = trend.direction === "up";
|
|
@@ -2143,9 +1868,16 @@ const LogEntryHeader = reactExports.memo(function({
|
|
|
2143
1868
|
expanded,
|
|
2144
1869
|
onToggle,
|
|
2145
1870
|
responseToolNames = null,
|
|
2146
|
-
cacheTrend = null
|
|
1871
|
+
cacheTrend = null,
|
|
1872
|
+
onReplay,
|
|
1873
|
+
onCopyRequest,
|
|
1874
|
+
requestCopied = false,
|
|
1875
|
+
onToggleRequestExpansion,
|
|
1876
|
+
requestExpansionState = null,
|
|
1877
|
+
slowResponseThresholdSeconds = 0
|
|
2147
1878
|
}) {
|
|
2148
1879
|
const statusCategory = getStatusCategory(log.responseStatus);
|
|
1880
|
+
const isSlowResponse = log.elapsedMs !== null && slowResponseThresholdSeconds > 0 && log.elapsedMs > slowResponseThresholdSeconds * 1e3;
|
|
2149
1881
|
const hasTokens = log.inputTokens !== null || log.outputTokens !== null;
|
|
2150
1882
|
const toolNamesJoined = reactExports.useMemo(() => responseToolNames?.join(", ") ?? null, [responseToolNames]);
|
|
2151
1883
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
@@ -2153,10 +1885,13 @@ const LogEntryHeader = reactExports.memo(function({
|
|
|
2153
1885
|
{
|
|
2154
1886
|
role: "button",
|
|
2155
1887
|
tabIndex: 0,
|
|
1888
|
+
"data-nav-id": `log-${log.id}`,
|
|
1889
|
+
"data-nav-action": expanded ? "collapse" : "expand",
|
|
2156
1890
|
className: cn(
|
|
2157
1891
|
"flex items-center gap-2 px-3 py-1 cursor-pointer transition-colors",
|
|
2158
1892
|
"hover:bg-muted/50",
|
|
2159
|
-
"select-none"
|
|
1893
|
+
"select-none",
|
|
1894
|
+
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none"
|
|
2160
1895
|
),
|
|
2161
1896
|
onClick: onToggle,
|
|
2162
1897
|
onKeyDown: (e) => {
|
|
@@ -2170,50 +1905,58 @@ const LogEntryHeader = reactExports.memo(function({
|
|
|
2170
1905
|
"#",
|
|
2171
1906
|
log.id
|
|
2172
1907
|
] }),
|
|
1908
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
1909
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
|
|
1910
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
|
|
1911
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatTimestamp(log.timestamp) })
|
|
1912
|
+
] }) }),
|
|
1913
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: log.timestamp })
|
|
1914
|
+
] }),
|
|
2173
1915
|
log.model !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
2174
1916
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderLogo, { provider: detectProvider(log.model), className: "size-4" }) }) }),
|
|
2175
1917
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: log.model })
|
|
2176
1918
|
] }),
|
|
2177
|
-
statusCategory !== "success" && /* @__PURE__ */ jsxRuntimeExports.
|
|
2178
|
-
Badge,
|
|
2179
|
-
{
|
|
2180
|
-
variant: "destructive",
|
|
2181
|
-
className: "text-[10px] px-1.5 py-0 h-4 font-mono tabular-nums",
|
|
2182
|
-
children: log.responseStatus
|
|
2183
|
-
}
|
|
2184
|
-
) : statusCategory === "pending" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2185
|
-
Badge,
|
|
2186
|
-
{
|
|
2187
|
-
variant: "outline",
|
|
2188
|
-
className: cn(
|
|
2189
|
-
"text-[10px] px-1.5 py-0 h-4 font-mono tabular-nums",
|
|
2190
|
-
STATUS_BADGE_CLASSES[statusCategory]
|
|
2191
|
-
),
|
|
2192
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin" })
|
|
2193
|
-
}
|
|
2194
|
-
) : /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1919
|
+
statusCategory !== "success" && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2195
1920
|
Badge,
|
|
2196
1921
|
{
|
|
2197
1922
|
variant: "outline",
|
|
2198
1923
|
className: cn(
|
|
2199
|
-
"text-[10px] px-1.5 py-0 h-
|
|
1924
|
+
"text-[10px] px-1.5 py-0 h-5 font-mono tabular-nums gap-1",
|
|
2200
1925
|
STATUS_BADGE_CLASSES[statusCategory]
|
|
2201
1926
|
),
|
|
2202
|
-
children:
|
|
1927
|
+
children: [
|
|
1928
|
+
statusCategory === "server_error" ? /* @__PURE__ */ jsxRuntimeExports.jsx(OctagonAlert, { className: "size-3", "aria-label": "Server error" }) : statusCategory === "client_error" ? /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3", "aria-label": "Client error" }) : statusCategory === "pending" ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin", "aria-label": "Pending" }) : null,
|
|
1929
|
+
log.responseStatus
|
|
1930
|
+
]
|
|
2203
1931
|
}
|
|
2204
|
-
)
|
|
2205
|
-
log.elapsedMs !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2206
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
1932
|
+
),
|
|
1933
|
+
log.elapsedMs !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
1934
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1935
|
+
"span",
|
|
1936
|
+
{
|
|
1937
|
+
className: cn(
|
|
1938
|
+
"flex items-center gap-1 text-xs shrink-0",
|
|
1939
|
+
isSlowResponse ? "text-amber-400" : "text-muted-foreground"
|
|
1940
|
+
),
|
|
1941
|
+
children: [
|
|
1942
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
|
|
1943
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed$1(log.elapsedMs) }),
|
|
1944
|
+
isSlowResponse && /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3", "aria-label": "Slow response" })
|
|
1945
|
+
]
|
|
1946
|
+
}
|
|
1947
|
+
) }),
|
|
1948
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: isSlowResponse ? `Slow response: ${formatElapsed$1(log.elapsedMs)} exceeds ${formatElapsed$1(
|
|
1949
|
+
slowResponseThresholdSeconds * 1e3
|
|
1950
|
+
)}` : "Elapsed response time" })
|
|
1951
|
+
] }),
|
|
1952
|
+
hasTokens && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs shrink-0", children: [
|
|
1953
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3 text-muted-foreground" }),
|
|
1954
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
|
|
1955
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1956
|
+
"span",
|
|
1957
|
+
{
|
|
1958
|
+
className: log.inputTokens !== null ? "text-blue-400" : "text-muted-foreground",
|
|
1959
|
+
children: [
|
|
2217
1960
|
"IN ",
|
|
2218
1961
|
log.inputTokens !== null ? formatTokens(log.inputTokens) : "—"
|
|
2219
1962
|
]
|
|
@@ -2290,337 +2033,91 @@ const LogEntryHeader = reactExports.memo(function({
|
|
|
2290
2033
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Request used SSE streaming" })
|
|
2291
2034
|
] }),
|
|
2292
2035
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 min-w-0" }),
|
|
2293
|
-
expanded
|
|
2294
|
-
|
|
2295
|
-
}
|
|
2296
|
-
) });
|
|
2297
|
-
});
|
|
2298
|
-
function Separator({
|
|
2299
|
-
className,
|
|
2300
|
-
orientation = "horizontal",
|
|
2301
|
-
decorative = true,
|
|
2302
|
-
...props
|
|
2303
|
-
}) {
|
|
2304
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2305
|
-
Root$1,
|
|
2306
|
-
{
|
|
2307
|
-
"data-slot": "separator",
|
|
2308
|
-
decorative,
|
|
2309
|
-
orientation,
|
|
2310
|
-
className: cn(
|
|
2311
|
-
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
|
2312
|
-
className
|
|
2313
|
-
),
|
|
2314
|
-
...props
|
|
2315
|
-
}
|
|
2316
|
-
);
|
|
2317
|
-
}
|
|
2318
|
-
function Collapsible({
|
|
2319
|
-
...props
|
|
2320
|
-
}) {
|
|
2321
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Root$2, { "data-slot": "collapsible", ...props });
|
|
2322
|
-
}
|
|
2323
|
-
function CollapsibleTrigger({
|
|
2324
|
-
...props
|
|
2325
|
-
}) {
|
|
2326
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleTrigger$1, { "data-slot": "collapsible-trigger", ...props });
|
|
2327
|
-
}
|
|
2328
|
-
function CollapsibleContent({
|
|
2329
|
-
...props
|
|
2330
|
-
}) {
|
|
2331
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent$1, { "data-slot": "collapsible-content", ...props });
|
|
2332
|
-
}
|
|
2333
|
-
function ScrollArea({
|
|
2334
|
-
className,
|
|
2335
|
-
children,
|
|
2336
|
-
...props
|
|
2337
|
-
}) {
|
|
2338
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2339
|
-
Root$3,
|
|
2340
|
-
{
|
|
2341
|
-
"data-slot": "scroll-area",
|
|
2342
|
-
className: cn("relative", className),
|
|
2343
|
-
...props,
|
|
2344
|
-
children: [
|
|
2345
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2346
|
-
Viewport$1,
|
|
2036
|
+
expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2037
|
+
"span",
|
|
2347
2038
|
{
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2039
|
+
className: "flex items-center gap-1.5 shrink-0",
|
|
2040
|
+
onClick: (e) => e.stopPropagation(),
|
|
2041
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
2042
|
+
children: [
|
|
2043
|
+
requestExpansionState !== null && onToggleRequestExpansion !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
2044
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2045
|
+
Button,
|
|
2046
|
+
{
|
|
2047
|
+
variant: "outline",
|
|
2048
|
+
size: "icon",
|
|
2049
|
+
className: "size-8",
|
|
2050
|
+
onClick: onToggleRequestExpansion,
|
|
2051
|
+
disabled: requestExpansionState.isPending,
|
|
2052
|
+
"aria-pressed": requestExpansionState.isExpanded,
|
|
2053
|
+
"aria-label": requestExpansionState.isExpanded ? "Collapse all JSON" : "Expand all JSON",
|
|
2054
|
+
children: requestExpansionState.isExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsUp, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsDown, { className: "size-3.5" })
|
|
2055
|
+
}
|
|
2056
|
+
) }),
|
|
2057
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: requestExpansionState.isExpanded ? "Collapse all JSON nodes" : "Expand all JSON nodes" })
|
|
2058
|
+
] }),
|
|
2059
|
+
onCopyRequest !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
2060
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2061
|
+
Button,
|
|
2062
|
+
{
|
|
2063
|
+
variant: "outline",
|
|
2064
|
+
size: "icon",
|
|
2065
|
+
className: "size-8",
|
|
2066
|
+
onClick: onCopyRequest,
|
|
2067
|
+
"aria-label": "Copy request body",
|
|
2068
|
+
children: requestCopied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 text-emerald-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
|
|
2069
|
+
}
|
|
2070
|
+
) }),
|
|
2071
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: requestCopied ? "Copied to clipboard" : "Copy request body" })
|
|
2072
|
+
] }),
|
|
2073
|
+
onReplay !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
2074
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2075
|
+
Button,
|
|
2076
|
+
{
|
|
2077
|
+
variant: "outline",
|
|
2078
|
+
size: "icon",
|
|
2079
|
+
className: "size-8",
|
|
2080
|
+
onClick: onReplay,
|
|
2081
|
+
"aria-label": "Replay request",
|
|
2082
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { className: "size-3.5" })
|
|
2083
|
+
}
|
|
2084
|
+
) }),
|
|
2085
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Re-send this request to the provider" })
|
|
2086
|
+
] })
|
|
2087
|
+
]
|
|
2351
2088
|
}
|
|
2352
2089
|
),
|
|
2353
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2354
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Corner, {})
|
|
2090
|
+
expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4 text-muted-foreground shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-4 text-muted-foreground shrink-0" })
|
|
2355
2091
|
]
|
|
2356
2092
|
}
|
|
2357
|
-
);
|
|
2358
|
-
}
|
|
2359
|
-
function ScrollBar({
|
|
2360
|
-
className,
|
|
2361
|
-
orientation = "vertical",
|
|
2362
|
-
...props
|
|
2363
|
-
}) {
|
|
2364
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2365
|
-
ScrollAreaScrollbar,
|
|
2366
|
-
{
|
|
2367
|
-
"data-slot": "scroll-area-scrollbar",
|
|
2368
|
-
orientation,
|
|
2369
|
-
className: cn(
|
|
2370
|
-
"flex touch-none p-px transition-colors select-none",
|
|
2371
|
-
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent",
|
|
2372
|
-
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent",
|
|
2373
|
-
className
|
|
2374
|
-
),
|
|
2375
|
-
...props,
|
|
2376
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2377
|
-
ScrollAreaThumb,
|
|
2378
|
-
{
|
|
2379
|
-
"data-slot": "scroll-area-thumb",
|
|
2380
|
-
className: "bg-border relative flex-1 rounded-full"
|
|
2381
|
-
}
|
|
2382
|
-
)
|
|
2383
|
-
}
|
|
2384
|
-
);
|
|
2385
|
-
}
|
|
2386
|
-
function assertNever(_value) {
|
|
2387
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, {});
|
|
2388
|
-
}
|
|
2389
|
-
function SystemReminderBlock({ text }) {
|
|
2390
|
-
const [open, setOpen] = reactExports.useState(false);
|
|
2391
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Collapsible, { open, onOpenChange: setOpen, children: [
|
|
2392
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(CollapsibleTrigger, { className: "flex items-center gap-1.5 py-0.5 cursor-pointer hover:opacity-80 transition-opacity group", children: [
|
|
2393
|
-
open ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 text-muted-foreground" }),
|
|
2394
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground text-xs italic select-none opacity-60", children: "[system-reminder]" })
|
|
2395
|
-
] }),
|
|
2396
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pl-4 pt-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "prose prose-sm dark:prose-invert max-w-none [&_pre]:bg-muted [&_pre]:text-foreground [&_code]:text-[0.8em] [&_p]:my-1 [&_ul]:my-1 [&_ol]:my-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: text }) }) }) })
|
|
2397
|
-
] });
|
|
2398
|
-
}
|
|
2399
|
-
const THINKING_TAG_REGEX = /<think>([\s\S]*?)<\/think>/gi;
|
|
2400
|
-
const THINKING_TAG_REGEX_SINGLE = /<think>([\s\S]*?)<\/think>/i;
|
|
2401
|
-
function extractThinkingFromContent(text) {
|
|
2402
|
-
const match = THINKING_TAG_REGEX_SINGLE.exec(text);
|
|
2403
|
-
if (!match || match[1] === void 0) {
|
|
2404
|
-
return { thinking: null, remainingText: text };
|
|
2405
|
-
}
|
|
2406
|
-
const thinking = match[1].trim();
|
|
2407
|
-
const remainingText = text.replace(THINKING_TAG_REGEX, "").trim();
|
|
2408
|
-
return { thinking, remainingText };
|
|
2409
|
-
}
|
|
2410
|
-
const TextBlock = reactExports.memo(function TextBlock2({ text }) {
|
|
2411
|
-
if (text.includes("<system-reminder>")) {
|
|
2412
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(SystemReminderBlock, { text });
|
|
2413
|
-
}
|
|
2414
|
-
const { thinking, remainingText } = extractThinkingFromContent(text);
|
|
2415
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
2416
|
-
thinking !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(ThinkingBlock, { thinking }),
|
|
2417
|
-
remainingText.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "prose prose-sm dark:prose-invert max-w-none [&_pre]:bg-muted [&_pre]:text-foreground [&_code]:text-[0.8em] [&_p]:my-1 [&_ul]:my-1 [&_ol]:my-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: remainingText }) }),
|
|
2418
|
-
thinking === null && remainingText.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "Empty text block" })
|
|
2419
|
-
] });
|
|
2420
|
-
});
|
|
2421
|
-
const ThinkingBlock = reactExports.memo(function ThinkingBlock2({
|
|
2422
|
-
thinking
|
|
2423
|
-
}) {
|
|
2424
|
-
const [open, setOpen] = reactExports.useState(false);
|
|
2425
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Collapsible, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-l-2 border-purple-500/40 my-1", children: [
|
|
2426
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(CollapsibleTrigger, { className: "flex items-center gap-1.5 px-3 py-1 w-full text-left cursor-pointer hover:bg-purple-500/5 transition-colors rounded-r-sm group", children: [
|
|
2427
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Brain, { className: "size-3.5 text-purple-400 shrink-0" }),
|
|
2428
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-medium text-purple-400", children: "Thinking" }),
|
|
2429
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2430
|
-
Badge,
|
|
2431
|
-
{
|
|
2432
|
-
variant: "ghost",
|
|
2433
|
-
className: "text-[10px] text-muted-foreground px-1.5 py-0 h-4 font-mono",
|
|
2434
|
-
children: [
|
|
2435
|
-
thinking.length.toLocaleString(),
|
|
2436
|
-
" chars"
|
|
2437
|
-
]
|
|
2438
|
-
}
|
|
2439
|
-
),
|
|
2440
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
|
|
2441
|
-
open ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 text-muted-foreground" })
|
|
2442
|
-
] }),
|
|
2443
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "max-h-[60vh]", children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono leading-relaxed", children: thinking }) }) }) })
|
|
2444
|
-
] }) });
|
|
2445
|
-
});
|
|
2446
|
-
const ToolUseBlock = reactExports.memo(function ToolUseBlock2({
|
|
2447
|
-
name,
|
|
2448
|
-
input
|
|
2449
|
-
}) {
|
|
2450
|
-
const [open, setOpen] = reactExports.useState(false);
|
|
2451
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Collapsible, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-l-2 border-blue-500/40 my-1", children: [
|
|
2452
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(CollapsibleTrigger, { className: "flex items-center gap-1.5 px-3 py-1 w-full text-left cursor-pointer hover:bg-blue-500/5 transition-colors rounded-r-sm group", children: [
|
|
2453
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { className: "size-3.5 text-blue-400 shrink-0" }),
|
|
2454
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "text-[10px] font-mono px-1.5 py-0 h-4", children: name }),
|
|
2455
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
|
|
2456
|
-
open ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 text-muted-foreground" })
|
|
2457
|
-
] }),
|
|
2458
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "max-h-[60vh]", children: /* @__PURE__ */ jsxRuntimeExports.jsx(JsonViewer, { data: safeJsonValue(input), defaultExpandDepth: 0 }) }) }) })
|
|
2459
|
-
] }) });
|
|
2460
|
-
});
|
|
2461
|
-
const ResponseContentBlockRenderer = reactExports.memo(function ResponseContentBlockRenderer2({
|
|
2462
|
-
block
|
|
2463
|
-
}) {
|
|
2464
|
-
switch (block.type) {
|
|
2465
|
-
case "text":
|
|
2466
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(TextBlock, { text: block.text });
|
|
2467
|
-
case "thinking":
|
|
2468
|
-
case "think":
|
|
2469
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(ThinkingBlock, { thinking: block.thinking });
|
|
2470
|
-
case "tool_use":
|
|
2471
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(ToolUseBlock, { name: block.name, input: block.input });
|
|
2472
|
-
default:
|
|
2473
|
-
return assertNever();
|
|
2474
|
-
}
|
|
2475
|
-
});
|
|
2476
|
-
const StructuredResponseViewAnthropic = reactExports.memo(function StructuredResponseViewAnthropic2({
|
|
2477
|
-
response
|
|
2478
|
-
}) {
|
|
2479
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
|
|
2480
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
|
|
2481
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "secondary", className: "text-[10px] px-1.5 py-0 h-5 font-mono", children: response.model }),
|
|
2482
|
-
response.stop_reason !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2483
|
-
Badge,
|
|
2484
|
-
{
|
|
2485
|
-
variant: "outline",
|
|
2486
|
-
className: "text-[10px] px-1.5 py-0 h-5 font-mono flex items-center gap-1",
|
|
2487
|
-
children: [
|
|
2488
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(CircleStop, { className: "size-2.5" }),
|
|
2489
|
-
response.stop_reason
|
|
2490
|
-
]
|
|
2491
|
-
}
|
|
2492
|
-
),
|
|
2493
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs", children: [
|
|
2494
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3" }),
|
|
2495
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
|
|
2496
|
-
formatTokens(response.usage.input_tokens ?? 0),
|
|
2497
|
-
" in /",
|
|
2498
|
-
" ",
|
|
2499
|
-
formatTokens(response.usage.output_tokens ?? 0),
|
|
2500
|
-
" out"
|
|
2501
|
-
] }),
|
|
2502
|
-
response.usage.cache_creation_input_tokens !== void 0 && response.usage.cache_creation_input_tokens !== null && response.usage.cache_creation_input_tokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-emerald-400", children: [
|
|
2503
|
-
"Cache +",
|
|
2504
|
-
formatTokens(response.usage.cache_creation_input_tokens)
|
|
2505
|
-
] }),
|
|
2506
|
-
response.usage.cache_read_input_tokens !== void 0 && response.usage.cache_read_input_tokens !== null && response.usage.cache_read_input_tokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-purple-400", children: [
|
|
2507
|
-
"Cache ~",
|
|
2508
|
-
formatTokens(response.usage.cache_read_input_tokens)
|
|
2509
|
-
] })
|
|
2510
|
-
] })
|
|
2511
|
-
] }),
|
|
2512
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Separator, { className: "opacity-50" }),
|
|
2513
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
2514
|
-
response.content.map((block, i) => /* @__PURE__ */ jsxRuntimeExports.jsx(ResponseContentBlockRenderer, { block }, i)),
|
|
2515
|
-
response.content.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "Empty response content" })
|
|
2516
|
-
] })
|
|
2517
|
-
] });
|
|
2093
|
+
) });
|
|
2518
2094
|
});
|
|
2519
|
-
function
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
return JSON.parse(raw);
|
|
2523
|
-
} catch {
|
|
2524
|
-
return null;
|
|
2095
|
+
function forEachCodePoint(text, visit) {
|
|
2096
|
+
for (const ch of Array.from(text)) {
|
|
2097
|
+
visit(ch);
|
|
2525
2098
|
}
|
|
2526
2099
|
}
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
// JSON.parse failed — show the raw string so the user can
|
|
2541
|
-
// still see what the model tried to call.
|
|
2542
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap break-words text-rose-300/90", children: call.function.arguments })
|
|
2543
|
-
) : /* @__PURE__ */ jsxRuntimeExports.jsx(JsonViewer, { data: safeJsonValue(parsed), defaultExpandDepth: 0 }) }) }) })
|
|
2544
|
-
] }) });
|
|
2545
|
-
}
|
|
2546
|
-
const OpenAIResponseView = reactExports.memo(function OpenAIResponseView2({
|
|
2547
|
-
response
|
|
2548
|
-
}) {
|
|
2549
|
-
const choice = response.choices[0];
|
|
2550
|
-
const message = choice?.message;
|
|
2551
|
-
const toolCalls = message?.tool_calls ?? [];
|
|
2552
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
|
|
2553
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
|
|
2554
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "secondary", className: "text-[10px] px-1.5 py-0 h-5 font-mono", children: response.model }),
|
|
2555
|
-
choice?.finish_reason !== null && choice?.finish_reason !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2556
|
-
Badge,
|
|
2557
|
-
{
|
|
2558
|
-
variant: "outline",
|
|
2559
|
-
className: "text-[10px] px-1.5 py-0 h-5 font-mono flex items-center gap-1",
|
|
2560
|
-
children: [
|
|
2561
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(CircleStop, { className: "size-2.5" }),
|
|
2562
|
-
choice.finish_reason
|
|
2563
|
-
]
|
|
2564
|
-
}
|
|
2565
|
-
),
|
|
2566
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs", children: [
|
|
2567
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3" }),
|
|
2568
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
|
|
2569
|
-
formatTokens(response.usage.prompt_tokens ?? 0),
|
|
2570
|
-
" in /",
|
|
2571
|
-
" ",
|
|
2572
|
-
formatTokens(response.usage.completion_tokens ?? 0),
|
|
2573
|
-
" out"
|
|
2574
|
-
] })
|
|
2575
|
-
] })
|
|
2576
|
-
] }),
|
|
2577
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Separator, { className: "opacity-50" }),
|
|
2578
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
2579
|
-
message?.reasoning_content !== null && message?.reasoning_content !== void 0 && message.reasoning_content.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(ThinkingBlock, { thinking: message.reasoning_content }),
|
|
2580
|
-
message?.content !== null && message?.content !== void 0 && message.content.length > 0 && (() => {
|
|
2581
|
-
const hasReasoningField = message.reasoning_content !== null && message.reasoning_content !== void 0 && message.reasoning_content.length > 0;
|
|
2582
|
-
const { thinking, remainingText } = extractThinkingFromContent(message.content);
|
|
2583
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
2584
|
-
thinking !== null && !hasReasoningField && /* @__PURE__ */ jsxRuntimeExports.jsx(ThinkingBlock, { thinking }),
|
|
2585
|
-
remainingText.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "prose prose-sm dark:prose-invert max-w-none [&_pre]:bg-muted [&_pre]:text-foreground [&_code]:text-[0.8em] [&_p]:my-1 [&_ul]:my-1 [&_ol]:my-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: remainingText }) })
|
|
2586
|
-
] });
|
|
2587
|
-
})(),
|
|
2588
|
-
toolCalls.map((call, i) => (
|
|
2589
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: tool_calls is the positionally stable list from the response
|
|
2590
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(OpenAIToolCallBlock, { call }, call.id ?? `tc-${i}`)
|
|
2591
|
-
)),
|
|
2592
|
-
message?.function_call !== null && message?.function_call !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-blue-500/30 rounded-md p-3 bg-blue-500/5", children: [
|
|
2593
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-blue-400 font-mono mb-1", children: "function_call" }),
|
|
2594
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-mono text-xs", children: [
|
|
2595
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-blue-300", children: message.function_call.name }),
|
|
2596
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground", children: [
|
|
2597
|
-
"(",
|
|
2598
|
-
message.function_call.arguments,
|
|
2599
|
-
")"
|
|
2600
|
-
] })
|
|
2601
|
-
] })
|
|
2602
|
-
] }),
|
|
2603
|
-
(message?.content === null || message?.content === void 0 || message.content.length === 0) && (message?.reasoning_content === null || message?.reasoning_content === void 0 || message.reasoning_content.length === 0) && (message?.function_call === null || message?.function_call === void 0) && toolCalls.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "Empty response content" })
|
|
2604
|
-
] })
|
|
2605
|
-
] });
|
|
2606
|
-
});
|
|
2607
|
-
function isRecord(value) {
|
|
2608
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2609
|
-
}
|
|
2610
|
-
function isOpenAIResponse(response) {
|
|
2611
|
-
return isRecord(response) && response.object === "chat.completion";
|
|
2612
|
-
}
|
|
2613
|
-
function isAnthropicResponse(response) {
|
|
2614
|
-
return isRecord(response) && response.type === "message" && Array.isArray(response.content);
|
|
2100
|
+
const CJK_REGEX = /[\u3000-\u9FFF\uAC00-\uD7FF\uF900-\uFAFF\uFF00-\uFFEF\u{1F300}-\u{1FAFF}]/u;
|
|
2101
|
+
function estimateTokens(text) {
|
|
2102
|
+
if (text.length === 0) return 0;
|
|
2103
|
+
let cjk = 0;
|
|
2104
|
+
let other = 0;
|
|
2105
|
+
forEachCodePoint(text, (ch) => {
|
|
2106
|
+
if (CJK_REGEX.test(ch)) {
|
|
2107
|
+
cjk += 1;
|
|
2108
|
+
} else {
|
|
2109
|
+
other += 1;
|
|
2110
|
+
}
|
|
2111
|
+
});
|
|
2112
|
+
return Math.ceil(cjk * 1) + Math.ceil(other / 4);
|
|
2615
2113
|
}
|
|
2616
|
-
function
|
|
2617
|
-
if (
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
return null;
|
|
2114
|
+
function countCharacters(text) {
|
|
2115
|
+
if (text.length === 0) return 0;
|
|
2116
|
+
let n = 0;
|
|
2117
|
+
forEachCodePoint(text, () => {
|
|
2118
|
+
n += 1;
|
|
2119
|
+
});
|
|
2120
|
+
return n;
|
|
2624
2121
|
}
|
|
2625
2122
|
function emptyRequestAnalysis(rawBody) {
|
|
2626
2123
|
return {
|
|
@@ -2634,6 +2131,84 @@ const EMPTY_RESPONSE_ANALYSIS = {
|
|
|
2634
2131
|
parsed: null,
|
|
2635
2132
|
toolNames: null
|
|
2636
2133
|
};
|
|
2134
|
+
function contentToText$1(content) {
|
|
2135
|
+
if (typeof content === "string") return content;
|
|
2136
|
+
if (!Array.isArray(content)) return "";
|
|
2137
|
+
const parts = [];
|
|
2138
|
+
for (const block of content) {
|
|
2139
|
+
if (block === null || typeof block !== "object") continue;
|
|
2140
|
+
const b = block;
|
|
2141
|
+
const type = b.type;
|
|
2142
|
+
switch (type) {
|
|
2143
|
+
case "text":
|
|
2144
|
+
if (typeof b.text === "string") parts.push(b.text);
|
|
2145
|
+
break;
|
|
2146
|
+
case "thinking":
|
|
2147
|
+
case "think":
|
|
2148
|
+
if (typeof b.thinking === "string") parts.push(b.thinking);
|
|
2149
|
+
break;
|
|
2150
|
+
case "tool_use": {
|
|
2151
|
+
const name = typeof b.name === "string" ? b.name : "";
|
|
2152
|
+
const input = b.input !== void 0 ? JSON.stringify(b.input) : "";
|
|
2153
|
+
parts.push(`${name} ${input}`.trim());
|
|
2154
|
+
break;
|
|
2155
|
+
}
|
|
2156
|
+
case "tool_result": {
|
|
2157
|
+
const inner = b.content;
|
|
2158
|
+
if (typeof inner === "string") {
|
|
2159
|
+
parts.push(inner);
|
|
2160
|
+
} else if (Array.isArray(inner)) {
|
|
2161
|
+
for (const item of inner) {
|
|
2162
|
+
if (item !== null && typeof item === "object") {
|
|
2163
|
+
const t = item.text;
|
|
2164
|
+
if (typeof t === "string") parts.push(t);
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
break;
|
|
2169
|
+
}
|
|
2170
|
+
case "image":
|
|
2171
|
+
parts.push("[image]");
|
|
2172
|
+
break;
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
return parts.join("\n");
|
|
2176
|
+
}
|
|
2177
|
+
function systemToText(system) {
|
|
2178
|
+
if (typeof system === "string") return system;
|
|
2179
|
+
if (!Array.isArray(system)) return "";
|
|
2180
|
+
const parts = [];
|
|
2181
|
+
for (const block of system) {
|
|
2182
|
+
if (block !== null && typeof block === "object") {
|
|
2183
|
+
const t = block.text;
|
|
2184
|
+
const type = block.type;
|
|
2185
|
+
if (type === "text" && typeof t === "string") parts.push(t);
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
return parts.join("\n");
|
|
2189
|
+
}
|
|
2190
|
+
function toolsToText$1(tools) {
|
|
2191
|
+
if (!Array.isArray(tools)) return "";
|
|
2192
|
+
const parts = [];
|
|
2193
|
+
for (const tool of tools) {
|
|
2194
|
+
if (tool === null || typeof tool !== "object") continue;
|
|
2195
|
+
const t = tool;
|
|
2196
|
+
if (typeof t.name === "string") parts.push(t.name);
|
|
2197
|
+
if (typeof t.description === "string") parts.push(t.description);
|
|
2198
|
+
if (t.input_schema !== void 0) parts.push(JSON.stringify(t.input_schema));
|
|
2199
|
+
}
|
|
2200
|
+
return parts.join("\n");
|
|
2201
|
+
}
|
|
2202
|
+
function segment$1(role, label, text, path) {
|
|
2203
|
+
return {
|
|
2204
|
+
role,
|
|
2205
|
+
label,
|
|
2206
|
+
text,
|
|
2207
|
+
size: estimateTokens(text),
|
|
2208
|
+
characters: countCharacters(text),
|
|
2209
|
+
path
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2637
2212
|
const anthropicLogFormatAdapter = {
|
|
2638
2213
|
format: "anthropic",
|
|
2639
2214
|
analyzeRequest(rawBody) {
|
|
@@ -2666,8 +2241,92 @@ const anthropicLogFormatAdapter = {
|
|
|
2666
2241
|
} catch {
|
|
2667
2242
|
return EMPTY_RESPONSE_ANALYSIS;
|
|
2668
2243
|
}
|
|
2244
|
+
},
|
|
2245
|
+
anatomySegments(parsed) {
|
|
2246
|
+
if (parsed === null || typeof parsed !== "object") return null;
|
|
2247
|
+
const body = parsed;
|
|
2248
|
+
const segments = [];
|
|
2249
|
+
if (body.system !== void 0) {
|
|
2250
|
+
const text = systemToText(body.system);
|
|
2251
|
+
if (text.length > 0) {
|
|
2252
|
+
segments.push(segment$1("system", "system", text, "/system"));
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
if (Array.isArray(body.messages)) {
|
|
2256
|
+
body.messages.forEach((message, index2) => {
|
|
2257
|
+
if (message === null || typeof message !== "object") return;
|
|
2258
|
+
const m = message;
|
|
2259
|
+
const role = m.role === "user" || m.role === "assistant" ? m.role : "user";
|
|
2260
|
+
const text = contentToText$1(m.content);
|
|
2261
|
+
segments.push(segment$1(role, `[${index2}] ${role}`, text, `/messages/${index2}`));
|
|
2262
|
+
});
|
|
2263
|
+
}
|
|
2264
|
+
if (Array.isArray(body.tools) && body.tools.length > 0) {
|
|
2265
|
+
const text = toolsToText$1(body.tools);
|
|
2266
|
+
segments.push(segment$1("tools", "tools", text, "/tools"));
|
|
2267
|
+
}
|
|
2268
|
+
return segments.length > 0 ? segments : null;
|
|
2669
2269
|
}
|
|
2670
2270
|
};
|
|
2271
|
+
function contentToText(content) {
|
|
2272
|
+
if (typeof content === "string") return content;
|
|
2273
|
+
if (!Array.isArray(content)) return "";
|
|
2274
|
+
const parts = [];
|
|
2275
|
+
for (const block of content) {
|
|
2276
|
+
if (block === null || typeof block !== "object") continue;
|
|
2277
|
+
const b = block;
|
|
2278
|
+
const type = b.type;
|
|
2279
|
+
if (type === "text" && typeof b.text === "string") {
|
|
2280
|
+
parts.push(b.text);
|
|
2281
|
+
} else if (type === "image_url") {
|
|
2282
|
+
parts.push("[image]");
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
return parts.join("\n");
|
|
2286
|
+
}
|
|
2287
|
+
function messageToText(message) {
|
|
2288
|
+
const parts = [];
|
|
2289
|
+
const text = contentToText(message.content);
|
|
2290
|
+
if (text.length > 0) parts.push(text);
|
|
2291
|
+
const reasoning = typeof message.reasoning_content === "string" ? message.reasoning_content : typeof message.thinking === "string" ? message.thinking : typeof message.think === "string" ? message.think : "";
|
|
2292
|
+
if (reasoning.length > 0) parts.push(reasoning);
|
|
2293
|
+
const toolCalls = message.tool_calls;
|
|
2294
|
+
if (Array.isArray(toolCalls)) {
|
|
2295
|
+
for (const call of toolCalls) {
|
|
2296
|
+
if (call === null || typeof call !== "object") continue;
|
|
2297
|
+
const fn = call.function;
|
|
2298
|
+
if (fn === void 0) continue;
|
|
2299
|
+
const name = typeof fn.name === "string" ? fn.name : "";
|
|
2300
|
+
const args = typeof fn.arguments === "string" ? fn.arguments : "";
|
|
2301
|
+
parts.push(`${name} ${args}`.trim());
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
return parts.join("\n");
|
|
2305
|
+
}
|
|
2306
|
+
function toolsToText(tools) {
|
|
2307
|
+
if (!Array.isArray(tools)) return "";
|
|
2308
|
+
const parts = [];
|
|
2309
|
+
for (const tool of tools) {
|
|
2310
|
+
if (tool === null || typeof tool !== "object") continue;
|
|
2311
|
+
const t = tool;
|
|
2312
|
+
const fn = t.function;
|
|
2313
|
+
if (fn === void 0) continue;
|
|
2314
|
+
if (typeof fn.name === "string") parts.push(fn.name);
|
|
2315
|
+
if (typeof fn.description === "string") parts.push(fn.description);
|
|
2316
|
+
if (fn.parameters !== void 0) parts.push(JSON.stringify(fn.parameters));
|
|
2317
|
+
}
|
|
2318
|
+
return parts.join("\n");
|
|
2319
|
+
}
|
|
2320
|
+
function segment(role, label, text, path) {
|
|
2321
|
+
return {
|
|
2322
|
+
role,
|
|
2323
|
+
label,
|
|
2324
|
+
text,
|
|
2325
|
+
size: estimateTokens(text),
|
|
2326
|
+
characters: countCharacters(text),
|
|
2327
|
+
path
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2671
2330
|
const openAILogFormatAdapter = {
|
|
2672
2331
|
format: "openai",
|
|
2673
2332
|
analyzeRequest(rawBody) {
|
|
@@ -2694,6 +2353,25 @@ const openAILogFormatAdapter = {
|
|
|
2694
2353
|
parsed,
|
|
2695
2354
|
toolNames: toolNames.length > 0 ? toolNames : null
|
|
2696
2355
|
};
|
|
2356
|
+
},
|
|
2357
|
+
anatomySegments(parsed) {
|
|
2358
|
+
if (parsed === null || typeof parsed !== "object") return null;
|
|
2359
|
+
const body = parsed;
|
|
2360
|
+
const segments = [];
|
|
2361
|
+
if (Array.isArray(body.messages)) {
|
|
2362
|
+
body.messages.forEach((message, index2) => {
|
|
2363
|
+
if (message === null || typeof message !== "object") return;
|
|
2364
|
+
const m = message;
|
|
2365
|
+
const role = m.role === "user" || m.role === "assistant" || m.role === "system" || m.role === "tool" ? m.role : "user";
|
|
2366
|
+
const text = messageToText(m);
|
|
2367
|
+
segments.push(segment(role, `[${index2}] ${role}`, text, `/messages/${index2}`));
|
|
2368
|
+
});
|
|
2369
|
+
}
|
|
2370
|
+
if (Array.isArray(body.tools) && body.tools.length > 0) {
|
|
2371
|
+
const text = toolsToText(body.tools);
|
|
2372
|
+
segments.push(segment("tools", "tools", text, "/tools"));
|
|
2373
|
+
}
|
|
2374
|
+
return segments.length > 0 ? segments : null;
|
|
2697
2375
|
}
|
|
2698
2376
|
};
|
|
2699
2377
|
const unknownLogFormatAdapter = {
|
|
@@ -2703,6 +2381,9 @@ const unknownLogFormatAdapter = {
|
|
|
2703
2381
|
},
|
|
2704
2382
|
analyzeResponse() {
|
|
2705
2383
|
return EMPTY_RESPONSE_ANALYSIS;
|
|
2384
|
+
},
|
|
2385
|
+
anatomySegments() {
|
|
2386
|
+
return null;
|
|
2706
2387
|
}
|
|
2707
2388
|
};
|
|
2708
2389
|
const ADAPTERS = {
|
|
@@ -2717,422 +2398,42 @@ function resolveLogFormat(log) {
|
|
|
2717
2398
|
const pathFormat = requestFormatForPath(log.path);
|
|
2718
2399
|
return pathFormat === "unknown" ? log.apiFormat : pathFormat;
|
|
2719
2400
|
}
|
|
2720
|
-
function
|
|
2721
|
-
|
|
2722
|
-
case "success":
|
|
2723
|
-
return "text-emerald-400";
|
|
2724
|
-
case "client_error":
|
|
2725
|
-
return "text-amber-400";
|
|
2726
|
-
case "server_error":
|
|
2727
|
-
return "text-red-400";
|
|
2728
|
-
case "pending":
|
|
2729
|
-
return "text-muted-foreground";
|
|
2730
|
-
}
|
|
2401
|
+
function shouldShowRawRequestTab(apiFormat, viewMode, strip) {
|
|
2402
|
+
return apiFormat === "anthropic" && viewMode === "full" && strip;
|
|
2731
2403
|
}
|
|
2732
|
-
function
|
|
2733
|
-
|
|
2734
|
-
const classes = getStatusClasses(category);
|
|
2735
|
-
if (status === null) {
|
|
2736
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground italic", children: "pending" });
|
|
2737
|
-
}
|
|
2738
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: cn("flex items-center gap-1 text-xs font-mono font-semibold", classes), children: [
|
|
2739
|
-
category === "server_error" && /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3" }),
|
|
2740
|
-
status
|
|
2741
|
-
] });
|
|
2404
|
+
function shouldShowHeadersDiffButton(viewMode, hasRawHeaders) {
|
|
2405
|
+
return viewMode === "full" && hasRawHeaders;
|
|
2742
2406
|
}
|
|
2743
|
-
function
|
|
2744
|
-
return
|
|
2407
|
+
function shouldShowRequestDiffButton(apiFormat, viewMode, strip, hasRawRequest) {
|
|
2408
|
+
return apiFormat === "anthropic" && viewMode === "full" && strip && hasRawRequest;
|
|
2745
2409
|
}
|
|
2746
|
-
function
|
|
2747
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "
|
|
2410
|
+
function TabFallback() {
|
|
2411
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-1", "aria-hidden": "true" });
|
|
2748
2412
|
}
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
outputTokens,
|
|
2755
|
-
cacheCreationInputTokens,
|
|
2756
|
-
cacheReadInputTokens,
|
|
2757
|
-
apiFormat,
|
|
2758
|
-
error
|
|
2413
|
+
function CopyButton({
|
|
2414
|
+
text,
|
|
2415
|
+
label,
|
|
2416
|
+
copied,
|
|
2417
|
+
onCopy
|
|
2759
2418
|
}) {
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
(
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-semibold text-destructive mb-1", children: "SSE Error" }),
|
|
2778
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground font-mono", children: error })
|
|
2779
|
-
] })
|
|
2780
|
-
] });
|
|
2781
|
-
}
|
|
2782
|
-
if (error !== void 0 && error !== null) {
|
|
2783
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
2784
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(StatusIndicator, { status: responseStatus }),
|
|
2785
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded border border-destructive/50 bg-destructive/10 p-3 text-xs", children: [
|
|
2786
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-semibold text-destructive mb-1", children: "SSE Error" }),
|
|
2787
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground font-mono", children: error })
|
|
2788
|
-
] }),
|
|
2789
|
-
responseText !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorResponseView, { text: responseText }) })
|
|
2790
|
-
] });
|
|
2791
|
-
}
|
|
2792
|
-
if (parsed !== null) {
|
|
2793
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
2794
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(StatusIndicator, { status: responseStatus }),
|
|
2795
|
-
formatViewFor(resolvedFormat, parsed)
|
|
2796
|
-
] });
|
|
2797
|
-
}
|
|
2798
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
2799
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2800
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(StatusIndicator, { status: responseStatus }),
|
|
2801
|
-
streaming && (inputTokens !== null || outputTokens !== null) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs", children: [
|
|
2802
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3" }),
|
|
2803
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
|
|
2804
|
-
inputTokens !== null ? formatTokens(inputTokens) : "—",
|
|
2805
|
-
" in /",
|
|
2806
|
-
" ",
|
|
2807
|
-
outputTokens !== null ? formatTokens(outputTokens) : "—",
|
|
2808
|
-
" out"
|
|
2809
|
-
] }),
|
|
2810
|
-
cacheCreationInputTokens !== null && cacheCreationInputTokens !== void 0 && cacheCreationInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-emerald-400", children: [
|
|
2811
|
-
"Cache +",
|
|
2812
|
-
formatTokens(cacheCreationInputTokens)
|
|
2813
|
-
] }),
|
|
2814
|
-
cacheReadInputTokens !== null && cacheReadInputTokens !== void 0 && cacheReadInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-purple-400", children: [
|
|
2815
|
-
"Cache ~",
|
|
2816
|
-
formatTokens(cacheReadInputTokens)
|
|
2817
|
-
] })
|
|
2818
|
-
] })
|
|
2819
|
-
] }),
|
|
2820
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(MarkdownFallbackView, { text: responseText ?? "" })
|
|
2419
|
+
if (text === null) return null;
|
|
2420
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
2421
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2422
|
+
Button,
|
|
2423
|
+
{
|
|
2424
|
+
variant: "outline",
|
|
2425
|
+
size: "sm",
|
|
2426
|
+
className: "h-8 text-xs",
|
|
2427
|
+
onClick: onCopy,
|
|
2428
|
+
"aria-label": label,
|
|
2429
|
+
children: [
|
|
2430
|
+
copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 mr-1 text-emerald-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5 mr-1" }),
|
|
2431
|
+
copied ? "Copied!" : label
|
|
2432
|
+
]
|
|
2433
|
+
}
|
|
2434
|
+
) }),
|
|
2435
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: copied ? "Copied to clipboard" : label })
|
|
2821
2436
|
] });
|
|
2822
|
-
});
|
|
2823
|
-
const ReplayResultSchema = object({
|
|
2824
|
-
success: boolean(),
|
|
2825
|
-
error: string().optional(),
|
|
2826
|
-
responseStatus: number().optional(),
|
|
2827
|
-
responseText: string().optional(),
|
|
2828
|
-
inputTokens: number().optional(),
|
|
2829
|
-
outputTokens: number().optional(),
|
|
2830
|
-
elapsedMs: number().optional(),
|
|
2831
|
-
streaming: boolean().optional()
|
|
2832
|
-
});
|
|
2833
|
-
function ReplayDialog({ log, open, onOpenChange }) {
|
|
2834
|
-
const [modifiedBody, setModifiedBody] = reactExports.useState(() => {
|
|
2835
|
-
return log.rawRequestBody ?? "{}";
|
|
2836
|
-
});
|
|
2837
|
-
const [replayResult, setReplayResult] = reactExports.useState(null);
|
|
2838
|
-
const [loading, setLoading] = reactExports.useState(false);
|
|
2839
|
-
const [error, setError] = reactExports.useState(null);
|
|
2840
|
-
async function handleReplay() {
|
|
2841
|
-
setLoading(true);
|
|
2842
|
-
setError(null);
|
|
2843
|
-
setReplayResult(null);
|
|
2844
|
-
try {
|
|
2845
|
-
const res = await fetch(`/api/logs/${log.id}/replay`, {
|
|
2846
|
-
method: "POST",
|
|
2847
|
-
headers: { "Content-Type": "application/json" },
|
|
2848
|
-
body: JSON.stringify({ modifiedBody })
|
|
2849
|
-
});
|
|
2850
|
-
const json = await res.json();
|
|
2851
|
-
const parsed = ReplayResultSchema.safeParse(json);
|
|
2852
|
-
if (!parsed.success) {
|
|
2853
|
-
setError("Invalid response from server");
|
|
2854
|
-
setLoading(false);
|
|
2855
|
-
return;
|
|
2856
|
-
}
|
|
2857
|
-
const data = parsed.data;
|
|
2858
|
-
setReplayResult(data);
|
|
2859
|
-
if (!data.success) {
|
|
2860
|
-
setError(data.error ?? "Replay failed");
|
|
2861
|
-
}
|
|
2862
|
-
} catch (err) {
|
|
2863
|
-
setError(err instanceof Error ? err.message : "Network error");
|
|
2864
|
-
} finally {
|
|
2865
|
-
setLoading(false);
|
|
2866
|
-
}
|
|
2867
|
-
}
|
|
2868
|
-
function handleClose() {
|
|
2869
|
-
setReplayResult(null);
|
|
2870
|
-
setError(null);
|
|
2871
|
-
onOpenChange(false);
|
|
2872
|
-
}
|
|
2873
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogContent, { className: "max-w-4xl max-h-[85vh] overflow-auto", children: [
|
|
2874
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(DialogHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
|
|
2875
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { className: "size-4" }),
|
|
2876
|
-
"Replay Request #",
|
|
2877
|
-
log.id
|
|
2878
|
-
] }) }),
|
|
2879
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { defaultValue: "modified", children: [
|
|
2880
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { children: [
|
|
2881
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "modified", children: "Modified Request" }),
|
|
2882
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "original", children: "Original Response" }),
|
|
2883
|
-
replayResult && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "replay", children: "Replay Response" })
|
|
2884
|
-
] }),
|
|
2885
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(TabsContent, { value: "modified", className: "space-y-4", children: [
|
|
2886
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
|
|
2887
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-sm font-medium mb-2 block", children: "Request Body (JSON)" }),
|
|
2888
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
2889
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2890
|
-
"textarea",
|
|
2891
|
-
{
|
|
2892
|
-
className: "w-full h-64 p-3 font-mono text-xs bg-muted rounded-md border border-input resize-none focus:outline-none focus:ring-2 focus:ring-ring",
|
|
2893
|
-
value: modifiedBody,
|
|
2894
|
-
onChange: (e) => setModifiedBody(e.target.value),
|
|
2895
|
-
spellCheck: false
|
|
2896
|
-
}
|
|
2897
|
-
) }),
|
|
2898
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Edit the request body before re-sending to the provider" })
|
|
2899
|
-
] }) })
|
|
2900
|
-
] }),
|
|
2901
|
-
error !== null && error !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm text-destructive bg-destructive/10 px-3 py-2 rounded-md", children: error }),
|
|
2902
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2903
|
-
Button,
|
|
2904
|
-
{
|
|
2905
|
-
onClick: () => {
|
|
2906
|
-
void handleReplay();
|
|
2907
|
-
},
|
|
2908
|
-
disabled: loading,
|
|
2909
|
-
children: loading ? "Replaying..." : "Replay"
|
|
2910
|
-
}
|
|
2911
|
-
) }),
|
|
2912
|
-
replayResult && replayResult.success && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { defaultValue: "parsed", children: [
|
|
2913
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { children: [
|
|
2914
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "parsed", children: "Response" }),
|
|
2915
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "raw", children: "Raw Response" })
|
|
2916
|
-
] }),
|
|
2917
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "parsed", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2918
|
-
ResponseView,
|
|
2919
|
-
{
|
|
2920
|
-
responseText: replayResult.responseText ?? null,
|
|
2921
|
-
responseStatus: replayResult.responseStatus ?? null,
|
|
2922
|
-
streaming: replayResult.streaming ?? false,
|
|
2923
|
-
inputTokens: replayResult.inputTokens ?? null,
|
|
2924
|
-
outputTokens: replayResult.outputTokens ?? null,
|
|
2925
|
-
apiFormat: log.apiFormat
|
|
2926
|
-
}
|
|
2927
|
-
) }),
|
|
2928
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw", children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap bg-muted p-3 rounded-md max-h-96 overflow-auto", children: replayResult.responseText ?? "No response" }) })
|
|
2929
|
-
] })
|
|
2930
|
-
] }),
|
|
2931
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "original", children: log.responseText !== null ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { defaultValue: "parsed", children: [
|
|
2932
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { children: [
|
|
2933
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "parsed", children: "Response" }),
|
|
2934
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "raw", children: "Raw Response" })
|
|
2935
|
-
] }),
|
|
2936
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "parsed", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2937
|
-
ResponseView,
|
|
2938
|
-
{
|
|
2939
|
-
responseText: log.responseText,
|
|
2940
|
-
responseStatus: log.responseStatus,
|
|
2941
|
-
streaming: log.streaming,
|
|
2942
|
-
inputTokens: log.inputTokens,
|
|
2943
|
-
outputTokens: log.outputTokens,
|
|
2944
|
-
cacheCreationInputTokens: log.cacheCreationInputTokens,
|
|
2945
|
-
cacheReadInputTokens: log.cacheReadInputTokens,
|
|
2946
|
-
apiFormat: log.apiFormat
|
|
2947
|
-
}
|
|
2948
|
-
) }),
|
|
2949
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw", children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap bg-muted p-3 rounded-md max-h-96 overflow-auto", children: log.responseText }) })
|
|
2950
|
-
] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground italic", children: "No original response" }) }),
|
|
2951
|
-
replayResult && replayResult.success && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "replay", children: replayResult.responseText !== null ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { defaultValue: "parsed", children: [
|
|
2952
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { children: [
|
|
2953
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "parsed", children: "Response" }),
|
|
2954
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "raw", children: "Raw Response" })
|
|
2955
|
-
] }),
|
|
2956
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "parsed", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2957
|
-
ResponseView,
|
|
2958
|
-
{
|
|
2959
|
-
responseText: replayResult.responseText ?? null,
|
|
2960
|
-
responseStatus: replayResult.responseStatus ?? null,
|
|
2961
|
-
streaming: replayResult.streaming ?? false,
|
|
2962
|
-
inputTokens: replayResult.inputTokens ?? null,
|
|
2963
|
-
outputTokens: replayResult.outputTokens ?? null,
|
|
2964
|
-
apiFormat: log.apiFormat
|
|
2965
|
-
}
|
|
2966
|
-
) }),
|
|
2967
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw", children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap bg-muted p-3 rounded-md max-h-96 overflow-auto", children: replayResult.responseText }) })
|
|
2968
|
-
] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground italic", children: "No replay response" }) })
|
|
2969
|
-
] })
|
|
2970
|
-
] }) });
|
|
2971
|
-
}
|
|
2972
|
-
const StreamingChunkSequence = reactExports.memo(function StreamingChunkSequence2({
|
|
2973
|
-
logId,
|
|
2974
|
-
truncated
|
|
2975
|
-
}) {
|
|
2976
|
-
const [containerExpanded, setContainerExpanded] = reactExports.useState(false);
|
|
2977
|
-
const [chunkState, setChunkState] = reactExports.useState({ status: "idle" });
|
|
2978
|
-
const [expandedIndices, setExpandedIndices] = reactExports.useState(/* @__PURE__ */ new Set());
|
|
2979
|
-
reactExports.useEffect(() => {
|
|
2980
|
-
if (!containerExpanded || chunkState.status !== "idle") return;
|
|
2981
|
-
let cancelled = false;
|
|
2982
|
-
setChunkState({ status: "loading" });
|
|
2983
|
-
fetch(`/api/logs/${logId}/chunks`).then((res) => {
|
|
2984
|
-
if (!res.ok) {
|
|
2985
|
-
return Promise.reject(new Error("Chunks not found"));
|
|
2986
|
-
}
|
|
2987
|
-
return res.json();
|
|
2988
|
-
}).then((data) => {
|
|
2989
|
-
if (!cancelled) {
|
|
2990
|
-
setChunkState({ status: "success", chunks: data.chunks });
|
|
2991
|
-
}
|
|
2992
|
-
}).catch(() => {
|
|
2993
|
-
if (!cancelled) {
|
|
2994
|
-
setChunkState({ status: "error", message: "Chunk data unavailable" });
|
|
2995
|
-
}
|
|
2996
|
-
});
|
|
2997
|
-
return () => {
|
|
2998
|
-
cancelled = true;
|
|
2999
|
-
};
|
|
3000
|
-
}, [containerExpanded, logId]);
|
|
3001
|
-
const groups = reactExports.useMemo(() => {
|
|
3002
|
-
if (chunkState.status !== "success") return [];
|
|
3003
|
-
const map = /* @__PURE__ */ new Map();
|
|
3004
|
-
for (const chunk of chunkState.chunks) {
|
|
3005
|
-
const existing = map.get(chunk.index);
|
|
3006
|
-
if (existing) {
|
|
3007
|
-
existing.push(chunk);
|
|
3008
|
-
} else {
|
|
3009
|
-
map.set(chunk.index, [chunk]);
|
|
3010
|
-
}
|
|
3011
|
-
}
|
|
3012
|
-
return Array.from(map.entries()).map(([index, chunks]) => ({ index, chunks })).sort((a, b) => a.index - b.index);
|
|
3013
|
-
}, [chunkState]);
|
|
3014
|
-
const toggleIndex = (index) => {
|
|
3015
|
-
setExpandedIndices((prev) => {
|
|
3016
|
-
const next = new Set(prev);
|
|
3017
|
-
if (next.has(index)) {
|
|
3018
|
-
next.delete(index);
|
|
3019
|
-
} else {
|
|
3020
|
-
next.add(index);
|
|
3021
|
-
}
|
|
3022
|
-
return next;
|
|
3023
|
-
});
|
|
3024
|
-
};
|
|
3025
|
-
function renderBody() {
|
|
3026
|
-
if (chunkState.status === "idle" || chunkState.status === "loading") {
|
|
3027
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 py-2 text-xs text-muted-foreground", children: [
|
|
3028
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin" }),
|
|
3029
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Loading chunks..." })
|
|
3030
|
-
] });
|
|
3031
|
-
}
|
|
3032
|
-
if (chunkState.status === "error") {
|
|
3033
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-2 text-xs text-muted-foreground italic", children: chunkState.message });
|
|
3034
|
-
}
|
|
3035
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-2 space-y-1", children: [
|
|
3036
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-muted-foreground font-mono mb-2", children: [
|
|
3037
|
-
groups.length,
|
|
3038
|
-
" index group",
|
|
3039
|
-
groups.length !== 1 ? "s" : "",
|
|
3040
|
-
" available"
|
|
3041
|
-
] }),
|
|
3042
|
-
groups.map((group) => {
|
|
3043
|
-
const isExpanded = expandedIndices.has(group.index);
|
|
3044
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded border border-border bg-background", children: [
|
|
3045
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3046
|
-
"button",
|
|
3047
|
-
{
|
|
3048
|
-
type: "button",
|
|
3049
|
-
className: "flex items-center gap-2 w-full px-2 py-1.5 text-left hover:bg-muted/50 transition-colors cursor-pointer",
|
|
3050
|
-
onClick: () => toggleIndex(group.index),
|
|
3051
|
-
children: [
|
|
3052
|
-
isExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 text-muted-foreground" }),
|
|
3053
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-muted-foreground font-mono", children: [
|
|
3054
|
-
"[",
|
|
3055
|
-
group.index,
|
|
3056
|
-
"] ",
|
|
3057
|
-
group.chunks[0]?.type ?? ""
|
|
3058
|
-
] }),
|
|
3059
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-muted-foreground font-mono", children: [
|
|
3060
|
-
group.chunks.length,
|
|
3061
|
-
" chunk",
|
|
3062
|
-
group.chunks.length !== 1 ? "s" : ""
|
|
3063
|
-
] })
|
|
3064
|
-
]
|
|
3065
|
-
}
|
|
3066
|
-
),
|
|
3067
|
-
isExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2 pb-2 space-y-1", children: group.chunks.map((chunk) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded border border-border bg-muted/20 p-2", children: [
|
|
3068
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
|
|
3069
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-muted-foreground font-mono", children: [
|
|
3070
|
-
"+",
|
|
3071
|
-
chunk.timestamp,
|
|
3072
|
-
"ms"
|
|
3073
|
-
] }),
|
|
3074
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-muted-foreground font-mono", children: chunk.type })
|
|
3075
|
-
] }),
|
|
3076
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(JsonViewer, { data: chunk, defaultExpandDepth: 0, showCopy: true })
|
|
3077
|
-
] }, chunk.index)) })
|
|
3078
|
-
] }, group.index);
|
|
3079
|
-
})
|
|
3080
|
-
] });
|
|
3081
|
-
}
|
|
3082
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
|
|
3083
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
3084
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3085
|
-
"button",
|
|
3086
|
-
{
|
|
3087
|
-
type: "button",
|
|
3088
|
-
className: "flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer",
|
|
3089
|
-
onClick: () => setContainerExpanded((v) => !v),
|
|
3090
|
-
children: [
|
|
3091
|
-
containerExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3" }),
|
|
3092
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Raw SSE Events" }),
|
|
3093
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, { variant: "outline", className: "text-[9px] px-1 py-0 h-4 font-mono ml-1", children: [
|
|
3094
|
-
logId,
|
|
3095
|
-
truncated === true ? "+" : ""
|
|
3096
|
-
] })
|
|
3097
|
-
]
|
|
3098
|
-
}
|
|
3099
|
-
) }),
|
|
3100
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Server-Sent Events streaming chunks from the provider" })
|
|
3101
|
-
] }) }),
|
|
3102
|
-
containerExpanded === true ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-md border border-border bg-muted/20 overflow-auto max-h-64", children: renderBody() }) : null
|
|
3103
|
-
] });
|
|
3104
|
-
});
|
|
3105
|
-
function shouldShowRawRequestTab(apiFormat, viewMode, strip) {
|
|
3106
|
-
return apiFormat === "anthropic" && viewMode === "full" && strip;
|
|
3107
|
-
}
|
|
3108
|
-
function shouldShowHeadersDiffButton(viewMode, hasRawHeaders) {
|
|
3109
|
-
return viewMode === "full" && hasRawHeaders;
|
|
3110
|
-
}
|
|
3111
|
-
function shouldShowRequestDiffButton(apiFormat, viewMode, strip, hasRawRequest) {
|
|
3112
|
-
return apiFormat === "anthropic" && viewMode === "full" && strip && hasRawRequest;
|
|
3113
|
-
}
|
|
3114
|
-
function CopyButton({
|
|
3115
|
-
text,
|
|
3116
|
-
label,
|
|
3117
|
-
copied,
|
|
3118
|
-
onCopy
|
|
3119
|
-
}) {
|
|
3120
|
-
if (text === null) return null;
|
|
3121
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3122
|
-
"button",
|
|
3123
|
-
{
|
|
3124
|
-
type: "button",
|
|
3125
|
-
onClick: onCopy,
|
|
3126
|
-
className: "flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors px-2 py-1 rounded hover:bg-muted",
|
|
3127
|
-
children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
3128
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3 text-green-500" }),
|
|
3129
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-green-500", children: "Copied!" })
|
|
3130
|
-
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
3131
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3" }),
|
|
3132
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: label })
|
|
3133
|
-
] })
|
|
3134
|
-
}
|
|
3135
|
-
);
|
|
3136
2437
|
}
|
|
3137
2438
|
function DiffToggleButton({
|
|
3138
2439
|
active,
|
|
@@ -3140,17 +2441,15 @@ function DiffToggleButton({
|
|
|
3140
2441
|
}) {
|
|
3141
2442
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
3142
2443
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3143
|
-
|
|
2444
|
+
Button,
|
|
3144
2445
|
{
|
|
3145
|
-
|
|
2446
|
+
variant: active ? "default" : "outline",
|
|
2447
|
+
size: "sm",
|
|
2448
|
+
className: "h-8 text-xs",
|
|
3146
2449
|
onClick,
|
|
3147
2450
|
"aria-pressed": active,
|
|
3148
|
-
className: cn(
|
|
3149
|
-
"flex items-center gap-1.5 text-xs px-2 py-1 rounded transition-colors",
|
|
3150
|
-
active ? "bg-primary/10 text-primary" : "text-muted-foreground hover:text-foreground hover:bg-muted"
|
|
3151
|
-
),
|
|
3152
2451
|
children: [
|
|
3153
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(GitCompareArrows, { className: "size-3" }),
|
|
2452
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(GitCompareArrows, { className: "size-3.5 mr-1" }),
|
|
3154
2453
|
active ? "Showing diff" : "Diff with Raw"
|
|
3155
2454
|
]
|
|
3156
2455
|
}
|
|
@@ -3204,6 +2503,7 @@ const LogEntry = reactExports.memo(function({
|
|
|
3204
2503
|
log,
|
|
3205
2504
|
viewMode = "simple",
|
|
3206
2505
|
strip,
|
|
2506
|
+
slowResponseThresholdSeconds,
|
|
3207
2507
|
cacheTrend = null,
|
|
3208
2508
|
onCompareWithPrevious
|
|
3209
2509
|
}) {
|
|
@@ -3212,6 +2512,8 @@ const LogEntry = reactExports.memo(function({
|
|
|
3212
2512
|
const [headersDiff, setHeadersDiff] = reactExports.useState(false);
|
|
3213
2513
|
const [requestDiff, setRequestDiff] = reactExports.useState(false);
|
|
3214
2514
|
const [activeTab, setActiveTab] = reactExports.useState("request");
|
|
2515
|
+
const [expandToPath, setExpandToPath] = reactExports.useState(null);
|
|
2516
|
+
const requestJsonRef = reactExports.useRef(null);
|
|
3215
2517
|
const resolvedFormat = resolveLogFormat(log);
|
|
3216
2518
|
const adapter = getLogFormatAdapter(resolvedFormat);
|
|
3217
2519
|
const requestAnalysis = reactExports.useMemo(
|
|
@@ -3234,6 +2536,21 @@ const LogEntry = reactExports.memo(function({
|
|
|
3234
2536
|
const responseCopy = useCopyFeedback(log.responseText);
|
|
3235
2537
|
const requestExpansion = useJsonBulkExpansion(displayedRequestBody);
|
|
3236
2538
|
const rawRequestExpansion = useJsonBulkExpansion(log.rawRequestBody);
|
|
2539
|
+
const anatomySegments = reactExports.useMemo(
|
|
2540
|
+
() => requestExpansion.parsedData !== null ? adapter.anatomySegments(requestExpansion.parsedData) : null,
|
|
2541
|
+
[adapter, requestExpansion.parsedData]
|
|
2542
|
+
);
|
|
2543
|
+
const anatomyPaths = reactExports.useMemo(() => {
|
|
2544
|
+
if (anatomySegments === null) return void 0;
|
|
2545
|
+
return new Set(anatomySegments.map((s) => s.path));
|
|
2546
|
+
}, [anatomySegments]);
|
|
2547
|
+
const jumpToAnatomySegment = useAnatomyJump({
|
|
2548
|
+
containerRef: requestJsonRef,
|
|
2549
|
+
setExpandToPath,
|
|
2550
|
+
ensureTabActive: () => {
|
|
2551
|
+
if (activeTab !== "request") setActiveTab("request");
|
|
2552
|
+
}
|
|
2553
|
+
});
|
|
3237
2554
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(TooltipProvider, { children: [
|
|
3238
2555
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-border rounded-lg mb-1 overflow-hidden", children: [
|
|
3239
2556
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -3245,7 +2562,20 @@ const LogEntry = reactExports.memo(function({
|
|
|
3245
2562
|
expanded,
|
|
3246
2563
|
onToggle: () => setExpanded(!expanded),
|
|
3247
2564
|
responseToolNames: responseAnalysis.toolNames,
|
|
3248
|
-
cacheTrend
|
|
2565
|
+
cacheTrend,
|
|
2566
|
+
slowResponseThresholdSeconds,
|
|
2567
|
+
onReplay: onCompareWithPrevious === void 0 ? void 0 : () => {
|
|
2568
|
+
setReplayOpen(true);
|
|
2569
|
+
},
|
|
2570
|
+
onCopyRequest: displayedRequestBody === null ? void 0 : (e) => {
|
|
2571
|
+
requestCopy.copy(e);
|
|
2572
|
+
},
|
|
2573
|
+
requestCopied: requestCopy.copied,
|
|
2574
|
+
onToggleRequestExpansion: requestExpansion.toggle,
|
|
2575
|
+
requestExpansionState: requestExpansion.policy === null ? null : {
|
|
2576
|
+
isExpanded: requestExpansion.isExpanded,
|
|
2577
|
+
isPending: requestExpansion.isPending
|
|
2578
|
+
}
|
|
3249
2579
|
}
|
|
3250
2580
|
),
|
|
3251
2581
|
expanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { onClick: (e) => e.stopPropagation(), onKeyDown: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, children: [
|
|
@@ -3254,10 +2584,11 @@ const LogEntry = reactExports.memo(function({
|
|
|
3254
2584
|
viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "headers", children: "Headers" }),
|
|
3255
2585
|
shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "raw-request", children: "Raw Request" }),
|
|
3256
2586
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "request", children: "Request" }),
|
|
2587
|
+
anatomySegments !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "anatomy", children: "Anatomy" }),
|
|
3257
2588
|
viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "raw", children: "Raw Response" }),
|
|
3258
2589
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "parsed", children: "Response" })
|
|
3259
2590
|
] }),
|
|
3260
|
-
shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-request", children: activeTab === "raw-request" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4
|
|
2591
|
+
shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-request", children: activeTab === "raw-request" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 pt-1 pb-3", children: [
|
|
3261
2592
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-2 mb-2", children: [
|
|
3262
2593
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3263
2594
|
JsonExpansionButton,
|
|
@@ -3278,17 +2609,22 @@ const LogEntry = reactExports.memo(function({
|
|
|
3278
2609
|
}
|
|
3279
2610
|
)
|
|
3280
2611
|
] }),
|
|
3281
|
-
log.rawRequestBody === null ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No request body" }) : rawRequestExpansion.parsedData !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3282
|
-
|
|
2612
|
+
log.rawRequestBody === null ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No request body" }) : rawRequestExpansion.parsedData !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2613
|
+
LazyJsonViewer,
|
|
3283
2614
|
{
|
|
3284
2615
|
data: rawRequestExpansion.parsedData,
|
|
3285
2616
|
bulkDepth: rawRequestExpansion.bulkDepth,
|
|
3286
2617
|
bulkRevision: rawRequestExpansion.bulkRevision
|
|
3287
2618
|
}
|
|
3288
|
-
) : /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap break-words", children: log.rawRequestBody })
|
|
2619
|
+
) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap break-words", children: log.rawRequestBody })
|
|
3289
2620
|
] }) }),
|
|
3290
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "request", children: activeTab === "request" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4
|
|
3291
|
-
|
|
2621
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "request", children: activeTab === "request" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 pt-1 pb-3", children: [
|
|
2622
|
+
(shouldShowRequestDiffButton(
|
|
2623
|
+
resolvedFormat,
|
|
2624
|
+
viewMode,
|
|
2625
|
+
strip,
|
|
2626
|
+
log.rawRequestBody !== null
|
|
2627
|
+
) || onCompareWithPrevious !== void 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-2 mb-2", children: [
|
|
3292
2628
|
shouldShowRequestDiffButton(
|
|
3293
2629
|
resolvedFormat,
|
|
3294
2630
|
viewMode,
|
|
@@ -3310,7 +2646,7 @@ const LogEntry = reactExports.memo(function({
|
|
|
3310
2646
|
{
|
|
3311
2647
|
variant: "outline",
|
|
3312
2648
|
size: "sm",
|
|
3313
|
-
className: "h-
|
|
2649
|
+
className: "h-8 text-xs",
|
|
3314
2650
|
onClick: (e) => {
|
|
3315
2651
|
e.stopPropagation();
|
|
3316
2652
|
onCompareWithPrevious(log);
|
|
@@ -3322,44 +2658,7 @@ const LogEntry = reactExports.memo(function({
|
|
|
3322
2658
|
}
|
|
3323
2659
|
) }),
|
|
3324
2660
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Compare this request with the immediately preceding one" })
|
|
3325
|
-
] })
|
|
3326
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
3327
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3328
|
-
Button,
|
|
3329
|
-
{
|
|
3330
|
-
variant: "outline",
|
|
3331
|
-
size: "sm",
|
|
3332
|
-
className: "h-7 text-xs",
|
|
3333
|
-
onClick: (e) => {
|
|
3334
|
-
e.stopPropagation();
|
|
3335
|
-
setReplayOpen(true);
|
|
3336
|
-
},
|
|
3337
|
-
children: [
|
|
3338
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { className: "size-3 mr-1" }),
|
|
3339
|
-
"Replay"
|
|
3340
|
-
]
|
|
3341
|
-
}
|
|
3342
|
-
) }),
|
|
3343
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Re-send this request to the provider" })
|
|
3344
|
-
] }),
|
|
3345
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3346
|
-
JsonExpansionButton,
|
|
3347
|
-
{
|
|
3348
|
-
policy: requestExpansion.policy,
|
|
3349
|
-
isExpanded: requestExpansion.isExpanded,
|
|
3350
|
-
isPending: requestExpansion.isPending,
|
|
3351
|
-
onToggle: requestExpansion.toggle
|
|
3352
|
-
}
|
|
3353
|
-
),
|
|
3354
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3355
|
-
CopyButton,
|
|
3356
|
-
{
|
|
3357
|
-
text: displayedRequestBody,
|
|
3358
|
-
label: "Copy",
|
|
3359
|
-
copied: requestCopy.copied,
|
|
3360
|
-
onCopy: requestCopy.copy
|
|
3361
|
-
}
|
|
3362
|
-
)
|
|
2661
|
+
] })
|
|
3363
2662
|
] }),
|
|
3364
2663
|
requestDiff ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3365
2664
|
RequestDiffContent,
|
|
@@ -3368,16 +2667,27 @@ const LogEntry = reactExports.memo(function({
|
|
|
3368
2667
|
displayedBody: displayedRequestBody,
|
|
3369
2668
|
emptyLabel: "No transformation applied — raw and sent request bodies are identical."
|
|
3370
2669
|
}
|
|
3371
|
-
) : displayedRequestBody === null ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No request body" }) : requestExpansion.parsedData !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3372
|
-
|
|
2670
|
+
) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: requestJsonRef, children: displayedRequestBody === null ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No request body" }) : requestExpansion.parsedData !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2671
|
+
LazyJsonViewer,
|
|
3373
2672
|
{
|
|
3374
2673
|
data: requestExpansion.parsedData,
|
|
3375
2674
|
bulkDepth: requestExpansion.bulkDepth,
|
|
3376
|
-
bulkRevision: requestExpansion.bulkRevision
|
|
2675
|
+
bulkRevision: requestExpansion.bulkRevision,
|
|
2676
|
+
anatomyPaths,
|
|
2677
|
+
expandToPath
|
|
3377
2678
|
}
|
|
3378
|
-
) : /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap break-words", children: displayedRequestBody })
|
|
2679
|
+
) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap break-words", children: displayedRequestBody }) })
|
|
3379
2680
|
] }) }),
|
|
3380
|
-
|
|
2681
|
+
anatomySegments !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "anatomy", children: activeTab === "anatomy" && /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2682
|
+
LazyRequestAnatomy,
|
|
2683
|
+
{
|
|
2684
|
+
parsed: null,
|
|
2685
|
+
inputTokens: log.inputTokens ?? null,
|
|
2686
|
+
segments: anatomySegments,
|
|
2687
|
+
onSegmentActivate: jumpToAnatomySegment
|
|
2688
|
+
}
|
|
2689
|
+
) }) }),
|
|
2690
|
+
viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "headers", children: activeTab === "headers" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 pt-1 pb-3", children: [
|
|
3381
2691
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end gap-2 mb-2", children: shouldShowHeadersDiffButton(
|
|
3382
2692
|
viewMode,
|
|
3383
2693
|
log.rawHeaders !== void 0 && Object.keys(log.rawHeaders).length > 0
|
|
@@ -3406,14 +2716,14 @@ const LogEntry = reactExports.memo(function({
|
|
|
3406
2716
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground truncate", title: value, children: value })
|
|
3407
2717
|
] }, key)) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No headers captured" })
|
|
3408
2718
|
] }) }),
|
|
3409
|
-
viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-headers", children: activeTab === "raw-headers" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4
|
|
2719
|
+
viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-headers", children: activeTab === "raw-headers" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 pt-1 pb-3", children: log.rawHeaders && Object.keys(log.rawHeaders).length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 font-mono text-xs", children: Object.entries(log.rawHeaders).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
|
|
3410
2720
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-600 dark:text-blue-400 font-semibold shrink-0", children: [
|
|
3411
2721
|
key,
|
|
3412
2722
|
":"
|
|
3413
2723
|
] }),
|
|
3414
2724
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground truncate", title: value, children: value })
|
|
3415
2725
|
] }, key)) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No raw headers captured" }) }) }),
|
|
3416
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw", children: activeTab === "raw" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4
|
|
2726
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw", children: activeTab === "raw" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 pt-1 pb-3 space-y-3", children: [
|
|
3417
2727
|
log.error !== void 0 && log.error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded border border-destructive/50 bg-destructive/10 p-3 text-xs", children: [
|
|
3418
2728
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-semibold text-destructive mb-1", children: "SSE Error" }),
|
|
3419
2729
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground font-mono", children: log.error })
|
|
@@ -3427,17 +2737,17 @@ const LogEntry = reactExports.memo(function({
|
|
|
3427
2737
|
onCopy: responseCopy.copy
|
|
3428
2738
|
}
|
|
3429
2739
|
) }),
|
|
3430
|
-
log.responseText !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3431
|
-
log.streaming === true && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3432
|
-
|
|
2740
|
+
log.responseText !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(LazyJsonViewerFromString, { text: log.responseText, defaultExpandDepth: 0 }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No response" }),
|
|
2741
|
+
log.streaming === true && /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2742
|
+
LazyStreamingChunkSequence,
|
|
3433
2743
|
{
|
|
3434
2744
|
logId: log.id,
|
|
3435
2745
|
truncated: log.streamingChunksPath !== null
|
|
3436
2746
|
}
|
|
3437
|
-
)
|
|
2747
|
+
) })
|
|
3438
2748
|
] }) }),
|
|
3439
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "parsed", children: activeTab === "parsed" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4
|
|
3440
|
-
|
|
2749
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "parsed", children: activeTab === "parsed" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 pt-1 pb-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2750
|
+
LazyResponseView,
|
|
3441
2751
|
{
|
|
3442
2752
|
responseText: log.responseText,
|
|
3443
2753
|
responseStatus: log.responseStatus,
|
|
@@ -3449,10 +2759,10 @@ const LogEntry = reactExports.memo(function({
|
|
|
3449
2759
|
apiFormat: resolvedFormat,
|
|
3450
2760
|
error: log.error
|
|
3451
2761
|
}
|
|
3452
|
-
) }) })
|
|
2762
|
+
) }) }) })
|
|
3453
2763
|
] }) })
|
|
3454
2764
|
] }),
|
|
3455
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2765
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntimeExports.jsx(LazyReplayDialog, { log, open: replayOpen, onOpenChange: setReplayOpen }) })
|
|
3456
2766
|
] });
|
|
3457
2767
|
});
|
|
3458
2768
|
function ThreadConnector({
|
|
@@ -3460,13 +2770,16 @@ function ThreadConnector({
|
|
|
3460
2770
|
isPending,
|
|
3461
2771
|
isFirst,
|
|
3462
2772
|
isTurnStart,
|
|
2773
|
+
isOnlyEntry = false,
|
|
3463
2774
|
crabIndex = 0,
|
|
3464
2775
|
collapsible = false,
|
|
3465
2776
|
onToggle
|
|
3466
2777
|
}) {
|
|
3467
2778
|
const isBoundary = stopReason === "end_turn" || stopReason === "stop";
|
|
2779
|
+
const isFusedBoundary = isOnlyEntry && isTurnStart && isBoundary;
|
|
3468
2780
|
const isRunning = isPending && !isBoundary;
|
|
3469
2781
|
const Crab = reactExports.useMemo(() => getCrabVariant(crabIndex), [crabIndex]);
|
|
2782
|
+
const FusedCrab = reactExports.useMemo(() => getInteriorCrabVariant(crabIndex), [crabIndex]);
|
|
3470
2783
|
const interactiveProps = collapsible && onToggle ? {
|
|
3471
2784
|
role: "button",
|
|
3472
2785
|
tabIndex: 0,
|
|
@@ -3484,7 +2797,16 @@ function ThreadConnector({
|
|
|
3484
2797
|
} : {};
|
|
3485
2798
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center w-6 shrink-0 pt-0.5 pb-0.5", children: [
|
|
3486
2799
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-center h-[calc(0.75rem-8px)]", children: !isFirst && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-0.5 bg-muted-foreground/30 h-full" }) }),
|
|
3487
|
-
|
|
2800
|
+
isFusedBoundary ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { title: "Start and end of turn", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2801
|
+
FusedCrab,
|
|
2802
|
+
{
|
|
2803
|
+
className: cn(
|
|
2804
|
+
"size-3.5 text-[#80FF00]",
|
|
2805
|
+
"animate-crab-settle",
|
|
2806
|
+
"drop-shadow-[0_0_4px_rgba(128,255,0,0.5)]"
|
|
2807
|
+
)
|
|
2808
|
+
}
|
|
2809
|
+
) }) : isBoundary ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3488
2810
|
"span",
|
|
3489
2811
|
{
|
|
3490
2812
|
title: stopReason === "end_turn" ? "End of Turn (Anthropic)" : "End of Turn (OpenAI)",
|
|
@@ -3521,6 +2843,43 @@ function ThreadConnector({
|
|
|
3521
2843
|
) })
|
|
3522
2844
|
] });
|
|
3523
2845
|
}
|
|
2846
|
+
function shouldRenderConversationContent(standalone, expanded) {
|
|
2847
|
+
return standalone || expanded;
|
|
2848
|
+
}
|
|
2849
|
+
function isTurnCollapsible(entryCount, isComplete, isPending) {
|
|
2850
|
+
return entryCount > 1 && isComplete && !isPending;
|
|
2851
|
+
}
|
|
2852
|
+
function buildTurnGroups(logs) {
|
|
2853
|
+
const groups = [];
|
|
2854
|
+
let entries = [];
|
|
2855
|
+
let turnIndex = 0;
|
|
2856
|
+
for (const log of logs) {
|
|
2857
|
+
entries.push({ log, stopReason: extractStopReason(log) });
|
|
2858
|
+
const current = entries[entries.length - 1];
|
|
2859
|
+
if (current !== void 0 && isTurnBoundary(current.stopReason)) {
|
|
2860
|
+
groups.push({ entries, turnIndex });
|
|
2861
|
+
entries = [];
|
|
2862
|
+
turnIndex += 1;
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
if (entries.length > 0) groups.push({ entries, turnIndex });
|
|
2866
|
+
return groups;
|
|
2867
|
+
}
|
|
2868
|
+
function buildValidPredecessors(groups) {
|
|
2869
|
+
const predecessors = /* @__PURE__ */ new Map();
|
|
2870
|
+
for (const group of groups) {
|
|
2871
|
+
for (let index2 = 1; index2 < group.logs.length; index2 += 1) {
|
|
2872
|
+
const current = group.logs[index2];
|
|
2873
|
+
const previous = group.logs[index2 - 1];
|
|
2874
|
+
if (current === void 0 || previous === void 0) continue;
|
|
2875
|
+
const currentFormat = resolveLogFormat(current);
|
|
2876
|
+
const previousFormat = resolveLogFormat(previous);
|
|
2877
|
+
if (currentFormat === "unknown" || currentFormat !== previousFormat) continue;
|
|
2878
|
+
predecessors.set(current.id, previous);
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
return predecessors;
|
|
2882
|
+
}
|
|
3524
2883
|
function formatElapsed(ms) {
|
|
3525
2884
|
if (ms < 1e3) return `${ms}ms`;
|
|
3526
2885
|
return `${(ms / 1e3).toFixed(1)}s`;
|
|
@@ -3529,6 +2888,7 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
3529
2888
|
entries,
|
|
3530
2889
|
viewMode,
|
|
3531
2890
|
strip,
|
|
2891
|
+
slowResponseThresholdSeconds,
|
|
3532
2892
|
cacheTrends,
|
|
3533
2893
|
onCompareWithPrevious,
|
|
3534
2894
|
comparisonPredecessors,
|
|
@@ -3538,15 +2898,18 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
3538
2898
|
const lastStop = entries[lastIdx]?.stopReason ?? null;
|
|
3539
2899
|
const isComplete = lastStop !== null ? isTurnBoundary(lastStop) : false;
|
|
3540
2900
|
const isPending = entries[lastIdx]?.log.responseStatus === null;
|
|
3541
|
-
const
|
|
2901
|
+
const isSingleLog = entries.length === 1;
|
|
2902
|
+
const collapsible = isTurnCollapsible(entries.length, isComplete, isPending);
|
|
3542
2903
|
const [collapsed, setCollapsed] = reactExports.useState(false);
|
|
3543
|
-
const prevCompleteRef = reactExports.useRef(
|
|
2904
|
+
const prevCompleteRef = reactExports.useRef(false);
|
|
3544
2905
|
reactExports.useEffect(() => {
|
|
3545
|
-
if (
|
|
2906
|
+
if (!collapsible) {
|
|
2907
|
+
setCollapsed(false);
|
|
2908
|
+
} else if (isComplete && !prevCompleteRef.current) {
|
|
3546
2909
|
setCollapsed(true);
|
|
3547
2910
|
}
|
|
3548
2911
|
prevCompleteRef.current = isComplete;
|
|
3549
|
-
}, [isComplete]);
|
|
2912
|
+
}, [collapsible, isComplete]);
|
|
3550
2913
|
const toggleCollapse = reactExports.useCallback(() => {
|
|
3551
2914
|
if (collapsible) setCollapsed((prev) => !prev);
|
|
3552
2915
|
}, [collapsible]);
|
|
@@ -3589,6 +2952,7 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
3589
2952
|
const StartCrab = reactExports.useMemo(() => getCrabVariant(entries[0]?.log.id ?? 0), [entries]);
|
|
3590
2953
|
const EndCrab = reactExports.useMemo(() => getCrabVariant(entries[lastIdx]?.log.id ?? 0), [entries, lastIdx]);
|
|
3591
2954
|
const bgClass = turnIndex % 2 === 0 ? "bg-muted/10" : "bg-muted/25";
|
|
2955
|
+
const aggregateIsSlow = aggregate.hasElapsed && slowResponseThresholdSeconds > 0 && aggregate.totalElapsed > slowResponseThresholdSeconds * 1e3;
|
|
3592
2956
|
const [layoutVersion, setLayoutVersion] = reactExports.useState(0);
|
|
3593
2957
|
const containerRef = reactExports.useRef(null);
|
|
3594
2958
|
reactExports.useEffect(() => {
|
|
@@ -3611,129 +2975,152 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
3611
2975
|
ref: containerRef,
|
|
3612
2976
|
className: cn("border rounded-lg", isPending ? "border-amber-500/10" : "border-transparent"),
|
|
3613
2977
|
children: collapsed ? (
|
|
3614
|
-
/* ---- Collapsed: dual-crab + summary ---- */
|
|
3615
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
StartCrab,
|
|
2978
|
+
/* ---- Collapsed: dual-crab (+ summary card for multi-log turns) ---- */
|
|
2979
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2980
|
+
"div",
|
|
2981
|
+
{
|
|
2982
|
+
"data-nav-id": `turn-collapsed-${entries[0]?.log.id ?? turnIndex}`,
|
|
2983
|
+
"data-nav-action": "expand",
|
|
2984
|
+
role: "button",
|
|
2985
|
+
tabIndex: 0,
|
|
2986
|
+
className: "flex items-stretch cursor-pointer focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none rounded-lg",
|
|
2987
|
+
onClick: toggleCollapse,
|
|
2988
|
+
onKeyDown: (e) => {
|
|
2989
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
2990
|
+
e.preventDefault();
|
|
2991
|
+
toggleCollapse();
|
|
2992
|
+
}
|
|
2993
|
+
},
|
|
2994
|
+
children: [
|
|
2995
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-5 shrink-0 flex items-start pt-1.5", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-muted-foreground/50 font-mono tabular-nums leading-none select-none", children: turnIndex + 1 }) }),
|
|
2996
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-6 shrink-0 flex flex-col items-center pt-0.5 pb-0.5", children: [
|
|
2997
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-center h-[calc(0.75rem-8px)]" }),
|
|
2998
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2999
|
+
"span",
|
|
3637
3000
|
{
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3001
|
+
role: "button",
|
|
3002
|
+
tabIndex: 0,
|
|
3003
|
+
title: "Start of turn — click to expand",
|
|
3004
|
+
className: "cursor-pointer",
|
|
3005
|
+
onClick: (e) => {
|
|
3006
|
+
e.stopPropagation();
|
|
3007
|
+
toggleCollapse();
|
|
3008
|
+
},
|
|
3009
|
+
onKeyDown: (e) => {
|
|
3010
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
3011
|
+
e.preventDefault();
|
|
3012
|
+
toggleCollapse();
|
|
3013
|
+
}
|
|
3014
|
+
},
|
|
3015
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3016
|
+
StartCrab,
|
|
3017
|
+
{
|
|
3018
|
+
className: cn(
|
|
3019
|
+
"size-3.5 text-emerald-400",
|
|
3020
|
+
"animate-crab-appear drop-shadow-[0_0_4px_rgba(52,211,153,0.5)]"
|
|
3021
|
+
)
|
|
3022
|
+
}
|
|
3642
3023
|
)
|
|
3643
3024
|
}
|
|
3644
|
-
)
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3649
|
-
"span",
|
|
3650
|
-
{
|
|
3651
|
-
role: "button",
|
|
3652
|
-
tabIndex: 0,
|
|
3653
|
-
title: "End of Turn — click to expand",
|
|
3654
|
-
className: "cursor-pointer",
|
|
3655
|
-
onClick: (e) => {
|
|
3656
|
-
e.stopPropagation();
|
|
3657
|
-
toggleCollapse();
|
|
3658
|
-
},
|
|
3659
|
-
onKeyDown: (e) => {
|
|
3660
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
3661
|
-
e.preventDefault();
|
|
3662
|
-
toggleCollapse();
|
|
3663
|
-
}
|
|
3664
|
-
},
|
|
3665
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3666
|
-
EndCrab,
|
|
3025
|
+
),
|
|
3026
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex justify-center min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-0.5 bg-muted-foreground/30 h-full" }) }),
|
|
3027
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3028
|
+
"span",
|
|
3667
3029
|
{
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3030
|
+
role: "button",
|
|
3031
|
+
tabIndex: 0,
|
|
3032
|
+
title: "End of Turn — click to expand",
|
|
3033
|
+
className: "cursor-pointer",
|
|
3034
|
+
onClick: (e) => {
|
|
3035
|
+
e.stopPropagation();
|
|
3036
|
+
toggleCollapse();
|
|
3037
|
+
},
|
|
3038
|
+
onKeyDown: (e) => {
|
|
3039
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
3040
|
+
e.preventDefault();
|
|
3041
|
+
toggleCollapse();
|
|
3042
|
+
}
|
|
3043
|
+
},
|
|
3044
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3045
|
+
EndCrab,
|
|
3046
|
+
{
|
|
3047
|
+
className: cn(
|
|
3048
|
+
"size-3.5 text-amber-400",
|
|
3049
|
+
"animate-crab-settle drop-shadow-[0_0_4px_rgba(251,191,36,0.5)]"
|
|
3050
|
+
)
|
|
3051
|
+
}
|
|
3672
3052
|
)
|
|
3673
3053
|
}
|
|
3674
3054
|
)
|
|
3675
|
-
}
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
3690
|
-
e.preventDefault();
|
|
3691
|
-
toggleCollapse();
|
|
3692
|
-
}
|
|
3693
|
-
},
|
|
3694
|
-
children: [
|
|
3695
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-400/80 font-mono font-semibold tabular-nums shrink-0", children: [
|
|
3696
|
-
"#",
|
|
3697
|
-
entries[0]?.log.id ?? "?",
|
|
3698
|
-
" ~ #",
|
|
3699
|
-
entries[lastIdx]?.log.id ?? "?"
|
|
3700
|
-
] }),
|
|
3701
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground shrink-0", children: [
|
|
3702
|
-
entries.length,
|
|
3703
|
-
" request",
|
|
3704
|
-
entries.length > 1 ? "s" : ""
|
|
3705
|
-
] }),
|
|
3706
|
-
uniqueProviders.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex items-center gap-0.5 shrink-0", children: uniqueProviders.map((p) => /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderLogo, { provider: p, className: "size-4" }, p)) }),
|
|
3707
|
-
aggregate.hasElapsed && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground shrink-0", children: [
|
|
3708
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
|
|
3709
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed(aggregate.totalElapsed) })
|
|
3710
|
-
] }),
|
|
3711
|
-
aggregate.hasTokens && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 shrink-0", children: [
|
|
3712
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3 text-muted-foreground" }),
|
|
3713
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
|
|
3714
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-400", children: [
|
|
3715
|
-
"IN ",
|
|
3716
|
-
formatTokens(aggregate.totalInput)
|
|
3055
|
+
] }),
|
|
3056
|
+
entries.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3057
|
+
"div",
|
|
3058
|
+
{
|
|
3059
|
+
className: cn(
|
|
3060
|
+
"flex-1 min-w-0 mb-0.5 rounded-lg border border-border py-1 px-3 flex items-center gap-3 text-xs",
|
|
3061
|
+
bgClass
|
|
3062
|
+
),
|
|
3063
|
+
children: [
|
|
3064
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-400/80 font-mono font-semibold tabular-nums shrink-0", children: [
|
|
3065
|
+
"#",
|
|
3066
|
+
entries[0]?.log.id ?? "?",
|
|
3067
|
+
" ~ #",
|
|
3068
|
+
entries[lastIdx]?.log.id ?? "?"
|
|
3717
3069
|
] }),
|
|
3718
|
-
"
|
|
3719
|
-
|
|
3720
|
-
"
|
|
3721
|
-
|
|
3722
|
-
] })
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3070
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground shrink-0", children: [
|
|
3071
|
+
entries.length,
|
|
3072
|
+
" request",
|
|
3073
|
+
entries.length > 1 ? "s" : ""
|
|
3074
|
+
] }),
|
|
3075
|
+
uniqueProviders.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex items-center gap-0.5 shrink-0", children: uniqueProviders.map((p) => /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderLogo, { provider: p, className: "size-4" }, p)) }),
|
|
3076
|
+
aggregate.hasElapsed && /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
3077
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3078
|
+
"span",
|
|
3079
|
+
{
|
|
3080
|
+
className: cn(
|
|
3081
|
+
"flex items-center gap-1 shrink-0",
|
|
3082
|
+
aggregateIsSlow ? "text-amber-400" : "text-muted-foreground"
|
|
3083
|
+
),
|
|
3084
|
+
children: [
|
|
3085
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
|
|
3086
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed(aggregate.totalElapsed) }),
|
|
3087
|
+
aggregateIsSlow && /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3", "aria-label": "Slow response" })
|
|
3088
|
+
]
|
|
3089
|
+
}
|
|
3090
|
+
) }),
|
|
3091
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: aggregateIsSlow ? `Slow response: ${formatElapsed(
|
|
3092
|
+
aggregate.totalElapsed
|
|
3093
|
+
)} exceeds ${formatElapsed(slowResponseThresholdSeconds * 1e3)}` : "Total elapsed response time" })
|
|
3094
|
+
] }) }),
|
|
3095
|
+
aggregate.hasTokens && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 shrink-0", children: [
|
|
3096
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3 text-muted-foreground" }),
|
|
3097
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
|
|
3098
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-400", children: [
|
|
3099
|
+
"IN ",
|
|
3100
|
+
formatTokens(aggregate.totalInput)
|
|
3101
|
+
] }),
|
|
3102
|
+
" / ",
|
|
3103
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-amber-400", children: [
|
|
3104
|
+
"OUT ",
|
|
3105
|
+
formatTokens(aggregate.totalOutput)
|
|
3106
|
+
] })
|
|
3107
|
+
] })
|
|
3108
|
+
] }),
|
|
3109
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 min-w-0" }),
|
|
3110
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-4 text-muted-foreground shrink-0" })
|
|
3111
|
+
]
|
|
3112
|
+
}
|
|
3113
|
+
)
|
|
3114
|
+
]
|
|
3115
|
+
}
|
|
3116
|
+
)
|
|
3731
3117
|
) : (
|
|
3732
3118
|
/* ---- Expanded: full entries ---- */
|
|
3733
3119
|
entries.map((entry, visibleIdx) => {
|
|
3734
3120
|
const { log, stopReason: reason } = entry;
|
|
3735
3121
|
const isTurnStart = visibleIdx === 0;
|
|
3736
3122
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-stretch", children: [
|
|
3123
|
+
isTurnStart ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-5 shrink-0 flex items-start pt-1.5", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-muted-foreground/50 font-mono tabular-nums leading-none select-none", children: turnIndex + 1 }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-5 shrink-0" }),
|
|
3737
3124
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3738
3125
|
ThreadConnector,
|
|
3739
3126
|
{
|
|
@@ -3741,8 +3128,9 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
3741
3128
|
isPending: log.responseStatus === null,
|
|
3742
3129
|
isFirst: visibleIdx === 0,
|
|
3743
3130
|
isTurnStart,
|
|
3131
|
+
isOnlyEntry: isSingleLog,
|
|
3744
3132
|
crabIndex: log.id % 12,
|
|
3745
|
-
collapsible: collapsible &&
|
|
3133
|
+
collapsible: collapsible && isTurnStart,
|
|
3746
3134
|
onToggle: toggleCollapse
|
|
3747
3135
|
}
|
|
3748
3136
|
),
|
|
@@ -3752,6 +3140,7 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
3752
3140
|
log,
|
|
3753
3141
|
viewMode,
|
|
3754
3142
|
strip,
|
|
3143
|
+
slowResponseThresholdSeconds,
|
|
3755
3144
|
cacheTrend: cacheTrends?.get(log.id) ?? null,
|
|
3756
3145
|
onCompareWithPrevious: comparisonPredecessors.has(log.id) ? onCompareWithPrevious : void 0
|
|
3757
3146
|
}
|
|
@@ -3762,40 +3151,6 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
3762
3151
|
}
|
|
3763
3152
|
);
|
|
3764
3153
|
});
|
|
3765
|
-
function shouldRenderConversationContent(standalone, expanded) {
|
|
3766
|
-
return standalone || expanded;
|
|
3767
|
-
}
|
|
3768
|
-
function buildTurnGroups(logs) {
|
|
3769
|
-
const groups = [];
|
|
3770
|
-
let entries = [];
|
|
3771
|
-
let turnIndex = 0;
|
|
3772
|
-
for (const log of logs) {
|
|
3773
|
-
entries.push({ log, stopReason: extractStopReason(log) });
|
|
3774
|
-
const current = entries[entries.length - 1];
|
|
3775
|
-
if (current !== void 0 && isTurnBoundary(current.stopReason)) {
|
|
3776
|
-
groups.push({ entries, turnIndex });
|
|
3777
|
-
entries = [];
|
|
3778
|
-
turnIndex += 1;
|
|
3779
|
-
}
|
|
3780
|
-
}
|
|
3781
|
-
if (entries.length > 0) groups.push({ entries, turnIndex });
|
|
3782
|
-
return groups;
|
|
3783
|
-
}
|
|
3784
|
-
function buildValidPredecessors(groups) {
|
|
3785
|
-
const predecessors = /* @__PURE__ */ new Map();
|
|
3786
|
-
for (const group of groups) {
|
|
3787
|
-
for (let index = 1; index < group.logs.length; index += 1) {
|
|
3788
|
-
const current = group.logs[index];
|
|
3789
|
-
const previous = group.logs[index - 1];
|
|
3790
|
-
if (current === void 0 || previous === void 0) continue;
|
|
3791
|
-
const currentFormat = resolveLogFormat(current);
|
|
3792
|
-
const previousFormat = resolveLogFormat(previous);
|
|
3793
|
-
if (currentFormat === "unknown" || currentFormat !== previousFormat) continue;
|
|
3794
|
-
predecessors.set(current.id, previous);
|
|
3795
|
-
}
|
|
3796
|
-
}
|
|
3797
|
-
return predecessors;
|
|
3798
|
-
}
|
|
3799
3154
|
function computeStats(logs) {
|
|
3800
3155
|
let totalInput = 0;
|
|
3801
3156
|
let totalOutput = 0;
|
|
@@ -3809,6 +3164,7 @@ const ConversationGroup = reactExports.memo(function({
|
|
|
3809
3164
|
group,
|
|
3810
3165
|
viewMode = "simple",
|
|
3811
3166
|
strip,
|
|
3167
|
+
slowResponseThresholdSeconds,
|
|
3812
3168
|
cacheTrends,
|
|
3813
3169
|
onCompareWithPrevious,
|
|
3814
3170
|
comparisonPredecessors,
|
|
@@ -3842,12 +3198,13 @@ const ConversationGroup = reactExports.memo(function({
|
|
|
3842
3198
|
onClear: () => onClearGroup(group.logs.map((l) => l.id))
|
|
3843
3199
|
}
|
|
3844
3200
|
),
|
|
3845
|
-
shouldRenderConversationContent(standalone, expanded) && /* @__PURE__ */ jsxRuntimeExports.jsx("div", {
|
|
3201
|
+
shouldRenderConversationContent(standalone, expanded) && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: turnGroups.map((tg) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3846
3202
|
TurnGroup,
|
|
3847
3203
|
{
|
|
3848
3204
|
entries: tg.entries,
|
|
3849
3205
|
viewMode,
|
|
3850
3206
|
strip,
|
|
3207
|
+
slowResponseThresholdSeconds,
|
|
3851
3208
|
cacheTrends,
|
|
3852
3209
|
onCompareWithPrevious,
|
|
3853
3210
|
comparisonPredecessors,
|
|
@@ -4144,13 +3501,13 @@ function ImportWizardDialog({
|
|
|
4144
3501
|
scan();
|
|
4145
3502
|
}
|
|
4146
3503
|
}, [open, scan]);
|
|
4147
|
-
const toggleProvider = reactExports.useCallback((
|
|
3504
|
+
const toggleProvider = reactExports.useCallback((index2) => {
|
|
4148
3505
|
setSelected((prev) => {
|
|
4149
3506
|
const next = new Set(prev);
|
|
4150
|
-
if (next.has(
|
|
4151
|
-
next.delete(
|
|
3507
|
+
if (next.has(index2)) {
|
|
3508
|
+
next.delete(index2);
|
|
4152
3509
|
} else {
|
|
4153
|
-
next.add(
|
|
3510
|
+
next.add(index2);
|
|
4154
3511
|
}
|
|
4155
3512
|
return next;
|
|
4156
3513
|
});
|
|
@@ -4675,10 +4032,10 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
4675
4032
|
const modelRowRefs = reactExports.useRef([]);
|
|
4676
4033
|
reactExports.useEffect(() => {
|
|
4677
4034
|
if (openModelDropdown === null) return;
|
|
4678
|
-
const
|
|
4035
|
+
const index2 = openModelDropdown;
|
|
4679
4036
|
function handleClick(e) {
|
|
4680
4037
|
if (!(e.target instanceof Node)) return;
|
|
4681
|
-
const ref = modelRowRefs.current[
|
|
4038
|
+
const ref = modelRowRefs.current[index2];
|
|
4682
4039
|
if (ref !== null && ref !== void 0 && !ref.contains(e.target)) {
|
|
4683
4040
|
setOpenModelDropdown(null);
|
|
4684
4041
|
}
|
|
@@ -5620,968 +4977,290 @@ function SettingsDialog() {
|
|
|
5620
4977
|
isLoading,
|
|
5621
4978
|
externalTestResults: testResults,
|
|
5622
4979
|
externalTestingProviders: testingProviders,
|
|
5623
|
-
externalTestingTimeLeft: testingTimeLeft,
|
|
5624
|
-
onProvidersMutate: () => {
|
|
5625
|
-
return mutate();
|
|
5626
|
-
},
|
|
5627
|
-
onTestResultsChange: handleTestResultsChange,
|
|
5628
|
-
onTestingProvidersChange: handleTestingProvidersChange,
|
|
5629
|
-
onTestingTimeLeftChange: handleTestingTimeLeftChange
|
|
5630
|
-
}
|
|
5631
|
-
) }),
|
|
5632
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "proxy", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProxySettingsTab, {}) })
|
|
5633
|
-
] })
|
|
5634
|
-
] })
|
|
5635
|
-
] })
|
|
5636
|
-
] });
|
|
5637
|
-
}
|
|
5638
|
-
function ProxySettingsTab() {
|
|
5639
|
-
const { strip, isLoading, setStrip } = useStripConfig();
|
|
5640
|
-
const [error, setError] = reactExports.useState(null);
|
|
5641
|
-
const [pending, setPending] = reactExports.useState(false);
|
|
5642
|
-
const handleToggle = reactExports.useCallback(
|
|
5643
|
-
async (next) => {
|
|
5644
|
-
setError(null);
|
|
5645
|
-
setPending(true);
|
|
5646
|
-
try {
|
|
5647
|
-
await setStrip(next);
|
|
5648
|
-
} catch (err) {
|
|
5649
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
5650
|
-
} finally {
|
|
5651
|
-
setPending(false);
|
|
5652
|
-
}
|
|
5653
|
-
},
|
|
5654
|
-
[setStrip]
|
|
5655
|
-
);
|
|
5656
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
|
|
5657
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
|
|
5658
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-semibold", children: "Claude Code billing header" }),
|
|
5659
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
5660
|
-
"When enabled, the proxy strips the synthetic ",
|
|
5661
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "x-anthropic-billing-header" }),
|
|
5662
|
-
" text block that Claude Code prepends to the system prompt before forwarding to upstream. Improves prefix-cache hit rates against third-party upstreams. The default at startup is controlled by the ",
|
|
5663
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "LLM_INSPECTOR_STRIP_CLAUDE_CODE_BILLING_HEADER" }),
|
|
5664
|
-
" environment variable; your choice is persisted to ",
|
|
5665
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "config.json" }),
|
|
5666
|
-
" and overrides the env var for subsequent requests."
|
|
5667
|
-
] })
|
|
5668
|
-
] }),
|
|
5669
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-3", children: [
|
|
5670
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
5671
|
-
"input",
|
|
5672
|
-
{
|
|
5673
|
-
type: "checkbox",
|
|
5674
|
-
role: "switch",
|
|
5675
|
-
checked: strip,
|
|
5676
|
-
disabled: isLoading || pending,
|
|
5677
|
-
onChange: (e) => {
|
|
5678
|
-
void handleToggle(e.currentTarget.checked);
|
|
5679
|
-
},
|
|
5680
|
-
className: "size-4 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50"
|
|
5681
|
-
}
|
|
5682
|
-
),
|
|
5683
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm", children: isLoading ? "Loading…" : strip ? "Stripping enabled" : "Stripping disabled" })
|
|
5684
|
-
] }),
|
|
5685
|
-
error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-destructive", children: [
|
|
5686
|
-
"Failed to save: ",
|
|
5687
|
-
error
|
|
5688
|
-
] })
|
|
5689
|
-
] });
|
|
5690
|
-
}
|
|
5691
|
-
function computeCacheTrends(groups) {
|
|
5692
|
-
const result = /* @__PURE__ */ new Map();
|
|
5693
|
-
for (const group of groups) {
|
|
5694
|
-
const logs = group.logs;
|
|
5695
|
-
for (let i = 1; i < logs.length; i++) {
|
|
5696
|
-
const prev = logs[i - 1];
|
|
5697
|
-
const curr = logs[i];
|
|
5698
|
-
if (prev === void 0 || curr === void 0) continue;
|
|
5699
|
-
result.set(curr.id, {
|
|
5700
|
-
creation: compareField(prev.cacheCreationInputTokens, curr.cacheCreationInputTokens),
|
|
5701
|
-
read: compareField(prev.cacheReadInputTokens, curr.cacheReadInputTokens)
|
|
5702
|
-
});
|
|
5703
|
-
}
|
|
5704
|
-
}
|
|
5705
|
-
return result;
|
|
5706
|
-
}
|
|
5707
|
-
function compareField(previous, current) {
|
|
5708
|
-
if (current === null) return null;
|
|
5709
|
-
if (previous === null) return null;
|
|
5710
|
-
if (current > previous) return { direction: "up", delta: current - previous };
|
|
5711
|
-
if (current < previous) return { direction: "down", delta: previous - current };
|
|
5712
|
-
return null;
|
|
5713
|
-
}
|
|
5714
|
-
const ROOT_PATH = "";
|
|
5715
|
-
function formatPath(segments) {
|
|
5716
|
-
if (segments.length === 0) return ROOT_PATH;
|
|
5717
|
-
let out = "";
|
|
5718
|
-
for (let i = 0; i < segments.length; i++) {
|
|
5719
|
-
const seg = segments[i];
|
|
5720
|
-
if (seg === void 0) continue;
|
|
5721
|
-
if (typeof seg === "number") {
|
|
5722
|
-
out += `[${seg}]`;
|
|
5723
|
-
} else if (i === 0) {
|
|
5724
|
-
out += seg;
|
|
5725
|
-
} else {
|
|
5726
|
-
out += `.${seg}`;
|
|
5727
|
-
}
|
|
5728
|
-
}
|
|
5729
|
-
return out;
|
|
5730
|
-
}
|
|
5731
|
-
function isPlainObject(value) {
|
|
5732
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5733
|
-
}
|
|
5734
|
-
function normalizeRequest(raw) {
|
|
5735
|
-
if (typeof raw === "string") {
|
|
5736
|
-
try {
|
|
5737
|
-
return toNode(JSON.parse(raw));
|
|
5738
|
-
} catch {
|
|
5739
|
-
return { kind: "primitive", value: raw };
|
|
5740
|
-
}
|
|
5741
|
-
}
|
|
5742
|
-
return toNode(raw);
|
|
5743
|
-
}
|
|
5744
|
-
function toNode(value) {
|
|
5745
|
-
if (value === null) return { kind: "primitive", value: null };
|
|
5746
|
-
if (typeof value === "string") return { kind: "primitive", value };
|
|
5747
|
-
if (typeof value === "number") return { kind: "primitive", value };
|
|
5748
|
-
if (typeof value === "boolean") return { kind: "primitive", value };
|
|
5749
|
-
if (Array.isArray(value)) {
|
|
5750
|
-
return { kind: "array", value: value.map((v) => toNode(v)) };
|
|
5751
|
-
}
|
|
5752
|
-
if (isPlainObject(value)) {
|
|
5753
|
-
const out = {};
|
|
5754
|
-
for (const k of Object.keys(value).sort()) {
|
|
5755
|
-
out[k] = toNode(value[k]);
|
|
5756
|
-
}
|
|
5757
|
-
return { kind: "object", value: out };
|
|
5758
|
-
}
|
|
5759
|
-
return { kind: "primitive", value: null };
|
|
5760
|
-
}
|
|
5761
|
-
function diffTrees(left, right) {
|
|
5762
|
-
const ops = [];
|
|
5763
|
-
walk([], left, right, ops);
|
|
5764
|
-
return ops;
|
|
5765
|
-
}
|
|
5766
|
-
function walk(segments, left, right, out) {
|
|
5767
|
-
const path = formatPath(segments);
|
|
5768
|
-
if (nodeEqual(left, right)) {
|
|
5769
|
-
out.push({ kind: "equal", path, value: left });
|
|
5770
|
-
return;
|
|
5771
|
-
}
|
|
5772
|
-
if (left.kind !== right.kind) {
|
|
5773
|
-
out.push({ kind: "changed", path, left, right });
|
|
5774
|
-
return;
|
|
5775
|
-
}
|
|
5776
|
-
if (left.kind === "primitive" && right.kind === "primitive") {
|
|
5777
|
-
out.push({ kind: "changed", path, left, right });
|
|
5778
|
-
return;
|
|
5779
|
-
}
|
|
5780
|
-
if (left.kind === "object" && right.kind === "object") {
|
|
5781
|
-
const leftKeys = Object.keys(left.value);
|
|
5782
|
-
const rightKeys = Object.keys(right.value);
|
|
5783
|
-
const rightKeySet = new Set(rightKeys);
|
|
5784
|
-
for (const k of leftKeys) {
|
|
5785
|
-
const lChild = left.value[k];
|
|
5786
|
-
if (lChild === void 0) continue;
|
|
5787
|
-
if (!rightKeySet.has(k)) {
|
|
5788
|
-
out.push({
|
|
5789
|
-
kind: "removed",
|
|
5790
|
-
path: formatPath([...segments, k]),
|
|
5791
|
-
value: lChild
|
|
5792
|
-
});
|
|
5793
|
-
} else {
|
|
5794
|
-
const rChild = right.value[k];
|
|
5795
|
-
if (rChild === void 0) continue;
|
|
5796
|
-
walk([...segments, k], lChild, rChild, out);
|
|
5797
|
-
}
|
|
5798
|
-
}
|
|
5799
|
-
for (const k of rightKeys) {
|
|
5800
|
-
if (leftKeys.includes(k)) continue;
|
|
5801
|
-
const rChild = right.value[k];
|
|
5802
|
-
if (rChild === void 0) continue;
|
|
5803
|
-
out.push({
|
|
5804
|
-
kind: "added",
|
|
5805
|
-
path: formatPath([...segments, k]),
|
|
5806
|
-
value: rChild
|
|
5807
|
-
});
|
|
5808
|
-
}
|
|
5809
|
-
return;
|
|
5810
|
-
}
|
|
5811
|
-
if (left.kind === "array" && right.kind === "array") {
|
|
5812
|
-
const minLen = Math.min(left.value.length, right.value.length);
|
|
5813
|
-
for (let i = 0; i < minLen; i++) {
|
|
5814
|
-
const lChild = left.value[i];
|
|
5815
|
-
const rChild = right.value[i];
|
|
5816
|
-
if (lChild === void 0 || rChild === void 0) continue;
|
|
5817
|
-
walk([...segments, i], lChild, rChild, out);
|
|
5818
|
-
}
|
|
5819
|
-
for (let i = minLen; i < right.value.length; i++) {
|
|
5820
|
-
const rChild = right.value[i];
|
|
5821
|
-
if (rChild === void 0) continue;
|
|
5822
|
-
out.push({
|
|
5823
|
-
kind: "added",
|
|
5824
|
-
path: formatPath([...segments, i]),
|
|
5825
|
-
value: rChild
|
|
5826
|
-
});
|
|
5827
|
-
}
|
|
5828
|
-
for (let i = minLen; i < left.value.length; i++) {
|
|
5829
|
-
const lChild = left.value[i];
|
|
5830
|
-
if (lChild === void 0) continue;
|
|
5831
|
-
out.push({
|
|
5832
|
-
kind: "removed",
|
|
5833
|
-
path: formatPath([...segments, i]),
|
|
5834
|
-
value: lChild
|
|
5835
|
-
});
|
|
5836
|
-
}
|
|
5837
|
-
}
|
|
5838
|
-
}
|
|
5839
|
-
function nodeEqual(a, b) {
|
|
5840
|
-
if (a.kind !== b.kind) return false;
|
|
5841
|
-
if (a.kind === "primitive" && b.kind === "primitive") {
|
|
5842
|
-
return a.value === b.value;
|
|
5843
|
-
}
|
|
5844
|
-
if (a.kind === "array" && b.kind === "array") {
|
|
5845
|
-
if (a.value.length !== b.value.length) return false;
|
|
5846
|
-
for (let i = 0; i < a.value.length; i++) {
|
|
5847
|
-
const ai = a.value[i];
|
|
5848
|
-
const bi = b.value[i];
|
|
5849
|
-
if (ai === void 0 || bi === void 0) return false;
|
|
5850
|
-
if (!nodeEqual(ai, bi)) return false;
|
|
5851
|
-
}
|
|
5852
|
-
return true;
|
|
5853
|
-
}
|
|
5854
|
-
if (a.kind === "object" && b.kind === "object") {
|
|
5855
|
-
const aKeys = Object.keys(a.value);
|
|
5856
|
-
const bKeys = Object.keys(b.value);
|
|
5857
|
-
if (aKeys.length !== bKeys.length) return false;
|
|
5858
|
-
for (const k of aKeys) {
|
|
5859
|
-
const av = a.value[k];
|
|
5860
|
-
const bv = b.value[k];
|
|
5861
|
-
if (av === void 0 || bv === void 0) return false;
|
|
5862
|
-
if (!nodeEqual(av, bv)) return false;
|
|
5863
|
-
}
|
|
5864
|
-
return true;
|
|
5865
|
-
}
|
|
5866
|
-
return false;
|
|
5867
|
-
}
|
|
5868
|
-
function previewNode(node, maxLen = 80) {
|
|
5869
|
-
let s;
|
|
5870
|
-
switch (node.kind) {
|
|
5871
|
-
case "primitive":
|
|
5872
|
-
s = node.value === null ? "null" : JSON.stringify(node.value);
|
|
5873
|
-
break;
|
|
5874
|
-
case "array":
|
|
5875
|
-
s = `[… ${node.value.length} items]`;
|
|
5876
|
-
break;
|
|
5877
|
-
case "object":
|
|
5878
|
-
s = `{… ${Object.keys(node.value).length} keys}`;
|
|
5879
|
-
break;
|
|
5880
|
-
}
|
|
5881
|
-
if (s.length > maxLen) s = `${s.slice(0, maxLen - 1)}…`;
|
|
5882
|
-
return s;
|
|
5883
|
-
}
|
|
5884
|
-
function nodeToJsonString(node, indent = 2) {
|
|
5885
|
-
return JSON.stringify(nodeToJsonValue(node), null, indent);
|
|
5886
|
-
}
|
|
5887
|
-
function nodeToJsonValue(node) {
|
|
5888
|
-
switch (node.kind) {
|
|
5889
|
-
case "primitive":
|
|
5890
|
-
return node.value;
|
|
5891
|
-
case "array":
|
|
5892
|
-
return node.value.map(nodeToJsonValue);
|
|
5893
|
-
case "object": {
|
|
5894
|
-
const out = {};
|
|
5895
|
-
for (const [k, v] of Object.entries(node.value)) {
|
|
5896
|
-
out[k] = nodeToJsonValue(v);
|
|
5897
|
-
}
|
|
5898
|
-
return out;
|
|
5899
|
-
}
|
|
5900
|
-
}
|
|
5901
|
-
}
|
|
5902
|
-
function parentPath(path) {
|
|
5903
|
-
if (path === "") return "";
|
|
5904
|
-
for (let i = path.length - 1; i >= 0; i--) {
|
|
5905
|
-
const ch = path[i];
|
|
5906
|
-
if (ch === "." || ch === "[") {
|
|
5907
|
-
return path.substring(0, i);
|
|
5908
|
-
}
|
|
5909
|
-
}
|
|
5910
|
-
return "";
|
|
5911
|
-
}
|
|
5912
|
-
function isDeepEqual(op) {
|
|
5913
|
-
return op.kind === "equal" && (op.value.kind === "object" || op.value.kind === "array");
|
|
5914
|
-
}
|
|
5915
|
-
function groupContiguousEquals(ops) {
|
|
5916
|
-
const out = [];
|
|
5917
|
-
let i = 0;
|
|
5918
|
-
while (i < ops.length) {
|
|
5919
|
-
const op = ops[i];
|
|
5920
|
-
if (op !== void 0 && isDeepEqual(op)) {
|
|
5921
|
-
const startParent = parentPath(op.path);
|
|
5922
|
-
let j = i + 1;
|
|
5923
|
-
while (j < ops.length) {
|
|
5924
|
-
const next = ops[j];
|
|
5925
|
-
if (next === void 0) break;
|
|
5926
|
-
if (!isDeepEqual(next)) break;
|
|
5927
|
-
if (parentPath(next.path) !== startParent) break;
|
|
5928
|
-
j++;
|
|
5929
|
-
}
|
|
5930
|
-
if (j - i > 1) {
|
|
5931
|
-
const equalOps = [];
|
|
5932
|
-
for (let k = i; k < j; k++) {
|
|
5933
|
-
const eop = ops[k];
|
|
5934
|
-
if (eop !== void 0 && eop.kind === "equal") {
|
|
5935
|
-
equalOps.push(eop);
|
|
5936
|
-
}
|
|
5937
|
-
}
|
|
5938
|
-
out.push({ kind: "equal-run", ops: equalOps });
|
|
5939
|
-
i = j;
|
|
5940
|
-
continue;
|
|
5941
|
-
}
|
|
5942
|
-
}
|
|
5943
|
-
if (op !== void 0) {
|
|
5944
|
-
out.push({ kind: "single", op });
|
|
5945
|
-
}
|
|
5946
|
-
i++;
|
|
5947
|
-
}
|
|
5948
|
-
return out;
|
|
5949
|
-
}
|
|
5950
|
-
const KIND_VISUAL = {
|
|
5951
|
-
added: {
|
|
5952
|
-
icon: Plus,
|
|
5953
|
-
accent: "text-emerald-600 dark:text-emerald-400",
|
|
5954
|
-
bg: "bg-emerald-500/5 hover:bg-emerald-500/10",
|
|
5955
|
-
border: "border-l-emerald-500",
|
|
5956
|
-
label: "ADDED"
|
|
5957
|
-
},
|
|
5958
|
-
removed: {
|
|
5959
|
-
icon: Minus,
|
|
5960
|
-
accent: "text-rose-600 dark:text-rose-400",
|
|
5961
|
-
bg: "bg-rose-500/5 hover:bg-rose-500/10",
|
|
5962
|
-
border: "border-l-rose-500",
|
|
5963
|
-
label: "REMOVED"
|
|
5964
|
-
},
|
|
5965
|
-
changed: {
|
|
5966
|
-
icon: Pencil,
|
|
5967
|
-
accent: "text-amber-600 dark:text-amber-400",
|
|
5968
|
-
bg: "bg-amber-500/5 hover:bg-amber-500/10",
|
|
5969
|
-
border: "border-l-amber-500",
|
|
5970
|
-
label: "CHANGED"
|
|
5971
|
-
},
|
|
5972
|
-
equal: {
|
|
5973
|
-
icon: Equal,
|
|
5974
|
-
accent: "text-muted-foreground/70",
|
|
5975
|
-
bg: "bg-muted/20 hover:bg-muted/30",
|
|
5976
|
-
border: "border-l-muted-foreground/20",
|
|
5977
|
-
label: "EQUAL"
|
|
5978
|
-
}
|
|
5979
|
-
};
|
|
5980
|
-
function EqualRunRow({
|
|
5981
|
-
ops,
|
|
5982
|
-
expanded,
|
|
5983
|
-
onToggle
|
|
5984
|
-
}) {
|
|
5985
|
-
const first = ops[0];
|
|
5986
|
-
const last = ops[ops.length - 1];
|
|
5987
|
-
if (first === void 0 || last === void 0) {
|
|
5988
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground/40 text-xs", children: "—" });
|
|
5989
|
-
}
|
|
5990
|
-
const firstPath = first.path;
|
|
5991
|
-
const lastPath = last.path;
|
|
5992
|
-
const label = ops.length === 1 ? firstPath : `${firstPath} … ${lastPath}`;
|
|
5993
|
-
const summary = first.value.kind === "array" ? `${ops.length} equal arrays` : first.value.kind === "object" ? `${ops.length} equal objects` : "equal";
|
|
5994
|
-
const v = KIND_VISUAL.equal;
|
|
5995
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("border-l-4 rounded-sm", v.border, v.bg), children: [
|
|
5996
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
5997
|
-
"button",
|
|
5998
|
-
{
|
|
5999
|
-
type: "button",
|
|
6000
|
-
onClick: onToggle,
|
|
6001
|
-
className: "w-full text-left flex items-center gap-2 px-3 py-1.5 text-xs text-muted-foreground cursor-pointer",
|
|
6002
|
-
children: [
|
|
6003
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6004
|
-
ChevronRight,
|
|
6005
|
-
{
|
|
6006
|
-
className: cn("size-3 transition-transform shrink-0", expanded && "rotate-90")
|
|
6007
|
-
}
|
|
6008
|
-
),
|
|
6009
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(v.icon, { className: cn("size-3 shrink-0", v.accent) }),
|
|
6010
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono truncate flex-1", title: `${firstPath} … ${lastPath}`, children: label }),
|
|
6011
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("text-[10px] uppercase tracking-wider shrink-0", v.accent), children: v.label }),
|
|
6012
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground/60 shrink-0", children: [
|
|
6013
|
-
"(",
|
|
6014
|
-
summary,
|
|
6015
|
-
")"
|
|
6016
|
-
] })
|
|
6017
|
-
]
|
|
6018
|
-
}
|
|
6019
|
-
),
|
|
6020
|
-
expanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ml-5 mt-1 mb-2 space-y-2 pr-2", children: ops.map((op) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-border/50 rounded p-2 bg-muted/20", children: [
|
|
6021
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-mono text-xs text-muted-foreground mb-1", children: op.path }),
|
|
6022
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(JsonViewerFromString, { text: nodeToJsonString(op.value), defaultExpandDepth: 0 })
|
|
6023
|
-
] }, op.path)) })
|
|
6024
|
-
] });
|
|
6025
|
-
}
|
|
6026
|
-
function UnifiedOpRow({
|
|
6027
|
-
op,
|
|
6028
|
-
idx,
|
|
6029
|
-
copiedPath,
|
|
6030
|
-
onCopyPath,
|
|
6031
|
-
expanded,
|
|
6032
|
-
onToggle
|
|
6033
|
-
}) {
|
|
6034
|
-
const v = KIND_VISUAL[op.kind];
|
|
6035
|
-
const Icon2 = v.icon;
|
|
6036
|
-
const isExpandable2 = op.kind === "added" || op.kind === "removed" ? op.value.kind === "object" || op.value.kind === "array" : op.kind === "changed" ? op.left.kind === "object" || op.left.kind === "array" || op.right.kind === "object" || op.right.kind === "array" : false;
|
|
6037
|
-
const preview = op.kind === "changed" ? [
|
|
6038
|
-
{
|
|
6039
|
-
text: previewNode(op.left, 400),
|
|
6040
|
-
tone: "text-rose-700 dark:text-rose-300 line-through"
|
|
6041
|
-
},
|
|
6042
|
-
{ text: previewNode(op.right, 400), tone: "text-emerald-700 dark:text-emerald-300" }
|
|
6043
|
-
] : op.kind === "removed" ? [
|
|
6044
|
-
{
|
|
6045
|
-
text: previewNode(op.value, 400),
|
|
6046
|
-
tone: "text-rose-700 dark:text-rose-300 line-through"
|
|
6047
|
-
}
|
|
6048
|
-
] : op.kind === "added" ? [{ text: previewNode(op.value, 400), tone: "text-emerald-700 dark:text-emerald-300" }] : [{ text: previewNode(op.value, 400), tone: "text-muted-foreground" }];
|
|
6049
|
-
const justCopied = copiedPath === op.path && op.path !== "";
|
|
6050
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
6051
|
-
"div",
|
|
6052
|
-
{
|
|
6053
|
-
"data-diff-idx": idx,
|
|
6054
|
-
"data-diff-kind": op.kind,
|
|
6055
|
-
className: cn("border-l-4 rounded-sm px-3 py-2 my-0.5 transition-colors", v.border, v.bg),
|
|
6056
|
-
children: [
|
|
6057
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
6058
|
-
"button",
|
|
6059
|
-
{
|
|
6060
|
-
type: "button",
|
|
6061
|
-
onClick: onToggle,
|
|
6062
|
-
disabled: !isExpandable2,
|
|
6063
|
-
className: cn(
|
|
6064
|
-
"w-full flex items-center gap-2 text-xs text-left rounded-sm",
|
|
6065
|
-
isExpandable2 ? "cursor-pointer" : "cursor-default"
|
|
6066
|
-
),
|
|
6067
|
-
"aria-expanded": isExpandable2 ? expanded : void 0,
|
|
6068
|
-
"aria-label": isExpandable2 ? expanded ? `Collapse ${op.path || "root"}` : `Expand ${op.path || "root"}` : void 0,
|
|
6069
|
-
children: [
|
|
6070
|
-
isExpandable2 ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6071
|
-
ChevronRight,
|
|
6072
|
-
{
|
|
6073
|
-
className: cn(
|
|
6074
|
-
"size-3 shrink-0 transition-transform",
|
|
6075
|
-
v.accent,
|
|
6076
|
-
expanded && "rotate-90"
|
|
6077
|
-
)
|
|
6078
|
-
}
|
|
6079
|
-
) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "size-3 shrink-0", "aria-hidden": "true" }),
|
|
6080
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Icon2, { className: cn("size-3.5 shrink-0", v.accent), strokeWidth: 2.5 }),
|
|
6081
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono truncate flex-1 min-w-0", title: op.path || "(root)", children: op.path === "" ? "(root)" : op.path }),
|
|
6082
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6083
|
-
"span",
|
|
6084
|
-
{
|
|
6085
|
-
className: cn(
|
|
6086
|
-
"text-[9px] font-bold uppercase tracking-wider shrink-0 px-1.5 py-0.5 rounded",
|
|
6087
|
-
v.accent,
|
|
6088
|
-
op.kind === "equal" ? "bg-muted/40" : "bg-background/60"
|
|
6089
|
-
),
|
|
6090
|
-
children: v.label
|
|
6091
|
-
}
|
|
6092
|
-
),
|
|
6093
|
-
op.path !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6094
|
-
"span",
|
|
6095
|
-
{
|
|
6096
|
-
role: "button",
|
|
6097
|
-
tabIndex: 0,
|
|
6098
|
-
onClick: (e) => {
|
|
6099
|
-
e.stopPropagation();
|
|
6100
|
-
onCopyPath(op.path);
|
|
6101
|
-
},
|
|
6102
|
-
onKeyDown: (e) => {
|
|
6103
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
6104
|
-
e.stopPropagation();
|
|
6105
|
-
e.preventDefault();
|
|
6106
|
-
onCopyPath(op.path);
|
|
6107
|
-
}
|
|
6108
|
-
},
|
|
6109
|
-
className: cn(
|
|
6110
|
-
"shrink-0 p-1 rounded transition-colors cursor-pointer inline-flex items-center justify-center",
|
|
6111
|
-
justCopied ? "text-emerald-500" : "text-muted-foreground/50 hover:text-foreground hover:bg-muted"
|
|
6112
|
-
),
|
|
6113
|
-
"aria-label": justCopied ? "Copied" : "Copy",
|
|
6114
|
-
title: justCopied ? "Copied!" : "Copy",
|
|
6115
|
-
children: justCopied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3" })
|
|
6116
|
-
}
|
|
6117
|
-
)
|
|
6118
|
-
]
|
|
6119
|
-
}
|
|
6120
|
-
),
|
|
6121
|
-
preview.map((p, i) => (
|
|
6122
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: preview list is rebuilt on every render and is positional
|
|
6123
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("font-mono text-xs mt-1 break-all pl-5", p.tone), children: p.text }, i)
|
|
6124
|
-
)),
|
|
6125
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6126
|
-
"div",
|
|
6127
|
-
{
|
|
6128
|
-
className: "overflow-hidden transition-all duration-200",
|
|
6129
|
-
style: { maxHeight: expanded && isExpandable2 ? "2000px" : "0" },
|
|
6130
|
-
"aria-hidden": !expanded,
|
|
6131
|
-
children: expanded && isExpandable2 && op.kind !== "equal" ? /* @__PURE__ */ jsxRuntimeExports.jsx(ExpandedSubtree, { op }) : null
|
|
6132
|
-
}
|
|
6133
|
-
)
|
|
6134
|
-
]
|
|
6135
|
-
}
|
|
6136
|
-
);
|
|
6137
|
-
}
|
|
6138
|
-
function ExpandedSubtree({ op }) {
|
|
6139
|
-
if (op.kind === "added" || op.kind === "removed") {
|
|
6140
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pl-5 mt-2 border border-border/50 rounded p-2 bg-muted/20", children: /* @__PURE__ */ jsxRuntimeExports.jsx(JsonViewerFromString, { text: nodeToJsonString(op.value), defaultExpandDepth: 0 }) });
|
|
6141
|
-
}
|
|
6142
|
-
const leftIsStructured = op.left.kind === "object" || op.left.kind === "array";
|
|
6143
|
-
const rightIsStructured = op.right.kind === "object" || op.right.kind === "array";
|
|
6144
|
-
if (!leftIsStructured && !rightIsStructured) {
|
|
6145
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pl-5 mt-2 text-xs text-muted-foreground/70 italic", children: "Primitive values are shown inline above." });
|
|
6146
|
-
}
|
|
6147
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pl-5 mt-2 grid grid-cols-1 md:grid-cols-2 gap-2", children: [
|
|
6148
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-rose-500/30 rounded p-2 bg-rose-500/5", children: [
|
|
6149
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase tracking-wider text-rose-500 mb-1", children: "Old" }),
|
|
6150
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(JsonViewerFromString, { text: nodeToJsonString(op.left), defaultExpandDepth: 0 })
|
|
6151
|
-
] }),
|
|
6152
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-emerald-500/30 rounded p-2 bg-emerald-500/5", children: [
|
|
6153
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase tracking-wider text-emerald-500 mb-1", children: "New" }),
|
|
6154
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(JsonViewerFromString, { text: nodeToJsonString(op.right), defaultExpandDepth: 0 })
|
|
6155
|
-
] })
|
|
6156
|
-
] });
|
|
6157
|
-
}
|
|
6158
|
-
function SummaryChips({
|
|
6159
|
-
counts,
|
|
6160
|
-
onJumpTo
|
|
6161
|
-
}) {
|
|
6162
|
-
const total = counts.added + counts.removed + counts.changed;
|
|
6163
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-2 border-b border-border bg-muted/20 flex items-center gap-2 text-xs flex-wrap", children: [
|
|
6164
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground font-medium", children: [
|
|
6165
|
-
total,
|
|
6166
|
-
" ",
|
|
6167
|
-
total === 1 ? "change" : "changes"
|
|
6168
|
-
] }),
|
|
6169
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
6170
|
-
"button",
|
|
6171
|
-
{
|
|
6172
|
-
type: "button",
|
|
6173
|
-
onClick: () => onJumpTo("removed"),
|
|
6174
|
-
disabled: counts.removed === 0,
|
|
6175
|
-
className: cn(
|
|
6176
|
-
"inline-flex items-center gap-1 px-2 py-0.5 rounded-full border cursor-pointer transition-colors",
|
|
6177
|
-
counts.removed > 0 ? "border-rose-500/40 text-rose-600 dark:text-rose-400 bg-rose-500/10 hover:bg-rose-500/20" : "border-border text-muted-foreground/40 cursor-not-allowed"
|
|
6178
|
-
),
|
|
6179
|
-
title: counts.removed > 0 ? "Jump to first removed" : "No removals",
|
|
6180
|
-
children: [
|
|
6181
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Minus, { className: "size-3" }),
|
|
6182
|
-
counts.removed,
|
|
6183
|
-
" removed"
|
|
6184
|
-
]
|
|
6185
|
-
}
|
|
6186
|
-
),
|
|
6187
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
6188
|
-
"button",
|
|
6189
|
-
{
|
|
6190
|
-
type: "button",
|
|
6191
|
-
onClick: () => onJumpTo("added"),
|
|
6192
|
-
disabled: counts.added === 0,
|
|
6193
|
-
className: cn(
|
|
6194
|
-
"inline-flex items-center gap-1 px-2 py-0.5 rounded-full border cursor-pointer transition-colors",
|
|
6195
|
-
counts.added > 0 ? "border-emerald-500/40 text-emerald-600 dark:text-emerald-400 bg-emerald-500/10 hover:bg-emerald-500/20" : "border-border text-muted-foreground/40 cursor-not-allowed"
|
|
6196
|
-
),
|
|
6197
|
-
title: counts.added > 0 ? "Jump to first added" : "No additions",
|
|
6198
|
-
children: [
|
|
6199
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-3" }),
|
|
6200
|
-
counts.added,
|
|
6201
|
-
" added"
|
|
6202
|
-
]
|
|
6203
|
-
}
|
|
6204
|
-
),
|
|
6205
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
6206
|
-
"button",
|
|
6207
|
-
{
|
|
6208
|
-
type: "button",
|
|
6209
|
-
onClick: () => onJumpTo("changed"),
|
|
6210
|
-
disabled: counts.changed === 0,
|
|
6211
|
-
className: cn(
|
|
6212
|
-
"inline-flex items-center gap-1 px-2 py-0.5 rounded-full border cursor-pointer transition-colors",
|
|
6213
|
-
counts.changed > 0 ? "border-amber-500/40 text-amber-600 dark:text-amber-400 bg-amber-500/10 hover:bg-amber-500/20" : "border-border text-muted-foreground/40 cursor-not-allowed"
|
|
6214
|
-
),
|
|
6215
|
-
title: counts.changed > 0 ? "Jump to first changed" : "No changes",
|
|
6216
|
-
children: [
|
|
6217
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-3" }),
|
|
6218
|
-
counts.changed,
|
|
6219
|
-
" changed"
|
|
6220
|
-
]
|
|
6221
|
-
}
|
|
6222
|
-
)
|
|
6223
|
-
] });
|
|
6224
|
-
}
|
|
6225
|
-
function ModeToggle({
|
|
6226
|
-
mode,
|
|
6227
|
-
onChange
|
|
6228
|
-
}) {
|
|
6229
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "inline-flex rounded-md border border-border overflow-hidden", children: [
|
|
6230
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
6231
|
-
"button",
|
|
6232
|
-
{
|
|
6233
|
-
type: "button",
|
|
6234
|
-
onClick: () => onChange("unified"),
|
|
6235
|
-
"aria-pressed": mode === "unified",
|
|
6236
|
-
className: cn(
|
|
6237
|
-
"flex items-center gap-1 px-2 py-1 text-xs transition-colors cursor-pointer",
|
|
6238
|
-
mode === "unified" ? "bg-muted text-foreground" : "hover:bg-muted/50 text-muted-foreground"
|
|
6239
|
-
),
|
|
6240
|
-
title: "Unified view (single column, emphasized diffs)",
|
|
6241
|
-
children: [
|
|
6242
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Rows3, { className: "size-3" }),
|
|
6243
|
-
"Unified"
|
|
6244
|
-
]
|
|
6245
|
-
}
|
|
6246
|
-
),
|
|
6247
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
6248
|
-
"button",
|
|
6249
|
-
{
|
|
6250
|
-
type: "button",
|
|
6251
|
-
onClick: () => onChange("split"),
|
|
6252
|
-
"aria-pressed": mode === "split",
|
|
6253
|
-
className: cn(
|
|
6254
|
-
"flex items-center gap-1 px-2 py-1 text-xs transition-colors border-l border-border cursor-pointer",
|
|
6255
|
-
mode === "split" ? "bg-muted text-foreground" : "hover:bg-muted/50 text-muted-foreground"
|
|
6256
|
-
),
|
|
6257
|
-
title: "Split view (path | left | right)",
|
|
6258
|
-
children: [
|
|
6259
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Columns2, { className: "size-3" }),
|
|
6260
|
-
"Split"
|
|
6261
|
-
]
|
|
6262
|
-
}
|
|
6263
|
-
)
|
|
4980
|
+
externalTestingTimeLeft: testingTimeLeft,
|
|
4981
|
+
onProvidersMutate: () => {
|
|
4982
|
+
return mutate();
|
|
4983
|
+
},
|
|
4984
|
+
onTestResultsChange: handleTestResultsChange,
|
|
4985
|
+
onTestingProvidersChange: handleTestingProvidersChange,
|
|
4986
|
+
onTestingTimeLeftChange: handleTestingTimeLeftChange
|
|
4987
|
+
}
|
|
4988
|
+
) }),
|
|
4989
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "proxy", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProxySettingsTab, {}) })
|
|
4990
|
+
] })
|
|
4991
|
+
] })
|
|
4992
|
+
] })
|
|
6264
4993
|
] });
|
|
6265
4994
|
}
|
|
6266
|
-
function
|
|
6267
|
-
const
|
|
6268
|
-
|
|
6269
|
-
|
|
4995
|
+
function ProxySettingsTab() {
|
|
4996
|
+
const {
|
|
4997
|
+
strip,
|
|
4998
|
+
slowResponseThresholdSeconds,
|
|
4999
|
+
isLoading,
|
|
5000
|
+
setStrip,
|
|
5001
|
+
setSlowResponseThresholdSeconds
|
|
5002
|
+
} = useStripConfig();
|
|
5003
|
+
const [error, setError] = reactExports.useState(null);
|
|
5004
|
+
const [pending, setPending] = reactExports.useState(false);
|
|
5005
|
+
const handleToggle = reactExports.useCallback(
|
|
5006
|
+
async (next) => {
|
|
5007
|
+
setError(null);
|
|
5008
|
+
setPending(true);
|
|
5009
|
+
try {
|
|
5010
|
+
await setStrip(next);
|
|
5011
|
+
} catch (err) {
|
|
5012
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
5013
|
+
} finally {
|
|
5014
|
+
setPending(false);
|
|
5015
|
+
}
|
|
5016
|
+
},
|
|
5017
|
+
[setStrip]
|
|
5018
|
+
);
|
|
5019
|
+
const handleThresholdChange = reactExports.useCallback(
|
|
5020
|
+
async (next) => {
|
|
5021
|
+
setError(null);
|
|
5022
|
+
setPending(true);
|
|
5023
|
+
try {
|
|
5024
|
+
await setSlowResponseThresholdSeconds(next);
|
|
5025
|
+
} catch (err) {
|
|
5026
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
5027
|
+
} finally {
|
|
5028
|
+
setPending(false);
|
|
5029
|
+
}
|
|
5030
|
+
},
|
|
5031
|
+
[setSlowResponseThresholdSeconds]
|
|
5032
|
+
);
|
|
5033
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
|
|
5034
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
|
|
5035
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-semibold", children: "Claude Code billing header" }),
|
|
5036
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
5037
|
+
"When enabled, the proxy strips the synthetic ",
|
|
5038
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "x-anthropic-billing-header" }),
|
|
5039
|
+
" text block that Claude Code prepends to the system prompt before forwarding to upstream. Improves prefix-cache hit rates against third-party upstreams. The default at startup is controlled by the ",
|
|
5040
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "LLM_INSPECTOR_STRIP_CLAUDE_CODE_BILLING_HEADER" }),
|
|
5041
|
+
" environment variable; your choice is persisted to ",
|
|
5042
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "config.json" }),
|
|
5043
|
+
" and overrides the env var for subsequent requests."
|
|
5044
|
+
] })
|
|
5045
|
+
] }),
|
|
5046
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-3", children: [
|
|
6270
5047
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6271
|
-
|
|
5048
|
+
"input",
|
|
6272
5049
|
{
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
)
|
|
6278
|
-
|
|
5050
|
+
type: "checkbox",
|
|
5051
|
+
role: "switch",
|
|
5052
|
+
checked: strip,
|
|
5053
|
+
disabled: isLoading || pending,
|
|
5054
|
+
onChange: (e) => {
|
|
5055
|
+
void handleToggle(e.currentTarget.checked);
|
|
5056
|
+
},
|
|
5057
|
+
className: "size-4 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50"
|
|
6279
5058
|
}
|
|
6280
5059
|
),
|
|
6281
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
6282
|
-
"#",
|
|
6283
|
-
log.id
|
|
6284
|
-
] }),
|
|
6285
|
-
log.model !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-muted-foreground truncate", children: log.model })
|
|
5060
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm", children: isLoading ? "Loading…" : strip ? "Stripping enabled" : "Stripping disabled" })
|
|
6286
5061
|
] }),
|
|
6287
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
"Cache ~",
|
|
6294
|
-
formatTokens(log.cacheReadInputTokens)
|
|
5062
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
|
|
5063
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "slow-response-threshold", className: "text-sm font-semibold", children: "Slow response threshold" }),
|
|
5064
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
5065
|
+
"Logs whose elapsed time exceeds this many seconds show a warning icon next to the duration. Set to ",
|
|
5066
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "0" }),
|
|
5067
|
+
" to disable the indicator."
|
|
6295
5068
|
] }),
|
|
6296
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
5069
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5070
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
5071
|
+
"input",
|
|
5072
|
+
{
|
|
5073
|
+
id: "slow-response-threshold",
|
|
5074
|
+
type: "number",
|
|
5075
|
+
min: 0,
|
|
5076
|
+
max: MAX_SLOW_RESPONSE_THRESHOLD_SECONDS,
|
|
5077
|
+
step: 1,
|
|
5078
|
+
value: slowResponseThresholdSeconds,
|
|
5079
|
+
disabled: isLoading || pending,
|
|
5080
|
+
onChange: (e) => {
|
|
5081
|
+
const next = Number(e.currentTarget.value);
|
|
5082
|
+
if (!Number.isInteger(next)) return;
|
|
5083
|
+
if (next < 0 || next > MAX_SLOW_RESPONSE_THRESHOLD_SECONDS) return;
|
|
5084
|
+
void handleThresholdChange(next);
|
|
5085
|
+
},
|
|
5086
|
+
className: "h-8 w-24 rounded-md border border-input bg-transparent px-2 text-sm font-mono disabled:cursor-not-allowed disabled:opacity-50"
|
|
5087
|
+
}
|
|
5088
|
+
),
|
|
5089
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground", children: "seconds" })
|
|
5090
|
+
] })
|
|
6297
5091
|
] }),
|
|
6298
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("
|
|
6299
|
-
"
|
|
6300
|
-
|
|
5092
|
+
error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-destructive", children: [
|
|
5093
|
+
"Failed to save: ",
|
|
5094
|
+
error
|
|
6301
5095
|
] })
|
|
6302
5096
|
] });
|
|
6303
5097
|
}
|
|
6304
|
-
function
|
|
6305
|
-
const
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
)
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
left.apiFormat,
|
|
6317
|
-
left.path,
|
|
6318
|
-
left.rawRequestBody,
|
|
6319
|
-
right.apiFormat,
|
|
6320
|
-
right.path,
|
|
6321
|
-
right.rawRequestBody
|
|
6322
|
-
]);
|
|
6323
|
-
const grouped = reactExports.useMemo(() => groupContiguousEquals(ops), [ops]);
|
|
6324
|
-
const counts = reactExports.useMemo(() => {
|
|
6325
|
-
let added = 0;
|
|
6326
|
-
let removed = 0;
|
|
6327
|
-
let changed = 0;
|
|
6328
|
-
for (const g of grouped) {
|
|
6329
|
-
if (g.kind !== "single") continue;
|
|
6330
|
-
switch (g.op.kind) {
|
|
6331
|
-
case "added":
|
|
6332
|
-
added++;
|
|
6333
|
-
break;
|
|
6334
|
-
case "removed":
|
|
6335
|
-
removed++;
|
|
6336
|
-
break;
|
|
6337
|
-
case "changed":
|
|
6338
|
-
changed++;
|
|
6339
|
-
break;
|
|
6340
|
-
}
|
|
6341
|
-
}
|
|
6342
|
-
return { added, removed, changed };
|
|
6343
|
-
}, [grouped]);
|
|
6344
|
-
const [expandedRuns, setExpandedRuns] = reactExports.useState(/* @__PURE__ */ new Set());
|
|
6345
|
-
const toggleRun = (idx) => {
|
|
6346
|
-
setExpandedRuns((prev) => {
|
|
6347
|
-
const next = new Set(prev);
|
|
6348
|
-
if (next.has(idx)) next.delete(idx);
|
|
6349
|
-
else next.add(idx);
|
|
6350
|
-
return next;
|
|
6351
|
-
});
|
|
6352
|
-
};
|
|
6353
|
-
const [expandedRows, setExpandedRows] = reactExports.useState(/* @__PURE__ */ new Set());
|
|
6354
|
-
const toggleRow = (idx) => {
|
|
6355
|
-
setExpandedRows((prev) => {
|
|
6356
|
-
const next = new Set(prev);
|
|
6357
|
-
if (next.has(idx)) next.delete(idx);
|
|
6358
|
-
else next.add(idx);
|
|
6359
|
-
return next;
|
|
6360
|
-
});
|
|
6361
|
-
};
|
|
6362
|
-
reactExports.useEffect(() => {
|
|
6363
|
-
setExpandedRows(/* @__PURE__ */ new Set());
|
|
6364
|
-
}, [left.id, right.id]);
|
|
6365
|
-
const [mode, setMode] = reactExports.useState("unified");
|
|
6366
|
-
const bodyRef = reactExports.useRef(null);
|
|
6367
|
-
const [copiedPath, setCopiedPath] = reactExports.useState(null);
|
|
6368
|
-
const copyResetTimer = reactExports.useRef(null);
|
|
6369
|
-
const onCopyPath = (path) => {
|
|
6370
|
-
void window.navigator.clipboard.writeText(path).then(() => {
|
|
6371
|
-
setCopiedPath(path);
|
|
6372
|
-
if (copyResetTimer.current !== null) clearTimeout(copyResetTimer.current);
|
|
6373
|
-
copyResetTimer.current = setTimeout(() => setCopiedPath(null), 1500);
|
|
6374
|
-
});
|
|
6375
|
-
};
|
|
6376
|
-
reactExports.useEffect(() => {
|
|
6377
|
-
return () => {
|
|
6378
|
-
if (copyResetTimer.current !== null) clearTimeout(copyResetTimer.current);
|
|
6379
|
-
};
|
|
6380
|
-
}, []);
|
|
6381
|
-
const jumpToKind = (kind) => {
|
|
6382
|
-
const idx = grouped.findIndex((g) => g.kind === "single" && g.op.kind === kind);
|
|
6383
|
-
if (idx === -1) return;
|
|
6384
|
-
const root = bodyRef.current;
|
|
6385
|
-
if (root === null) return;
|
|
6386
|
-
const el = root.querySelector(`[data-diff-idx="${idx}"]`);
|
|
6387
|
-
if (el !== null) {
|
|
6388
|
-
el.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
5098
|
+
function computeCacheTrends(groups) {
|
|
5099
|
+
const result = /* @__PURE__ */ new Map();
|
|
5100
|
+
for (const group of groups) {
|
|
5101
|
+
const logs = group.logs;
|
|
5102
|
+
for (let i = 1; i < logs.length; i++) {
|
|
5103
|
+
const prev = logs[i - 1];
|
|
5104
|
+
const curr = logs[i];
|
|
5105
|
+
if (prev === void 0 || curr === void 0) continue;
|
|
5106
|
+
result.set(curr.id, {
|
|
5107
|
+
creation: compareField(prev.cacheCreationInputTokens, curr.cacheCreationInputTokens),
|
|
5108
|
+
read: compareField(prev.cacheReadInputTokens, curr.cacheReadInputTokens)
|
|
5109
|
+
});
|
|
6389
5110
|
}
|
|
6390
|
-
}
|
|
5111
|
+
}
|
|
5112
|
+
return result;
|
|
5113
|
+
}
|
|
5114
|
+
function compareField(previous, current) {
|
|
5115
|
+
if (current === null) return null;
|
|
5116
|
+
if (previous === null) return null;
|
|
5117
|
+
if (current > previous) return { direction: "up", delta: current - previous };
|
|
5118
|
+
if (current < previous) return { direction: "down", delta: previous - current };
|
|
5119
|
+
return null;
|
|
5120
|
+
}
|
|
5121
|
+
const NAV_ATTR = "data-nav-id";
|
|
5122
|
+
const NAV_ACTION_ATTR = "data-nav-action";
|
|
5123
|
+
function findNavItems(container) {
|
|
5124
|
+
return Array.from(container.querySelectorAll(`[${NAV_ATTR}]`));
|
|
5125
|
+
}
|
|
5126
|
+
function isFocusTarget(node) {
|
|
5127
|
+
return node instanceof HTMLElement;
|
|
5128
|
+
}
|
|
5129
|
+
function findClosestNavItem(target, container) {
|
|
5130
|
+
let el = target;
|
|
5131
|
+
while (el !== null && el !== container) {
|
|
5132
|
+
if (el.hasAttribute(NAV_ATTR)) return el;
|
|
5133
|
+
el = el.parentElement;
|
|
5134
|
+
}
|
|
5135
|
+
if (target.hasAttribute(NAV_ATTR)) return target;
|
|
5136
|
+
return null;
|
|
5137
|
+
}
|
|
5138
|
+
function getAction(el) {
|
|
5139
|
+
const val = el.getAttribute(NAV_ACTION_ATTR);
|
|
5140
|
+
if (val === "toggle" || val === "expand" || val === "collapse") return val;
|
|
5141
|
+
return null;
|
|
5142
|
+
}
|
|
5143
|
+
function focusAndScroll(el) {
|
|
5144
|
+
el.focus({ preventScroll: true });
|
|
5145
|
+
el.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
5146
|
+
}
|
|
5147
|
+
function safeItemAt(items, index2) {
|
|
5148
|
+
const el = items[index2];
|
|
5149
|
+
return el ?? null;
|
|
5150
|
+
}
|
|
5151
|
+
function isEditableTarget(target) {
|
|
5152
|
+
const tag = target.tagName;
|
|
5153
|
+
if (tag === "INPUT" || tag === "TEXTAREA") return true;
|
|
5154
|
+
if (target.isContentEditable) return true;
|
|
5155
|
+
return false;
|
|
5156
|
+
}
|
|
5157
|
+
function useKeyboardNavigation(containerRef, wrapperRef) {
|
|
5158
|
+
const rootRef = wrapperRef ?? containerRef;
|
|
5159
|
+
const handleFocusContainer = reactExports.useCallback(
|
|
5160
|
+
(e) => {
|
|
5161
|
+
const container = containerRef.current;
|
|
5162
|
+
if (!container) return;
|
|
5163
|
+
const target = e.target;
|
|
5164
|
+
if (!isFocusTarget(target)) return;
|
|
5165
|
+
if (target !== rootRef.current) return;
|
|
5166
|
+
if (!container.contains(target)) return;
|
|
5167
|
+
const items = findNavItems(container);
|
|
5168
|
+
const first = safeItemAt(items, 0);
|
|
5169
|
+
if (first !== null) focusAndScroll(first);
|
|
5170
|
+
},
|
|
5171
|
+
[containerRef, rootRef]
|
|
5172
|
+
);
|
|
6391
5173
|
reactExports.useEffect(() => {
|
|
6392
|
-
const
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
"button",
|
|
6415
|
-
{
|
|
6416
|
-
type: "button",
|
|
6417
|
-
onClick: onClose,
|
|
6418
|
-
"aria-label": "Close compare drawer",
|
|
6419
|
-
className: "absolute inset-0 bg-black/40 cursor-default",
|
|
6420
|
-
tabIndex: -1
|
|
5174
|
+
const root = rootRef.current;
|
|
5175
|
+
const container = containerRef.current;
|
|
5176
|
+
if (!root || !container) return;
|
|
5177
|
+
const handleKeyDown = (e) => {
|
|
5178
|
+
const target = e.target;
|
|
5179
|
+
if (!isFocusTarget(target)) return;
|
|
5180
|
+
if (!container.contains(target)) return;
|
|
5181
|
+
if (isEditableTarget(target)) return;
|
|
5182
|
+
const items = findNavItems(container);
|
|
5183
|
+
if (items.length === 0) return;
|
|
5184
|
+
const current = findClosestNavItem(target, container);
|
|
5185
|
+
const currentIdx = current !== null ? items.indexOf(current) : -1;
|
|
5186
|
+
let handled = false;
|
|
5187
|
+
switch (e.key) {
|
|
5188
|
+
case "ArrowUp":
|
|
5189
|
+
case "W": {
|
|
5190
|
+
if (e.key === "W" && !e.shiftKey) break;
|
|
5191
|
+
e.preventDefault();
|
|
5192
|
+
const prevIdx = currentIdx > 0 ? currentIdx - 1 : currentIdx === -1 ? items.length - 1 : -1;
|
|
5193
|
+
if (prevIdx !== -1) {
|
|
5194
|
+
const prev = safeItemAt(items, prevIdx);
|
|
5195
|
+
if (prev !== null) focusAndScroll(prev);
|
|
6421
5196
|
}
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-4 px-4 py-3 border-b border-border", children: [
|
|
6434
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex gap-4 min-w-0", children: [
|
|
6435
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(SideSummary, { log: left, side: "left" }),
|
|
6436
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(SideSummary, { log: right, side: "right" })
|
|
6437
|
-
] }),
|
|
6438
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
6439
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(ModeToggle, { mode, onChange: setMode }),
|
|
6440
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6441
|
-
"button",
|
|
6442
|
-
{
|
|
6443
|
-
type: "button",
|
|
6444
|
-
onClick: onClose,
|
|
6445
|
-
"aria-label": "Close",
|
|
6446
|
-
className: "p-1 rounded text-muted-foreground hover:text-foreground hover:bg-muted cursor-pointer",
|
|
6447
|
-
children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-4" })
|
|
6448
|
-
}
|
|
6449
|
-
)
|
|
6450
|
-
] })
|
|
6451
|
-
] }),
|
|
6452
|
-
!sameSession && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 py-1.5 text-xs text-amber-400 bg-amber-500/10 border-b border-border", children: "Heads up: the two selected logs are from different sessions." }),
|
|
6453
|
-
allEqual ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 overflow-y-auto flex items-center justify-center text-muted-foreground text-sm", children: "The two Request payloads are identical." }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
6454
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(SummaryChips, { counts, onJumpTo: jumpToKind }),
|
|
6455
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: bodyRef, className: "flex-1 min-h-0 overflow-y-auto", children: mode === "unified" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 py-2 space-y-0.5", children: grouped.map((g, i) => {
|
|
6456
|
-
if (g.kind === "equal-run") {
|
|
6457
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6458
|
-
EqualRunRow,
|
|
6459
|
-
{
|
|
6460
|
-
ops: g.ops,
|
|
6461
|
-
expanded: expandedRuns.has(i),
|
|
6462
|
-
onToggle: () => toggleRun(i)
|
|
6463
|
-
},
|
|
6464
|
-
`r${i}`
|
|
6465
|
-
);
|
|
6466
|
-
}
|
|
6467
|
-
const op = g.op;
|
|
6468
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6469
|
-
UnifiedOpRow,
|
|
6470
|
-
{
|
|
6471
|
-
op,
|
|
6472
|
-
idx: i,
|
|
6473
|
-
copiedPath,
|
|
6474
|
-
onCopyPath,
|
|
6475
|
-
expanded: expandedRows.has(i),
|
|
6476
|
-
onToggle: () => toggleRow(i)
|
|
6477
|
-
},
|
|
6478
|
-
`o${i}`
|
|
6479
|
-
);
|
|
6480
|
-
}) }) : /* @__PURE__ */ jsxRuntimeExports.jsx(SplitBody, { grouped, left, right }) })
|
|
6481
|
-
] })
|
|
6482
|
-
]
|
|
5197
|
+
handled = true;
|
|
5198
|
+
break;
|
|
5199
|
+
}
|
|
5200
|
+
case "ArrowDown":
|
|
5201
|
+
case "S": {
|
|
5202
|
+
if (e.key === "S" && !e.shiftKey) break;
|
|
5203
|
+
e.preventDefault();
|
|
5204
|
+
const nextIdx = currentIdx < items.length - 1 ? currentIdx + 1 : currentIdx === -1 ? 0 : -1;
|
|
5205
|
+
if (nextIdx !== -1) {
|
|
5206
|
+
const next = safeItemAt(items, nextIdx);
|
|
5207
|
+
if (next !== null) focusAndScroll(next);
|
|
6483
5208
|
}
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
"
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
"div",
|
|
6526
|
-
{
|
|
6527
|
-
className: "col-span-3 grid grid-cols-[200px_1fr_1fr] gap-x-2 px-2 py-0.5 text-muted-foreground",
|
|
6528
|
-
children: [
|
|
6529
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-xs truncate", title: op.path, children: op.path }),
|
|
6530
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-xs break-all opacity-60", children: previewNode(op.value, 200) }),
|
|
6531
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-xs break-all opacity-60", children: previewNode(op.value, 200) })
|
|
6532
|
-
]
|
|
6533
|
-
},
|
|
6534
|
-
i
|
|
6535
|
-
);
|
|
6536
|
-
}
|
|
6537
|
-
if (op.kind === "added") {
|
|
6538
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
6539
|
-
"div",
|
|
6540
|
-
{
|
|
6541
|
-
className: "col-span-3 px-2 py-1 rounded text-xs border-l-2 border-l-emerald-400/70 bg-emerald-500/5",
|
|
6542
|
-
children: [
|
|
6543
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-mono text-xs text-muted-foreground mb-0.5", children: op.path }),
|
|
6544
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-mono break-all text-emerald-300/90", children: [
|
|
6545
|
-
"+ ",
|
|
6546
|
-
previewNode(op.value, 400)
|
|
6547
|
-
] })
|
|
6548
|
-
]
|
|
6549
|
-
},
|
|
6550
|
-
i
|
|
6551
|
-
);
|
|
5209
|
+
handled = true;
|
|
5210
|
+
break;
|
|
5211
|
+
}
|
|
5212
|
+
case "ArrowLeft":
|
|
5213
|
+
case "A": {
|
|
5214
|
+
if (e.key === "A" && !e.shiftKey) break;
|
|
5215
|
+
if (current === null) break;
|
|
5216
|
+
e.preventDefault();
|
|
5217
|
+
const action = getAction(current);
|
|
5218
|
+
if (action === "collapse") {
|
|
5219
|
+
current.click();
|
|
5220
|
+
} else if (currentIdx > 0) {
|
|
5221
|
+
const prev = safeItemAt(items, currentIdx - 1);
|
|
5222
|
+
if (prev !== null) focusAndScroll(prev);
|
|
5223
|
+
}
|
|
5224
|
+
handled = true;
|
|
5225
|
+
break;
|
|
5226
|
+
}
|
|
5227
|
+
case "ArrowRight":
|
|
5228
|
+
case "D": {
|
|
5229
|
+
if (e.key === "D" && !e.shiftKey) break;
|
|
5230
|
+
if (current === null) break;
|
|
5231
|
+
e.preventDefault();
|
|
5232
|
+
const action = getAction(current);
|
|
5233
|
+
if (action === "expand") {
|
|
5234
|
+
current.click();
|
|
5235
|
+
} else if (currentIdx < items.length - 1) {
|
|
5236
|
+
const next = safeItemAt(items, currentIdx + 1);
|
|
5237
|
+
if (next !== null) focusAndScroll(next);
|
|
5238
|
+
}
|
|
5239
|
+
handled = true;
|
|
5240
|
+
break;
|
|
5241
|
+
}
|
|
5242
|
+
case " ": {
|
|
5243
|
+
if (current === null) break;
|
|
5244
|
+
e.preventDefault();
|
|
5245
|
+
const action = getAction(current);
|
|
5246
|
+
if (action !== null) current.click();
|
|
5247
|
+
handled = true;
|
|
5248
|
+
break;
|
|
5249
|
+
}
|
|
6552
5250
|
}
|
|
6553
|
-
if (
|
|
6554
|
-
|
|
6555
|
-
"div",
|
|
6556
|
-
{
|
|
6557
|
-
className: "col-span-3 px-2 py-1 rounded text-xs border-l-2 border-l-rose-400/70 bg-rose-500/5",
|
|
6558
|
-
children: [
|
|
6559
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-mono text-xs text-muted-foreground mb-0.5", children: op.path }),
|
|
6560
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-mono break-all text-rose-300/90 line-through", children: [
|
|
6561
|
-
"− ",
|
|
6562
|
-
previewNode(op.value, 400)
|
|
6563
|
-
] })
|
|
6564
|
-
]
|
|
6565
|
-
},
|
|
6566
|
-
i
|
|
6567
|
-
);
|
|
5251
|
+
if (handled) {
|
|
5252
|
+
e.stopPropagation();
|
|
6568
5253
|
}
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
]
|
|
6580
|
-
},
|
|
6581
|
-
i
|
|
6582
|
-
);
|
|
6583
|
-
})
|
|
6584
|
-
] });
|
|
5254
|
+
};
|
|
5255
|
+
document.addEventListener("keydown", handleKeyDown, { capture: true });
|
|
5256
|
+
return () => document.removeEventListener("keydown", handleKeyDown, { capture: true });
|
|
5257
|
+
}, [containerRef, rootRef]);
|
|
5258
|
+
reactExports.useEffect(() => {
|
|
5259
|
+
const root = rootRef.current;
|
|
5260
|
+
if (!root) return;
|
|
5261
|
+
root.addEventListener("focus", handleFocusContainer);
|
|
5262
|
+
return () => root.removeEventListener("focus", handleFocusContainer);
|
|
5263
|
+
}, [handleFocusContainer, rootRef]);
|
|
6585
5264
|
}
|
|
6586
5265
|
function truncateSessionId(id) {
|
|
6587
5266
|
if (id.length <= 30) return id;
|
|
@@ -6659,7 +5338,8 @@ function ProxyViewer({
|
|
|
6659
5338
|
onClearGroup,
|
|
6660
5339
|
viewMode,
|
|
6661
5340
|
onViewModeChange,
|
|
6662
|
-
strip
|
|
5341
|
+
strip,
|
|
5342
|
+
slowResponseThresholdSeconds
|
|
6663
5343
|
}) {
|
|
6664
5344
|
const { totalIn, totalOut } = reactExports.useMemo(() => computeTokenSummary(logs), [logs]);
|
|
6665
5345
|
const [exporting, setExporting] = reactExports.useState(false);
|
|
@@ -6667,6 +5347,9 @@ function ProxyViewer({
|
|
|
6667
5347
|
const [crabEntrancePhase, setCrabEntrancePhase] = reactExports.useState(
|
|
6668
5348
|
"hidden"
|
|
6669
5349
|
);
|
|
5350
|
+
const logListRef = reactExports.useRef(null);
|
|
5351
|
+
const logListWrapperRef = reactExports.useRef(null);
|
|
5352
|
+
useKeyboardNavigation(logListRef, logListWrapperRef);
|
|
6670
5353
|
reactExports.useEffect(() => {
|
|
6671
5354
|
const perCrabDuration = 400;
|
|
6672
5355
|
const startDelay = 50;
|
|
@@ -6702,8 +5385,8 @@ function ProxyViewer({
|
|
|
6702
5385
|
},
|
|
6703
5386
|
[comparisonPredecessors]
|
|
6704
5387
|
);
|
|
6705
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-[
|
|
6706
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-end
|
|
5388
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-[1400px] xl:max-w-[1600px] 2xl:max-w-[1800px] mx-auto px-6 pb-6", children: [
|
|
5389
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-end pt-6 pb-8 relative", children: [
|
|
6707
5390
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "text-lg font-bold flex items-end gap-2 absolute left-1/2 -translate-x-1/2 whitespace-nowrap", children: [
|
|
6708
5391
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-end gap-1 group cursor-default", "aria-hidden": "true", children: [
|
|
6709
5392
|
/* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "size-10 text-amber-500 transition-all duration-300 group-hover:scale-125 group-hover:-translate-y-1.5" }),
|
|
@@ -6746,7 +5429,7 @@ function ProxyViewer({
|
|
|
6746
5429
|
] }),
|
|
6747
5430
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ml-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsDialog, {}) })
|
|
6748
5431
|
] }),
|
|
6749
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3
|
|
5432
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
|
|
6750
5433
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedSession, onValueChange: onSessionChange, children: [
|
|
6751
5434
|
/* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "flex-1 max-w-[350px] text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All sessions" }) }),
|
|
6752
5435
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
|
|
@@ -6767,7 +5450,7 @@ function ProxyViewer({
|
|
|
6767
5450
|
{
|
|
6768
5451
|
type: "button",
|
|
6769
5452
|
onClick: () => onViewModeChange("simple"),
|
|
6770
|
-
className: `
|
|
5453
|
+
className: `h-8 px-3 cursor-pointer transition-colors text-xs ${viewMode === "simple" ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
|
|
6771
5454
|
children: "Simple"
|
|
6772
5455
|
}
|
|
6773
5456
|
),
|
|
@@ -6776,7 +5459,7 @@ function ProxyViewer({
|
|
|
6776
5459
|
{
|
|
6777
5460
|
type: "button",
|
|
6778
5461
|
onClick: () => onViewModeChange("full"),
|
|
6779
|
-
className: `
|
|
5462
|
+
className: `h-8 px-3 cursor-pointer transition-colors text-xs ${viewMode === "full" ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
|
|
6780
5463
|
children: "Full"
|
|
6781
5464
|
}
|
|
6782
5465
|
)
|
|
@@ -6786,7 +5469,7 @@ function ProxyViewer({
|
|
|
6786
5469
|
logs.length,
|
|
6787
5470
|
" request",
|
|
6788
5471
|
logs.length !== 1 ? "s" : "",
|
|
6789
|
-
totalIn > 0 || totalOut > 0 ? ` · ${totalIn
|
|
5472
|
+
totalIn > 0 || totalOut > 0 ? ` · ${formatTokens(totalIn)} in / ${formatTokens(totalOut)} out` : ""
|
|
6790
5473
|
] }),
|
|
6791
5474
|
logs.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6792
5475
|
"button",
|
|
@@ -6796,10 +5479,10 @@ function ProxyViewer({
|
|
|
6796
5479
|
void handleExport();
|
|
6797
5480
|
},
|
|
6798
5481
|
disabled: exporting,
|
|
6799
|
-
className: "text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1",
|
|
5482
|
+
className: "h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5 rounded-md hover:bg-muted",
|
|
6800
5483
|
title: "Export all logs as JSON ZIP",
|
|
6801
5484
|
children: exporting ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Exporting..." }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
6802
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Download, { className: "size-3" }),
|
|
5485
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Download, { className: "size-3.5" }),
|
|
6803
5486
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Export" })
|
|
6804
5487
|
] })
|
|
6805
5488
|
}
|
|
@@ -6809,31 +5492,40 @@ function ProxyViewer({
|
|
|
6809
5492
|
{
|
|
6810
5493
|
type: "button",
|
|
6811
5494
|
onClick: onClearAll,
|
|
6812
|
-
className: "text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer",
|
|
5495
|
+
className: "h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer rounded-md hover:bg-muted",
|
|
6813
5496
|
title: "Clear all logs",
|
|
6814
5497
|
children: "Clear"
|
|
6815
5498
|
}
|
|
6816
5499
|
)
|
|
6817
5500
|
] }),
|
|
6818
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", {
|
|
5501
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: logs.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center text-muted-foreground py-16 space-y-4", children: [
|
|
6819
5502
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm", children: "No requests captured yet." }),
|
|
6820
5503
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs", children: "Route AI coding tools through the proxy:" }),
|
|
6821
5504
|
/* @__PURE__ */ jsxRuntimeExports.jsx(CopyableCommand, { command: "LLM_BASE_URL=http://localhost:25947/proxy <your-tool>" })
|
|
6822
|
-
] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6823
|
-
|
|
5505
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
5506
|
+
"div",
|
|
6824
5507
|
{
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
5508
|
+
ref: logListWrapperRef,
|
|
5509
|
+
tabIndex: 0,
|
|
5510
|
+
className: "flex flex-col gap-2 focus:outline-none",
|
|
5511
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: logListRef, children: groups.map((group) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
5512
|
+
ConversationGroup,
|
|
5513
|
+
{
|
|
5514
|
+
group,
|
|
5515
|
+
viewMode,
|
|
5516
|
+
strip,
|
|
5517
|
+
slowResponseThresholdSeconds,
|
|
5518
|
+
cacheTrends,
|
|
5519
|
+
onCompareWithPrevious: handleCompareWithPrevious,
|
|
5520
|
+
comparisonPredecessors,
|
|
5521
|
+
onClearGroup,
|
|
5522
|
+
standalone: groups.length === 1
|
|
5523
|
+
},
|
|
5524
|
+
group.id
|
|
5525
|
+
)) })
|
|
5526
|
+
}
|
|
5527
|
+
) }),
|
|
5528
|
+
comparePair !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntimeExports.jsx(LazyCompareDrawer, { left: comparePair[0], right: comparePair[1], onClose: closeCompare }) })
|
|
6837
5529
|
] });
|
|
6838
5530
|
}
|
|
6839
5531
|
const SSEUpdateSchema = union([
|
|
@@ -6871,8 +5563,6 @@ function filterLogs(logs, selectedSession, selectedModel) {
|
|
|
6871
5563
|
const DEBOUNCE_MS = 50;
|
|
6872
5564
|
function ProxyViewerContainer() {
|
|
6873
5565
|
const [allLogs, setAllLogs] = reactExports.useState([]);
|
|
6874
|
-
const [sessions, setSessions] = reactExports.useState([]);
|
|
6875
|
-
const [models, setModels] = reactExports.useState([]);
|
|
6876
5566
|
const [selectedSession, setSelectedSession] = reactExports.useState("__all__");
|
|
6877
5567
|
const [selectedModel, setSelectedModel] = reactExports.useState("__all__");
|
|
6878
5568
|
const [viewMode, setViewMode] = reactExports.useState("simple");
|
|
@@ -6880,13 +5570,14 @@ function ProxyViewerContainer() {
|
|
|
6880
5570
|
const eventSourceRef = reactExports.useRef(null);
|
|
6881
5571
|
const reconnectTimeoutRef = reactExports.useRef(null);
|
|
6882
5572
|
const logIndexRef = reactExports.useRef(/* @__PURE__ */ new Map());
|
|
6883
|
-
const logsRef = reactExports.useRef([]);
|
|
6884
5573
|
const pendingUpdatesRef = reactExports.useRef([]);
|
|
6885
5574
|
const flushTimerRef = reactExports.useRef(null);
|
|
6886
5575
|
const logs = reactExports.useMemo(
|
|
6887
5576
|
() => filterLogs(allLogs, selectedSession, selectedModel),
|
|
6888
5577
|
[allLogs, selectedSession, selectedModel]
|
|
6889
5578
|
);
|
|
5579
|
+
const sessions = reactExports.useMemo(() => extractSessions(allLogs), [allLogs]);
|
|
5580
|
+
const models = reactExports.useMemo(() => extractModels(allLogs), [allLogs]);
|
|
6890
5581
|
const flushUpdates = reactExports.useCallback(() => {
|
|
6891
5582
|
flushTimerRef.current = null;
|
|
6892
5583
|
const updates = pendingUpdatesRef.current;
|
|
@@ -6894,8 +5585,6 @@ function ProxyViewerContainer() {
|
|
|
6894
5585
|
if (updates.length === 0) return;
|
|
6895
5586
|
setAllLogs((prev) => {
|
|
6896
5587
|
let next = prev;
|
|
6897
|
-
let sessionsChanged = false;
|
|
6898
|
-
let modelsChanged = false;
|
|
6899
5588
|
for (const log of updates) {
|
|
6900
5589
|
const idx = logIndexRef.current.get(log.id);
|
|
6901
5590
|
if (idx !== void 0) {
|
|
@@ -6904,19 +5593,10 @@ function ProxyViewerContainer() {
|
|
|
6904
5593
|
logIndexRef.current.set(log.id, next.length);
|
|
6905
5594
|
next = [...next, log];
|
|
6906
5595
|
}
|
|
6907
|
-
if (log.sessionId !== null && log.sessionId !== "" && !sessions.includes(log.sessionId)) {
|
|
6908
|
-
sessionsChanged = true;
|
|
6909
|
-
}
|
|
6910
|
-
if (log.model !== null && log.model !== "" && !models.includes(log.model)) {
|
|
6911
|
-
modelsChanged = true;
|
|
6912
|
-
}
|
|
6913
5596
|
}
|
|
6914
|
-
logsRef.current = next;
|
|
6915
|
-
if (sessionsChanged) setSessions((s) => extractSessions(next));
|
|
6916
|
-
if (modelsChanged) setModels((m) => extractModels(next));
|
|
6917
5597
|
return next;
|
|
6918
5598
|
});
|
|
6919
|
-
}, [
|
|
5599
|
+
}, []);
|
|
6920
5600
|
const scheduleUpdate = reactExports.useCallback(
|
|
6921
5601
|
(log) => {
|
|
6922
5602
|
pendingUpdatesRef.current.push(log);
|
|
@@ -6954,10 +5634,7 @@ function ProxyViewerContainer() {
|
|
|
6954
5634
|
if (log !== void 0) idx.set(log.id, i);
|
|
6955
5635
|
}
|
|
6956
5636
|
logIndexRef.current = idx;
|
|
6957
|
-
logsRef.current = update.logs;
|
|
6958
5637
|
setAllLogs(update.logs);
|
|
6959
|
-
setSessions(extractSessions(update.logs));
|
|
6960
|
-
setModels(extractModels(update.logs));
|
|
6961
5638
|
setError(null);
|
|
6962
5639
|
} else if (update.type === "update") {
|
|
6963
5640
|
scheduleUpdate(update.log);
|
|
@@ -7001,10 +5678,7 @@ function ProxyViewerContainer() {
|
|
|
7001
5678
|
return;
|
|
7002
5679
|
}
|
|
7003
5680
|
logIndexRef.current.clear();
|
|
7004
|
-
logsRef.current = [];
|
|
7005
5681
|
setAllLogs([]);
|
|
7006
|
-
setSessions([]);
|
|
7007
|
-
setModels([]);
|
|
7008
5682
|
setError(null);
|
|
7009
5683
|
} catch (err) {
|
|
7010
5684
|
setError(err instanceof Error ? err.message : "Unknown error clearing logs");
|
|
@@ -7033,9 +5707,6 @@ function ProxyViewerContainer() {
|
|
|
7033
5707
|
if (log !== void 0) idx.set(log.id, i);
|
|
7034
5708
|
}
|
|
7035
5709
|
logIndexRef.current = idx;
|
|
7036
|
-
logsRef.current = remaining;
|
|
7037
|
-
setSessions(extractSessions(remaining));
|
|
7038
|
-
setModels(extractModels(remaining));
|
|
7039
5710
|
return remaining;
|
|
7040
5711
|
});
|
|
7041
5712
|
setError(null);
|
|
@@ -7044,7 +5715,7 @@ function ProxyViewerContainer() {
|
|
|
7044
5715
|
}
|
|
7045
5716
|
})();
|
|
7046
5717
|
}, []);
|
|
7047
|
-
const { strip } = useStripConfig();
|
|
5718
|
+
const { strip, slowResponseThresholdSeconds } = useStripConfig();
|
|
7048
5719
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
7049
5720
|
error !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed top-4 right-4 bg-destructive text-destructive-foreground px-4 py-2 rounded-md text-sm z-50", children: error }),
|
|
7050
5721
|
/* @__PURE__ */ jsxRuntimeExports.jsx(OnboardingBanner, {}),
|
|
@@ -7062,12 +5733,39 @@ function ProxyViewerContainer() {
|
|
|
7062
5733
|
onClearGroup: handleClearGroup,
|
|
7063
5734
|
viewMode,
|
|
7064
5735
|
onViewModeChange: setViewMode,
|
|
7065
|
-
strip
|
|
5736
|
+
strip,
|
|
5737
|
+
slowResponseThresholdSeconds
|
|
7066
5738
|
}
|
|
7067
5739
|
)
|
|
7068
5740
|
] });
|
|
7069
5741
|
}
|
|
7070
5742
|
const SplitComponent = ProxyViewerContainer;
|
|
5743
|
+
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
5744
|
+
__proto__: null,
|
|
5745
|
+
component: SplitComponent
|
|
5746
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
7071
5747
|
export {
|
|
7072
|
-
|
|
5748
|
+
Badge as B,
|
|
5749
|
+
Dialog as D,
|
|
5750
|
+
Tabs as T,
|
|
5751
|
+
getConversationId as a,
|
|
5752
|
+
DialogContent as b,
|
|
5753
|
+
cn as c,
|
|
5754
|
+
DialogHeader as d,
|
|
5755
|
+
DialogTitle as e,
|
|
5756
|
+
formatTokens as f,
|
|
5757
|
+
getLogFormatAdapter as g,
|
|
5758
|
+
TabsList as h,
|
|
5759
|
+
TabsTrigger as i,
|
|
5760
|
+
TabsContent as j,
|
|
5761
|
+
TooltipProvider as k,
|
|
5762
|
+
Tooltip as l,
|
|
5763
|
+
TooltipTrigger as m,
|
|
5764
|
+
TooltipContent as n,
|
|
5765
|
+
Button as o,
|
|
5766
|
+
getStatusCategory as p,
|
|
5767
|
+
parseJsonText as q,
|
|
5768
|
+
resolveLogFormat as r,
|
|
5769
|
+
safeJsonValue as s,
|
|
5770
|
+
index as t
|
|
7073
5771
|
};
|