@tonyclaw/llm-inspector 1.18.2 → 1.19.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.
Files changed (42) hide show
  1. package/.output/cli.js +776 -139
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-C-4ypEWs.js → CompareDrawer-DwayZPPO.js} +1 -1
  4. package/.output/public/assets/ProxyViewerContainer-iv3LVMEW.js +101 -0
  5. package/.output/public/assets/{ReplayDialog-CyBKOgba.js → ReplayDialog-CaV1elYO.js} +1 -1
  6. package/.output/public/assets/{RequestAnatomy-C0IrVQ3q.js → RequestAnatomy-CSfnjK7j.js} +1 -1
  7. package/.output/public/assets/{ResponseView-MogToC4i.js → ResponseView-YkOL__xm.js} +1 -1
  8. package/.output/public/assets/{StreamingChunkSequence-ClhUhT-s.js → StreamingChunkSequence-D_p6L-oB.js} +1 -1
  9. package/.output/public/assets/_sessionId-BgCVUC6R.js +1 -0
  10. package/.output/public/assets/index-CWA4S0FO.js +1 -0
  11. package/.output/public/assets/index-DeJyypsp.css +1 -0
  12. package/.output/public/assets/{json-viewer-BicGakI5.js → json-viewer-BB-9bqnP.js} +2 -2
  13. package/.output/public/assets/{main-Be2qqUUW.js → main-COVN451W.js} +2 -2
  14. package/.output/server/_libs/lucide-react.mjs +93 -72
  15. package/.output/server/{_sessionId-DhKJIdQC.mjs → _sessionId-BJT5qIib.mjs} +2 -2
  16. package/.output/server/_ssr/{CompareDrawer-BGUgukJ8.mjs → CompareDrawer-DNGYdUXs.mjs} +4 -4
  17. package/.output/server/_ssr/{ProxyViewerContainer--3K3o3Sm.mjs → ProxyViewerContainer-B-zDOLYE.mjs} +354 -343
  18. package/.output/server/_ssr/{ReplayDialog-Bo86xZI4.mjs → ReplayDialog-DWeqMA4y.mjs} +4 -4
  19. package/.output/server/_ssr/{RequestAnatomy-jRU5qgwB.mjs → RequestAnatomy-TOsrMu9-.mjs} +3 -3
  20. package/.output/server/_ssr/{ResponseView-DdO_-79a.mjs → ResponseView-BuqdPrzm.mjs} +4 -4
  21. package/.output/server/_ssr/{StreamingChunkSequence-BigLwhh4.mjs → StreamingChunkSequence-DuzNZkqL.mjs} +3 -3
  22. package/.output/server/_ssr/{index-BHG6vOnr.mjs → index-1nCQUt3y.mjs} +2 -2
  23. package/.output/server/_ssr/index.mjs +2 -2
  24. package/.output/server/_ssr/{json-viewer-B4c_WjXD.mjs → json-viewer-BL8xhHbi.mjs} +9 -5
  25. package/.output/server/_ssr/{router-DVixpJO-.mjs → router-aCaUgVTW.mjs} +3 -3
  26. package/.output/server/{_tanstack-start-manifest_v-BbvWUF4v.mjs → _tanstack-start-manifest_v-cBRxvCjb.mjs} +1 -1
  27. package/.output/server/index.mjs +61 -61
  28. package/package.json +2 -1
  29. package/src/cli/detect-tools.ts +146 -0
  30. package/src/cli/onboard.ts +229 -0
  31. package/src/cli/templates/command-onboard.ts +17 -0
  32. package/src/cli/templates/skill-onboard.ts +325 -0
  33. package/src/cli.ts +185 -163
  34. package/src/components/ProxyViewer.tsx +153 -142
  35. package/src/components/proxy-viewer/LogEntry.tsx +136 -157
  36. package/src/components/proxy-viewer/LogEntryHeader.tsx +147 -66
  37. package/src/components/proxy-viewer/useCopyFeedback.ts +36 -0
  38. package/src/components/ui/json-viewer.tsx +12 -0
  39. package/.output/public/assets/ProxyViewerContainer-WRenRpeh.js +0 -101
  40. package/.output/public/assets/_sessionId-BO47oA3Z.js +0 -1
  41. package/.output/public/assets/index-BRvz6-L6.css +0 -1
  42. package/.output/public/assets/index-Btw8ec7-.js +0 -1
