next-fetch-panel 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,922 @@
1
+ "use client";
2
+
3
+ // src/Panel.tsx
4
+ import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from "react";
5
+ import { useGroupRef } from "react-resizable-panels";
6
+ import { Settings2Icon, XIcon as XIcon3 } from "lucide-react";
7
+
8
+ // src/utils.ts
9
+ import { clsx } from "clsx";
10
+ import { twMerge } from "tailwind-merge";
11
+ function cn(...inputs) {
12
+ return twMerge(clsx(inputs));
13
+ }
14
+
15
+ // src/ui/button.tsx
16
+ import { Button as ButtonPrimitive } from "@base-ui/react/button";
17
+ import { cva } from "class-variance-authority";
18
+ import { jsx } from "react/jsx-runtime";
19
+ var buttonVariants = cva(
20
+ "group/button inline-flex shrink-0 cursor-pointer items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
21
+ {
22
+ variants: {
23
+ variant: {
24
+ default: "bg-primary text-primary-foreground hover:bg-primary/80",
25
+ outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
26
+ secondary: "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
27
+ ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
28
+ destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
29
+ link: "text-primary underline-offset-4 hover:underline"
30
+ },
31
+ size: {
32
+ default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
33
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
34
+ sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
35
+ lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
36
+ icon: "size-8",
37
+ "icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
38
+ "icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
39
+ "icon-lg": "size-9"
40
+ }
41
+ },
42
+ defaultVariants: {
43
+ variant: "default",
44
+ size: "default"
45
+ }
46
+ }
47
+ );
48
+ function Button({
49
+ className,
50
+ variant = "default",
51
+ size = "default",
52
+ ...props
53
+ }) {
54
+ return /* @__PURE__ */ jsx(
55
+ ButtonPrimitive,
56
+ {
57
+ "data-slot": "button",
58
+ className: cn(buttonVariants({ variant, size, className })),
59
+ ...props
60
+ }
61
+ );
62
+ }
63
+
64
+ // src/ui/badge.tsx
65
+ import { mergeProps } from "@base-ui/react/merge-props";
66
+ import { useRender } from "@base-ui/react/use-render";
67
+ import { cva as cva2 } from "class-variance-authority";
68
+ var badgeVariants = cva2(
69
+ "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
70
+ {
71
+ variants: {
72
+ variant: {
73
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
74
+ secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
75
+ destructive: "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
76
+ outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
77
+ ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
78
+ link: "text-primary underline-offset-4 hover:underline"
79
+ }
80
+ },
81
+ defaultVariants: { variant: "default" }
82
+ }
83
+ );
84
+ function Badge({
85
+ className,
86
+ variant = "default",
87
+ render,
88
+ ...props
89
+ }) {
90
+ return useRender({
91
+ defaultTagName: "span",
92
+ props: mergeProps({ className: cn(badgeVariants({ variant }), className) }, props),
93
+ render,
94
+ state: { slot: "badge", variant }
95
+ });
96
+ }
97
+
98
+ // src/ui/scroll-area.tsx
99
+ import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area";
100
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
101
+ function ScrollArea({ className, children, ...props }) {
102
+ return /* @__PURE__ */ jsxs(
103
+ ScrollAreaPrimitive.Root,
104
+ {
105
+ "data-slot": "scroll-area",
106
+ className: cn("relative", className),
107
+ ...props,
108
+ children: [
109
+ /* @__PURE__ */ jsx2(
110
+ ScrollAreaPrimitive.Viewport,
111
+ {
112
+ "data-slot": "scroll-area-viewport",
113
+ className: "size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1",
114
+ children
115
+ }
116
+ ),
117
+ /* @__PURE__ */ jsx2(ScrollBar, {}),
118
+ /* @__PURE__ */ jsx2(ScrollAreaPrimitive.Corner, {})
119
+ ]
120
+ }
121
+ );
122
+ }
123
+ function ScrollBar({ className, orientation = "vertical", ...props }) {
124
+ return /* @__PURE__ */ jsx2(
125
+ ScrollAreaPrimitive.Scrollbar,
126
+ {
127
+ "data-slot": "scroll-area-scrollbar",
128
+ "data-orientation": orientation,
129
+ orientation,
130
+ className: cn(
131
+ "flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent",
132
+ className
133
+ ),
134
+ ...props,
135
+ children: /* @__PURE__ */ jsx2(
136
+ ScrollAreaPrimitive.Thumb,
137
+ {
138
+ "data-slot": "scroll-area-thumb",
139
+ className: "relative flex-1 rounded-full bg-border"
140
+ }
141
+ )
142
+ }
143
+ );
144
+ }
145
+
146
+ // src/ui/tabs.tsx
147
+ import { Tabs as TabsPrimitive } from "@base-ui/react/tabs";
148
+ import { cva as cva3 } from "class-variance-authority";
149
+ import { jsx as jsx3 } from "react/jsx-runtime";
150
+ function Tabs({ className, orientation = "horizontal", ...props }) {
151
+ return /* @__PURE__ */ jsx3(
152
+ TabsPrimitive.Root,
153
+ {
154
+ "data-slot": "tabs",
155
+ "data-orientation": orientation,
156
+ className: cn("group/tabs flex gap-2 data-horizontal:flex-col", className),
157
+ ...props
158
+ }
159
+ );
160
+ }
161
+ var tabsListVariants = cva3(
162
+ "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none",
163
+ {
164
+ variants: {
165
+ variant: {
166
+ default: "bg-muted",
167
+ line: "gap-1 bg-transparent"
168
+ }
169
+ },
170
+ defaultVariants: { variant: "default" }
171
+ }
172
+ );
173
+ function TabsList({
174
+ className,
175
+ variant = "default",
176
+ ...props
177
+ }) {
178
+ return /* @__PURE__ */ jsx3(
179
+ TabsPrimitive.List,
180
+ {
181
+ "data-slot": "tabs-list",
182
+ "data-variant": variant,
183
+ className: cn(tabsListVariants({ variant }), className),
184
+ ...props
185
+ }
186
+ );
187
+ }
188
+ function TabsTrigger({ className, ...props }) {
189
+ return /* @__PURE__ */ jsx3(
190
+ TabsPrimitive.Tab,
191
+ {
192
+ "data-slot": "tabs-trigger",
193
+ className: cn(
194
+ "relative inline-flex h-[calc(100%-1px)] flex-1 cursor-pointer items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
195
+ "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
196
+ "data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
197
+ "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
198
+ className
199
+ ),
200
+ ...props
201
+ }
202
+ );
203
+ }
204
+ function TabsContent({ className, ...props }) {
205
+ return /* @__PURE__ */ jsx3(
206
+ TabsPrimitive.Panel,
207
+ {
208
+ "data-slot": "tabs-content",
209
+ className: cn("flex-1 text-sm outline-none", className),
210
+ ...props
211
+ }
212
+ );
213
+ }
214
+
215
+ // src/ui/resizable.tsx
216
+ import * as ResizablePrimitive from "react-resizable-panels";
217
+ import { jsx as jsx4 } from "react/jsx-runtime";
218
+ function ResizablePanelGroup({ className, ...props }) {
219
+ return /* @__PURE__ */ jsx4(
220
+ ResizablePrimitive.Group,
221
+ {
222
+ "data-slot": "resizable-panel-group",
223
+ className: cn("flex h-full w-full aria-[orientation=vertical]:flex-col", className),
224
+ ...props
225
+ }
226
+ );
227
+ }
228
+ function ResizablePanel({ ...props }) {
229
+ return /* @__PURE__ */ jsx4(ResizablePrimitive.Panel, { "data-slot": "resizable-panel", ...props });
230
+ }
231
+ function ResizableHandle({
232
+ withHandle,
233
+ className,
234
+ ...props
235
+ }) {
236
+ return /* @__PURE__ */ jsx4(
237
+ ResizablePrimitive.Separator,
238
+ {
239
+ "data-slot": "resizable-handle",
240
+ className: cn(
241
+ "relative flex w-px items-center justify-center bg-border ring-offset-background after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-hidden aria-[orientation=horizontal]:h-px aria-[orientation=horizontal]:w-full aria-[orientation=horizontal]:after:left-0 aria-[orientation=horizontal]:after:h-1 aria-[orientation=horizontal]:after:w-full aria-[orientation=horizontal]:after:translate-x-0 aria-[orientation=horizontal]:after:-translate-y-1/2 [&[aria-orientation=horizontal]>div]:rotate-90",
242
+ className
243
+ ),
244
+ ...props,
245
+ children: withHandle && /* @__PURE__ */ jsx4("div", { className: "z-10 flex h-6 w-1 shrink-0 rounded-lg bg-border" })
246
+ }
247
+ );
248
+ }
249
+
250
+ // src/ui/sheet.tsx
251
+ import { Dialog as SheetPrimitive } from "@base-ui/react/dialog";
252
+ import { XIcon } from "lucide-react";
253
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
254
+ function Sheet({ ...props }) {
255
+ return /* @__PURE__ */ jsx5(SheetPrimitive.Root, { "data-slot": "sheet", ...props });
256
+ }
257
+ function SheetPortal({ ...props }) {
258
+ return /* @__PURE__ */ jsx5(SheetPrimitive.Portal, { "data-slot": "sheet-portal", ...props });
259
+ }
260
+ function SheetOverlay({ className, ...props }) {
261
+ return /* @__PURE__ */ jsx5(
262
+ SheetPrimitive.Backdrop,
263
+ {
264
+ "data-slot": "sheet-overlay",
265
+ className: cn(
266
+ "fixed inset-0 z-50 bg-black/10 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs",
267
+ className
268
+ ),
269
+ ...props
270
+ }
271
+ );
272
+ }
273
+ function SheetContent({
274
+ className,
275
+ children,
276
+ side = "right",
277
+ showCloseButton = true,
278
+ showOverlay = true,
279
+ ...props
280
+ }) {
281
+ return /* @__PURE__ */ jsxs2(SheetPortal, { children: [
282
+ showOverlay && /* @__PURE__ */ jsx5(SheetOverlay, {}),
283
+ /* @__PURE__ */ jsxs2(
284
+ SheetPrimitive.Popup,
285
+ {
286
+ "data-slot": "sheet-content",
287
+ "data-side": side,
288
+ className: cn(
289
+ "fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem] data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm",
290
+ className
291
+ ),
292
+ ...props,
293
+ children: [
294
+ children,
295
+ showCloseButton && /* @__PURE__ */ jsxs2(
296
+ SheetPrimitive.Close,
297
+ {
298
+ "data-slot": "sheet-close",
299
+ render: /* @__PURE__ */ jsx5(Button, { variant: "ghost", className: "absolute top-3 right-3", size: "icon-sm" }),
300
+ children: [
301
+ /* @__PURE__ */ jsx5(XIcon, {}),
302
+ /* @__PURE__ */ jsx5("span", { className: "sr-only", children: "Close" })
303
+ ]
304
+ }
305
+ )
306
+ ]
307
+ }
308
+ )
309
+ ] });
310
+ }
311
+ function SheetTitle({ className, ...props }) {
312
+ return /* @__PURE__ */ jsx5(
313
+ SheetPrimitive.Title,
314
+ {
315
+ "data-slot": "sheet-title",
316
+ className: cn("font-heading text-base font-medium text-foreground", className),
317
+ ...props
318
+ }
319
+ );
320
+ }
321
+ function SheetDescription({ className, ...props }) {
322
+ return /* @__PURE__ */ jsx5(
323
+ SheetPrimitive.Description,
324
+ {
325
+ "data-slot": "sheet-description",
326
+ className: cn("text-sm text-muted-foreground", className),
327
+ ...props
328
+ }
329
+ );
330
+ }
331
+
332
+ // src/ui/dialog.tsx
333
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
334
+ import { XIcon as XIcon2 } from "lucide-react";
335
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
336
+ function Dialog({ ...props }) {
337
+ return /* @__PURE__ */ jsx6(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
338
+ }
339
+ function DialogPortal({ ...props }) {
340
+ return /* @__PURE__ */ jsx6(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
341
+ }
342
+ function DialogClose({ ...props }) {
343
+ return /* @__PURE__ */ jsx6(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
344
+ }
345
+ function DialogOverlay({ className, ...props }) {
346
+ return /* @__PURE__ */ jsx6(
347
+ DialogPrimitive.Backdrop,
348
+ {
349
+ "data-slot": "dialog-overlay",
350
+ className: cn(
351
+ "fixed inset-0 isolate z-10000 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
352
+ className
353
+ ),
354
+ ...props
355
+ }
356
+ );
357
+ }
358
+ function DialogContent({
359
+ className,
360
+ children,
361
+ showCloseButton = true,
362
+ ...props
363
+ }) {
364
+ return /* @__PURE__ */ jsxs3(DialogPortal, { children: [
365
+ /* @__PURE__ */ jsx6(DialogOverlay, {}),
366
+ /* @__PURE__ */ jsxs3(
367
+ DialogPrimitive.Popup,
368
+ {
369
+ "data-slot": "dialog-content",
370
+ className: cn(
371
+ "fixed top-1/2 left-1/2 z-10000 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
372
+ className
373
+ ),
374
+ ...props,
375
+ children: [
376
+ children,
377
+ showCloseButton && /* @__PURE__ */ jsxs3(
378
+ DialogPrimitive.Close,
379
+ {
380
+ "data-slot": "dialog-close",
381
+ render: /* @__PURE__ */ jsx6(Button, { variant: "ghost", className: "absolute top-2 right-2", size: "icon-sm" }),
382
+ children: [
383
+ /* @__PURE__ */ jsx6(XIcon2, {}),
384
+ /* @__PURE__ */ jsx6("span", { className: "sr-only", children: "Close" })
385
+ ]
386
+ }
387
+ )
388
+ ]
389
+ }
390
+ )
391
+ ] });
392
+ }
393
+ function DialogHeader({ className, ...props }) {
394
+ return /* @__PURE__ */ jsx6("div", { "data-slot": "dialog-header", className: cn("flex flex-col gap-2", className), ...props });
395
+ }
396
+ function DialogTitle({ className, ...props }) {
397
+ return /* @__PURE__ */ jsx6(
398
+ DialogPrimitive.Title,
399
+ {
400
+ "data-slot": "dialog-title",
401
+ className: cn("font-heading text-base leading-none font-medium", className),
402
+ ...props
403
+ }
404
+ );
405
+ }
406
+ function DialogDescription({ className, ...props }) {
407
+ return /* @__PURE__ */ jsx6(
408
+ DialogPrimitive.Description,
409
+ {
410
+ "data-slot": "dialog-description",
411
+ className: cn(
412
+ "text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
413
+ className
414
+ ),
415
+ ...props
416
+ }
417
+ );
418
+ }
419
+
420
+ // src/ui/separator.tsx
421
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator";
422
+ import { jsx as jsx7 } from "react/jsx-runtime";
423
+ function Separator2({ className, orientation = "horizontal", ...props }) {
424
+ return /* @__PURE__ */ jsx7(
425
+ SeparatorPrimitive,
426
+ {
427
+ "data-slot": "separator",
428
+ orientation,
429
+ className: cn(
430
+ "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
431
+ className
432
+ ),
433
+ ...props
434
+ }
435
+ );
436
+ }
437
+
438
+ // src/ui/label.tsx
439
+ import { jsx as jsx8 } from "react/jsx-runtime";
440
+ function Label({ className, ...props }) {
441
+ return /* @__PURE__ */ jsx8(
442
+ "label",
443
+ {
444
+ "data-slot": "label",
445
+ className: cn(
446
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
447
+ className
448
+ ),
449
+ ...props
450
+ }
451
+ );
452
+ }
453
+
454
+ // src/Panel.tsx
455
+ import { Fragment, jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
456
+ var DEFAULT_CONFIG = {
457
+ mode: "sheet",
458
+ sheetSide: "bottom",
459
+ sheetModal: false,
460
+ width: 820,
461
+ height: 420,
462
+ colorScheme: "system",
463
+ detailSize: 40
464
+ };
465
+ var STORAGE_KEY = "ssr-panel-config";
466
+ function usePanelConfig() {
467
+ const [config, setConfig] = useState(() => {
468
+ if (typeof window === "undefined") return DEFAULT_CONFIG;
469
+ try {
470
+ const saved = localStorage.getItem(STORAGE_KEY);
471
+ if (saved) return { ...DEFAULT_CONFIG, ...JSON.parse(saved) };
472
+ } catch {
473
+ }
474
+ return DEFAULT_CONFIG;
475
+ });
476
+ const update = useCallback((patch) => {
477
+ setConfig((prev) => {
478
+ const next = { ...prev, ...patch };
479
+ try {
480
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
481
+ } catch {
482
+ }
483
+ return next;
484
+ });
485
+ }, []);
486
+ return { config, update };
487
+ }
488
+ function useEffectiveTheme(colorScheme) {
489
+ const [systemDark, setSystemDark] = useState(
490
+ () => typeof window !== "undefined" ? window.matchMedia("(prefers-color-scheme: dark)").matches : false
491
+ );
492
+ useEffect(() => {
493
+ if (colorScheme !== "system") return;
494
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
495
+ const handler = (e) => setSystemDark(e.matches);
496
+ mq.addEventListener("change", handler);
497
+ return () => mq.removeEventListener("change", handler);
498
+ }, [colorScheme]);
499
+ if (colorScheme !== "system") return colorScheme;
500
+ return systemDark ? "dark" : "light";
501
+ }
502
+ var MAX_ENTRIES = 100;
503
+ var METHOD_COLORS = {
504
+ GET: "text-blue-400",
505
+ POST: "text-violet-400",
506
+ PUT: "text-orange-400",
507
+ PATCH: "text-orange-400",
508
+ DELETE: "text-red-400"
509
+ };
510
+ function methodClass(m) {
511
+ return METHOD_COLORS[m] ?? "text-slate-400";
512
+ }
513
+ function statusClass(status, error) {
514
+ if (error || status === null) return "text-red-400";
515
+ if (status < 300) return "text-green-400";
516
+ if (status < 400) return "text-amber-400";
517
+ return "text-red-400";
518
+ }
519
+ function durationBarColor(ms) {
520
+ if (ms < 200) return "bg-green-500";
521
+ if (ms < 1e3) return "bg-amber-500";
522
+ return "bg-red-500";
523
+ }
524
+ function formatTs(ts) {
525
+ return new Date(ts).toLocaleTimeString("en-US", {
526
+ hour12: false,
527
+ hour: "2-digit",
528
+ minute: "2-digit",
529
+ second: "2-digit",
530
+ fractionalSecondDigits: 3
531
+ });
532
+ }
533
+ function tryPrettyJson(text) {
534
+ try {
535
+ return JSON.stringify(JSON.parse(text), null, 2);
536
+ } catch {
537
+ return text;
538
+ }
539
+ }
540
+ function OptionButton({
541
+ active,
542
+ onClick,
543
+ children
544
+ }) {
545
+ return /* @__PURE__ */ jsx9(
546
+ "button",
547
+ {
548
+ onClick,
549
+ className: cn(
550
+ "cursor-pointer rounded-md px-3 py-1.5 text-xs font-medium transition-colors border",
551
+ active ? "bg-primary text-primary-foreground border-primary" : "bg-transparent text-muted-foreground border-border hover:bg-muted hover:text-foreground"
552
+ ),
553
+ children
554
+ }
555
+ );
556
+ }
557
+ function NumInput({
558
+ label,
559
+ value,
560
+ onChange,
561
+ min = 200,
562
+ max = 2e3
563
+ }) {
564
+ return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1.5", children: [
565
+ /* @__PURE__ */ jsx9(Label, { className: "text-xs text-muted-foreground", children: label }),
566
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
567
+ /* @__PURE__ */ jsx9(
568
+ "input",
569
+ {
570
+ type: "number",
571
+ min,
572
+ max,
573
+ value,
574
+ onChange: (e) => {
575
+ const n = parseInt(e.target.value, 10);
576
+ if (!isNaN(n) && n >= min && n <= max) onChange(n);
577
+ },
578
+ className: "w-24 rounded-md border border-border bg-muted px-2 py-1 text-xs text-foreground outline-none focus:ring-1 focus:ring-ring"
579
+ }
580
+ ),
581
+ /* @__PURE__ */ jsx9("span", { className: "text-xs text-muted-foreground", children: "px" })
582
+ ] })
583
+ ] });
584
+ }
585
+ function SettingsDialog({
586
+ config,
587
+ update,
588
+ open,
589
+ onOpenChange,
590
+ theme
591
+ }) {
592
+ const isDark = theme === "dark";
593
+ const isHorizontal = config.sheetSide === "bottom" || config.sheetSide === "top";
594
+ return /* @__PURE__ */ jsx9(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs4(
595
+ DialogContent,
596
+ {
597
+ className: cn(
598
+ isDark && "dark",
599
+ "bg-background text-foreground border-border sm:max-w-lg gap-0 p-0 overflow-hidden"
600
+ ),
601
+ showCloseButton: false,
602
+ children: [
603
+ /* @__PURE__ */ jsxs4(DialogHeader, { className: "px-5 pt-5 pb-4 border-b border-border", children: [
604
+ /* @__PURE__ */ jsx9(DialogTitle, { className: "text-sm font-semibold text-foreground", children: "Panel Settings" }),
605
+ /* @__PURE__ */ jsx9(DialogDescription, { className: "text-xs text-muted-foreground", children: "Preferences are saved to localStorage automatically." })
606
+ ] }),
607
+ /* @__PURE__ */ jsx9(ScrollArea, { className: "max-h-[60vh]", children: /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-5 px-5 py-4 text-xs", children: [
608
+ /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
609
+ /* @__PURE__ */ jsx9("p", { className: "font-medium text-foreground", children: "Color scheme" }),
610
+ /* @__PURE__ */ jsx9("div", { className: "flex gap-2", children: ["system", "light", "dark"].map((s) => /* @__PURE__ */ jsx9(OptionButton, { active: config.colorScheme === s, onClick: () => update({ colorScheme: s }), children: s.charAt(0).toUpperCase() + s.slice(1) }, s)) })
611
+ ] }),
612
+ /* @__PURE__ */ jsx9(Separator2, { className: "bg-border" }),
613
+ /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
614
+ /* @__PURE__ */ jsx9("p", { className: "font-medium text-foreground", children: "Display mode" }),
615
+ /* @__PURE__ */ jsxs4("div", { className: "flex gap-2", children: [
616
+ /* @__PURE__ */ jsx9(OptionButton, { active: config.mode === "sheet", onClick: () => update({ mode: "sheet" }), children: "Sheet" }),
617
+ /* @__PURE__ */ jsx9(OptionButton, { active: config.mode === "fixed", onClick: () => update({ mode: "fixed" }), children: "Fixed modal" })
618
+ ] })
619
+ ] }),
620
+ /* @__PURE__ */ jsx9(Separator2, { className: "bg-border" }),
621
+ config.mode === "sheet" && /* @__PURE__ */ jsxs4(Fragment, { children: [
622
+ /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
623
+ /* @__PURE__ */ jsx9("p", { className: "font-medium text-foreground", children: "Sheet side" }),
624
+ /* @__PURE__ */ jsx9("div", { className: "flex gap-2 flex-wrap", children: ["bottom", "right", "top", "left"].map((s) => /* @__PURE__ */ jsx9(OptionButton, { active: config.sheetSide === s, onClick: () => update({ sheetSide: s }), children: s.charAt(0).toUpperCase() + s.slice(1) }, s)) })
625
+ ] }),
626
+ /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
627
+ /* @__PURE__ */ jsx9("p", { className: "font-medium text-foreground", children: "Page interaction" }),
628
+ /* @__PURE__ */ jsxs4("div", { className: "flex gap-2", children: [
629
+ /* @__PURE__ */ jsx9(OptionButton, { active: !config.sheetModal, onClick: () => update({ sheetModal: false }), children: "Allow (non-modal)" }),
630
+ /* @__PURE__ */ jsx9(OptionButton, { active: config.sheetModal, onClick: () => update({ sheetModal: true }), children: "Block (modal)" })
631
+ ] }),
632
+ /* @__PURE__ */ jsx9("p", { className: "text-muted-foreground text-[11px]", children: "Non-modal lets you click the page while the panel is open." })
633
+ ] }),
634
+ /* @__PURE__ */ jsx9("div", { className: "flex gap-6", children: isHorizontal ? /* @__PURE__ */ jsx9(NumInput, { label: "Height", value: config.height, onChange: (v) => update({ height: v }) }) : /* @__PURE__ */ jsx9(NumInput, { label: "Width", value: config.width, onChange: (v) => update({ width: v }) }) })
635
+ ] }),
636
+ config.mode === "fixed" && /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-4", children: [
637
+ /* @__PURE__ */ jsx9("p", { className: "font-medium text-foreground", children: "Dimensions" }),
638
+ /* @__PURE__ */ jsxs4("div", { className: "flex gap-6", children: [
639
+ /* @__PURE__ */ jsx9(NumInput, { label: "Width", value: config.width, onChange: (v) => update({ width: v }) }),
640
+ /* @__PURE__ */ jsx9(NumInput, { label: "Height", value: config.height, onChange: (v) => update({ height: v }) })
641
+ ] })
642
+ ] })
643
+ ] }) }),
644
+ /* @__PURE__ */ jsx9("div", { className: "flex justify-end border-t border-border px-5 py-3", children: /* @__PURE__ */ jsx9(DialogClose, { render: /* @__PURE__ */ jsx9(Button, { variant: "outline", size: "sm", children: "Close" }) }) })
645
+ ]
646
+ }
647
+ ) });
648
+ }
649
+ function HeaderList({ headers }) {
650
+ const entries = Object.entries(headers ?? {});
651
+ if (entries.length === 0)
652
+ return /* @__PURE__ */ jsx9("p", { className: "text-muted-foreground text-[11px]", children: "No headers" });
653
+ return /* @__PURE__ */ jsx9("div", { className: "flex flex-col divide-y divide-border", children: entries.map(([k, v]) => /* @__PURE__ */ jsxs4("div", { className: "py-1.5", children: [
654
+ /* @__PURE__ */ jsx9("p", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wide mb-0.5", children: k }),
655
+ /* @__PURE__ */ jsx9("p", { className: "text-[11px] text-foreground break-all leading-relaxed", children: v })
656
+ ] }, k)) });
657
+ }
658
+ function BodyViewer({ body, contentType, emptyLabel }) {
659
+ if (!body) return /* @__PURE__ */ jsx9("p", { className: "text-muted-foreground text-[11px]", children: emptyLabel });
660
+ const isJson = contentType?.includes("json") || body.trimStart().startsWith("{") || body.trimStart().startsWith("[");
661
+ return /* @__PURE__ */ jsx9("pre", { className: "text-[11px] text-foreground whitespace-pre-wrap break-all leading-relaxed", children: isJson ? tryPrettyJson(body) : body });
662
+ }
663
+ function DetailPanel({ entry, onClose }) {
664
+ let pathname = entry.url;
665
+ try {
666
+ pathname = new URL(entry.url).pathname;
667
+ } catch {
668
+ }
669
+ return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col h-full overflow-hidden border-border", children: [
670
+ /* @__PURE__ */ jsxs4("div", { className: "flex shrink-0 items-center justify-between border-b border-border px-2.5 py-1.5 gap-2", children: [
671
+ /* @__PURE__ */ jsx9("span", { className: "truncate text-[11px] font-semibold text-foreground", title: entry.url, children: pathname }),
672
+ /* @__PURE__ */ jsx9(Button, { variant: "ghost", size: "icon-xs", className: "shrink-0 text-muted-foreground", onClick: onClose, children: "\u2715" })
673
+ ] }),
674
+ /* @__PURE__ */ jsxs4(Tabs, { defaultValue: "general", className: "flex flex-col flex-1 overflow-hidden gap-0", children: [
675
+ /* @__PURE__ */ jsx9(TabsList, { variant: "line", className: "shrink-0 w-full justify-start rounded-none border-b border-border bg-transparent h-auto px-1 overflow-x-auto scrollbar-none", children: ["general", "headers", "payload", "response"].map((v) => /* @__PURE__ */ jsx9(TabsTrigger, { value: v, className: "text-[11px] capitalize px-2.5 py-1.5", children: v }, v)) }),
676
+ /* @__PURE__ */ jsxs4("div", { className: "flex-1 overflow-hidden min-h-0", children: [
677
+ /* @__PURE__ */ jsx9(TabsContent, { value: "general", className: "h-full m-0", children: /* @__PURE__ */ jsx9(ScrollArea, { className: "h-full", children: /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-3 p-3 text-[11px]", children: [
678
+ [
679
+ ["Request URL", entry.url],
680
+ ["Request Method", entry.method],
681
+ ["Status Code", entry.status != null ? `${entry.status}${entry.statusText ? " " + entry.statusText : ""}` : "\u2014"],
682
+ ["Started at", formatTs(entry.ts)],
683
+ ["Duration", `${entry.duration}ms`],
684
+ ...entry.error ? [["Error", entry.error]] : []
685
+ ].map(([label, value]) => /* @__PURE__ */ jsxs4("div", { children: [
686
+ /* @__PURE__ */ jsx9("p", { className: "text-muted-foreground mb-0.5", children: label }),
687
+ /* @__PURE__ */ jsx9("p", { className: cn(
688
+ "break-all leading-relaxed",
689
+ label === "Status Code" ? statusClass(entry.status, entry.error) : label === "Error" ? "text-red-400" : "text-foreground"
690
+ ), children: value })
691
+ ] }, label)),
692
+ /* @__PURE__ */ jsxs4("div", { children: [
693
+ /* @__PURE__ */ jsx9("p", { className: "text-muted-foreground mb-1.5", children: "Duration bar" }),
694
+ /* @__PURE__ */ jsx9("div", { className: "h-1.5 rounded-full bg-muted overflow-hidden", children: /* @__PURE__ */ jsx9(
695
+ "div",
696
+ {
697
+ className: cn("h-full rounded-full", durationBarColor(entry.duration)),
698
+ style: { width: `${Math.min(entry.duration / 2e3 * 100, 100)}%` }
699
+ }
700
+ ) }),
701
+ /* @__PURE__ */ jsx9("p", { className: "text-[10px] text-muted-foreground/50 mt-1", children: "scale: 0\u20132000ms" })
702
+ ] })
703
+ ] }) }) }),
704
+ /* @__PURE__ */ jsx9(TabsContent, { value: "headers", className: "h-full m-0", children: /* @__PURE__ */ jsx9(ScrollArea, { className: "h-full", children: /* @__PURE__ */ jsx9("div", { className: "flex flex-col gap-4 p-3", children: [["Response Headers", entry.responseHeaders], ["Request Headers", entry.requestHeaders]].map(([label, hdrs]) => /* @__PURE__ */ jsxs4("div", { children: [
705
+ /* @__PURE__ */ jsx9("p", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-widest mb-2", children: label }),
706
+ /* @__PURE__ */ jsx9(HeaderList, { headers: hdrs })
707
+ ] }, label)) }) }) }),
708
+ /* @__PURE__ */ jsx9(TabsContent, { value: "payload", className: "h-full m-0", children: /* @__PURE__ */ jsx9(ScrollArea, { className: "h-full", children: /* @__PURE__ */ jsx9("div", { className: "p-3", children: /* @__PURE__ */ jsx9(BodyViewer, { body: entry.requestBody, contentType: entry.requestHeaders?.["content-type"] ?? null, emptyLabel: "No request body" }) }) }) }),
709
+ /* @__PURE__ */ jsx9(TabsContent, { value: "response", className: "h-full m-0", children: /* @__PURE__ */ jsx9(ScrollArea, { className: "h-full", children: /* @__PURE__ */ jsx9("div", { className: "p-3", children: /* @__PURE__ */ jsx9(BodyViewer, { body: entry.responseBody, contentType: entry.responseHeaders?.["content-type"] ?? null, emptyLabel: "No response body" }) }) }) })
710
+ ] })
711
+ ] })
712
+ ] });
713
+ }
714
+ function PanelInner({
715
+ entries,
716
+ selected,
717
+ setSelected,
718
+ setEntries,
719
+ onOpenSettings,
720
+ onClose,
721
+ bottomRef,
722
+ detailSize,
723
+ onDetailSizeChange
724
+ }) {
725
+ const groupRef = useGroupRef();
726
+ const detailSizeRef = useRef(detailSize);
727
+ const wasSelectedRef = useRef(false);
728
+ useEffect(() => {
729
+ detailSizeRef.current = detailSize;
730
+ }, [detailSize]);
731
+ useEffect(() => {
732
+ if (selected && !wasSelectedRef.current) {
733
+ const sz = detailSizeRef.current;
734
+ const id = setTimeout(() => {
735
+ groupRef.current?.setLayout({ main: 100 - sz, detail: sz });
736
+ }, 0);
737
+ wasSelectedRef.current = true;
738
+ return () => clearTimeout(id);
739
+ }
740
+ if (!selected) wasSelectedRef.current = false;
741
+ }, [selected, groupRef]);
742
+ return /* @__PURE__ */ jsxs4(Fragment, { children: [
743
+ /* @__PURE__ */ jsxs4("div", { className: "flex shrink-0 items-center justify-between border-b border-border px-3 py-1.5", children: [
744
+ /* @__PURE__ */ jsxs4("span", { className: "font-semibold text-foreground", children: [
745
+ "Server Network",
746
+ entries.length > 0 && /* @__PURE__ */ jsxs4("span", { className: "ml-1.5 font-normal text-muted-foreground", children: [
747
+ entries.length,
748
+ " requests"
749
+ ] })
750
+ ] }),
751
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1", children: [
752
+ /* @__PURE__ */ jsx9(Button, { variant: "ghost", size: "icon-xs", className: "text-muted-foreground", onClick: onOpenSettings, children: /* @__PURE__ */ jsx9(Settings2Icon, {}) }),
753
+ /* @__PURE__ */ jsx9(Button, { variant: "ghost", size: "xs", className: "text-muted-foreground", onClick: () => {
754
+ setEntries([]);
755
+ setSelected(null);
756
+ }, children: "Clear" }),
757
+ /* @__PURE__ */ jsx9(Button, { variant: "ghost", size: "icon-xs", className: "text-muted-foreground ml-1", onClick: onClose, children: /* @__PURE__ */ jsx9(XIcon3, {}) })
758
+ ] })
759
+ ] }),
760
+ /* @__PURE__ */ jsxs4(
761
+ ResizablePanelGroup,
762
+ {
763
+ orientation: "horizontal",
764
+ className: "flex-1 overflow-hidden",
765
+ groupRef,
766
+ onLayoutChanged: (layout) => {
767
+ const sz = layout["detail"];
768
+ if (sz !== void 0 && sz > 0) onDetailSizeChange(sz);
769
+ },
770
+ children: [
771
+ /* @__PURE__ */ jsx9(ResizablePanel, { id: "main", minSize: 10, children: /* @__PURE__ */ jsxs4(ScrollArea, { className: "h-full", children: [
772
+ /* @__PURE__ */ jsxs4("table", { className: "w-full border-collapse", children: [
773
+ /* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsx9("tr", { className: "sticky top-0 z-10 bg-background border-b border-border", children: [
774
+ { label: "Method", cls: "w-[58px] text-left" },
775
+ { label: "Status", cls: "w-[52px] text-left" },
776
+ { label: "URL", cls: "text-left" },
777
+ { label: "Duration", cls: "w-[68px] text-right" }
778
+ ].map(({ label, cls }) => /* @__PURE__ */ jsx9("th", { className: cn("px-2.5 py-1.5 text-[10px] font-semibold text-muted-foreground uppercase tracking-wider whitespace-nowrap", cls), children: label }, label)) }) }),
779
+ /* @__PURE__ */ jsx9("tbody", { children: entries.length === 0 ? /* @__PURE__ */ jsx9("tr", { children: /* @__PURE__ */ jsx9("td", { colSpan: 4, className: "py-6 text-center text-muted-foreground", children: "No requests yet" }) }) : entries.map((entry) => {
780
+ const isSelected = selected?.id === entry.id;
781
+ return /* @__PURE__ */ jsxs4(
782
+ "tr",
783
+ {
784
+ title: entry.error,
785
+ onClick: () => setSelected(isSelected ? null : entry),
786
+ className: cn(
787
+ "cursor-pointer border-b border-border transition-colors",
788
+ isSelected ? "bg-primary/15" : entry.error ? "bg-destructive/5 hover:bg-destructive/10" : "hover:bg-muted/40"
789
+ ),
790
+ children: [
791
+ /* @__PURE__ */ jsx9("td", { className: "px-2.5 py-1.25 whitespace-nowrap", children: /* @__PURE__ */ jsx9("span", { className: cn("font-semibold text-[11px]", methodClass(entry.method)), children: entry.method }) }),
792
+ /* @__PURE__ */ jsx9("td", { className: "px-2.5 py-1.25 whitespace-nowrap", children: /* @__PURE__ */ jsx9("span", { className: cn("font-semibold", statusClass(entry.status, entry.error)), children: entry.status ?? "ERR" }) }),
793
+ /* @__PURE__ */ jsx9("td", { className: "px-2.5 py-1.25 max-w-50 overflow-hidden text-ellipsis whitespace-nowrap text-foreground/80", children: entry.url }),
794
+ /* @__PURE__ */ jsxs4("td", { className: "px-2.5 py-1.25 text-right whitespace-nowrap text-muted-foreground", children: [
795
+ entry.duration,
796
+ "ms"
797
+ ] })
798
+ ]
799
+ },
800
+ entry.id
801
+ );
802
+ }) })
803
+ ] }),
804
+ /* @__PURE__ */ jsx9("div", { ref: bottomRef })
805
+ ] }) }),
806
+ selected && /* @__PURE__ */ jsxs4(Fragment, { children: [
807
+ /* @__PURE__ */ jsx9(ResizableHandle, { withHandle: true }),
808
+ /* @__PURE__ */ jsx9(ResizablePanel, { id: "detail", minSize: 10, children: /* @__PURE__ */ jsx9(DetailPanel, { entry: selected, onClose: () => setSelected(null) }) })
809
+ ] })
810
+ ]
811
+ }
812
+ )
813
+ ] });
814
+ }
815
+ function Panel2() {
816
+ const [entries, setEntries] = useState([]);
817
+ const [open, setOpen] = useState(false);
818
+ const [selected, setSelected] = useState(null);
819
+ const [settingsOpen, setSettingsOpen] = useState(false);
820
+ const bottomRef = useRef(null);
821
+ const mounted = useSyncExternalStore(() => () => {
822
+ }, () => true, () => false);
823
+ const { config, update } = usePanelConfig();
824
+ const theme = useEffectiveTheme(config.colorScheme);
825
+ const isDark = mounted && theme === "dark";
826
+ useEffect(() => {
827
+ const es = new EventSource("/api/dev-network");
828
+ es.onmessage = (event) => {
829
+ try {
830
+ const entry = JSON.parse(event.data);
831
+ setEntries((prev) => {
832
+ const next = [...prev, entry];
833
+ return next.length > MAX_ENTRIES ? next.slice(-MAX_ENTRIES) : next;
834
+ });
835
+ } catch {
836
+ }
837
+ };
838
+ return () => es.close();
839
+ }, []);
840
+ useEffect(() => {
841
+ if (open && !selected) bottomRef.current?.scrollIntoView({ behavior: "smooth" });
842
+ }, [entries, open, selected]);
843
+ const innerProps = {
844
+ entries,
845
+ selected,
846
+ setSelected,
847
+ setEntries,
848
+ onOpenSettings: () => setSettingsOpen(true),
849
+ onClose: () => setOpen(false),
850
+ bottomRef,
851
+ detailSize: config.detailSize,
852
+ onDetailSizeChange: (size) => update({ detailSize: size })
853
+ };
854
+ if (!mounted) return null;
855
+ const isHorizontal = config.sheetSide === "bottom" || config.sheetSide === "top";
856
+ const sheetSize = isHorizontal ? { height: config.height } : { width: config.width, maxWidth: config.width };
857
+ return /* @__PURE__ */ jsxs4(Fragment, { children: [
858
+ /* @__PURE__ */ jsx9(
859
+ SettingsDialog,
860
+ {
861
+ config,
862
+ update,
863
+ open: settingsOpen,
864
+ onOpenChange: setSettingsOpen,
865
+ theme
866
+ }
867
+ ),
868
+ config.mode === "sheet" && /* @__PURE__ */ jsx9(
869
+ Sheet,
870
+ {
871
+ open,
872
+ onOpenChange: (next) => {
873
+ if (next || config.sheetModal) setOpen(next);
874
+ },
875
+ modal: config.sheetModal,
876
+ children: /* @__PURE__ */ jsxs4(
877
+ SheetContent,
878
+ {
879
+ side: config.sheetSide,
880
+ showCloseButton: false,
881
+ showOverlay: config.sheetModal,
882
+ className: cn(isDark && "dark", "bg-background text-foreground border-border font-mono text-xs gap-0 p-0"),
883
+ style: sheetSize,
884
+ children: [
885
+ /* @__PURE__ */ jsx9(SheetTitle, { className: "sr-only", children: "Server Network Panel" }),
886
+ /* @__PURE__ */ jsx9(SheetDescription, { className: "sr-only", children: "Real-time SSR fetch inspector" }),
887
+ /* @__PURE__ */ jsx9(PanelInner, { ...innerProps })
888
+ ]
889
+ }
890
+ )
891
+ }
892
+ ),
893
+ config.mode === "fixed" && open && /* @__PURE__ */ jsx9("div", { className: cn(isDark && "dark"), suppressHydrationWarning: true, children: /* @__PURE__ */ jsx9(
894
+ "div",
895
+ {
896
+ className: "fixed bottom-14 right-4 z-9998 flex flex-col rounded-lg border border-border bg-background text-foreground font-mono text-xs shadow-2xl",
897
+ style: { width: config.width, height: config.height },
898
+ children: /* @__PURE__ */ jsx9(PanelInner, { ...innerProps })
899
+ }
900
+ ) }),
901
+ !open && /* @__PURE__ */ jsx9("div", { className: cn(isDark && "dark"), suppressHydrationWarning: true, children: /* @__PURE__ */ jsxs4(
902
+ Button,
903
+ {
904
+ variant: "outline",
905
+ size: "sm",
906
+ className: "fixed bottom-4 right-4 z-9999 font-mono gap-1.5 shadow-md dark:bg-zinc-900 dark:border-zinc-700 dark:text-zinc-100 dark:hover:bg-zinc-800",
907
+ onClick: () => setOpen(true),
908
+ children: [
909
+ /* @__PURE__ */ jsx9("span", { className: entries.length > 0 ? "text-green-500" : "text-muted-foreground", children: "\u25CF" }),
910
+ "Server Network",
911
+ entries.length > 0 && /* @__PURE__ */ jsx9(Badge, { variant: "secondary", className: "ml-0.5 tabular-nums", children: entries.length })
912
+ ]
913
+ }
914
+ ) })
915
+ ] });
916
+ }
917
+ function DevNetworkPanel() {
918
+ return /* @__PURE__ */ jsx9(Panel2, {});
919
+ }
920
+ export {
921
+ DevNetworkPanel
922
+ };