@tonyclaw/llm-inspector 1.10.0 → 1.11.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 (74) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/index-BIZeMR5N.js +105 -0
  3. package/.output/public/assets/index-SmtLx1lM.css +1 -0
  4. package/.output/public/assets/main-QfbQ5oBP.js +17 -0
  5. package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +1 -1
  6. package/.output/server/_libs/ajv-formats.mjs +18 -18
  7. package/.output/server/_libs/ajv.mjs +6306 -942
  8. package/.output/server/_libs/cookie-es.mjs +21 -7
  9. package/.output/server/_libs/diff.mjs +320 -0
  10. package/.output/server/_libs/floating-ui__core.mjs +17 -18
  11. package/.output/server/_libs/floating-ui__dom.mjs +20 -24
  12. package/.output/server/_libs/floating-ui__react-dom.mjs +28 -55
  13. package/.output/server/_libs/floating-ui__utils.mjs +36 -36
  14. package/.output/server/_libs/h3-v2.mjs +9 -20
  15. package/.output/server/_libs/isbot.mjs +2 -3
  16. package/.output/server/_libs/json-schema-traverse.mjs +90 -1
  17. package/.output/server/_libs/jszip.mjs +28 -28
  18. package/.output/server/_libs/lucide-react.mjs +103 -80
  19. package/.output/server/_libs/mdast-util-from-markdown.mjs +1 -1
  20. package/.output/server/_libs/pako.mjs +13 -13
  21. package/.output/server/_libs/property-information.mjs +1 -13
  22. package/.output/server/_libs/radix-ui__react-collection.mjs +1 -1
  23. package/.output/server/_libs/radix-ui__react-id.mjs +1 -1
  24. package/.output/server/_libs/react-dom.mjs +10 -10
  25. package/.output/server/_libs/react.mjs +44 -44
  26. package/.output/server/_libs/readable-stream.mjs +15 -15
  27. package/.output/server/_libs/rou3.mjs +7 -1
  28. package/.output/server/_libs/safe-buffer.mjs +3 -3
  29. package/.output/server/_libs/semver.mjs +10 -10
  30. package/.output/server/_libs/seroval-plugins.mjs +5 -5
  31. package/.output/server/_libs/seroval.mjs +596 -606
  32. package/.output/server/_libs/srvx.mjs +245 -10
  33. package/.output/server/_libs/swr.mjs +1 -1
  34. package/.output/server/_libs/tailwind-merge.mjs +41 -286
  35. package/.output/server/_libs/tanstack__history.mjs +44 -31
  36. package/.output/server/_libs/tanstack__react-router.mjs +1089 -780
  37. package/.output/server/_libs/tanstack__react-store.mjs +1 -1
  38. package/.output/server/_libs/tanstack__react-virtual.mjs +7 -82
  39. package/.output/server/_libs/tanstack__router-core.mjs +2331 -2226
  40. package/.output/server/_libs/tanstack__virtual-core.mjs +17 -58
  41. package/.output/server/_libs/tiny-invariant.mjs +12 -0
  42. package/.output/server/_libs/tiny-warning.mjs +5 -0
  43. package/.output/server/_libs/ungap__structured-clone.mjs +2 -14
  44. package/.output/server/_libs/use-sync-external-store.mjs +1 -1
  45. package/.output/server/_libs/zod.mjs +207 -505
  46. package/.output/server/_ssr/{index-XyU4WVWt.mjs → index-I_Nx8QEB.mjs} +532 -79
  47. package/.output/server/_ssr/index.mjs +776 -1098
  48. package/.output/server/_ssr/{router-BR4XO8XJ.mjs → router-BVxfVFsb.mjs} +98 -53
  49. package/.output/server/_tanstack-start-manifest_v-Cm2Owszu.mjs +4 -0
  50. package/.output/server/index.mjs +28 -28
  51. package/README.md +1 -1
  52. package/package.json +2 -1
  53. package/src/components/ProxyViewer.tsx +10 -3
  54. package/src/components/ProxyViewerContainer.tsx +6 -0
  55. package/src/components/proxy-viewer/ConversationGroup.tsx +10 -1
  56. package/src/components/proxy-viewer/LogEntry.tsx +114 -5
  57. package/src/components/proxy-viewer/diff/DiffView.tsx +319 -0
  58. package/src/components/proxy-viewer/diff/computeDiff.ts +178 -0
  59. package/src/components/proxy-viewer/diff/index.ts +3 -0
  60. package/src/components/ui/crab-logo.tsx +45 -0
  61. package/src/proxy/config.ts +3 -17
  62. package/src/proxy/dataDir.ts +56 -0
  63. package/src/proxy/formats/anthropic/stream.ts +5 -2
  64. package/src/proxy/formats/jsonSchema.ts +12 -0
  65. package/src/proxy/formats/openai/stream.ts +21 -16
  66. package/src/proxy/handler.ts +15 -5
  67. package/src/proxy/providers.ts +61 -1
  68. package/src/proxy/schemas.ts +42 -0
  69. package/src/proxy/store.ts +6 -23
  70. package/.output/public/assets/index-BFuUQkWu.js +0 -11
  71. package/.output/public/assets/index-F2ojFPJp.css +0 -1
  72. package/.output/public/assets/index-nKnhyW4Y.js +0 -122
  73. package/.output/server/_ssr/empty-plugin-adapters-BFgPZ6_d.mjs +0 -6
  74. package/.output/server/_tanstack-start-manifest_v-DFoSjBkb.mjs +0 -4