@@ -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, D as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, a as RuntimeConfigSchema, r as requestFormatForPath, c as createPendingProviderTestResults, P as ProviderTestResultsSchema, d as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, g as getSessionPath, e as ProviderConfigSchema, s as stripClaudeCodeBillingHeader, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, b as AnthropicRequestSchema } from "./router-DVixpJO-.mjs";
2
+ import { C as CapturedLogSchema, D as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, a as RuntimeConfigSchema, r as requestFormatForPath, c as createPendingProviderTestResults, P as ProviderTestResultsSchema, d as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, g as getSessionPath, e as ProviderConfigSchema, s as stripClaudeCodeBillingHeader, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, b as AnthropicRequestSchema } from "./router-aCaUgVTW.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,7 +9,7 @@ import { R as Root, T as Trigger$2, 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 { C as Check, X, P as Plus, D as Download, S as Settings, A as ArrowLeft, a as Copy, b as ChevronDown, U as Upload, c as Scan, d as CircleAlert, e as ChevronUp, L as LoaderCircle, f as ChevronRight, g as User, h as Clock, M as MessageSquare, Z as Zap, E as ExternalLink, T as Trash2, i as TriangleAlert, j as EyeOff, k as Eye, 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, z as ArrowUp, B as ArrowDown, H as Rows3, I as Columns2 } from "../_libs/lucide-react.mjs";
12
+ import { C as Check, X, P as Plus, D as Download, S as Settings, A as ArrowLeft, a as Copy, b as ChevronDown, U as Upload, c as Scan, d as CircleAlert, e as ChevronUp, L as LoaderCircle, f as ChevronRight, g as User, h as Clock, M as MessageSquare, Z as Zap, E as ExternalLink, T as Trash2, i as TriangleAlert, j as EyeOff, k as Eye, R as RotateCw, l as Pencil, m as Minus, n as CircleCheckBig, O as OctagonAlert, W as Wrench, G as Globe, F as FileTerminal, o as Radio, p as ChevronsUp, q as ChevronsDown, r as FileDiff, H as History, s as RotateCcw, t as GitCompareArrows, u as CircleQuestionMark, v as Server, w as Gauge, x as Lock, y as Wifi, z as WifiOff, B as ArrowUp, I as ArrowDown, J as Rows3, K as Columns2 } from "../_libs/lucide-react.mjs";
13
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";
14
14
  import { P as Provider, R as Root3, T as Trigger$1, a as Portal$1, C as Content2$1, A as Arrow2 } from "../_libs/radix-ui__react-tooltip.mjs";
15
15
  import { R as Root2$1, L as List, T as Trigger$3, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
@@ -275,7 +275,7 @@ function getStatusCategory(status) {
275
275
  if (status >= 500) return "server_error";
276
276
  return "pending";
277
277
  }
278
- const version = "1.18.2";
278
+ const version = "1.19.0";
279
279
  const packageJson = {
280
280
  version
281
281
  };
@@ -1181,37 +1181,6 @@ function TooltipContent({
1181
1181
  }
1182
1182
  ) });
1183
1183
  }
1184
- function JsonExpansionButton({
1185
- policy,
1186
- isExpanded,
1187
- isPending,
1188
- onToggle
1189
- }) {
1190
- if (policy === null) return null;
1191
- const label = isPending ? "Updating..." : isExpanded ? "Collapse all" : "Expand all";
1192
- const tooltip = isExpanded ? "Collapse all JSON nodes" : "Expand all JSON nodes";
1193
- return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
1194
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
1195
- Button,
1196
- {
1197
- variant: "outline",
1198
- size: "sm",
1199
- className: "h-8 text-xs",
1200
- onClick: (e) => {
1201
- e.stopPropagation();
1202
- onToggle();
1203
- },
1204
- disabled: isPending,
1205
- "aria-pressed": isExpanded,
1206
- children: [
1207
- isExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsUp, { className: "size-3.5 mr-1" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsDown, { className: "size-3.5 mr-1" }),
1208
- label
1209
- ]
1210
- }
1211
- ) }),
1212
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: tooltip })
1213
- ] }) });
1214
- }
1215
1184
  function getJsonExpansionPolicy(_value) {
1216
1185
  return { depth: Number.POSITIVE_INFINITY };
1217
1186
  }
@@ -1350,28 +1319,51 @@ function TabsContent({
1350
1319
  }
1351
1320
  );
1352
1321
  }
1322
+ function useCopyFeedback(text) {
1323
+ const [copied, setCopied] = reactExports.useState(false);
1324
+ const timerRef = reactExports.useRef(null);
1325
+ reactExports.useEffect(
1326
+ () => () => {
1327
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
1328
+ },
1329
+ []
1330
+ );
1331
+ const copy = reactExports.useCallback(
1332
+ (event) => {
1333
+ event.stopPropagation();
1334
+ if (text === null) return;
1335
+ void window.navigator.clipboard.writeText(text).then(() => {
1336
+ setCopied(true);
1337
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
1338
+ timerRef.current = setTimeout(() => setCopied(false), 2e3);
1339
+ });
1340
+ },
1341
+ [text]
1342
+ );
1343
+ return { copied, copy };
1344
+ }
1353
1345
  const LazyCompareDrawer = reactExports.lazy(
1354
- () => import("./CompareDrawer-BGUgukJ8.mjs").then((m) => ({ default: m.CompareDrawer }))
1346
+ () => import("./CompareDrawer-DNGYdUXs.mjs").then((m) => ({ default: m.CompareDrawer }))
1355
1347
  );
1356
1348
  const LazyReplayDialog = reactExports.lazy(
1357
- () => import("./ReplayDialog-Bo86xZI4.mjs").then((m) => ({ default: m.ReplayDialog }))
1349
+ () => import("./ReplayDialog-DWeqMA4y.mjs").then((m) => ({ default: m.ReplayDialog }))
1358
1350
  );
1359
1351
  const LazyRequestAnatomy = reactExports.lazy(
1360
- () => import("./RequestAnatomy-jRU5qgwB.mjs").then((m) => ({ default: m.RequestAnatomy }))
1352
+ () => import("./RequestAnatomy-TOsrMu9-.mjs").then((m) => ({ default: m.RequestAnatomy }))
1361
1353
  );
1362
1354
  const LazyResponseView = reactExports.lazy(
1363
- () => import("./ResponseView-DdO_-79a.mjs").then((m) => ({ default: m.ResponseView }))
1355
+ () => import("./ResponseView-BuqdPrzm.mjs").then((m) => ({ default: m.ResponseView }))
1364
1356
  );
