@nexttrace/devtools 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1943 @@
1
+ 'use client';
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ InspectorPanel: () => InspectorPanel,
25
+ JsonViewer: () => JsonViewer,
26
+ NextTraceDevTools: () => NextTraceDevTools,
27
+ SettingsModal: () => SettingsModal,
28
+ TimelineList: () => TimelineList,
29
+ tokens: () => tokens,
30
+ typography: () => typography
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+
34
+ // src/NextTraceDevTools.tsx
35
+ var import_react4 = require("react");
36
+ var import_core2 = require("@nexttrace/core");
37
+
38
+ // src/theme.ts
39
+ var tokens = {
40
+ dark: {
41
+ bg: "#0c0f17",
42
+ bgSurface: "#131823",
43
+ bgElevated: "#1a2232",
44
+ bgHover: "#222b3e",
45
+ bgActive: "#2a354c",
46
+ border: "#263147",
47
+ borderMuted: "#1d2638",
48
+ text: "#e2e8f0",
49
+ textMuted: "#8b9bb4",
50
+ textDim: "#52627a",
51
+ accent: "#3b82f6",
52
+ accentHover: "#60a5fa",
53
+ accentBg: "rgba(59, 130, 246, 0.12)",
54
+ success: "#10b981",
55
+ successBg: "rgba(16, 185, 129, 0.12)",
56
+ warning: "#f59e0b",
57
+ warningBg: "rgba(245, 158, 11, 0.12)",
58
+ error: "#ef4444",
59
+ errorBg: "rgba(239, 68, 68, 0.12)",
60
+ purple: "#a855f7",
61
+ purpleBg: "rgba(168, 85, 247, 0.12)",
62
+ badgeBg: "#1f293d",
63
+ shadow: "0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 8px 10px -6px rgba(0, 0, 0, 0.5)",
64
+ scrollbarThumb: "rgba(255, 255, 255, 0.18)",
65
+ scrollbarThumbHover: "rgba(255, 255, 255, 0.38)"
66
+ },
67
+ light: {
68
+ bg: "#f8fafc",
69
+ bgSurface: "#ffffff",
70
+ bgElevated: "#f1f5f9",
71
+ bgHover: "#e2e8f0",
72
+ bgActive: "#cbd5e1",
73
+ border: "#e2e8f0",
74
+ borderMuted: "#cbd5e1",
75
+ text: "#0f172a",
76
+ textMuted: "#475569",
77
+ textDim: "#94a3b8",
78
+ accent: "#2563eb",
79
+ accentHover: "#1d4ed8",
80
+ accentBg: "rgba(37, 99, 235, 0.08)",
81
+ success: "#059669",
82
+ successBg: "rgba(5, 150, 105, 0.08)",
83
+ warning: "#d97706",
84
+ warningBg: "rgba(217, 119, 6, 0.08)",
85
+ error: "#dc2626",
86
+ errorBg: "rgba(220, 38, 38, 0.08)",
87
+ purple: "#9333ea",
88
+ purpleBg: "rgba(147, 51, 234, 0.08)",
89
+ badgeBg: "#e2e8f0",
90
+ shadow: "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)",
91
+ scrollbarThumb: "rgba(0, 0, 0, 0.18)",
92
+ scrollbarThumbHover: "rgba(0, 0, 0, 0.38)"
93
+ }
94
+ };
95
+ var typography = {
96
+ fontSans: 'var(--font-sans, var(--font-inter, "Inter", "Geist Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif))',
97
+ fontMono: 'var(--font-mono, var(--font-jetbrains-mono, "JetBrains Mono", "Geist Mono", "Fira Code", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace))'
98
+ };
99
+
100
+ // src/TimelineList.tsx
101
+ var import_jsx_runtime = require("react/jsx-runtime");
102
+ var TimelineList = ({
103
+ events,
104
+ selectedEventId,
105
+ onSelectEvent,
106
+ theme
107
+ }) => {
108
+ const t = tokens[theme];
109
+ const getMethodColor = (method) => {
110
+ switch (method.toUpperCase()) {
111
+ case "GET":
112
+ return "#3b82f6";
113
+ case "POST":
114
+ return "#10b981";
115
+ case "PUT":
116
+ case "PATCH":
117
+ return "#f59e0b";
118
+ case "DELETE":
119
+ return "#ef4444";
120
+ case "QUERY":
121
+ return "#8b5cf6";
122
+ default:
123
+ return t.textMuted;
124
+ }
125
+ };
126
+ const getStatusBadge = (event) => {
127
+ if (event.phase === "start") {
128
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
129
+ "span",
130
+ {
131
+ style: {
132
+ color: t.accent,
133
+ backgroundColor: t.accentBg,
134
+ padding: "2px 6px",
135
+ borderRadius: "4px",
136
+ fontSize: "11px",
137
+ fontWeight: 600
138
+ },
139
+ children: "..."
140
+ }
141
+ );
142
+ }
143
+ if (event.phase === "abort") {
144
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
145
+ "span",
146
+ {
147
+ style: {
148
+ color: t.textDim,
149
+ backgroundColor: t.badgeBg,
150
+ padding: "2px 6px",
151
+ borderRadius: "4px",
152
+ fontSize: "11px",
153
+ fontWeight: 600
154
+ },
155
+ children: "ABORT"
156
+ }
157
+ );
158
+ }
159
+ const status = event.response?.status;
160
+ if (!status || status >= 400 || event.phase === "error") {
161
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
162
+ "span",
163
+ {
164
+ style: {
165
+ color: t.error,
166
+ backgroundColor: t.errorBg,
167
+ padding: "2px 6px",
168
+ borderRadius: "4px",
169
+ fontSize: "11px",
170
+ fontWeight: 600
171
+ },
172
+ children: status || "ERR"
173
+ }
174
+ );
175
+ }
176
+ if (status >= 300) {
177
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
178
+ "span",
179
+ {
180
+ style: {
181
+ color: t.warning,
182
+ backgroundColor: t.warningBg,
183
+ padding: "2px 6px",
184
+ borderRadius: "4px",
185
+ fontSize: "11px",
186
+ fontWeight: 600
187
+ },
188
+ children: status
189
+ }
190
+ );
191
+ }
192
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
193
+ "span",
194
+ {
195
+ style: {
196
+ color: t.success,
197
+ backgroundColor: t.successBg,
198
+ padding: "2px 6px",
199
+ borderRadius: "4px",
200
+ fontSize: "11px",
201
+ fontWeight: 600
202
+ },
203
+ children: status
204
+ }
205
+ );
206
+ };
207
+ if (events.length === 0) {
208
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
209
+ "div",
210
+ {
211
+ style: {
212
+ flex: 1,
213
+ display: "flex",
214
+ flexDirection: "column",
215
+ alignItems: "center",
216
+ justifyContent: "center",
217
+ padding: "32px 16px",
218
+ color: t.textDim,
219
+ textAlign: "center",
220
+ fontFamily: typography.fontSans
221
+ },
222
+ children: [
223
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: "28px", marginBottom: "8px" }, children: "\u{1F4E1}" }),
224
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontWeight: 600, color: t.textMuted, marginBottom: "4px" }, children: "No requests captured yet" }),
225
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: "12px" }, children: "Trigger a fetch, Axios call, or TanStack query to see live events" })
226
+ ]
227
+ }
228
+ );
229
+ }
230
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
231
+ "div",
232
+ {
233
+ style: {
234
+ flex: 1,
235
+ overflowY: "auto",
236
+ fontFamily: typography.fontSans,
237
+ scrollbarWidth: "thin",
238
+ scrollbarColor: `${t.scrollbarThumb} transparent`
239
+ },
240
+ children: events.map((event) => {
241
+ const isSelected = event.eventId === selectedEventId;
242
+ const method = event.request.method.toUpperCase();
243
+ const durationText = event.timing.duration !== void 0 ? event.timing.duration > 999 ? `${(event.timing.duration / 1e3).toFixed(2)}s` : `${event.timing.duration}ms` : "...";
244
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
245
+ "div",
246
+ {
247
+ onClick: () => onSelectEvent(event.eventId),
248
+ style: {
249
+ display: "flex",
250
+ alignItems: "center",
251
+ padding: "8px 12px",
252
+ borderBottom: `1px solid ${t.borderMuted}`,
253
+ cursor: "pointer",
254
+ backgroundColor: isSelected ? t.bgHover : "transparent",
255
+ transition: "background 0.1s ease",
256
+ gap: "8px"
257
+ },
258
+ children: [
259
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: "42px", flexShrink: 0 }, children: getStatusBadge(event) }),
260
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
261
+ "div",
262
+ {
263
+ style: {
264
+ width: "54px",
265
+ flexShrink: 0,
266
+ fontWeight: 700,
267
+ fontSize: "11px",
268
+ color: getMethodColor(method),
269
+ fontFamily: typography.fontMono
270
+ },
271
+ children: method
272
+ }
273
+ ),
274
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
275
+ "div",
276
+ {
277
+ style: {
278
+ flex: 1,
279
+ minWidth: 0,
280
+ whiteSpace: "nowrap",
281
+ overflow: "hidden",
282
+ textOverflow: "ellipsis",
283
+ fontSize: "12px",
284
+ color: t.text,
285
+ fontFamily: typography.fontMono
286
+ },
287
+ title: event.request.sanitizedUrl,
288
+ children: event.request.sanitizedUrl
289
+ }
290
+ ),
291
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: "4px", flexShrink: 0 }, children: [
292
+ event.flags.isDuplicate && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
293
+ "span",
294
+ {
295
+ title: "Heuristic: Same method & URL called within short time window",
296
+ style: {
297
+ fontSize: "10px",
298
+ fontWeight: 600,
299
+ padding: "1px 5px",
300
+ borderRadius: "3px",
301
+ backgroundColor: t.warningBg,
302
+ color: t.warning
303
+ },
304
+ children: "DUP"
305
+ }
306
+ ),
307
+ event.flags.isSlow && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
308
+ "span",
309
+ {
310
+ style: {
311
+ fontSize: "10px",
312
+ fontWeight: 600,
313
+ padding: "1px 5px",
314
+ borderRadius: "3px",
315
+ backgroundColor: t.errorBg,
316
+ color: t.error
317
+ },
318
+ children: "SLOW"
319
+ }
320
+ ),
321
+ event.flags.isRedacted && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
322
+ "span",
323
+ {
324
+ style: {
325
+ fontSize: "10px",
326
+ fontWeight: 600,
327
+ padding: "1px 5px",
328
+ borderRadius: "3px",
329
+ backgroundColor: t.badgeBg,
330
+ color: t.textDim
331
+ },
332
+ children: "\u{1F512}"
333
+ }
334
+ )
335
+ ] }),
336
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
337
+ "div",
338
+ {
339
+ style: {
340
+ fontSize: "10px",
341
+ padding: "2px 5px",
342
+ borderRadius: "3px",
343
+ backgroundColor: t.bgElevated,
344
+ color: t.textMuted,
345
+ flexShrink: 0
346
+ },
347
+ children: [
348
+ event.runtime === "node" ? "server" : "browser",
349
+ " \xB7 ",
350
+ event.sourceAdapter
351
+ ]
352
+ }
353
+ ),
354
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
355
+ "div",
356
+ {
357
+ style: {
358
+ width: "52px",
359
+ textAlign: "right",
360
+ fontSize: "11px",
361
+ color: event.flags.isSlow ? t.error : t.textMuted,
362
+ fontFamily: typography.fontMono,
363
+ flexShrink: 0,
364
+ fontWeight: event.flags.isSlow ? 600 : 400
365
+ },
366
+ children: durationText
367
+ }
368
+ )
369
+ ]
370
+ },
371
+ event.eventId
372
+ );
373
+ })
374
+ }
375
+ );
376
+ };
377
+
378
+ // src/InspectorPanel.tsx
379
+ var import_react2 = require("react");
380
+ var import_core = require("@nexttrace/core");
381
+
382
+ // src/JsonViewer.tsx
383
+ var import_react = require("react");
384
+ var import_jsx_runtime2 = require("react/jsx-runtime");
385
+ var JsonViewer = ({ data, theme }) => {
386
+ const [copied, setCopied] = (0, import_react.useState)(false);
387
+ const t = tokens[theme];
388
+ const handleCopy = () => {
389
+ const text = typeof data === "string" ? data : JSON.stringify(data, null, 2);
390
+ navigator.clipboard.writeText(text);
391
+ setCopied(true);
392
+ setTimeout(() => setCopied(false), 1500);
393
+ };
394
+ let parsed = data;
395
+ let isJson = false;
396
+ if (typeof data === "string") {
397
+ try {
398
+ parsed = JSON.parse(data);
399
+ isJson = true;
400
+ } catch {
401
+ isJson = false;
402
+ }
403
+ } else if (data !== null && typeof data === "object") {
404
+ isJson = true;
405
+ }
406
+ const renderValue = (val, depth = 0) => {
407
+ if (val === null) {
408
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t.textDim, fontStyle: "italic" }, children: "null" });
409
+ }
410
+ if (typeof val === "boolean") {
411
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t.purple, fontWeight: 600 }, children: String(val) });
412
+ }
413
+ if (typeof val === "number") {
414
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t.warning, fontWeight: 600 }, children: val });
415
+ }
416
+ if (typeof val === "string") {
417
+ if (val === "[REDACTED]") {
418
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
419
+ "span",
420
+ {
421
+ style: {
422
+ backgroundColor: t.errorBg,
423
+ color: t.error,
424
+ padding: "1px 5px",
425
+ borderRadius: "3px",
426
+ fontWeight: 600,
427
+ fontSize: "11px"
428
+ },
429
+ children: "[REDACTED]"
430
+ }
431
+ );
432
+ }
433
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: t.success }, children: [
434
+ '"',
435
+ val,
436
+ '"'
437
+ ] });
438
+ }
439
+ if (Array.isArray(val)) {
440
+ if (val.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t.textDim }, children: "[]" });
441
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { paddingLeft: "14px", borderLeft: `1px dashed ${t.borderMuted}` }, children: [
442
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t.textDim }, children: "[" }),
443
+ val.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { lineHeight: "1.4" }, children: [
444
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: t.textDim, marginRight: "6px" }, children: [
445
+ idx,
446
+ ":"
447
+ ] }),
448
+ renderValue(item, depth + 1),
449
+ idx < val.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t.textDim }, children: "," })
450
+ ] }, idx)),
451
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t.textDim }, children: "]" })
452
+ ] });
453
+ }
454
+ if (typeof val === "object") {
455
+ const keys = Object.keys(val);
456
+ if (keys.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t.textDim }, children: "{}" });
457
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { paddingLeft: "14px", borderLeft: `1px dashed ${t.borderMuted}` }, children: [
458
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t.textDim }, children: "{" }),
459
+ keys.map((k, idx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { lineHeight: "1.4" }, children: [
460
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: t.accentHover, fontWeight: 500 }, children: [
461
+ '"',
462
+ k,
463
+ '"'
464
+ ] }),
465
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t.textDim, margin: "0 6px" }, children: ":" }),
466
+ renderValue(val[k], depth + 1),
467
+ idx < keys.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t.textDim }, children: "," })
468
+ ] }, k)),
469
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t.textDim }, children: "}" })
470
+ ] });
471
+ }
472
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: String(val) });
473
+ };
474
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
475
+ "div",
476
+ {
477
+ style: {
478
+ position: "relative",
479
+ backgroundColor: t.bgSurface,
480
+ border: `1px solid ${t.border}`,
481
+ borderRadius: "6px",
482
+ padding: "12px",
483
+ fontFamily: typography.fontMono,
484
+ fontSize: "12px",
485
+ maxHeight: "340px",
486
+ overflowY: "auto",
487
+ overflowX: "auto",
488
+ scrollbarWidth: "thin",
489
+ scrollbarColor: `${t.scrollbarThumb} transparent`
490
+ },
491
+ children: [
492
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", justifyContent: "flex-end", marginBottom: "6px" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
493
+ "button",
494
+ {
495
+ onClick: handleCopy,
496
+ style: {
497
+ backgroundColor: t.bgElevated,
498
+ border: `1px solid ${t.border}`,
499
+ color: t.text,
500
+ padding: "3px 8px",
501
+ borderRadius: "4px",
502
+ fontSize: "11px",
503
+ cursor: "pointer",
504
+ transition: "background 0.15s ease"
505
+ },
506
+ children: copied ? "\u2713 Copied" : "Copy"
507
+ }
508
+ ) }),
509
+ isJson ? renderValue(parsed) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
510
+ "pre",
511
+ {
512
+ style: {
513
+ margin: 0,
514
+ whiteSpace: "pre-wrap",
515
+ wordBreak: "break-all",
516
+ color: t.text,
517
+ fontFamily: typography.fontMono
518
+ },
519
+ children: String(data ?? "(empty body)")
520
+ }
521
+ )
522
+ ]
523
+ }
524
+ );
525
+ };
526
+
527
+ // src/InspectorPanel.tsx
528
+ var import_jsx_runtime3 = require("react/jsx-runtime");
529
+ var InspectorPanel = ({ event, onClose, theme }) => {
530
+ const [activeTab, setActiveTab] = (0, import_react2.useState)("overview");
531
+ const [curlCopied, setCurlCopied] = (0, import_react2.useState)(false);
532
+ const t = tokens[theme];
533
+ const handleCopyCurl = () => {
534
+ const curl = (0, import_core.toCurlCommand)(event);
535
+ navigator.clipboard.writeText(curl);
536
+ setCurlCopied(true);
537
+ setTimeout(() => setCurlCopied(false), 1500);
538
+ };
539
+ const tabs = [
540
+ { id: "overview", label: "Overview", show: true },
541
+ { id: "request", label: "Request", show: true },
542
+ { id: "response", label: "Response", show: Boolean(event.response) },
543
+ { id: "headers", label: "Headers", show: true },
544
+ { id: "timing", label: "Timing", show: true },
545
+ { id: "source", label: "Source", show: Boolean(event.context.initiatorStack || event.context.componentName) },
546
+ { id: "error", label: "Error", show: Boolean(event.error) }
547
+ ].filter((t2) => t2.show);
548
+ const currentTab = tabs.some((tab) => tab.id === activeTab) ? activeTab : "overview";
549
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
550
+ "div",
551
+ {
552
+ style: {
553
+ display: "flex",
554
+ flexDirection: "column",
555
+ height: "100%",
556
+ backgroundColor: t.bgSurface,
557
+ borderLeft: `1px solid ${t.border}`,
558
+ fontFamily: typography.fontSans
559
+ },
560
+ children: [
561
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
562
+ "div",
563
+ {
564
+ style: {
565
+ display: "flex",
566
+ alignItems: "center",
567
+ justifyContent: "space-between",
568
+ padding: "10px 14px",
569
+ borderBottom: `1px solid ${t.border}`,
570
+ backgroundColor: t.bgElevated
571
+ },
572
+ children: [
573
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px", minWidth: 0 }, children: [
574
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
575
+ "span",
576
+ {
577
+ style: {
578
+ fontWeight: 700,
579
+ fontSize: "12px",
580
+ fontFamily: typography.fontMono,
581
+ color: t.accent
582
+ },
583
+ children: event.request.method
584
+ }
585
+ ),
586
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
587
+ "span",
588
+ {
589
+ style: {
590
+ fontSize: "12px",
591
+ fontWeight: 500,
592
+ color: t.text,
593
+ whiteSpace: "nowrap",
594
+ overflow: "hidden",
595
+ textOverflow: "ellipsis",
596
+ maxWidth: "280px",
597
+ fontFamily: typography.fontMono
598
+ },
599
+ title: event.request.sanitizedUrl,
600
+ children: event.request.sanitizedUrl
601
+ }
602
+ )
603
+ ] }),
604
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
605
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
606
+ "button",
607
+ {
608
+ onClick: handleCopyCurl,
609
+ style: {
610
+ backgroundColor: t.bgSurface,
611
+ border: `1px solid ${t.border}`,
612
+ color: t.text,
613
+ padding: "4px 8px",
614
+ borderRadius: "4px",
615
+ fontSize: "11px",
616
+ fontWeight: 500,
617
+ cursor: "pointer",
618
+ display: "flex",
619
+ alignItems: "center",
620
+ gap: "4px"
621
+ },
622
+ children: [
623
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "\u{1F4CB}" }),
624
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: curlCopied ? "cURL Copied!" : "Copy cURL" })
625
+ ]
626
+ }
627
+ ),
628
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
629
+ "button",
630
+ {
631
+ onClick: onClose,
632
+ style: {
633
+ background: "transparent",
634
+ border: "none",
635
+ color: t.textMuted,
636
+ cursor: "pointer",
637
+ fontSize: "16px",
638
+ padding: "2px 6px",
639
+ borderRadius: "4px"
640
+ },
641
+ children: "\u2715"
642
+ }
643
+ )
644
+ ] })
645
+ ]
646
+ }
647
+ ),
648
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
649
+ "div",
650
+ {
651
+ style: {
652
+ display: "flex",
653
+ borderBottom: `1px solid ${t.border}`,
654
+ backgroundColor: t.bg,
655
+ padding: "0 8px"
656
+ },
657
+ children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
658
+ "button",
659
+ {
660
+ onClick: () => setActiveTab(tab.id),
661
+ style: {
662
+ background: "transparent",
663
+ border: "none",
664
+ borderBottom: currentTab === tab.id ? `2px solid ${t.accent}` : "2px solid transparent",
665
+ color: currentTab === tab.id ? t.accent : t.textMuted,
666
+ padding: "8px 12px",
667
+ fontSize: "12px",
668
+ fontWeight: currentTab === tab.id ? 600 : 500,
669
+ cursor: "pointer",
670
+ transition: "all 0.1s ease"
671
+ },
672
+ children: tab.label
673
+ },
674
+ tab.id
675
+ ))
676
+ }
677
+ ),
678
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
679
+ "div",
680
+ {
681
+ style: {
682
+ flex: 1,
683
+ overflowY: "auto",
684
+ padding: "16px",
685
+ fontSize: "13px",
686
+ scrollbarWidth: "thin",
687
+ scrollbarColor: `${t.scrollbarThumb} transparent`
688
+ },
689
+ children: [
690
+ currentTab === "overview" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "14px" }, children: [
691
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "120px 1fr", gap: "8px", alignItems: "center" }, children: [
692
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.textDim, fontSize: "12px" }, children: "Event ID:" }),
693
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontFamily: typography.fontMono, fontSize: "12px", color: t.text }, children: event.eventId }),
694
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.textDim, fontSize: "12px" }, children: "Status:" }),
695
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontWeight: 600, color: (event.response?.status ?? 500) < 400 ? t.success : t.error }, children: event.response ? `${event.response.status} (${event.response.statusText})` : event.phase }),
696
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.textDim, fontSize: "12px" }, children: "Duration:" }),
697
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.text }, children: event.timing.duration !== void 0 ? `${event.timing.duration}ms` : "In progress" }),
698
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.textDim, fontSize: "12px" }, children: "Runtime:" }),
699
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.text }, children: event.runtime === "node" ? "Node.js (Server-side)" : "Browser" }),
700
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.textDim, fontSize: "12px" }, children: "Adapter:" }),
701
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.text, textTransform: "capitalize" }, children: event.sourceAdapter }),
702
+ event.context.route && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
703
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.textDim, fontSize: "12px" }, children: "Page Route:" }),
704
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontFamily: typography.fontMono, fontSize: "12px", color: t.text }, children: event.context.route })
705
+ ] }),
706
+ event.context.componentName && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
707
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.textDim, fontSize: "12px" }, children: "Caller Component:" }),
708
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontFamily: typography.fontMono, fontSize: "12px", color: t.accent }, children: event.context.componentName })
709
+ ] }),
710
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.textDim, fontSize: "12px" }, children: "Attribution:" }),
711
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { fontSize: "12px", color: t.textMuted }, children: [
712
+ event.context.attributionMethod,
713
+ " (",
714
+ event.context.confidence,
715
+ " confidence)"
716
+ ] })
717
+ ] }),
718
+ event.flags.isRedacted && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
719
+ "div",
720
+ {
721
+ style: {
722
+ padding: "8px 12px",
723
+ backgroundColor: t.accentBg,
724
+ border: `1px solid ${t.accent}`,
725
+ borderRadius: "4px",
726
+ fontSize: "11px",
727
+ color: t.accentHover
728
+ },
729
+ children: "\u{1F512} Sensitive parameters (passwords, tokens, cookies, auth headers) were safely redacted before reaching this inspector."
730
+ }
731
+ )
732
+ ] }),
733
+ currentTab === "request" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: [
734
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
735
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontWeight: 600, marginBottom: "8px", color: t.text, fontSize: "12px" }, children: "Query Parameters" }),
736
+ Object.keys(event.request.queryParams).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: "12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { children: Object.entries(event.request.queryParams).map(([key, val]) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { style: { borderBottom: `1px solid ${t.borderMuted}` }, children: [
737
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { style: { padding: "4px 8px", color: t.accentHover, fontFamily: typography.fontMono }, children: key }),
738
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { style: { padding: "4px 8px", color: t.text, fontFamily: typography.fontMono }, children: val })
739
+ ] }, key)) }) }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { color: t.textDim, fontSize: "12px" }, children: "No query parameters" })
740
+ ] }),
741
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
742
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontWeight: 600, marginBottom: "8px", color: t.text, fontSize: "12px" }, children: [
743
+ "Request Body ",
744
+ event.request.isTruncated && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.warning }, children: "(Truncated)" })
745
+ ] }),
746
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(JsonViewer, { data: event.request.bodyPreview, theme })
747
+ ] })
748
+ ] }),
749
+ currentTab === "response" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: [
750
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
751
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontWeight: 600, color: t.text, fontSize: "12px" }, children: [
752
+ "Status: ",
753
+ event.response?.status,
754
+ " ",
755
+ event.response?.statusText
756
+ ] }),
757
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { color: t.textDim, fontSize: "11px" }, children: [
758
+ "Content-Type: ",
759
+ event.response?.contentType || "unknown"
760
+ ] })
761
+ ] }),
762
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
763
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontWeight: 600, marginBottom: "8px", color: t.text, fontSize: "12px" }, children: [
764
+ "Response Body ",
765
+ event.response?.isTruncated && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.warning }, children: "(Truncated)" })
766
+ ] }),
767
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(JsonViewer, { data: event.response?.bodyPreview, theme })
768
+ ] })
769
+ ] }),
770
+ currentTab === "headers" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "16px" }, children: [
771
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
772
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontWeight: 600, marginBottom: "8px", color: t.text, fontSize: "12px" }, children: "Request Headers" }),
773
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: "12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { children: Object.entries(event.request.headers).map(([k, v]) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { style: { borderBottom: `1px solid ${t.borderMuted}` }, children: [
774
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { style: { padding: "4px 8px", color: t.accentHover, fontFamily: typography.fontMono }, children: k }),
775
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { style: { padding: "4px 8px", color: t.text, fontFamily: typography.fontMono }, children: v })
776
+ ] }, k)) }) })
777
+ ] }),
778
+ event.response && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
779
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontWeight: 600, marginBottom: "8px", color: t.text, fontSize: "12px" }, children: "Response Headers" }),
780
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: "12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { children: Object.entries(event.response.headers).map(([k, v]) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { style: { borderBottom: `1px solid ${t.borderMuted}` }, children: [
781
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { style: { padding: "4px 8px", color: t.accentHover, fontFamily: typography.fontMono }, children: k }),
782
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { style: { padding: "4px 8px", color: t.text, fontFamily: typography.fontMono }, children: v })
783
+ ] }, k)) }) })
784
+ ] })
785
+ ] }),
786
+ currentTab === "timing" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: [
787
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontWeight: 600, color: t.text, fontSize: "12px" }, children: "Timing Breakdown" }),
788
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "140px 1fr", gap: "6px" }, children: [
789
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.textDim }, children: "Total Duration:" }),
790
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontWeight: 600, color: event.flags.isSlow ? t.error : t.success }, children: event.timing.duration !== void 0 ? `${event.timing.duration} ms` : "In progress" }),
791
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.textDim }, children: "Start Epoch:" }),
792
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: t.text }, children: new Date(event.timestamp).toLocaleTimeString() })
793
+ ] }),
794
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginTop: "12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { height: "8px", backgroundColor: t.bgElevated, borderRadius: "4px", overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
795
+ "div",
796
+ {
797
+ style: {
798
+ height: "100%",
799
+ width: "100%",
800
+ backgroundColor: event.flags.isSlow ? t.error : t.accent,
801
+ borderRadius: "4px"
802
+ }
803
+ }
804
+ ) }) })
805
+ ] }),
806
+ currentTab === "source" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: [
807
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontWeight: 600, color: t.text, fontSize: "12px" }, children: "Call Stack & Attribution" }),
808
+ event.context.initiatorStack ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
809
+ "pre",
810
+ {
811
+ style: {
812
+ backgroundColor: t.bg,
813
+ padding: "10px",
814
+ borderRadius: "4px",
815
+ fontSize: "11px",
816
+ fontFamily: typography.fontMono,
817
+ color: t.textMuted,
818
+ overflowX: "auto"
819
+ },
820
+ children: event.context.initiatorStack
821
+ }
822
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { color: t.textDim, fontSize: "12px" }, children: "Caller stack unavailable for this event." })
823
+ ] }),
824
+ currentTab === "error" && event.error && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: [
825
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
826
+ "div",
827
+ {
828
+ style: {
829
+ backgroundColor: t.errorBg,
830
+ border: `1px solid ${t.error}`,
831
+ borderRadius: "6px",
832
+ padding: "12px"
833
+ },
834
+ children: [
835
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontWeight: 700, color: t.error, marginBottom: "4px" }, children: [
836
+ event.error.name,
837
+ ": ",
838
+ event.error.message
839
+ ] }),
840
+ event.error.retryAttempt !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontSize: "12px", color: t.warning, marginTop: "4px" }, children: [
841
+ "Retry Attempts Observed: ",
842
+ event.error.retryAttempt
843
+ ] })
844
+ ]
845
+ }
846
+ ),
847
+ event.error.stack && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
848
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontWeight: 600, marginBottom: "6px", color: t.text, fontSize: "12px" }, children: "Stack Trace" }),
849
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
850
+ "pre",
851
+ {
852
+ style: {
853
+ backgroundColor: t.bg,
854
+ padding: "10px",
855
+ borderRadius: "4px",
856
+ fontSize: "11px",
857
+ fontFamily: typography.fontMono,
858
+ color: t.textMuted,
859
+ overflowX: "auto"
860
+ },
861
+ children: event.error.stack
862
+ }
863
+ )
864
+ ] })
865
+ ] })
866
+ ]
867
+ }
868
+ )
869
+ ]
870
+ }
871
+ );
872
+ };
873
+
874
+ // src/SettingsModal.tsx
875
+ var import_react3 = require("react");
876
+ var import_jsx_runtime4 = require("react/jsx-runtime");
877
+ var SettingsModal = ({
878
+ store,
879
+ isOpen,
880
+ onClose,
881
+ theme
882
+ }) => {
883
+ const t = tokens[theme];
884
+ const config = store.getConfig();
885
+ const [maxEvents, setMaxEvents] = (0, import_react3.useState)(config.maxEvents);
886
+ const [slowThresholdMs, setSlowThresholdMs] = (0, import_react3.useState)(config.slowThresholdMs);
887
+ const [duplicateWindowMs, setDuplicateWindowMs] = (0, import_react3.useState)(config.duplicateWindowMs);
888
+ const [captureBodies, setCaptureBodies] = (0, import_react3.useState)(config.captureBodies);
889
+ if (!isOpen) return null;
890
+ const handleSave = () => {
891
+ store.updateConfig({
892
+ maxEvents: Number(maxEvents),
893
+ slowThresholdMs: Number(slowThresholdMs),
894
+ duplicateWindowMs: Number(duplicateWindowMs),
895
+ captureBodies
896
+ });
897
+ onClose();
898
+ };
899
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
900
+ "div",
901
+ {
902
+ style: {
903
+ position: "fixed",
904
+ inset: 0,
905
+ backgroundColor: "rgba(0, 0, 0, 0.6)",
906
+ display: "flex",
907
+ alignItems: "center",
908
+ justifyContent: "center",
909
+ zIndex: 999999,
910
+ fontFamily: typography.fontSans
911
+ },
912
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
913
+ "div",
914
+ {
915
+ style: {
916
+ width: "420px",
917
+ backgroundColor: t.bgSurface,
918
+ border: `1px solid ${t.border}`,
919
+ borderRadius: "8px",
920
+ boxShadow: t.shadow,
921
+ overflow: "hidden"
922
+ },
923
+ children: [
924
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
925
+ "div",
926
+ {
927
+ style: {
928
+ display: "flex",
929
+ alignItems: "center",
930
+ justifyContent: "space-between",
931
+ padding: "12px 16px",
932
+ borderBottom: `1px solid ${t.border}`,
933
+ backgroundColor: t.bgElevated
934
+ },
935
+ children: [
936
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 600, color: t.text, fontSize: "14px" }, children: "NextTrace Settings" }),
937
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
938
+ "button",
939
+ {
940
+ onClick: onClose,
941
+ style: {
942
+ background: "transparent",
943
+ border: "none",
944
+ color: t.textMuted,
945
+ cursor: "pointer",
946
+ fontSize: "16px"
947
+ },
948
+ children: "\u2715"
949
+ }
950
+ )
951
+ ]
952
+ }
953
+ ),
954
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { padding: "16px", display: "flex", flexDirection: "column", gap: "14px" }, children: [
955
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
956
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { style: { display: "block", fontSize: "12px", fontWeight: 600, color: t.text, marginBottom: "4px" }, children: "Max Events in Buffer" }),
957
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
958
+ "input",
959
+ {
960
+ type: "number",
961
+ value: maxEvents,
962
+ onChange: (e) => setMaxEvents(Number(e.target.value)),
963
+ style: {
964
+ width: "100%",
965
+ padding: "6px 10px",
966
+ borderRadius: "4px",
967
+ border: `1px solid ${t.border}`,
968
+ backgroundColor: t.bg,
969
+ color: t.text,
970
+ fontSize: "13px"
971
+ }
972
+ }
973
+ ),
974
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "11px", color: t.textDim, marginTop: "2px" }, children: "Oldest requests are evicted once this limit is reached." })
975
+ ] }),
976
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
977
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { style: { display: "block", fontSize: "12px", fontWeight: 600, color: t.text, marginBottom: "4px" }, children: "Slow Request Threshold (ms)" }),
978
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
979
+ "input",
980
+ {
981
+ type: "number",
982
+ value: slowThresholdMs,
983
+ onChange: (e) => setSlowThresholdMs(Number(e.target.value)),
984
+ style: {
985
+ width: "100%",
986
+ padding: "6px 10px",
987
+ borderRadius: "4px",
988
+ border: `1px solid ${t.border}`,
989
+ backgroundColor: t.bg,
990
+ color: t.text,
991
+ fontSize: "13px"
992
+ }
993
+ }
994
+ ),
995
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "11px", color: t.textDim, marginTop: "2px" }, children: "Requests taking longer than this are flagged as SLOW." })
996
+ ] }),
997
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
998
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { style: { display: "block", fontSize: "12px", fontWeight: 600, color: t.text, marginBottom: "4px" }, children: "Duplicate Heuristic Window (ms)" }),
999
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1000
+ "input",
1001
+ {
1002
+ type: "number",
1003
+ value: duplicateWindowMs,
1004
+ onChange: (e) => setDuplicateWindowMs(Number(e.target.value)),
1005
+ style: {
1006
+ width: "100%",
1007
+ padding: "6px 10px",
1008
+ borderRadius: "4px",
1009
+ border: `1px solid ${t.border}`,
1010
+ backgroundColor: t.bg,
1011
+ color: t.text,
1012
+ fontSize: "13px"
1013
+ }
1014
+ }
1015
+ )
1016
+ ] }),
1017
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px", marginTop: "4px" }, children: [
1018
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1019
+ "input",
1020
+ {
1021
+ type: "checkbox",
1022
+ id: "nt-capture-bodies",
1023
+ checked: captureBodies,
1024
+ onChange: (e) => setCaptureBodies(e.target.checked),
1025
+ style: { cursor: "pointer" }
1026
+ }
1027
+ ),
1028
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { htmlFor: "nt-capture-bodies", style: { fontSize: "13px", color: t.text, cursor: "pointer" }, children: "Capture request & response payloads (redacted)" })
1029
+ ] })
1030
+ ] }),
1031
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1032
+ "div",
1033
+ {
1034
+ style: {
1035
+ display: "flex",
1036
+ justifyContent: "flex-end",
1037
+ gap: "8px",
1038
+ padding: "12px 16px",
1039
+ borderTop: `1px solid ${t.border}`,
1040
+ backgroundColor: t.bgElevated
1041
+ },
1042
+ children: [
1043
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1044
+ "button",
1045
+ {
1046
+ onClick: onClose,
1047
+ style: {
1048
+ padding: "6px 12px",
1049
+ borderRadius: "4px",
1050
+ border: `1px solid ${t.border}`,
1051
+ backgroundColor: t.bgSurface,
1052
+ color: t.text,
1053
+ fontSize: "12px",
1054
+ cursor: "pointer"
1055
+ },
1056
+ children: "Cancel"
1057
+ }
1058
+ ),
1059
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1060
+ "button",
1061
+ {
1062
+ onClick: handleSave,
1063
+ style: {
1064
+ padding: "6px 14px",
1065
+ borderRadius: "4px",
1066
+ border: "none",
1067
+ backgroundColor: t.accent,
1068
+ color: "#ffffff",
1069
+ fontSize: "12px",
1070
+ fontWeight: 600,
1071
+ cursor: "pointer"
1072
+ },
1073
+ children: "Save Settings"
1074
+ }
1075
+ )
1076
+ ]
1077
+ }
1078
+ )
1079
+ ]
1080
+ }
1081
+ )
1082
+ }
1083
+ );
1084
+ };
1085
+
1086
+ // src/NextTraceDevTools.tsx
1087
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1088
+ var NextTraceDevTools = ({
1089
+ store: propStore,
1090
+ defaultPosition = "bottom",
1091
+ defaultTheme = "dark",
1092
+ initialOpen = true
1093
+ }) => {
1094
+ const store = (0, import_react4.useMemo)(() => propStore || (0, import_core2.getGlobalStore)(), [propStore]);
1095
+ const [isOpen, setIsOpen] = (0, import_react4.useState)(initialOpen);
1096
+ const [position, setPosition] = (0, import_react4.useState)(defaultPosition);
1097
+ const [theme, setTheme] = (0, import_react4.useState)(defaultTheme);
1098
+ const [events, setEvents] = (0, import_react4.useState)([]);
1099
+ const [selectedEventId, setSelectedEventId] = (0, import_react4.useState)(null);
1100
+ const [isPaused, setIsPaused] = (0, import_react4.useState)(store.getIsPaused());
1101
+ const [isSettingsOpen, setIsSettingsOpen] = (0, import_react4.useState)(false);
1102
+ const [bottomHeight, setBottomHeight] = (0, import_react4.useState)(380);
1103
+ const [rightWidth, setRightWidth] = (0, import_react4.useState)(560);
1104
+ const [floatingSize, setFloatingSize] = (0, import_react4.useState)({ width: 840, height: 520 });
1105
+ const [floatingPos, setFloatingPos] = (0, import_react4.useState)({ x: 40, y: 60 });
1106
+ const [splitRatio, setSplitRatio] = (0, import_react4.useState)(0.5);
1107
+ const [isDraggingFloating, setIsDraggingFloating] = (0, import_react4.useState)(false);
1108
+ const [isResizing, setIsResizing] = (0, import_react4.useState)(null);
1109
+ const [btnPos, setBtnPos] = (0, import_react4.useState)(null);
1110
+ const [isDraggingBtn, setIsDraggingBtn] = (0, import_react4.useState)(false);
1111
+ const [isBtnHovered, setIsBtnHovered] = (0, import_react4.useState)(false);
1112
+ const btnDragRef = (0, import_react4.useRef)({
1113
+ startX: 0,
1114
+ startY: 0,
1115
+ origX: 0,
1116
+ origY: 0,
1117
+ moved: false
1118
+ });
1119
+ const [searchQuery, setSearchQuery] = (0, import_react4.useState)("");
1120
+ const [statusFilter, setStatusFilter] = (0, import_react4.useState)("all");
1121
+ const [onlySlow, setOnlySlow] = (0, import_react4.useState)(false);
1122
+ const [onlyDuplicates, setOnlyDuplicates] = (0, import_react4.useState)(false);
1123
+ const t = tokens[theme];
1124
+ (0, import_react4.useEffect)(() => {
1125
+ const unsubscribe = store.subscribe((allEvents) => {
1126
+ setEvents(allEvents);
1127
+ setIsPaused(store.getIsPaused());
1128
+ });
1129
+ return unsubscribe;
1130
+ }, [store]);
1131
+ (0, import_react4.useEffect)(() => {
1132
+ const handleKeyDown = (e) => {
1133
+ if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key.toLowerCase() === "x") {
1134
+ e.preventDefault();
1135
+ setIsOpen((prev) => !prev);
1136
+ }
1137
+ };
1138
+ window.addEventListener("keydown", handleKeyDown);
1139
+ return () => window.removeEventListener("keydown", handleKeyDown);
1140
+ }, []);
1141
+ (0, import_react4.useEffect)(() => {
1142
+ if (!isResizing && !isDraggingFloating) return;
1143
+ const handleMouseMove = (e) => {
1144
+ if (isDraggingFloating) {
1145
+ setFloatingPos((prev) => {
1146
+ const newX = Math.max(10, Math.min(window.innerWidth - 300, prev.x + e.movementX));
1147
+ const newY = Math.max(10, Math.min(window.innerHeight - 150, prev.y + e.movementY));
1148
+ return { x: newX, y: newY };
1149
+ });
1150
+ } else if (isResizing === "bottom") {
1151
+ const newH = Math.max(180, Math.min(window.innerHeight - 60, window.innerHeight - e.clientY));
1152
+ setBottomHeight(newH);
1153
+ } else if (isResizing === "right") {
1154
+ const newW = Math.max(320, Math.min(window.innerWidth - 60, window.innerWidth - e.clientX));
1155
+ setRightWidth(newW);
1156
+ } else if (isResizing === "floating") {
1157
+ setFloatingSize((prev) => {
1158
+ const newW = Math.max(400, Math.min(window.innerWidth - floatingPos.x - 20, prev.width + e.movementX));
1159
+ const newH = Math.max(260, Math.min(window.innerHeight - floatingPos.y - 20, prev.height + e.movementY));
1160
+ return { width: newW, height: newH };
1161
+ });
1162
+ } else if (isResizing === "split") {
1163
+ const container = document.getElementById("nexttrace-split-container");
1164
+ if (container) {
1165
+ const rect = container.getBoundingClientRect();
1166
+ const ratio = (e.clientX - rect.left) / rect.width;
1167
+ setSplitRatio(Math.max(0.25, Math.min(0.75, ratio)));
1168
+ }
1169
+ }
1170
+ };
1171
+ const handleMouseUp = () => {
1172
+ setIsDraggingFloating(false);
1173
+ setIsResizing(null);
1174
+ };
1175
+ window.addEventListener("mousemove", handleMouseMove);
1176
+ window.addEventListener("mouseup", handleMouseUp);
1177
+ return () => {
1178
+ window.removeEventListener("mousemove", handleMouseMove);
1179
+ window.removeEventListener("mouseup", handleMouseUp);
1180
+ };
1181
+ }, [isResizing, isDraggingFloating, floatingPos]);
1182
+ (0, import_react4.useEffect)(() => {
1183
+ if (!isDraggingBtn) return;
1184
+ const handleMouseMove = (e) => {
1185
+ const dx = e.clientX - btnDragRef.current.startX;
1186
+ const dy = e.clientY - btnDragRef.current.startY;
1187
+ if (Math.hypot(dx, dy) > 4) {
1188
+ btnDragRef.current.moved = true;
1189
+ }
1190
+ const newX = Math.max(10, Math.min(window.innerWidth - 180, btnDragRef.current.origX + dx));
1191
+ const newY = Math.max(10, Math.min(window.innerHeight - 60, btnDragRef.current.origY + dy));
1192
+ setBtnPos({ x: newX, y: newY });
1193
+ };
1194
+ const handleMouseUp = () => {
1195
+ setIsDraggingBtn(false);
1196
+ };
1197
+ window.addEventListener("mousemove", handleMouseMove);
1198
+ window.addEventListener("mouseup", handleMouseUp);
1199
+ return () => {
1200
+ window.removeEventListener("mousemove", handleMouseMove);
1201
+ window.removeEventListener("mouseup", handleMouseUp);
1202
+ };
1203
+ }, [isDraggingBtn]);
1204
+ const handleTogglePause = (0, import_react4.useCallback)(() => {
1205
+ if (isPaused) {
1206
+ store.resume();
1207
+ setIsPaused(false);
1208
+ } else {
1209
+ store.pause();
1210
+ setIsPaused(true);
1211
+ }
1212
+ }, [isPaused, store]);
1213
+ const handleClear = (0, import_react4.useCallback)(() => {
1214
+ store.clear();
1215
+ setSelectedEventId(null);
1216
+ }, [store]);
1217
+ const handleExportJson = (0, import_react4.useCallback)(() => {
1218
+ const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(store.exportSanitizedJson());
1219
+ const downloadAnchor = document.createElement("a");
1220
+ downloadAnchor.setAttribute("href", dataStr);
1221
+ downloadAnchor.setAttribute("download", `nexttrace-export-${Date.now()}.json`);
1222
+ document.body.appendChild(downloadAnchor);
1223
+ downloadAnchor.click();
1224
+ downloadAnchor.remove();
1225
+ }, [store]);
1226
+ const filteredEvents = (0, import_react4.useMemo)(() => {
1227
+ const filter = {
1228
+ searchQuery,
1229
+ statusFilter,
1230
+ onlySlow,
1231
+ onlyDuplicates
1232
+ };
1233
+ return store.filterEvents(filter);
1234
+ }, [store, events, searchQuery, statusFilter, onlySlow, onlyDuplicates]);
1235
+ const selectedEvent = (0, import_react4.useMemo)(() => {
1236
+ if (!selectedEventId) return null;
1237
+ return events.find((e) => e.eventId === selectedEventId) || null;
1238
+ }, [events, selectedEventId]);
1239
+ if (!isOpen) {
1240
+ const errorCount = events.filter(
1241
+ (e) => e.phase === "error" || e.response?.status && e.response.status >= 400
1242
+ ).length;
1243
+ const onBtnMouseDown = (e) => {
1244
+ const rect = e.currentTarget.getBoundingClientRect();
1245
+ btnDragRef.current = {
1246
+ startX: e.clientX,
1247
+ startY: e.clientY,
1248
+ origX: rect.left,
1249
+ origY: rect.top,
1250
+ moved: false
1251
+ };
1252
+ setIsDraggingBtn(true);
1253
+ };
1254
+ const onBtnClick = () => {
1255
+ if (!btnDragRef.current.moved) {
1256
+ setIsOpen(true);
1257
+ }
1258
+ };
1259
+ const isDark = theme === "dark";
1260
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1261
+ "div",
1262
+ {
1263
+ onMouseEnter: () => setIsBtnHovered(true),
1264
+ onMouseLeave: () => setIsBtnHovered(false),
1265
+ onMouseDown: onBtnMouseDown,
1266
+ onClick: onBtnClick,
1267
+ title: "NextTrace DevTools (Click to open \xB7 Drag to reposition \xB7 Ctrl+Shift+X)",
1268
+ style: {
1269
+ position: "fixed",
1270
+ zIndex: 999999,
1271
+ left: btnPos ? `${btnPos.x}px` : void 0,
1272
+ top: btnPos ? `${btnPos.y}px` : void 0,
1273
+ right: btnPos ? void 0 : "20px",
1274
+ bottom: btnPos ? void 0 : "20px",
1275
+ display: "flex",
1276
+ alignItems: "center",
1277
+ gap: "8px",
1278
+ padding: isBtnHovered ? "6px 12px 6px 8px" : "0px",
1279
+ height: "38px",
1280
+ width: isBtnHovered ? "auto" : "38px",
1281
+ borderRadius: "20px",
1282
+ backgroundColor: isDark ? "rgba(10, 15, 26, 0.92)" : "rgba(255, 255, 255, 0.96)",
1283
+ backdropFilter: "blur(16px)",
1284
+ WebkitBackdropFilter: "blur(16px)",
1285
+ border: `1px solid ${errorCount > 0 ? "rgba(239, 68, 68, 0.7)" : isBtnHovered ? "rgba(56, 189, 248, 0.6)" : isDark ? "rgba(255, 255, 255, 0.12)" : "rgba(0, 0, 0, 0.12)"}`,
1286
+ boxShadow: errorCount > 0 ? "0 0 20px -2px rgba(239, 68, 68, 0.4), 0 8px 16px -4px rgba(0, 0, 0, 0.5)" : isBtnHovered ? "0 0 24px -2px rgba(56, 189, 248, 0.35), 0 8px 20px -4px rgba(0, 0, 0, 0.4)" : "0 8px 20px -4px rgba(0, 0, 0, 0.35)",
1287
+ cursor: isDraggingBtn ? "grabbing" : "pointer",
1288
+ userSelect: "none",
1289
+ transition: isDraggingBtn ? "none" : "all 0.2s cubic-bezier(0.16, 1, 0.3, 1)",
1290
+ transform: isBtnHovered ? "scale(1.05)" : "scale(1)"
1291
+ },
1292
+ children: [
1293
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1294
+ "div",
1295
+ {
1296
+ style: {
1297
+ position: "relative",
1298
+ display: "flex",
1299
+ alignItems: "center",
1300
+ justifyContent: "center",
1301
+ width: "36px",
1302
+ height: "36px",
1303
+ flexShrink: 0
1304
+ },
1305
+ children: [
1306
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1307
+ "path",
1308
+ {
1309
+ d: "M13 2L3 14H12L11 22L21 10H12L13 2Z",
1310
+ fill: errorCount > 0 ? "#ef4444" : "#38bdf8",
1311
+ fillOpacity: errorCount > 0 ? 0.9 : 0.85,
1312
+ stroke: errorCount > 0 ? "#fca5a5" : "#7dd3fc",
1313
+ strokeWidth: "1.5",
1314
+ strokeLinejoin: "round"
1315
+ }
1316
+ ) }),
1317
+ errorCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1318
+ "span",
1319
+ {
1320
+ style: {
1321
+ position: "absolute",
1322
+ top: "-2px",
1323
+ right: "-2px",
1324
+ minWidth: "16px",
1325
+ height: "16px",
1326
+ padding: "0 4px",
1327
+ borderRadius: "8px",
1328
+ backgroundColor: "#ef4444",
1329
+ color: "#ffffff",
1330
+ fontSize: "10px",
1331
+ fontWeight: 700,
1332
+ fontFamily: typography.fontMono,
1333
+ display: "flex",
1334
+ alignItems: "center",
1335
+ justifyContent: "center",
1336
+ boxShadow: "0 0 8px #ef4444"
1337
+ },
1338
+ children: errorCount
1339
+ }
1340
+ ) : events.length > 0 && !isBtnHovered ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1341
+ "span",
1342
+ {
1343
+ style: {
1344
+ position: "absolute",
1345
+ top: "0px",
1346
+ right: "0px",
1347
+ width: "8px",
1348
+ height: "8px",
1349
+ borderRadius: "50%",
1350
+ backgroundColor: isPaused ? t.warning : "#10b981",
1351
+ boxShadow: `0 0 6px ${isPaused ? t.warning : "#10b981"}`
1352
+ }
1353
+ }
1354
+ ) : null
1355
+ ]
1356
+ }
1357
+ ),
1358
+ isBtnHovered && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1359
+ "div",
1360
+ {
1361
+ style: {
1362
+ display: "flex",
1363
+ alignItems: "center",
1364
+ gap: "6px",
1365
+ whiteSpace: "nowrap",
1366
+ paddingRight: "2px"
1367
+ },
1368
+ children: [
1369
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1370
+ "span",
1371
+ {
1372
+ style: {
1373
+ fontSize: "12px",
1374
+ fontWeight: 700,
1375
+ color: isDark ? "#ffffff" : "#0f172a",
1376
+ fontFamily: typography.fontSans,
1377
+ letterSpacing: "-0.01em"
1378
+ },
1379
+ children: "NextTrace"
1380
+ }
1381
+ ),
1382
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1383
+ "span",
1384
+ {
1385
+ style: {
1386
+ fontSize: "11px",
1387
+ fontFamily: typography.fontMono,
1388
+ color: t.accentHover,
1389
+ fontWeight: 600
1390
+ },
1391
+ children: events.length
1392
+ }
1393
+ )
1394
+ ]
1395
+ }
1396
+ )
1397
+ ]
1398
+ }
1399
+ );
1400
+ }
1401
+ const getContainerStyle = () => {
1402
+ const base = {
1403
+ position: "fixed",
1404
+ zIndex: 999998,
1405
+ backgroundColor: t.bg,
1406
+ color: t.text,
1407
+ fontFamily: typography.fontSans,
1408
+ boxShadow: t.shadow,
1409
+ display: "flex",
1410
+ flexDirection: "column",
1411
+ border: `1px solid ${t.border}`,
1412
+ boxSizing: "border-box"
1413
+ };
1414
+ if (position === "bottom") {
1415
+ return {
1416
+ ...base,
1417
+ bottom: 0,
1418
+ left: 0,
1419
+ right: 0,
1420
+ height: `${bottomHeight}px`,
1421
+ maxHeight: "92vh",
1422
+ borderBottom: "none"
1423
+ };
1424
+ }
1425
+ if (position === "right") {
1426
+ return {
1427
+ ...base,
1428
+ top: 0,
1429
+ right: 0,
1430
+ bottom: 0,
1431
+ width: `${rightWidth}px`,
1432
+ maxWidth: "100vw",
1433
+ borderRight: "none"
1434
+ };
1435
+ }
1436
+ return {
1437
+ ...base,
1438
+ top: `${floatingPos.y}px`,
1439
+ left: `${floatingPos.x}px`,
1440
+ width: `${floatingSize.width}px`,
1441
+ height: `${floatingSize.height}px`,
1442
+ maxWidth: "calc(100vw - 20px)",
1443
+ maxHeight: "calc(100vh - 20px)",
1444
+ borderRadius: "10px",
1445
+ overflow: "hidden"
1446
+ };
1447
+ };
1448
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1449
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: `
1450
+ .nexttrace-root, .nexttrace-root * {
1451
+ scrollbar-width: thin !important;
1452
+ scrollbar-color: ${t.scrollbarThumb} transparent !important;
1453
+ }
1454
+ .nexttrace-root ::-webkit-scrollbar,
1455
+ .nexttrace-root::-webkit-scrollbar {
1456
+ width: 5px !important;
1457
+ height: 5px !important;
1458
+ }
1459
+ .nexttrace-root ::-webkit-scrollbar-track,
1460
+ .nexttrace-root::-webkit-scrollbar-track {
1461
+ background: transparent !important;
1462
+ }
1463
+ .nexttrace-root ::-webkit-scrollbar-thumb,
1464
+ .nexttrace-root::-webkit-scrollbar-thumb {
1465
+ background-color: ${t.scrollbarThumb} !important;
1466
+ border-radius: 9999px !important;
1467
+ border: 1px solid transparent !important;
1468
+ background-clip: padding-box !important;
1469
+ transition: background-color 0.15s ease !important;
1470
+ }
1471
+ .nexttrace-root ::-webkit-scrollbar-thumb:hover,
1472
+ .nexttrace-root::-webkit-scrollbar-thumb:hover {
1473
+ background-color: ${t.scrollbarThumbHover} !important;
1474
+ }
1475
+ .nexttrace-root ::-webkit-scrollbar-corner,
1476
+ .nexttrace-root::-webkit-scrollbar-corner {
1477
+ background: transparent !important;
1478
+ }
1479
+ ` }),
1480
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "nexttrace-root", style: getContainerStyle(), children: [
1481
+ position === "bottom" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1482
+ "div",
1483
+ {
1484
+ onMouseDown: () => setIsResizing("bottom"),
1485
+ title: "Drag vertically to resize height",
1486
+ style: {
1487
+ height: "6px",
1488
+ width: "100%",
1489
+ cursor: "row-resize",
1490
+ backgroundColor: isResizing === "bottom" ? t.accent : "transparent",
1491
+ display: "flex",
1492
+ alignItems: "center",
1493
+ justifyContent: "center",
1494
+ transition: "background 0.15s ease",
1495
+ flexShrink: 0
1496
+ },
1497
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1498
+ "div",
1499
+ {
1500
+ style: {
1501
+ width: "44px",
1502
+ height: "3px",
1503
+ borderRadius: "2px",
1504
+ backgroundColor: isResizing === "bottom" ? t.accent : t.border
1505
+ }
1506
+ }
1507
+ )
1508
+ }
1509
+ ),
1510
+ position === "right" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1511
+ "div",
1512
+ {
1513
+ onMouseDown: () => setIsResizing("right"),
1514
+ title: "Drag horizontally to resize width",
1515
+ style: {
1516
+ position: "absolute",
1517
+ left: 0,
1518
+ top: 0,
1519
+ bottom: 0,
1520
+ width: "6px",
1521
+ cursor: "col-resize",
1522
+ backgroundColor: isResizing === "right" ? t.accent : "transparent",
1523
+ display: "flex",
1524
+ alignItems: "center",
1525
+ justifyContent: "center",
1526
+ transition: "background 0.15s ease",
1527
+ zIndex: 10
1528
+ },
1529
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1530
+ "div",
1531
+ {
1532
+ style: {
1533
+ width: "3px",
1534
+ height: "44px",
1535
+ borderRadius: "2px",
1536
+ backgroundColor: isResizing === "right" ? t.accent : t.border
1537
+ }
1538
+ }
1539
+ )
1540
+ }
1541
+ ),
1542
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1543
+ "div",
1544
+ {
1545
+ onMouseDown: (e) => {
1546
+ if (position === "floating" && e.target.tagName !== "BUTTON" && e.target.tagName !== "INPUT") {
1547
+ setIsDraggingFloating(true);
1548
+ }
1549
+ },
1550
+ style: {
1551
+ display: "flex",
1552
+ alignItems: "center",
1553
+ justifyContent: "space-between",
1554
+ padding: "7px 12px",
1555
+ borderBottom: `1px solid ${t.border}`,
1556
+ backgroundColor: t.bgElevated,
1557
+ gap: "8px",
1558
+ flexWrap: "wrap",
1559
+ cursor: position === "floating" ? isDraggingFloating ? "grabbing" : "grab" : "default",
1560
+ userSelect: "none"
1561
+ },
1562
+ children: [
1563
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
1564
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { fontWeight: 700, fontSize: "13px", display: "flex", alignItems: "center", gap: "4px" }, children: [
1565
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { color: t.accent }, children: "\u26A1" }),
1566
+ " NextTrace"
1567
+ ] }),
1568
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1569
+ "div",
1570
+ {
1571
+ title: isPaused ? "Recording paused" : "Active and recording",
1572
+ style: {
1573
+ width: "8px",
1574
+ height: "8px",
1575
+ borderRadius: "50%",
1576
+ backgroundColor: isPaused ? t.warning : t.success
1577
+ }
1578
+ }
1579
+ ),
1580
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { fontSize: "11px", color: t.textDim, fontFamily: typography.fontMono }, children: [
1581
+ "(",
1582
+ filteredEvents.length,
1583
+ " / ",
1584
+ events.length,
1585
+ ")"
1586
+ ] }),
1587
+ position === "floating" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { fontSize: "10px", color: t.textDim, fontStyle: "italic" }, children: "(Drag header to move)" })
1588
+ ] }),
1589
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", alignItems: "center", flex: 1, maxWidth: "240px", minWidth: "140px" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1590
+ "input",
1591
+ {
1592
+ type: "text",
1593
+ placeholder: "Filter URL, method, status...",
1594
+ value: searchQuery,
1595
+ onChange: (e) => setSearchQuery(e.target.value),
1596
+ style: {
1597
+ width: "100%",
1598
+ padding: "4px 8px",
1599
+ borderRadius: "4px",
1600
+ border: `1px solid ${t.border}`,
1601
+ backgroundColor: t.bg,
1602
+ color: t.text,
1603
+ fontSize: "11px",
1604
+ fontFamily: typography.fontSans,
1605
+ outline: "none"
1606
+ }
1607
+ }
1608
+ ) }),
1609
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
1610
+ ["all", "2xx", "4xx", "5xx", "error"].map((st) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1611
+ "button",
1612
+ {
1613
+ onClick: () => setStatusFilter(st),
1614
+ style: {
1615
+ padding: "3px 7px",
1616
+ borderRadius: "3px",
1617
+ border: "none",
1618
+ fontSize: "11px",
1619
+ cursor: "pointer",
1620
+ fontWeight: statusFilter === st ? 600 : 400,
1621
+ backgroundColor: statusFilter === st ? t.accentBg : "transparent",
1622
+ color: statusFilter === st ? t.accent : t.textMuted
1623
+ },
1624
+ children: st.toUpperCase()
1625
+ },
1626
+ st
1627
+ )),
1628
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1629
+ "button",
1630
+ {
1631
+ onClick: () => setOnlySlow((prev) => !prev),
1632
+ style: {
1633
+ padding: "3px 7px",
1634
+ borderRadius: "3px",
1635
+ border: "none",
1636
+ fontSize: "11px",
1637
+ cursor: "pointer",
1638
+ fontWeight: onlySlow ? 600 : 400,
1639
+ backgroundColor: onlySlow ? t.errorBg : "transparent",
1640
+ color: onlySlow ? t.error : t.textMuted
1641
+ },
1642
+ children: "Slow"
1643
+ }
1644
+ ),
1645
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1646
+ "button",
1647
+ {
1648
+ onClick: () => setOnlyDuplicates((prev) => !prev),
1649
+ style: {
1650
+ padding: "3px 7px",
1651
+ borderRadius: "3px",
1652
+ border: "none",
1653
+ fontSize: "11px",
1654
+ cursor: "pointer",
1655
+ fontWeight: onlyDuplicates ? 600 : 400,
1656
+ backgroundColor: onlyDuplicates ? t.warningBg : "transparent",
1657
+ color: onlyDuplicates ? t.warning : t.textMuted
1658
+ },
1659
+ children: "Dupes"
1660
+ }
1661
+ )
1662
+ ] }),
1663
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
1664
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1665
+ "button",
1666
+ {
1667
+ onClick: handleTogglePause,
1668
+ title: isPaused ? "Resume recording" : "Pause recording",
1669
+ style: {
1670
+ background: "transparent",
1671
+ border: `1px solid ${t.border}`,
1672
+ color: isPaused ? t.warning : t.textMuted,
1673
+ padding: "3px 7px",
1674
+ borderRadius: "4px",
1675
+ fontSize: "11px",
1676
+ cursor: "pointer"
1677
+ },
1678
+ children: isPaused ? "\u25B6 Resume" : "\u23F8 Pause"
1679
+ }
1680
+ ),
1681
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1682
+ "button",
1683
+ {
1684
+ onClick: handleClear,
1685
+ title: "Clear all events",
1686
+ style: {
1687
+ background: "transparent",
1688
+ border: `1px solid ${t.border}`,
1689
+ color: t.textMuted,
1690
+ padding: "3px 7px",
1691
+ borderRadius: "4px",
1692
+ fontSize: "11px",
1693
+ cursor: "pointer"
1694
+ },
1695
+ children: "Clear"
1696
+ }
1697
+ ),
1698
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1699
+ "button",
1700
+ {
1701
+ onClick: handleExportJson,
1702
+ title: "Export sanitized JSON",
1703
+ style: {
1704
+ background: "transparent",
1705
+ border: `1px solid ${t.border}`,
1706
+ color: t.textMuted,
1707
+ padding: "3px 7px",
1708
+ borderRadius: "4px",
1709
+ fontSize: "11px",
1710
+ cursor: "pointer"
1711
+ },
1712
+ children: "Export"
1713
+ }
1714
+ ),
1715
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", border: `1px solid ${t.border}`, borderRadius: "4px", overflow: "hidden" }, children: [
1716
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1717
+ "button",
1718
+ {
1719
+ onClick: () => setPosition("bottom"),
1720
+ title: "Dock to Bottom (Resizable height)",
1721
+ style: {
1722
+ background: position === "bottom" ? t.bgHover : "transparent",
1723
+ border: "none",
1724
+ color: position === "bottom" ? t.accent : t.textMuted,
1725
+ padding: "3px 7px",
1726
+ fontSize: "11px",
1727
+ cursor: "pointer",
1728
+ fontWeight: position === "bottom" ? 700 : 400
1729
+ },
1730
+ children: "\u2B13 Bottom"
1731
+ }
1732
+ ),
1733
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1734
+ "button",
1735
+ {
1736
+ onClick: () => setPosition("right"),
1737
+ title: "Dock to Right (Resizable width)",
1738
+ style: {
1739
+ background: position === "right" ? t.bgHover : "transparent",
1740
+ border: "none",
1741
+ color: position === "right" ? t.accent : t.textMuted,
1742
+ padding: "3px 7px",
1743
+ fontSize: "11px",
1744
+ cursor: "pointer",
1745
+ fontWeight: position === "right" ? 700 : 400
1746
+ },
1747
+ children: "\u25E7 Right"
1748
+ }
1749
+ ),
1750
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1751
+ "button",
1752
+ {
1753
+ onClick: () => setPosition("floating"),
1754
+ title: "Detach & Float (Movable & Resizable Window)",
1755
+ style: {
1756
+ background: position === "floating" ? t.bgHover : "transparent",
1757
+ border: "none",
1758
+ color: position === "floating" ? t.accent : t.textMuted,
1759
+ padding: "3px 7px",
1760
+ fontSize: "11px",
1761
+ cursor: "pointer",
1762
+ fontWeight: position === "floating" ? 700 : 400
1763
+ },
1764
+ children: "\u2750 Float"
1765
+ }
1766
+ )
1767
+ ] }),
1768
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1769
+ "button",
1770
+ {
1771
+ onClick: () => setTheme((prev) => prev === "dark" ? "light" : "dark"),
1772
+ title: "Toggle Dark / Light theme",
1773
+ style: {
1774
+ background: "transparent",
1775
+ border: `1px solid ${t.border}`,
1776
+ color: t.textMuted,
1777
+ padding: "3px 7px",
1778
+ borderRadius: "4px",
1779
+ fontSize: "11px",
1780
+ cursor: "pointer"
1781
+ },
1782
+ children: theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"
1783
+ }
1784
+ ),
1785
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1786
+ "button",
1787
+ {
1788
+ onClick: () => setIsSettingsOpen(true),
1789
+ title: "NextTrace Settings",
1790
+ style: {
1791
+ background: "transparent",
1792
+ border: `1px solid ${t.border}`,
1793
+ color: t.textMuted,
1794
+ padding: "3px 7px",
1795
+ borderRadius: "4px",
1796
+ fontSize: "11px",
1797
+ cursor: "pointer"
1798
+ },
1799
+ children: "\u2699"
1800
+ }
1801
+ ),
1802
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1803
+ "button",
1804
+ {
1805
+ onClick: () => setIsOpen(false),
1806
+ title: "Minimize panel (Ctrl+Shift+X)",
1807
+ style: {
1808
+ background: "transparent",
1809
+ border: "none",
1810
+ color: t.textMuted,
1811
+ padding: "3px 7px",
1812
+ fontSize: "14px",
1813
+ cursor: "pointer"
1814
+ },
1815
+ children: "\u2715"
1816
+ }
1817
+ )
1818
+ ] })
1819
+ ]
1820
+ }
1821
+ ),
1822
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1823
+ "div",
1824
+ {
1825
+ id: "nexttrace-split-container",
1826
+ style: { display: "flex", flex: 1, minHeight: 0, overflow: "hidden", position: "relative" },
1827
+ children: [
1828
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1829
+ "div",
1830
+ {
1831
+ style: {
1832
+ width: selectedEvent ? position === "right" ? "100%" : `${splitRatio * 100}%` : "100%",
1833
+ display: position === "right" && selectedEvent ? "none" : "flex",
1834
+ flexDirection: "column",
1835
+ minHeight: 0
1836
+ },
1837
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1838
+ TimelineList,
1839
+ {
1840
+ events: filteredEvents,
1841
+ selectedEventId,
1842
+ onSelectEvent: setSelectedEventId,
1843
+ theme
1844
+ }
1845
+ )
1846
+ }
1847
+ ),
1848
+ selectedEvent && position !== "right" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1849
+ "div",
1850
+ {
1851
+ onMouseDown: () => setIsResizing("split"),
1852
+ title: "Drag horizontally to adjust panel split",
1853
+ style: {
1854
+ width: "6px",
1855
+ cursor: "col-resize",
1856
+ backgroundColor: isResizing === "split" ? t.accent : t.borderMuted,
1857
+ display: "flex",
1858
+ alignItems: "center",
1859
+ justifyContent: "center",
1860
+ transition: "background 0.15s ease",
1861
+ flexShrink: 0,
1862
+ zIndex: 5
1863
+ },
1864
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1865
+ "div",
1866
+ {
1867
+ style: {
1868
+ width: "2px",
1869
+ height: "30px",
1870
+ borderRadius: "1px",
1871
+ backgroundColor: isResizing === "split" ? t.accent : t.border
1872
+ }
1873
+ }
1874
+ )
1875
+ }
1876
+ ),
1877
+ selectedEvent && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1878
+ "div",
1879
+ {
1880
+ style: {
1881
+ width: position === "right" ? "100%" : `${(1 - splitRatio) * 100}%`,
1882
+ flex: position === "right" ? 1 : void 0,
1883
+ minHeight: 0,
1884
+ display: "flex",
1885
+ flexDirection: "column"
1886
+ },
1887
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1888
+ InspectorPanel,
1889
+ {
1890
+ event: selectedEvent,
1891
+ onClose: () => setSelectedEventId(null),
1892
+ theme
1893
+ }
1894
+ )
1895
+ }
1896
+ )
1897
+ ]
1898
+ }
1899
+ ),
1900
+ position === "floating" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1901
+ "div",
1902
+ {
1903
+ onMouseDown: () => setIsResizing("floating"),
1904
+ title: "Drag corner to resize floating window",
1905
+ style: {
1906
+ position: "absolute",
1907
+ bottom: 0,
1908
+ right: 0,
1909
+ width: "18px",
1910
+ height: "18px",
1911
+ cursor: "nwse-resize",
1912
+ display: "flex",
1913
+ alignItems: "flex-end",
1914
+ justifyContent: "flex-end",
1915
+ padding: "2px",
1916
+ zIndex: 20,
1917
+ opacity: 0.6
1918
+ },
1919
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M10 2L2 10M10 6L6 10M10 10L10 10", stroke: t.accent, strokeWidth: "1.6", strokeLinecap: "round" }) })
1920
+ }
1921
+ )
1922
+ ] }),
1923
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1924
+ SettingsModal,
1925
+ {
1926
+ store,
1927
+ isOpen: isSettingsOpen,
1928
+ onClose: () => setIsSettingsOpen(false),
1929
+ theme
1930
+ }
1931
+ )
1932
+ ] });
1933
+ };
1934
+ // Annotate the CommonJS export names for ESM import in node:
1935
+ 0 && (module.exports = {
1936
+ InspectorPanel,
1937
+ JsonViewer,
1938
+ NextTraceDevTools,
1939
+ SettingsModal,
1940
+ TimelineList,
1941
+ tokens,
1942
+ typography
1943
+ });