@@ -1,14 +1,15 @@
1
- import { r as reactExports, j as jsxRuntimeExports, R as React } from "../_libs/react.mjs";
2
- import { C as CapturedLogSchema, a as parseRequest, s as stripClaudeCodeBillingHeader, R as RuntimeConfigSchema, p as parseOpenAIResponse, I as InspectorResponseSchema } from "./router-BR4XO8XJ.mjs";
1
+ import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.mjs";
2
+ import { C as CapturedLogSchema, R as RuntimeConfigSchema, a as parseRequest, s as stripClaudeCodeBillingHeader, p as parseOpenAIResponse, I as InspectorResponseSchema } from "./router-BVxfVFsb.mjs";
3
+ import { u as useSWR, a as useSWRConfig } from "../_libs/swr.mjs";
3
4
  import { u as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
4
5
  import { J as JSZip } from "../_libs/jszip.mjs";
5
6
  import { c as clsx } from "../_libs/clsx.mjs";
6
7
  import { t as twMerge } from "../_libs/tailwind-merge.mjs";
7
8
  import { c as cva } from "../_libs/class-variance-authority.mjs";
8
- import { u as useSWR, a as useSWRConfig } from "../_libs/swr.mjs";
9
+ import { d as diffLines, a as diffJson } from "../_libs/diff.mjs";
9
10
  import { R as Root, T as Trigger$1, C as Content, a as Close, b as Title, P as Portal$1, O as Overlay } from "../_libs/radix-ui__react-dialog.mjs";
10
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";
11
- import { D as Download, L as LayoutGrid, a as List, S as Settings, C as ChevronDown, b as Check, R as RotateCcw, X, U as Upload, P as Plus, c as Copy, d as CircleAlert, e as ChevronUp, f as ChevronRight, g as Clock, M as MessageSquare, Z as Zap, h as LoaderCircle, W as Wrench, G as Globe, i as User, F as FileTerminal, j as Radio, E as ExternalLink, k as EyeOff, l as Eye, m as RotateCw, n as Pencil, T as Trash2, o as TriangleAlert, p as Minus, q as CircleCheckBig, r as CircleStop, s as CircleQuestionMark, t as Server, u as Gauge, v as Lock, w as Wifi, x as WifiOff, y as ChevronsUp, z as ChevronsDown, B as Brain, A as Terminal } from "../_libs/lucide-react.mjs";
12
+ import { D as Download, L as LayoutGrid, a as List, S as Settings, C as ChevronDown, b as Check, R as RotateCcw, X, U as Upload, P as Plus, c as Copy, d as CircleAlert, e as ChevronUp, f as ChevronRight, g as Clock, M as MessageSquare, Z as Zap, h as LoaderCircle, W as Wrench, G as Globe, i as User, F as FileTerminal, j as Radio, k as GitCompareArrows, l as Rows3, m as Columns2, E as ExternalLink, n as EyeOff, o as Eye, p as RotateCw, q as Pencil, T as Trash2, r as TriangleAlert, s as Minus, t as CircleCheckBig, u as CircleStop, v as CircleQuestionMark, w as Server, x as Gauge, y as Lock, z as Wifi, A as WifiOff, B as ChevronsUp, H as ChevronsDown, I as Brain, J as Terminal } from "../_libs/lucide-react.mjs";
12
13
  import { M as Markdown } from "../_libs/react-markdown.mjs";
13
14
  import { a as array, s as string, u as union, o as object, l as literal, n as number, b as boolean } from "../_libs/zod.mjs";
14
15
  import { R as Root2$1, L as List$1, T as Trigger$2, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
@@ -18,9 +19,11 @@ import { R as Root$1 } from "../_libs/radix-ui__react-separator.mjs";
18
19
  import { R as Root$2, C as CollapsibleTrigger$1, a as CollapsibleContent$1 } from "../_libs/radix-ui__react-collapsible.mjs";
19
20
  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";
20
21
  import "../_libs/tanstack__react-router.mjs";
22
+ import "../_libs/tiny-warning.mjs";
21
23
  import "../_libs/tanstack__router-core.mjs";
22
- import "../_libs/tanstack__history.mjs";
23
24
  import "../_libs/cookie-es.mjs";
25
+ import "../_libs/tanstack__history.mjs";
26
+ import "../_libs/tiny-invariant.mjs";
24
27
  import "../_libs/seroval.mjs";
25
28
  import "../_libs/seroval-plugins.mjs";
26
29
  import "node:stream/web";
@@ -159,6 +162,62 @@ import "../_libs/hast-util-whitespace.mjs";
159
162
  import "../_libs/estree-util-is-identifier-name.mjs";
160
163
  import "../_libs/html-url-attributes.mjs";
161
164
  import "../_libs/radix-ui__react-roving-focus.mjs";
165
+ const STRIP_CONFIG_SWR_KEY = "/api/config";
166
+ async function fetcher$1(url) {
167
+ const response = await fetch(url);
168
+ if (!response.ok) {
169
+ throw new Error(`Failed to fetch ${url}: ${response.status}`);
170
+ }
171
+ const data = await response.json();
172
+ return RuntimeConfigSchema.parse(data);
173
+ }
174
+ function extractErrorMessage(errorBody, fallback) {
175
+ if (typeof errorBody !== "object" || errorBody === null) return fallback;
176
+ const candidate = errorBody.error;
177
+ return typeof candidate === "string" ? candidate : fallback;
178
+ }
179
+ async function setRuntimeConfig(patch) {
180
+ const response = await fetch(STRIP_CONFIG_SWR_KEY, {
181
+ method: "PATCH",
182
+ headers: { "content-type": "application/json" },
183
+ body: JSON.stringify(patch)
184
+ });
185
+ if (!response.ok) {
186
+ const errorBody = await response.json().catch(() => ({}));
187
+ throw new Error(extractErrorMessage(errorBody, `PATCH failed with status ${response.status}`));
188
+ }
189
+ const data = await response.json();
190
+ return RuntimeConfigSchema.parse(data);
191
+ }
192
+ function useStripConfig() {
193
+ const response = useSWR(
194
+ STRIP_CONFIG_SWR_KEY,
195
+ fetcher$1,
196
+ {
197
+ revalidateOnFocus: false,
198
+ revalidateIfStale: false
199
+ }
200
+ );
201
+ const { mutate: globalMutate } = useSWRConfig();
202
+ const strip = response.data?.stripClaudeCodeBillingHeader ?? false;
203
+ const setStrip = async (next) => {
204
+ await globalMutate(
205
+ STRIP_CONFIG_SWR_KEY,
206
+ setRuntimeConfig({ stripClaudeCodeBillingHeader: next }),
207
+ {
208
+ optimisticData: { stripClaudeCodeBillingHeader: next },
209
+ rollbackOnError: true,
210
+ revalidate: false
211
+ }
212
+ );
213
+ };
214
+ return {
215
+ strip,
216
+ isLoading: response.isLoading,
217
+ isError: response.error !== void 0,
218
+ setStrip
219
+ };
220
+ }
162
221
  async function fetchStreamingChunks(logId) {
163
222
  try {
164
223
  const response = await fetch(`/api/logs/${logId}/chunks`);
@@ -198,7 +257,7 @@ async function exportLogsAsZip(logs) {
198
257
  document.body.removeChild(anchor);
199
258
  URL.revokeObjectURL(url);
200
259
  }
201
- const version = "1.10.0";
260
+ const version = "1.11.0";
202
261
  const packageJson = {
203
262
  version
204
263
  };
@@ -391,62 +450,6 @@ function groupLogsByConversation(logs) {
391
450
  result.sort((a, b) => (a.logs[0]?.timestamp ?? "").localeCompare(b.logs[0]?.timestamp ?? ""));
392
451
  return result;
393
452
  }
394
- const STRIP_CONFIG_SWR_KEY = "/api/config";
395
- async function fetcher$1(url) {
396
- const response = await fetch(url);
397
- if (!response.ok) {
398
- throw new Error(`Failed to fetch ${url}: ${response.status}`);
399
- }
400
- const data = await response.json();
401
- return RuntimeConfigSchema.parse(data);
402
- }
403
- function extractErrorMessage(errorBody, fallback) {
404
- if (typeof errorBody !== "object" || errorBody === null) return fallback;
405
- const candidate = errorBody.error;
406
- return typeof candidate === "string" ? candidate : fallback;
407
- }
408
- async function setRuntimeConfig(patch) {
409
- const response = await fetch(STRIP_CONFIG_SWR_KEY, {
410
- method: "PATCH",
411
- headers: { "content-type": "application/json" },
412
- body: JSON.stringify(patch)
413
- });
414
- if (!response.ok) {
415
- const errorBody = await response.json().catch(() => ({}));
416
- throw new Error(extractErrorMessage(errorBody, `PATCH failed with status ${response.status}`));
417
- }
418
- const data = await response.json();
419
- return RuntimeConfigSchema.parse(data);
420
- }
421
- function useStripConfig() {
422
- const response = useSWR(
423
- STRIP_CONFIG_SWR_KEY,
424
- fetcher$1,
425
- {
426
- revalidateOnFocus: false,
427
- revalidateIfStale: false
428
- }
429
- );
430
- const { mutate: globalMutate } = useSWRConfig();
431
- const strip = response.data?.stripClaudeCodeBillingHeader ?? false;
432
- const setStrip = async (next) => {
433
- await globalMutate(
434
- STRIP_CONFIG_SWR_KEY,
435
- setRuntimeConfig({ stripClaudeCodeBillingHeader: next }),
436
- {
437
- optimisticData: { stripClaudeCodeBillingHeader: next },
438
- rollbackOnError: true,
439
- revalidate: false
440
- }
441
- );
442
- };
443
- return {
444
- strip,
445
- isLoading: response.isLoading,
446
- isError: response.error !== void 0,
447
- setStrip
448
- };
449
- }
450
453
  const buttonVariants = cva(
451
454
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
452
455
  {
@@ -958,6 +961,332 @@ function TabsContent({
958
961
  }
959
962
  );
960
963
  }
964
+ function normalizeHeaders(headers) {
965
+ if (headers === void 0) return [];
966
+ return Object.entries(headers).map((entry) => [entry[0].toLowerCase(), entry[1]]).sort(([a], [b]) => a.localeCompare(b));
967
+ }
968
+ function headerLine([key, value]) {
969
+ return `${key}: ${value}`;
970
+ }
971
+ function changesToLines(changes) {
972
+ const lines = [];
973
+ let oldNum = 0;
974
+ let newNum = 0;
975
+ for (const change of changes) {
976
+ const parts = change.value.split("\n");
977
+ if (parts.length > 0 && parts[parts.length - 1] === "") parts.pop();
978
+ for (const text of parts) {
979
+ if (change.added) {
980
+ newNum += 1;
981
+ lines.push({ kind: "added", text, oldLineNumber: null, newLineNumber: newNum });
982
+ } else if (change.removed) {
983
+ oldNum += 1;
984
+ lines.push({ kind: "removed", text, oldLineNumber: oldNum, newLineNumber: null });
985
+ } else {
986
+ oldNum += 1;
987
+ newNum += 1;
988
+ lines.push({
989
+ kind: "context",
990
+ text,
991
+ oldLineNumber: oldNum,
992
+ newLineNumber: newNum
993
+ });
994
+ }
995
+ }
996
+ }
997
+ return lines;
998
+ }
999
+ function computeHeadersDiff(rawHeaders, processedHeaders) {
1000
+ const left = normalizeHeaders(rawHeaders).map(headerLine).join("\n");
1001
+ const right = normalizeHeaders(processedHeaders).map(headerLine).join("\n");
1002
+ if (left === right) {
1003
+ return { lines: [], isEmpty: true };
1004
+ }
1005
+ const changes = diffLines(left, right);
1006
+ return { lines: changesToLines(changes), isEmpty: false };
1007
+ }
1008
+ function tryParseJson(text) {
1009
+ try {
1010
+ const parsed = JSON.parse(text);
1011
+ if (parsed === null) return parsed;
1012
+ if (typeof parsed === "string" || typeof parsed === "object") return parsed;
1013
+ return null;
1014
+ } catch {
1015
+ return null;
1016
+ }
1017
+ }
1018
+ function computeRequestDiff(rawBody, processedBody) {
1019
+ const left = rawBody ?? "";
1020
+ const right = processedBody ?? "";
1021
+ if (left === right) {
1022
+ return { lines: [], isEmpty: true };
1023
+ }
1024
+ const leftJson = tryParseJson(left);
1025
+ const rightJson = tryParseJson(right);
1026
+ if (leftJson !== null && rightJson !== null) {
1027
+ const changes2 = diffJson(leftJson, rightJson) ?? [];
1028
+ if (changes2.length === 1) {
1029
+ const first = changes2[0];
1030
+ if (first !== void 0 && !first.added && !first.removed) {
1031
+ return { lines: [], isEmpty: true };
1032
+ }
1033
+ }
1034
+ return { lines: changesToLines(changes2), isEmpty: false };
1035
+ }
1036
+ const changes = diffLines(left, right);
1037
+ return { lines: changesToLines(changes), isEmpty: false };
1038
+ }
1039
+ const ESTIMATED_LINE_HEIGHT = 22;
1040
+ const MAX_RENDERED_LINES = 1e4;
1041
+ function kindPrefix(kind) {
1042
+ if (kind === "added") return "+";
1043
+ if (kind === "removed") return "-";
1044
+ return " ";
1045
+ }
1046
+ function kindClass(kind) {
1047
+ if (kind === "added") {
1048
+ return "bg-green-500/10 text-green-700 dark:text-green-300";
1049
+ }
1050
+ if (kind === "removed") {
1051
+ return "bg-red-500/10 text-red-700 dark:text-red-300";
1052
+ }
1053
+ return "text-foreground/80";
1054
+ }
1055
+ function DiffView({ result, emptyLabel }) {
1056
+ const [mode, setMode] = reactExports.useState("unified");
1057
+ const scrollRef = reactExports.useRef(null);
1058
+ const virtualizer = useVirtualizer({
1059
+ count: result.lines.length,
1060
+ getScrollElement: () => scrollRef.current,
1061
+ estimateSize: () => ESTIMATED_LINE_HEIGHT,
1062
+ overscan: 10
1063
+ });
1064
+ if (result.isEmpty) {
1065
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1066
+ "div",
1067
+ {
1068
+ className: "rounded border border-dashed border-border bg-muted/30 px-4 py-6 text-center text-xs text-muted-foreground",
1069
+ "data-testid": "diff-empty",
1070
+ children: emptyLabel ?? "No transformation applied — raw and processed are identical."
1071
+ }
1072
+ );
1073
+ }
1074
+ const totalLines = result.lines.length;
1075
+ const truncated = totalLines > MAX_RENDERED_LINES;
1076
+ const visibleLines = truncated ? result.lines.slice(0, MAX_RENDERED_LINES) : result.lines;
1077
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
1078
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: [
1079
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
1080
+ totalLines.toLocaleString(),
1081
+ " line",
1082
+ totalLines === 1 ? "" : "s",
1083
+ truncated && ` (showing first ${MAX_RENDERED_LINES.toLocaleString()})`
1084
+ ] }),
1085
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "inline-flex rounded-md border border-border overflow-hidden", children: [
1086
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1087
+ "button",
1088
+ {
1089
+ type: "button",
1090
+ onClick: () => setMode("unified"),
1091
+ "aria-pressed": mode === "unified",
1092
+ className: cn(
1093
+ "flex items-center gap-1 px-2 py-1 transition-colors",
1094
+ mode === "unified" ? "bg-muted text-foreground" : "hover:bg-muted/50"
1095
+ ),
1096
+ title: "Unified diff (single column)",
1097
+ children: [
1098
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Rows3, { className: "size-3" }),
1099
+ "Unified"
1100
+ ]
1101
+ }
1102
+ ),
1103
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1104
+ "button",
1105
+ {
1106
+ type: "button",
1107
+ onClick: () => setMode("split"),
1108
+ "aria-pressed": mode === "split",
1109
+ className: cn(
1110
+ "flex items-center gap-1 px-2 py-1 transition-colors border-l border-border",
1111
+ mode === "split" ? "bg-muted text-foreground" : "hover:bg-muted/50"
1112
+ ),
1113
+ title: "Split diff (side by side)",
1114
+ children: [
1115
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Columns2, { className: "size-3" }),
1116
+ "Split"
1117
+ ]
1118
+ }
1119
+ )
1120
+ ] })
1121
+ ] }),
1122
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1123
+ "div",
1124
+ {
1125
+ ref: scrollRef,
1126
+ className: "max-h-[60vh] overflow-auto rounded border border-border bg-background font-mono text-xs",
1127
+ "data-testid": "diff-viewport",
1128
+ children: mode === "unified" ? /* @__PURE__ */ jsxRuntimeExports.jsx(UnifiedRows, { virtualizer, lines: visibleLines }) : /* @__PURE__ */ jsxRuntimeExports.jsx(SplitRows, { lines: visibleLines })
1129
+ }
1130
+ )
1131
+ ] });
1132
+ }
1133
+ function UnifiedRows({
1134
+ virtualizer,
1135
+ lines
1136
+ }) {
1137
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1138
+ "div",
1139
+ {
1140
+ style: {
1141
+ height: `${virtualizer.getTotalSize()}px`,
1142
+ width: "100%",
1143
+ position: "relative"
1144
+ },
1145
+ children: virtualizer.getVirtualItems().map((virtualRow) => {
1146
+ const line = lines[virtualRow.index];
1147
+ if (line === void 0) return null;
1148
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1149
+ "div",
1150
+ {
1151
+ "data-index": virtualRow.index,
1152
+ ref: virtualizer.measureElement,
1153
+ className: cn("flex items-start gap-3 px-2", kindClass(line.kind)),
1154
+ style: {
1155
+ position: "absolute",
1156
+ top: 0,
1157
+ left: 0,
1158
+ width: "100%",
1159
+ transform: `translateY(${virtualRow.start}px)`
1160
+ },
1161
+ children: [
1162
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "select-none text-muted-foreground/60 w-10 text-right tabular-nums", children: line.oldLineNumber ?? "" }),
1163
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "select-none text-muted-foreground/60 w-10 text-right tabular-nums", children: line.newLineNumber ?? "" }),
1164
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1165
+ "span",
1166
+ {
1167
+ className: cn(
1168
+ "select-none w-3 text-right font-bold",
1169
+ line.kind === "added" && "text-green-600 dark:text-green-400",
1170
+ line.kind === "removed" && "text-red-600 dark:text-red-400"
1171
+ ),
1172
+ children: kindPrefix(line.kind)
1173
+ }
1174
+ ),
1175
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "whitespace-pre-wrap break-all flex-1", children: line.text })
1176
+ ]
1177
+ },
1178
+ virtualRow.key
1179
+ );
1180
+ })
1181
+ }
1182
+ );
1183
+ }
1184
+ function buildSplitRows(lines) {
1185
+ const rows = [];
1186
+ let i = 0;
1187
+ while (i < lines.length) {
1188
+ const line = lines[i];
1189
+ if (line === void 0) break;
1190
+ if (line.kind === "context") {
1191
+ rows.push({
1192
+ left: { text: line.text, lineNumber: line.oldLineNumber },
1193
+ right: { text: line.text, lineNumber: line.newLineNumber },
1194
+ kind: "equal"
1195
+ });
1196
+ i += 1;
1197
+ continue;
1198
+ }
1199
+ const removes = [];
1200
+ const adds = [];
1201
+ while (i < lines.length && lines[i]?.kind === "removed") {
1202
+ const removed = lines[i];
1203
+ if (removed !== void 0) removes.push(removed);
1204
+ i += 1;
1205
+ }
1206
+ while (i < lines.length && lines[i]?.kind === "added") {
1207
+ const added = lines[i];
1208
+ if (added !== void 0) adds.push(added);
1209
+ i += 1;
1210
+ }
1211
+ const pairCount = Math.max(removes.length, adds.length);
1212
+ for (let p = 0; p < pairCount; p += 1) {
1213
+ const r = removes[p];
1214
+ const a = adds[p];
1215
+ if (r !== void 0 && a !== void 0) {
1216
+ rows.push({
1217
+ left: { text: r.text, lineNumber: r.oldLineNumber },
1218
+ right: { text: a.text, lineNumber: a.newLineNumber },
1219
+ kind: "changed"
1220
+ });
1221
+ } else if (r !== void 0) {
1222
+ rows.push({
1223
+ left: { text: r.text, lineNumber: r.oldLineNumber },
1224
+ right: null,
1225
+ kind: "removed"
1226
+ });
1227
+ } else if (a !== void 0) {
1228
+ rows.push({
1229
+ left: null,
1230
+ right: { text: a.text, lineNumber: a.newLineNumber },
1231
+ kind: "added"
1232
+ });
1233
+ }
1234
+ }
1235
+ }
1236
+ return rows;
1237
+ }
1238
+ function SplitRowClass(kind) {
1239
+ switch (kind) {
1240
+ case "changed":
1241
+ return "bg-amber-500/10";
1242
+ case "removed":
1243
+ return "bg-red-500/10";
1244
+ case "added":
1245
+ return "bg-green-500/10";
1246
+ case "equal":
1247
+ return "";
1248
+ }
1249
+ }
1250
+ function SplitRows({ lines }) {
1251
+ const rows = buildSplitRows(lines);
1252
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { "data-testid": "diff-split", children: rows.map((row, idx) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
1253
+ "div",
1254
+ {
1255
+ className: cn("grid grid-cols-2 font-mono text-xs", SplitRowClass(row.kind)),
1256
+ children: [
1257
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-2 px-2 border-r border-border/50", children: [
1258
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "select-none text-muted-foreground/60 w-8 text-right tabular-nums", children: row.left?.lineNumber ?? "" }),
1259
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1260
+ "span",
1261
+ {
1262
+ className: cn(
1263
+ "select-none w-3 text-right font-bold",
1264
+ (row.kind === "removed" || row.kind === "changed") && "text-red-600 dark:text-red-400"
1265
+ ),
1266
+ children: row.left !== null ? "-" : " "
1267
+ }
1268
+ ),
1269
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "whitespace-pre-wrap break-all flex-1", children: row.left?.text ?? "" })
1270
+ ] }),
1271
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-2 px-2", children: [
1272
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "select-none text-muted-foreground/60 w-8 text-right tabular-nums", children: row.right?.lineNumber ?? "" }),
1273
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1274
+ "span",
1275
+ {
1276
+ className: cn(
1277
+ "select-none w-3 text-right font-bold",
1278
+ (row.kind === "added" || row.kind === "changed") && "text-green-600 dark:text-green-400"
1279
+ ),
1280
+ children: row.right !== null ? "+" : " "
1281
+ }
1282
+ ),
1283
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "whitespace-pre-wrap break-all flex-1", children: row.right?.text ?? "" })
1284
+ ] })
1285
+ ]
1286
+ },
1287
+ idx
1288
+ )) });
1289
+ }
961
1290
  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";