1365
1357
  const LazyStreamingChunkSequence = reactExports.lazy(
1366
- () => import("./StreamingChunkSequence-BigLwhh4.mjs").then((m) => ({
1358
+ () => import("./StreamingChunkSequence-DuzNZkqL.mjs").then((m) => ({
1367
1359
  default: m.StreamingChunkSequence
1368
1360
  }))
1369
1361
  );
1370
1362
  const LazyJsonViewer = reactExports.lazy(
1371
- () => import("./json-viewer-B4c_WjXD.mjs").then((m) => ({ default: m.JsonViewer }))
1363
+ () => import("./json-viewer-BL8xhHbi.mjs").then((m) => ({ default: m.JsonViewer }))
1372
1364
  );
1373
1365
  const LazyJsonViewerFromString = reactExports.lazy(
1374
- () => import("./json-viewer-B4c_WjXD.mjs").then((m) => ({ default: m.JsonViewerFromString }))
1366
+ () => import("./json-viewer-BL8xhHbi.mjs").then((m) => ({ default: m.JsonViewerFromString }))
1375
1367
  );
1376
1368
  const HIGHLIGHT_DURATION_MS = 1200;
1377
1369
  const MAX_HIGHLIGHT_ATTEMPTS = 12;
@@ -1790,11 +1782,9 @@ const LogEntryHeader = reactExports.memo(function({
1790
1782
  onToggle,
1791
1783
  responseToolNames = null,
1792
1784
  cacheTrend = null,
1785
+ activeTab,
1786
+ tabActions,
1793
1787
  onReplay,
1794
- onCopyRequest,
1795
- requestCopied = false,
1796
- onToggleRequestExpansion,
1797
- requestExpansionState = null,
1798
1788
  slowResponseThresholdSeconds = 0
1799
1789
  }) {
1800
1790
  const statusCategory = getStatusCategory(log.responseStatus);
@@ -1958,36 +1948,74 @@ const LogEntryHeader = reactExports.memo(function({
1958
1948
  onClick: (e) => e.stopPropagation(),
1959
1949
  onKeyDown: (e) => e.stopPropagation(),
1960
1950
  children: [
1961
- requestExpansionState !== null && onToggleRequestExpansion !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
1962
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1963
- Button,
1964
- {
1965
- variant: "outline",
1966
- size: "icon",
1967
- className: "size-8",
1968
- onClick: onToggleRequestExpansion,
1969
- disabled: requestExpansionState.isPending,
1970
- "aria-pressed": requestExpansionState.isExpanded,
1971
- "aria-label": requestExpansionState.isExpanded ? "Collapse all JSON" : "Expand all JSON",
1972
- children: requestExpansionState.isExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsUp, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsDown, { className: "size-3.5" })
1973
- }
1974
- ) }),
1975
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: requestExpansionState.isExpanded ? "Collapse all JSON nodes" : "Expand all JSON nodes" })
1976
- ] }),
1977
- onCopyRequest !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
1978
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1979
- Button,
1980
- {
1981
- variant: "outline",
1982
- size: "icon",
1983
- className: "size-8",
1984
- onClick: onCopyRequest,
1985
- "aria-label": "Copy request body",
1986
- children: requestCopied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 text-emerald-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
1987
- }
1988
- ) }),
1989
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: requestCopied ? "Copied to clipboard" : "Copy request body" })
1990
- ] }),
1951
+ tabActions !== void 0 && activeTab !== void 0 && (() => {
1952
+ const action = tabActions[activeTab];
1953
+ if (action === void 0) return null;
1954
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1955
+ action.expansion !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
1956
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1957
+ Button,
1958
+ {
1959
+ variant: "outline",
1960
+ size: "icon",
1961
+ className: "size-8",
1962
+ onClick: action.expansion.onToggle,
1963
+ disabled: action.expansion.isPending,
1964
+ "aria-pressed": action.expansion.isExpanded,
1965
+ "aria-label": action.expansion.isExpanded ? "Collapse all JSON" : "Expand all JSON",
1966
+ children: action.expansion.isExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsUp, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsDown, { className: "size-3.5" })
1967
+ }
1968
+ ) }),
1969
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: action.expansion.isExpanded ? "Collapse all JSON nodes" : "Expand all JSON nodes" })
1970
+ ] }),
1971
+ action.diffWithRaw !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
1972
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1973
+ Button,
1974
+ {
1975
+ variant: "outline",
1976
+ size: "icon",
1977
+ className: cn(
1978
+ "size-8",
1979
+ action.diffWithRaw.active && "bg-accent text-accent-foreground"
1980
+ ),
1981
+ onClick: action.diffWithRaw.onToggle,
1982
+ "aria-pressed": action.diffWithRaw.active,
1983
+ "aria-label": action.diffWithRaw.active ? "Hide raw diff" : "Diff with raw",
1984
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(FileDiff, { className: "size-3.5" })
1985
+ }
1986
+ ) }),
1987
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: action.diffWithRaw.active ? "Hide diff with raw request" : "Show diff between displayed and raw request body" })
1988
+ ] }),
1989
+ action.diffWithPrevious !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
1990
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1991
+ Button,
1992
+ {
1993
+ variant: "outline",
1994
+ size: "icon",
1995
+ className: "size-8",
1996
+ onClick: action.diffWithPrevious,
1997
+ "aria-label": "Diff with previous",
1998
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(History, { className: "size-3.5" })
1999
+ }
2000
+ ) }),
2001
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Compare this request with the immediately preceding one" })
2002
+ ] }),
2003
+ action.copyText !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
2004
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2005
+ Button,
2006
+ {
2007
+ variant: "outline",
2008
+ size: "icon",
2009
+ className: "size-8",
2010
+ onClick: action.onCopy,
2011
+ "aria-label": action.copyCopied ? "Copied" : action.copyLabel,
2012
+ children: action.copyCopied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 text-emerald-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
2013
+ }
2014
+ ) }),
2015
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: action.copyCopied ? "Copied to clipboard" : action.copyLabel })
2016
+ ] })
2017
+ ] });
2018
+ })(),
1991
2019
  onReplay !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
