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