962
1291
  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";
963
1292
  const DeepSeekLogoSvg = "data:image/svg+xml,%3csvg%20fill='none'%20height='1320'%20viewBox='3.771%206.973%2023.993%2017.652'%20width='2500'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='m27.501%208.469c-.252-.123-.36.111-.508.23-.05.04-.093.09-.135.135-.368.395-.797.652-1.358.621-.821-.045-1.521.213-2.14.842-.132-.776-.57-1.238-1.235-1.535-.349-.155-.701-.309-.944-.645-.171-.238-.217-.504-.303-.765-.054-.159-.108-.32-.29-.348-.197-.031-.274.135-.352.273-.31.567-.43%201.192-.419%201.825.028%201.421.628%202.554%201.82%203.36.136.093.17.186.128.321-.081.278-.178.547-.264.824-.054.178-.135.217-.324.14a5.448%205.448%200%200%201%20-1.719-1.169c-.848-.82-1.614-1.726-2.57-2.435-.225-.166-.449-.32-.681-.467-.976-.95.128-1.729.383-1.82.267-.096.093-.428-.77-.424s-1.653.293-2.659.677a2.782%202.782%200%200%201%20-.46.135%209.554%209.554%200%200%200%20-2.853-.1c-1.866.21-3.356%201.092-4.452%202.6-1.315%201.81-1.625%203.87-1.246%206.018.399%202.261%201.552%204.136%203.326%205.601%201.837%201.518%203.955%202.262%206.37%202.12%201.466-.085%203.1-.282%204.942-1.842.465.23.952.322%201.762.392.623.059%201.223-.031%201.687-.127.728-.154.677-.828.414-.953-2.132-.994-1.665-.59-2.09-.916%201.084-1.285%202.717-2.619%203.356-6.94.05-.343.007-.558%200-.837-.004-.168.034-.235.228-.254a4.084%204.084%200%200%200%201.529-.47c1.382-.757%201.938-1.997%202.07-3.485.02-.227-.004-.463-.243-.582zm-12.041%2013.391c-2.067-1.627-3.07-2.162-3.483-2.138-.387.021-.318.465-.233.754.089.285.205.482.368.732.113.166.19.414-.112.598-.666.414-1.823-.139-1.878-.166-1.347-.793-2.473-1.842-3.267-3.276-.765-1.38-1.21-2.861-1.284-4.441-.02-.383.093-.518.472-.586a4.692%204.692%200%200%201%201.514-.04c2.109.31%203.905%201.255%205.41%202.749.86.853%201.51%201.871%202.18%202.865.711%201.057%201.478%202.063%202.454%202.887.343.289.619.51.881.672-.792.088-2.117.107-3.022-.61zm.99-6.38a.304.304%200%201%201%20.609%200c0%20.17-.136.304-.306.304a.3.3%200%200%201%20-.303-.305zm3.077%201.581c-.197.08-.394.15-.584.159a1.246%201.246%200%200%201%20-.79-.252c-.27-.227-.463-.354-.546-.752a1.752%201.752%200%200%201%20.016-.582c.07-.324-.008-.531-.235-.72-.187-.155-.422-.196-.682-.196a.551.551%200%200%201%20-.252-.078c-.108-.055-.197-.19-.112-.356.027-.053.159-.183.19-.207.352-.201.758-.135%201.134.016.349.142.611.404.99.773.388.448.457.573.678.906.174.264.333.534.441.842.066.192-.02.35-.248.448z'%20fill='%234d6bfe'/%3e%3c/svg%3e";