1992
2020
  /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1993
2021
  Button,
@@ -2328,31 +2356,6 @@ function shouldShowRequestDiffButton(apiFormat, viewMode, strip, hasRawRequest)
2328
2356
  function TabFallback() {
2329
2357
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-1", "aria-hidden": "true" });
2330
2358
  }
2331
- function CopyButton({
2332
- text,
2333
- label,
2334
- copied,
2335
- onCopy
2336
- }) {
2337
- if (text === null) return null;
2338
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
2339
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
2340
- Button,
2341
- {
2342
- variant: "outline",
2343
- size: "sm",
2344
- className: "h-8 text-xs",
2345
- onClick: onCopy,
2346
- "aria-label": label,
2347
- children: [
2348
- copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 mr-1 text-emerald-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5 mr-1" }),
2349
- copied ? "Copied!" : label
2350
- ]
2351
- }
2352
- ) }),
2353
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: copied ? "Copied to clipboard" : label })
2354
- ] });
2355
- }
2356
2359
  function DiffToggleButton({
2357
2360
  active,
2358
2361
  onClick
@@ -2394,29 +2397,6 @@ const HeadersDiffContent = reactExports.memo(function({
2394
2397
  const result = reactExports.useMemo(() => computeHeadersDiff(rawHeaders, headers), [rawHeaders, headers]);
2395
2398
  return /* @__PURE__ */ jsxRuntimeExports.jsx(DiffView, { result, emptyLabel });
2396
2399
  });
2397
- function useCopyFeedback(text) {
2398
- const [copied, setCopied] = reactExports.useState(false);
2399
- const timerRef = reactExports.useRef(null);
2400
- reactExports.useEffect(
2401
- () => () => {
2402
- if (timerRef.current !== null) clearTimeout(timerRef.current);
2403
- },
2404
- []
2405
- );
2406
- const copy = reactExports.useCallback(
2407
- (event) => {
2408
- event.stopPropagation();
2409
- if (text === null) return;
2410
- void window.navigator.clipboard.writeText(text).then(() => {
2411
- setCopied(true);
2412
- if (timerRef.current !== null) clearTimeout(timerRef.current);
2413
- timerRef.current = setTimeout(() => setCopied(false), 2e3);
2414
- });
2415
- },
2416
- [text]
2417
- );
2418
- return { copied, copy };
2419
- }
2420
2400
  const LogEntry = reactExports.memo(function({
2421
2401
  log,
2422
2402
  viewMode = "simple",
@@ -2449,11 +2429,106 @@ const LogEntry = reactExports.memo(function({
2449
2429
  return stripClaudeCodeBillingHeader(log.rawRequestBody).body;
2450
2430
  }, [log.rawRequestBody, resolvedFormat, strip]);
2451
2431
  const displayedRequestBody = strippedRequestBody ?? log.rawRequestBody;
2432
+ const requestExpansion = useJsonBulkExpansion(displayedRequestBody);
2433
+ const rawRequestExpansion = useJsonBulkExpansion(log.rawRequestBody);
2434
+ const responseExpansion = useJsonBulkExpansion(log.responseText);
2435
+ const headersText = reactExports.useMemo(
2436
+ () => log.headers && Object.keys(log.headers).length > 0 ? JSON.stringify(log.headers, null, 2) : null,
2437
+ [log.headers]
2438
+ );
2439
+ const rawHeadersText = reactExports.useMemo(
2440
+ () => log.rawHeaders && Object.keys(log.rawHeaders).length > 0 ? JSON.stringify(log.rawHeaders, null, 2) : null,
2441
+ [log.rawHeaders]
2442
+ );
2452
2443
  const requestCopy = useCopyFeedback(displayedRequestBody);
2453
2444
  const rawRequestCopy = useCopyFeedback(log.rawRequestBody);
2445
+ const headersCopy = useCopyFeedback(headersText);
2446
+ const rawHeadersCopy = useCopyFeedback(rawHeadersText);
2454
2447
  const responseCopy = useCopyFeedback(log.responseText);
2455
- const requestExpansion = useJsonBulkExpansion(displayedRequestBody);
2456
- const rawRequestExpansion = useJsonBulkExpansion(log.rawRequestBody);
2448
+ const tabActions = reactExports.useMemo(
2449
+ () => ({
2450
+ request: {
2451
+ copyLabel: "Copy request body",
2452
+ copyText: displayedRequestBody,
2453
+ copyCopied: requestCopy.copied,
2454
+ onCopy: requestCopy.copy,
2455
+ expansion: {
2456
+ isExpanded: requestExpansion.isExpanded,
2457
+ isPending: requestExpansion.isPending,
2458
+ onToggle: requestExpansion.toggle
2459
+ },
2460
+ // "Diff with Raw" only makes sense when there's a raw body to compare
2461
+ // against (Anthropic + strip pipeline produces one). "Diff with
2462
+ // Previous" only exists when the parent wired up the compare drawer.
2463
+ diffWithRaw: shouldShowRequestDiffButton(
2464
+ resolvedFormat,
2465
+ viewMode,
2466
+ strip,
2467
+ log.rawRequestBody !== null
2468
+ ) ? { active: requestDiff, onToggle: () => setRequestDiff(!requestDiff) } : void 0,
2469
+ diffWithPrevious: onCompareWithPrevious === void 0 ? void 0 : () => {
2470
+ onCompareWithPrevious(log);
2471
+ }
2472
+ },
2473
+ "raw-request": {
2474
+ copyLabel: "Copy raw request",
2475
+ copyText: log.rawRequestBody,
2476
+ copyCopied: rawRequestCopy.copied,
2477
+ onCopy: rawRequestCopy.copy,
2478
+ expansion: {
2479
+ isExpanded: rawRequestExpansion.isExpanded,
2480
+ isPending: rawRequestExpansion.isPending,
2481
+ onToggle: rawRequestExpansion.toggle
2482
+ }
2483
+ },
2484
+ headers: {
2485
+ copyLabel: "Copy headers",
2486
+ copyText: headersText,
2487
+ copyCopied: headersCopy.copied,
2488
+ onCopy: headersCopy.copy,
2489
+ // Headers are a flat dict, no JSON tree to expand.
2490
+ expansion: null
2491
+ },
2492
+ "raw-headers": {
2493
+ copyLabel: "Copy raw headers",
2494
+ copyText: rawHeadersText,
2495
+ copyCopied: rawHeadersCopy.copied,
2496
+ onCopy: rawHeadersCopy.copy,
2497
+ expansion: null
2498
+ },
2499
+ raw: {
2500
+ copyLabel: "Copy response",
2501
+ copyText: log.responseText,
2502
+ copyCopied: responseCopy.copied,
2503
+ onCopy: responseCopy.copy,
2504
+ expansion: {
2505
+ isExpanded: responseExpansion.isExpanded,
2506
+ isPending: responseExpansion.isPending,
2507
+ onToggle: responseExpansion.toggle
2508
+ }
2509
+ }
2510
+ }),
2511
+ [
2512
+ displayedRequestBody,
2513
+ requestCopy,
2514
+ requestExpansion,
2515
+ requestDiff,
2516
+ log.rawRequestBody,
2517
+ rawRequestCopy,
2518
+ rawRequestExpansion,
2519
+ headersText,
2520
+ headersCopy,
2521
+ rawHeadersText,
2522
+ rawHeadersCopy,
2523
+ log.responseText,
2524
+ responseCopy,
2525
+ responseExpansion,
2526
+ resolvedFormat,
2527
+ viewMode,
2528
+ strip,
2529
+ onCompareWithPrevious
2530
+ ]
2531
+ );
2457
2532
  const anatomySegments = reactExports.useMemo(
2458
2533
  () => requestExpansion.parsedData !== null ? adapter.anatomySegments(requestExpansion.parsedData) : null,
2459
2534
  [adapter, requestExpansion.parsedData]
@@ -2482,17 +2557,10 @@ const LogEntry = reactExports.memo(function({
2482
2557
  responseToolNames: responseAnalysis.toolNames,
2483
2558
  cacheTrend,
2484
2559
  slowResponseThresholdSeconds,
2560
+ activeTab,
2561
+ tabActions,
2485
2562
  onReplay: onCompareWithPrevious === void 0 ? void 0 : () => {
2486
2563
  setReplayOpen(true);
2487
- },
2488
- onCopyRequest: displayedRequestBody === null ? void 0 : (e) => {
2489
- requestCopy.copy(e);
2490
- },
2491
- requestCopied: requestCopy.copied,
2492
- onToggleRequestExpansion: requestExpansion.toggle,
2493
- requestExpansionState: requestExpansion.policy === null ? null : {
2494
- isExpanded: requestExpansion.isExpanded,
2495
- isPending: requestExpansion.isPending
2496
2564
  }
2497
2565
  }
2498
2566
  ),
@@ -2506,96 +2574,31 @@ const LogEntry = reactExports.memo(function({
2506
2574
  viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "raw", children: "Raw Response" }),
2507
2575
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "parsed", children: "Response" })
2508
2576
  ] }),
2509
- 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: [
2510
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-2 mb-2", children: [
2511
- /* @__PURE__ */ jsxRuntimeExports.jsx(
2512
- JsonExpansionButton,
2513
- {
2514
- policy: rawRequestExpansion.policy,
2515
- isExpanded: rawRequestExpansion.isExpanded,
2516
- isPending: rawRequestExpansion.isPending,
2517
- onToggle: rawRequestExpansion.toggle
2518
- }
2519
- ),
2520
- /* @__PURE__ */ jsxRuntimeExports.jsx(
2521
- CopyButton,
2522
- {
2523
- text: log.rawRequestBody,
2524
- label: "Copy",
2525
- copied: rawRequestCopy.copied,
2526
- onCopy: rawRequestCopy.copy
2527
- }
2528
- )
2529
- ] }),
2530
- 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(
2531
- LazyJsonViewer,
2532
- {
2533
- data: rawRequestExpansion.parsedData,
2534
- bulkDepth: rawRequestExpansion.bulkDepth,
2535
- bulkRevision: rawRequestExpansion.bulkRevision
2536
- }
2537
- ) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap break-words", children: log.rawRequestBody })
2538
- ] }) }),
2539
- /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "request", children: activeTab === "request" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 pt-1 pb-3", children: [
2540
- (shouldShowRequestDiffButton(
2541
- resolvedFormat,
2542
- viewMode,
2543
- strip,
2544
- log.rawRequestBody !== null
2545
- ) || onCompareWithPrevious !== void 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-2 mb-2", children: [
2546
- shouldShowRequestDiffButton(
2547
- resolvedFormat,
2548
- viewMode,
2549
- strip,
2550
- log.rawRequestBody !== null
2551
- ) && /* @__PURE__ */ jsxRuntimeExports.jsx(
2552
- DiffToggleButton,
2553
- {
2554
- active: requestDiff,
2555
- onClick: (e) => {
2556
- e.stopPropagation();
2557
- setRequestDiff(!requestDiff);
2558
- }
2559
- }
2560
- ),
2561
- onCompareWithPrevious !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
2562
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
2563
- Button,
2564
- {
2565
- variant: "outline",
2566
- size: "sm",
2567
- className: "h-8 text-xs",
2568
- onClick: (e) => {
2569
- e.stopPropagation();
2570
- onCompareWithPrevious(log);
2571
- },
2572
- children: [
2573
- /* @__PURE__ */ jsxRuntimeExports.jsx(GitCompareArrows, { className: "size-3 mr-1" }),
2574
- "Diff with Previous"
2575
- ]
2576
- }
2577
- ) }),
2578
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Compare this request with the immediately preceding one" })
2579
- ] })
2580
- ] }),
2581
- requestDiff ? /* @__PURE__ */ jsxRuntimeExports.jsx(
2582
- RequestDiffContent,
2583
- {
2584
- rawBody: log.rawRequestBody,
2585
- displayedBody: displayedRequestBody,
2586
- emptyLabel: "No transformation applied — raw and sent request bodies are identical."
2587
- }
2588
- ) : /* @__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(
2589
- LazyJsonViewer,
2590
- {
2591
- data: requestExpansion.parsedData,
2592
- bulkDepth: requestExpansion.bulkDepth,
2593
- bulkRevision: requestExpansion.bulkRevision,
2594
- anatomyPaths,
2595
- expandToPath
2596
- }
2597
- ) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap break-words", children: displayedRequestBody }) })
2598
- ] }) }),
2577
+ shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-request", children: activeTab === "raw-request" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 pt-1 pb-3", children: 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(
2578
+ LazyJsonViewer,
2579
+ {
2580
+ data: rawRequestExpansion.parsedData,
2581
+ bulkDepth: rawRequestExpansion.bulkDepth,
2582
+ bulkRevision: rawRequestExpansion.bulkRevision
2583
+ }
2584
+ ) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap break-words", children: log.rawRequestBody }) }) }),
2585
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "request", children: activeTab === "request" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 pt-1 pb-3", children: requestDiff ? /* @__PURE__ */ jsxRuntimeExports.jsx(
2586
+ RequestDiffContent,
2587
+ {
2588
+ rawBody: log.rawRequestBody,
2589
+ displayedBody: displayedRequestBody,
2590
+ emptyLabel: "No transformation applied — raw and sent request bodies are identical."
2591
+ }
2592
+ ) : /* @__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(
2593
+ LazyJsonViewer,
2594
+ {
2595
+ data: requestExpansion.parsedData,
2596
+ bulkDepth: requestExpansion.bulkDepth,
2597
+ bulkRevision: requestExpansion.bulkRevision,
2598
+ anatomyPaths,
2599
+ expandToPath
2600
+ }
2601
+ ) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap break-words", children: displayedRequestBody }) }) }) }),
2599
2602
  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(
2600
2603
  LazyRequestAnatomy,
2601
2604
  {
@@ -2646,16 +2649,15 @@ const LogEntry = reactExports.memo(function({
2646
2649
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-semibold text-destructive mb-1", children: "SSE Error" }),
2647
2650
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground font-mono", children: log.error })
2648
2651
  ] }),
2649
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2650
- CopyButton,
2652
+ log.responseText !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2653
+ LazyJsonViewerFromString,
2651
2654
  {
2652
2655
  text: log.responseText,
2653
- label: "Copy",
2654
- copied: responseCopy.copied,
2655
- onCopy: responseCopy.copy
2656
+ defaultExpandDepth: 0,
2657
+ bulkDepth: responseExpansion.bulkDepth,
2658
+ bulkRevision: responseExpansion.bulkRevision
2656
2659
  }
2657
- ) }),
2658
- 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" }),
2660
+ ) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No response" }),
2659
2661
  log.streaming === true && /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2660
2662
  LazyStreamingChunkSequence,
2661
2663
  {
@@ -5417,132 +5419,141 @@ function ProxyViewer({
5417
5419
  [comparisonPredecessors]
5418
5420
  );
5419
5421
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-[1400px] xl:max-w-[1600px] 2xl:max-w-[1800px] mx-auto px-6 pb-6", children: [
5420
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-[1fr_auto_1fr] items-start gap-3 pt-6 pb-8", children: [
5421
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", {}),
5422
- /* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "flex min-w-0 flex-col items-center gap-2 text-center", children: [
5423
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex max-w-[calc(100vw-7rem)] items-end gap-2 whitespace-nowrap", children: [
5424
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex shrink-0 items-end gap-1 group cursor-default", "aria-hidden": "true", children: [
5425
- /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "size-10 text-amber-500 transition-all duration-300 group-hover:scale-125 group-hover:-translate-y-1.5" }),
5426
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden items-end gap-0.5 sm:flex", children: crabVariants.map((Crab, i) => {
5427
- const color = [
5428
- "text-amber-500",
5429
- "text-rose-500",
5430
- "text-sky-500",
5431
- "text-emerald-500",
5432
- "text-violet-500",
5433
- "text-orange-500",
5434
- "text-cyan-500",
5435
- "text-pink-500",
5436
- "text-lime-500",
5437
- "text-blue-500",
5438
- "text-yellow-500",
5439
- "text-fuchsia-500"
5440
- ][i];
5441
- const entranceClass = crabEntrancePhase === "hidden" ? "opacity-0 scale-0" : crabEntrancePhase === "playing" ? "animate-crab-piano-pop" : "";
5442
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
5443
- Crab,
5444
- {
5445
- className: `size-5 ${color} transition-all duration-300 ease-out group-hover:scale-125 group-hover:-translate-y-1 ${entranceClass}`,
5446
- style: {
5447
- transitionDelay: `${i * 50}ms`,
5448
- ...crabEntrancePhase === "playing" ? { animationDelay: `${i * 400}ms` } : {}
5449
- }
5450
- },
5451
- i
5452
- );
5453
- }) })
5454
- ] }),
5455
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 items-baseline gap-2 pl-1", children: [
5456
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate text-lg font-bold", children: "LLM Inspector" }),
5457
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "shrink-0 font-mono text-xs font-semibold text-muted-foreground", children: [
5458
- "v",
5459
- packageJson.version
5460
- ] })
5422
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sticky top-0 z-30 bg-background pt-6", children: [
5423
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-[1fr_auto_1fr] items-start gap-3 pb-8", children: [
5424
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", {}),
5425
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "flex min-w-0 flex-col items-center gap-2 text-center", children: [
5426
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex max-w-[calc(100vw-7rem)] items-end gap-2 whitespace-nowrap", children: [
5427
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
5428
+ "span",
5429
+ {
5430
+ className: "flex shrink-0 items-end gap-1 group cursor-default",
5431
+ "aria-hidden": "true",
5432
+ children: [
5433
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "size-10 text-amber-500 transition-all duration-300 group-hover:scale-125 group-hover:-translate-y-1.5" }),
5434
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden items-end gap-0.5 sm:flex", children: crabVariants.map((Crab, i) => {
5435
+ const color = [
5436
+ "text-amber-500",
5437
+ "text-rose-500",
5438
+ "text-sky-500",
5439
+ "text-emerald-500",
5440
+ "text-violet-500",
5441
+ "text-orange-500",
5442
+ "text-cyan-500",
5443
+ "text-pink-500",
5444
+ "text-lime-500",
5445
+ "text-blue-500",
5446
+ "text-yellow-500",
5447
+ "text-fuchsia-500"
5448
+ ][i];
5449
+ const entranceClass = crabEntrancePhase === "hidden" ? "opacity-0 scale-0" : crabEntrancePhase === "playing" ? "animate-crab-piano-pop" : "";
5450
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
5451
+ Crab,
5452
+ {
5453
+ className: `size-5 ${color} transition-all duration-300 ease-out group-hover:scale-125 group-hover:-translate-y-1 ${entranceClass}`,
5454
+ style: {
5455
+ transitionDelay: `${i * 50}ms`,
5456
+ ...crabEntrancePhase === "playing" ? { animationDelay: `${i * 400}ms` } : {}
5457
+ }
5458
+ },
5459
+ i
5460
+ );
5461
+ }) })
5462
+ ]
5463
+ }
5464
+ ),
5465
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 items-baseline gap-2 pl-1", children: [
5466
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate text-lg font-bold", children: "LLM Inspector" }),
5467
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "shrink-0 font-mono text-xs font-semibold text-muted-foreground", children: [
5468
+ "v",
5469
+ packageJson.version
5470
+ ] })
5471
+ ] }),
5472
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4 shrink-0 text-muted-foreground/70", "aria-hidden": "true" }),
5473
+ /* @__PURE__ */ jsxRuntimeExports.jsx(McpLogo, { className: "size-10 shrink-0" })
5461
5474
  ] }),
5462
- /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4 shrink-0 text-muted-foreground/70", "aria-hidden": "true" }),
5463
- /* @__PURE__ */ jsxRuntimeExports.jsx(McpLogo, { className: "size-10 shrink-0" })
5475
+ /* @__PURE__ */ jsxRuntimeExports.jsx(McpReadyBadge, {})
5464
5476
  ] }),
5465
- /* @__PURE__ */ jsxRuntimeExports.jsx(McpReadyBadge, {})
5466
- ] }),
5467
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "justify-self-end", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsDialog, {}) })
5468
- ] }),
5469
- pinnedSessionId !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
5470
- SessionContextBar,
5471
- {
5472
- sessionId: pinnedSessionId,
5473
- logs,
5474
- totalIn,
5475
- totalOut
5476
- }
5477
- ),
5478
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
5479
- !hideSessionFilter && /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedSession, onValueChange: onSessionChange, children: [
5480
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "flex-1 max-w-[350px] text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All sessions" }) }),
5481
- /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
5482
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "__all__", children: "All sessions" }),
5483
- sessions.map((s) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: s, children: truncateSessionId(s) }, s))
5484
- ] })
5477
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "justify-self-end", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsDialog, {}) })
5485
5478
  ] }),
5486
- /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedModel, onValueChange: onModelChange, children: [
5487
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "flex-1 max-w-[250px] text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All models" }) }),
5488
- /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
5489
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "__all__", children: "All models" }),
5490
- models.map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: m, children: m }, m))
5491
- ] })
5492
- ] }),
5493
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center border border-border rounded-md overflow-hidden", children: [
5494
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5479
+ pinnedSessionId !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
5480
+ SessionContextBar,
5481
+ {
5482
+ sessionId: pinnedSessionId,
5483
+ logs,
5484
+ totalIn,
5485
+ totalOut
5486
+ }
5487
+ ),
5488
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
5489
+ !hideSessionFilter && /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedSession, onValueChange: onSessionChange, children: [
5490
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "flex-1 max-w-[350px] text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All sessions" }) }),
5491
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
5492
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "__all__", children: "All sessions" }),
5493
+ sessions.map((s) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: s, children: truncateSessionId(s) }, s))
5494
+ ] })
5495
+ ] }),
5496
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedModel, onValueChange: onModelChange, children: [
5497
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "flex-1 max-w-[250px] text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All models" }) }),
5498
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
5499
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "__all__", children: "All models" }),
5500
+ models.map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: m, children: m }, m))
5501
+ ] })
5502
+ ] }),
5503
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center border border-border rounded-md overflow-hidden", children: [
5504
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5505
+ "button",
5506
+ {
5507
+ type: "button",
5508
+ onClick: () => onViewModeChange("simple"),
5509
+ className: `h-8 px-3 cursor-pointer transition-colors text-xs ${viewMode === "simple" ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
5510
+ children: "Simple"
5511
+ }
5512
+ ),
5513
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5514
+ "button",
5515
+ {
5516
+ type: "button",
5517
+ onClick: () => onViewModeChange("full"),
5518
+ className: `h-8 px-3 cursor-pointer transition-colors text-xs ${viewMode === "full" ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
5519
+ children: "Full"
5520
+ }
5521
+ )
5522
+ ] }),
5523
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1" }),
5524
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground text-xs font-mono", children: [
5525
+ logs.length,
5526
+ " request",
5527
+ logs.length !== 1 ? "s" : "",
5528
+ totalIn > 0 || totalOut > 0 ? ` · ${formatTokens(totalIn)} in / ${formatTokens(totalOut)} out` : ""
5529
+ ] }),
5530
+ logs.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
5495
5531
  "button",