@@ -1958,6 +2287,12 @@ function StreamingChunkSequence({
1958
2287
  function shouldShowRawRequestTab(apiFormat, viewMode, strip) {
1959
2288
  return apiFormat === "anthropic" && viewMode === "full" && strip;
1960
2289
  }
2290
+ function shouldShowHeadersDiffButton(viewMode, hasRawHeaders) {
2291
+ return viewMode === "full" && hasRawHeaders;
2292
+ }
2293
+ function shouldShowRequestDiffButton(apiFormat, viewMode, strip, hasRawRequest) {
2294
+ return apiFormat === "anthropic" && viewMode === "full" && strip && hasRawRequest;
2295
+ }
1961
2296
  function CopyButton({
1962
2297
  text,
1963
2298
  label,
@@ -1981,17 +2316,41 @@ function CopyButton({
1981
2316
  }
1982
2317
  );
1983
2318
  }
2319
+ function DiffToggleButton({
2320
+ active,
2321
+ onClick
2322
+ }) {
2323
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
2324
+ "button",
2325
+ {
2326
+ type: "button",
2327
+ onClick,
2328
+ "aria-pressed": active,
2329
+ className: cn(
2330
+ "flex items-center gap-1.5 text-xs px-2 py-1 rounded transition-colors",
2331
+ active ? "bg-primary/10 text-primary" : "text-muted-foreground hover:text-foreground hover:bg-muted"
2332
+ ),
2333
+ title: active ? "Hide diff with raw" : "Diff this view against the raw version",
2334
+ children: [
2335
+ /* @__PURE__ */ jsxRuntimeExports.jsx(GitCompareArrows, { className: "size-3" }),
2336
+ active ? "Showing diff" : "Diff with Raw"
2337
+ ]
2338
+ }
2339
+ );
2340
+ }
1984
2341
  const LogEntry = reactExports.memo(function({
1985
2342
  log,
1986
2343
  viewMode = "simple",
1987
- suppressApiFormatBadge = false
2344
+ suppressApiFormatBadge = false,
2345
+ strip
1988
2346
  }) {
1989
2347
  const [expanded, setExpanded] = reactExports.useState(false);
1990
2348
  const [requestCopied, setRequestCopied] = reactExports.useState(false);
1991
2349
  const [rawRequestCopied, setRawRequestCopied] = reactExports.useState(false);
1992
2350
  const [responseCopied, setResponseCopied] = reactExports.useState(false);
1993
2351
  const [replayOpen, setReplayOpen] = reactExports.useState(false);
1994
- const { strip } = useStripConfig();
2352
+ const [headersDiff, setHeadersDiff] = reactExports.useState(false);
2353
+ const [requestDiff, setRequestDiff] = reactExports.useState(false);
1995
2354
  const parsedRequest = reactExports.useMemo(() => parseRequest(log.rawRequestBody), [log.rawRequestBody]);
1996
2355
  const strippedRequestBody = reactExports.useMemo(() => {
1997
2356
  if (!strip || log.apiFormat !== "anthropic" || log.rawRequestBody === null) {
@@ -2000,6 +2359,14 @@ const LogEntry = reactExports.memo(function({
2000
2359
  return stripClaudeCodeBillingHeader(log.rawRequestBody).body;
2001
2360
  }, [log.rawRequestBody, log.apiFormat, strip]);
2002
2361
  const displayedRequestBody = strippedRequestBody ?? log.rawRequestBody;
2362
+ const headersDiffResult = reactExports.useMemo(
2363
+ () => computeHeadersDiff(log.rawHeaders, log.headers),
2364
+ [log.rawHeaders, log.headers]
2365
+ );
2366
+ const requestDiffResult = reactExports.useMemo(
2367
+ () => computeRequestDiff(log.rawRequestBody, displayedRequestBody),
2368
+ [log.rawRequestBody, displayedRequestBody]
2369
+ );
2003
2370
  function handleCopyRequest(e) {
2004
2371
  e.stopPropagation();
2005
2372
  if (displayedRequestBody === null) return;
@@ -2059,6 +2426,21 @@ const LogEntry = reactExports.memo(function({
2059
2426
  ] }) }),
2060
2427
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "request", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3", children: [
2061
2428
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-2 mb-2", children: [
2429
+ shouldShowRequestDiffButton(
2430
+ log.apiFormat,
2431
+ viewMode,
2432
+ strip,
2433
+ log.rawRequestBody !== null
2434
+ ) && /* @__PURE__ */ jsxRuntimeExports.jsx(
2435
+ DiffToggleButton,
2436
+ {
2437
+ active: requestDiff,
2438
+ onClick: (e) => {
2439
+ e.stopPropagation();
2440
+ setRequestDiff(!requestDiff);
2441
+ }
2442
+ }
2443
+ ),
2062
2444
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
2063
2445
  Button,
2064
2446
  {
@@ -2085,15 +2467,42 @@ const LogEntry = reactExports.memo(function({
2085
2467
  }
2086
2468
  )
2087
2469
  ] }),
2088
- displayedRequestBody !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(JsonViewerFromString, { text: displayedRequestBody, defaultExpandDepth: 1 }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No request body" })
2470
+ requestDiff ? /* @__PURE__ */ jsxRuntimeExports.jsx(
2471
+ DiffView,
2472
+ {
2473
+ result: requestDiffResult,
2474
+ emptyLabel: "No transformation applied — raw and sent request bodies are identical."
2475
+ }
2476
+ ) : displayedRequestBody !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(JsonViewerFromString, { text: displayedRequestBody, defaultExpandDepth: 1 }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No request body" })
2477
+ ] }) }),
2478
+ viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "headers", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3", children: [
2479
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end gap-2 mb-2", children: shouldShowHeadersDiffButton(
2480
+ viewMode,
2481
+ log.rawHeaders !== void 0 && Object.keys(log.rawHeaders).length > 0
2482
+ ) && /* @__PURE__ */ jsxRuntimeExports.jsx(
2483
+ DiffToggleButton,
2484
+ {
2485
+ active: headersDiff,
2486
+ onClick: (e) => {
2487
+ e.stopPropagation();
2488
+ setHeadersDiff(!headersDiff);
2489
+ }
2490
+ }
2491
+ ) }),
2492
+ headersDiff ? /* @__PURE__ */ jsxRuntimeExports.jsx(
2493
+ DiffView,
2494
+ {
2495
+ result: headersDiffResult,
2496
+ emptyLabel: "No transformation applied — raw and processed headers are identical."
2497
+ }
2498
+ ) : log.headers && Object.keys(log.headers).length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 font-mono text-xs", children: Object.entries(log.headers).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
2499
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-600 dark:text-blue-400 font-semibold shrink-0", children: [
2500
+ key,
2501
+ ":"
2502
+ ] }),
2503
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground truncate", title: value, children: value })
2504
+ ] }, key)) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No headers captured" })
2089
2505
  ] }) }),
2090
- viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "headers", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 py-3", children: log.headers && Object.keys(log.headers).length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 font-mono text-xs", children: Object.entries(log.headers).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
2091
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-600 dark:text-blue-400 font-semibold shrink-0", children: [
2092
- key,
2093
- ":"
2094
- ] }),
2095
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground truncate", title: value, children: value })
2096
- ] }, key)) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No headers captured" }) }) }),
2097
2506
  viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-headers", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 py-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: [
2098
2507
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-600 dark:text-blue-400 font-semibold shrink-0", children: [
2099
2508
  key,
@@ -2154,7 +2563,8 @@ function computeStats(logs) {
2154
2563
  }
2155
2564
  const ConversationGroup = reactExports.memo(function({
2156
2565
  group,
2157
- viewMode = "simple"
2566
+ viewMode = "simple",
2567
+ strip
2158
2568
  }) {
2159
2569
  const [expanded, setExpanded] = reactExports.useState(false);
2160
2570
  const stats = computeStats(group.logs);
@@ -2178,9 +2588,46 @@ const ConversationGroup = reactExports.memo(function({
2178
2588
  hideApiFormat: mixed
2179
2589
  }
2180
2590
  ),
2181
- expanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pl-4 border-l-2 border-muted ml-3", children: group.logs.map((log) => /* @__PURE__ */ jsxRuntimeExports.jsx(LogEntry, { log, viewMode, suppressApiFormatBadge: !mixed }, log.id)) })
2591
+ expanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pl-4 border-l-2 border-muted ml-3", children: group.logs.map((log) => /* @__PURE__ */ jsxRuntimeExports.jsx(
2592
+ LogEntry,
2593
+ {
2594
+ log,
2595
+ viewMode,
2596
+ suppressApiFormatBadge: !mixed,
2597
+ strip
2598
+ },
2599
+ log.id
2600
+ )) })
2182
2601
  ] });