5496
5532
  {
5497
5533
  type: "button",
5498
- onClick: () => onViewModeChange("simple"),
5499
- className: `h-8 px-3 cursor-pointer transition-colors text-xs ${viewMode === "simple" ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
5500
- children: "Simple"
5534
+ onClick: () => {
5535
+ void handleExport();
5536
+ },
5537
+ disabled: exporting,
5538
+ 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",
5539
+ title: "Export all logs as JSON ZIP",
5540
+ children: exporting ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Exporting..." }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5541
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { className: "size-3.5" }),
5542
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Export" })
5543
+ ] })
5501
5544
  }
5502
5545
  ),
5503
5546
  /* @__PURE__ */ jsxRuntimeExports.jsx(
5504
5547
  "button",
5505
5548
  {
5506
5549
  type: "button",
5507
- onClick: () => onViewModeChange("full"),
5508
- className: `h-8 px-3 cursor-pointer transition-colors text-xs ${viewMode === "full" ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
5509
- children: "Full"
5550
+ onClick: onClearAll,
5551
+ className: "h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer rounded-md hover:bg-muted",
5552
+ title: "Clear all logs",
5553
+ children: "Clear"
5510
5554
  }
5511
5555
  )
5512
- ] }),
5513
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1" }),
5514
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground text-xs font-mono", children: [
5515
- logs.length,
5516
- " request",
5517
- logs.length !== 1 ? "s" : "",
5518
- totalIn > 0 || totalOut > 0 ? ` · ${formatTokens(totalIn)} in / ${formatTokens(totalOut)} out` : ""
5519
- ] }),
5520
- logs.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
5521
- "button",
5522
- {
5523
- type: "button",
5524
- onClick: () => {
5525
- void handleExport();
5526
- },
5527
- disabled: exporting,
5528
- 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",
5529
- title: "Export all logs as JSON ZIP",
5530
- children: exporting ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Exporting..." }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5531
- /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { className: "size-3.5" }),
5532
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Export" })
5533
- ] })
5534
- }
5535
- ),
5536
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5537
- "button",
5538
- {
5539
- type: "button",
5540
- onClick: onClearAll,
5541
- className: "h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer rounded-md hover:bg-muted",
5542
- title: "Clear all logs",
5543
- children: "Clear"
5544
- }
5545
- )
5556
+ ] })
5546
5557
  ] }),
5547
5558
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: logs.length === 0 ? selectedSession !== "__all__" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center text-muted-foreground py-16 space-y-4", children: [
5548
5559
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium", children: "Session not found" }),