2183
2602
  });
2603
+ function CrabLogo({ className }) {
2604
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
2605
+ "svg",
2606
+ {
2607
+ viewBox: "0 0 24 24",
2608
+ fill: "none",
2609
+ stroke: "currentColor",
2610
+ strokeWidth: "1.5",
2611
+ strokeLinecap: "round",
2612
+ strokeLinejoin: "round",
2613
+ "aria-hidden": "true",
2614
+ className: cn("inline-block size-5", className),
2615
+ children: [
2616
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z" }),
2617
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
2618
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" }),
2619
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
2620
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" }),
2621
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
2622
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
2623
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6.5", y1: "16", x2: "4.5", y2: "19.5" }),
2624
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9", y1: "17.5", x2: "8", y2: "20.5" }),
2625
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "15", y1: "17.5", x2: "16", y2: "20.5" }),
2626
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17.5", y1: "16", x2: "19.5", y2: "19.5" })
2627
+ ]
2628
+ }
2629
+ );
2630
+ }
2184
2631
  function Select({
2185
2632
  ...props
2186
2633
  }) {
@@ -3334,7 +3781,8 @@ function ProxyViewer({
3334
3781
  onModelChange,
3335
3782
  onClearAll,
3336
3783
  viewMode,
3337
- onViewModeChange
3784
+ onViewModeChange,
3785
+ strip
3338
3786
  }) {
3339
3787
  const { totalIn, totalOut } = computeTokenSummary(logs);
3340
3788
  const [groupedView, setGroupedView] = reactExports.useState(true);
@@ -3359,7 +3807,10 @@ function ProxyViewer({
3359
3807
  });
3360
3808
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-[1200px] mx-auto flex flex-col h-screen", style: { maxHeight: "100vh" }, children: [
3361
3809
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 mb-4 px-6 pt-6", children: [
3362
- /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-lg font-bold flex-1", children: "LLM Proxy Inspector" }),
3810
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "text-lg font-bold flex-1 flex items-center gap-2", children: [
3811
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "size-10 text-amber-500" }),
3812
+ "LLM Inspector"
3813
+ ] }),
3363
3814
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground text-xs font-mono", children: [
3364
3815
  "v",
3365
3816
  packageJson.version
@@ -3484,7 +3935,7 @@ function ProxyViewer({
3484
3935
  width: "100%",
3485
3936
  transform: `translateY(${virtualRow.start}px)`
3486
3937
  },
3487
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(ConversationGroup, { group, viewMode })
3938
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ConversationGroup, { group, viewMode, strip })
3488
3939
  },
3489
3940
  group.id
3490
3941
  );
@@ -3503,7 +3954,7 @@ function ProxyViewer({
3503
3954
  width: "100%",
3504
3955
  transform: `translateY(${virtualRow.start}px)`
3505
3956
  },
3506
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(LogEntry, { log, viewMode })
3957
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(LogEntry, { log, viewMode, strip })
3507
3958
  },
3508
3959
  log.id
3509
3960
  );
@@ -3664,6 +4115,7 @@ function ProxyViewerContainer() {
3664
4115
  }
3665
4116
  })();
3666
4117
  }, []);
4118
+ const { strip } = useStripConfig();
3667
4119
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3668
4120
  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 }),
3669
4121
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -3678,7 +4130,8 @@ function ProxyViewerContainer() {
3678
4130
  onModelChange: setSelectedModel,
3679
4131
  onClearAll: handleClearAll,
3680
4132
  viewMode,
3681
- onViewModeChange: setViewMode
4133
+ onViewModeChange: setViewMode,
4134
+ strip
3682
4135
  }
3683
4136
  )
3684
4137
  ] });