@camstack/ui-library 0.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2043 @@
1
+ // src/theme/defaults.ts
2
+ var providerColors = {
3
+ frigate: "#3b82f6",
4
+ scrypted: "#a855f7",
5
+ reolink: "#06b6d4",
6
+ homeAssistant: "#22d3ee",
7
+ rtsp: "#78716c"
8
+ };
9
+ var darkColors = {
10
+ primary: "#f59e42",
11
+ primaryForeground: "#0c0a09",
12
+ background: "#0c0a09",
13
+ backgroundElevated: "#1c1917",
14
+ surface: "#1c1917",
15
+ surfaceHover: "#292524",
16
+ border: "#292524",
17
+ borderSubtle: "#1c1917",
18
+ foreground: "#fafaf9",
19
+ foregroundMuted: "#a8a29e",
20
+ foregroundSubtle: "#78716c",
21
+ foregroundDisabled: "#57534e",
22
+ success: "#4ade80",
23
+ warning: "#fbbf24",
24
+ danger: "#f87171",
25
+ info: "#60a5fa",
26
+ provider: providerColors
27
+ };
28
+ var lightColors = {
29
+ primary: "#e67e22",
30
+ primaryForeground: "#ffffff",
31
+ background: "#fafaf9",
32
+ backgroundElevated: "#ffffff",
33
+ surface: "#f5f5f4",
34
+ surfaceHover: "#e7e5e4",
35
+ border: "#d6d3d1",
36
+ borderSubtle: "#e7e5e4",
37
+ foreground: "#1c1917",
38
+ foregroundMuted: "#57534e",
39
+ foregroundSubtle: "#78716c",
40
+ foregroundDisabled: "#a8a29e",
41
+ success: "#16a34a",
42
+ warning: "#d97706",
43
+ danger: "#dc2626",
44
+ info: "#2563eb",
45
+ provider: providerColors
46
+ };
47
+ var defaultTheme = {
48
+ colors: {
49
+ dark: darkColors,
50
+ light: lightColors
51
+ },
52
+ spacing: {
53
+ xs: 2,
54
+ sm: 4,
55
+ md: 8,
56
+ lg: 12,
57
+ xl: 16,
58
+ "2xl": 24,
59
+ "3xl": 32
60
+ },
61
+ radius: {
62
+ sm: 4,
63
+ md: 6,
64
+ lg: 8,
65
+ xl: 12
66
+ },
67
+ typography: {
68
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
69
+ sizes: {
70
+ xs: { fontSize: 10, lineHeight: 14 },
71
+ sm: { fontSize: 11, lineHeight: 16 },
72
+ base: { fontSize: 12, lineHeight: 18 },
73
+ lg: { fontSize: 13, lineHeight: 18 },
74
+ xl: { fontSize: 14, lineHeight: 20 },
75
+ "2xl": { fontSize: 16, lineHeight: 22 },
76
+ "3xl": { fontSize: 20, lineHeight: 28 }
77
+ },
78
+ weights: {
79
+ regular: 400,
80
+ medium: 500,
81
+ semibold: 600,
82
+ bold: 700
83
+ }
84
+ },
85
+ table: {
86
+ rowHeight: 28,
87
+ headerHeight: 24,
88
+ cellPaddingX: 8,
89
+ cellPaddingY: 6
90
+ },
91
+ sidebar: {
92
+ width: 176,
93
+ itemHeight: 28,
94
+ iconSize: 14
95
+ }
96
+ };
97
+
98
+ // src/theme/create-theme.ts
99
+ function deepMerge(target, source) {
100
+ const result = { ...target };
101
+ for (const key in source) {
102
+ const sourceVal = source[key];
103
+ const targetVal = target[key];
104
+ if (sourceVal !== void 0 && typeof sourceVal === "object" && sourceVal !== null && !Array.isArray(sourceVal) && typeof targetVal === "object" && targetVal !== null) {
105
+ result[key] = deepMerge(
106
+ targetVal,
107
+ sourceVal
108
+ );
109
+ } else if (sourceVal !== void 0) {
110
+ result[key] = sourceVal;
111
+ }
112
+ }
113
+ return result;
114
+ }
115
+ function createTheme(overrides) {
116
+ if (!overrides) return structuredClone(defaultTheme);
117
+ return deepMerge(structuredClone(defaultTheme), overrides);
118
+ }
119
+
120
+ // src/theme/theme-to-css.ts
121
+ function camelToKebab(str) {
122
+ return str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
123
+ }
124
+ function colorTokenToCssVar(key) {
125
+ return `--color-${camelToKebab(key)}`;
126
+ }
127
+ function generateColorBlock(colors) {
128
+ const lines = [];
129
+ for (const [key, value] of Object.entries(colors)) {
130
+ if (key === "provider") continue;
131
+ lines.push(` ${colorTokenToCssVar(key)}: ${value};`);
132
+ }
133
+ return lines.join("\n");
134
+ }
135
+ function generateProviderColors(provider) {
136
+ const lines = [];
137
+ for (const [key, value] of Object.entries(provider)) {
138
+ lines.push(` --color-provider-${camelToKebab(key)}: ${value};`);
139
+ }
140
+ return lines.join("\n");
141
+ }
142
+ function generateSpacingTokens(spacing) {
143
+ const lines = [];
144
+ for (const [key, value] of Object.entries(spacing)) {
145
+ lines.push(` --spacing-${key}: ${value}px;`);
146
+ }
147
+ return lines.join("\n");
148
+ }
149
+ function generateRadiusTokens(radius) {
150
+ const lines = [];
151
+ for (const [key, value] of Object.entries(radius)) {
152
+ lines.push(` --radius-${key}: ${value}px;`);
153
+ }
154
+ return lines.join("\n");
155
+ }
156
+ function themeToCss(theme) {
157
+ const darkColorBlock = generateColorBlock(theme.colors.dark);
158
+ const lightColorBlock = generateColorBlock(theme.colors.light);
159
+ const providerBlock = generateProviderColors(theme.colors.dark.provider);
160
+ const spacingBlock = generateSpacingTokens(theme.spacing);
161
+ const radiusBlock = generateRadiusTokens(theme.radius);
162
+ return `@theme {
163
+ ${providerBlock}
164
+ ${spacingBlock}
165
+ ${radiusBlock}
166
+ }
167
+
168
+ .dark {
169
+ ${darkColorBlock}
170
+ }
171
+
172
+ .light {
173
+ ${lightColorBlock}
174
+ }
175
+
176
+ @media (prefers-color-scheme: dark) {
177
+ :root {
178
+ ${darkColorBlock.replace(/^ /gm, " ")}
179
+ }
180
+ }
181
+
182
+ @media (prefers-color-scheme: light) {
183
+ :root {
184
+ ${lightColorBlock.replace(/^ /gm, " ")}
185
+ }
186
+ }
187
+ `;
188
+ }
189
+
190
+ // src/theme/theme-provider.tsx
191
+ import { createContext, useCallback, useEffect, useMemo, useState } from "react";
192
+ import { jsx } from "react/jsx-runtime";
193
+ var ThemeContext = createContext(null);
194
+ var TOGGLE_ORDER = ["dark", "light", "system"];
195
+ function getSystemPreference() {
196
+ if (typeof window === "undefined") return "dark";
197
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
198
+ }
199
+ function getInitialMode(storageKey, defaultMode) {
200
+ if (typeof window === "undefined") return defaultMode;
201
+ const stored = localStorage.getItem(storageKey);
202
+ if (stored === "dark" || stored === "light" || stored === "system") {
203
+ return stored;
204
+ }
205
+ return defaultMode;
206
+ }
207
+ function resolveMode(mode) {
208
+ if (mode === "system") return getSystemPreference();
209
+ return mode;
210
+ }
211
+ function ThemeProvider({
212
+ children,
213
+ defaultMode = "system",
214
+ storageKey = "camstack-theme-mode"
215
+ }) {
216
+ const [mode, setModeState] = useState(() => getInitialMode(storageKey, defaultMode));
217
+ const [resolvedMode, setResolvedMode] = useState(() => resolveMode(mode));
218
+ const setMode = useCallback(
219
+ (newMode) => {
220
+ setModeState(newMode);
221
+ setResolvedMode(resolveMode(newMode));
222
+ if (typeof window !== "undefined") {
223
+ localStorage.setItem(storageKey, newMode);
224
+ }
225
+ },
226
+ [storageKey]
227
+ );
228
+ const toggleMode = useCallback(() => {
229
+ const currentIndex = TOGGLE_ORDER.indexOf(mode);
230
+ const nextIndex = (currentIndex + 1) % TOGGLE_ORDER.length;
231
+ setMode(TOGGLE_ORDER[nextIndex] ?? "dark");
232
+ }, [mode, setMode]);
233
+ useEffect(() => {
234
+ if (typeof document === "undefined") return;
235
+ const root = document.documentElement;
236
+ root.classList.remove("dark", "light");
237
+ root.classList.add(resolvedMode);
238
+ }, [mode, resolvedMode]);
239
+ useEffect(() => {
240
+ if (typeof window === "undefined" || mode !== "system") return;
241
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
242
+ const handleChange = () => {
243
+ setResolvedMode(getSystemPreference());
244
+ };
245
+ mediaQuery.addEventListener("change", handleChange);
246
+ return () => mediaQuery.removeEventListener("change", handleChange);
247
+ }, [mode]);
248
+ const value = useMemo(
249
+ () => ({ mode, resolvedMode, setMode, toggleMode }),
250
+ [mode, resolvedMode, setMode, toggleMode]
251
+ );
252
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children });
253
+ }
254
+
255
+ // src/theme/use-theme-mode.ts
256
+ import { useContext } from "react";
257
+ function useThemeMode() {
258
+ const context = useContext(ThemeContext);
259
+ if (!context) {
260
+ throw new Error("useThemeMode must be used within a ThemeProvider");
261
+ }
262
+ return context;
263
+ }
264
+
265
+ // src/lib/cn.ts
266
+ import { clsx } from "clsx";
267
+ import { twMerge } from "tailwind-merge";
268
+ function cn(...inputs) {
269
+ return twMerge(clsx(inputs));
270
+ }
271
+
272
+ // src/icons/provider-icons.ts
273
+ import { Ship, Shield, Radio, Home, Cast } from "lucide-react";
274
+ var providerIcons = {
275
+ frigate: Ship,
276
+ scrypted: Shield,
277
+ reolink: Radio,
278
+ homeAssistant: Home,
279
+ rtsp: Cast
280
+ };
281
+
282
+ // src/icons/status-icons.ts
283
+ import { CircleCheck, CircleX, CircleAlert, CircleHelp } from "lucide-react";
284
+ var statusIcons = {
285
+ online: CircleCheck,
286
+ offline: CircleX,
287
+ degraded: CircleAlert,
288
+ unknown: CircleHelp
289
+ };
290
+
291
+ // src/primitives/button.tsx
292
+ import { forwardRef } from "react";
293
+ import { cva } from "class-variance-authority";
294
+ import { jsx as jsx2 } from "react/jsx-runtime";
295
+ var buttonVariants = cva(
296
+ "inline-flex items-center justify-center rounded-md font-medium transition-colors disabled:opacity-50 disabled:pointer-events-none",
297
+ {
298
+ variants: {
299
+ variant: {
300
+ primary: "bg-primary text-primary-foreground hover:bg-primary/90",
301
+ secondary: "bg-surface text-foreground hover:bg-surface-hover",
302
+ ghost: "hover:bg-surface-hover text-foreground-muted",
303
+ danger: "bg-danger text-white hover:bg-danger/90",
304
+ outline: "border border-border bg-transparent hover:bg-surface-hover"
305
+ },
306
+ size: {
307
+ sm: "h-7 px-2.5 text-xs gap-1.5",
308
+ md: "h-8 px-3 text-sm gap-2",
309
+ lg: "h-9 px-4 text-sm gap-2"
310
+ }
311
+ },
312
+ defaultVariants: {
313
+ variant: "primary",
314
+ size: "sm"
315
+ }
316
+ }
317
+ );
318
+ var Button = forwardRef(
319
+ ({ className, variant, size, ...props }, ref) => {
320
+ return /* @__PURE__ */ jsx2(
321
+ "button",
322
+ {
323
+ ref,
324
+ className: cn(buttonVariants({ variant, size }), className),
325
+ ...props
326
+ }
327
+ );
328
+ }
329
+ );
330
+ Button.displayName = "Button";
331
+
332
+ // src/primitives/icon-button.tsx
333
+ import { forwardRef as forwardRef2 } from "react";
334
+ import { cva as cva2 } from "class-variance-authority";
335
+ import { jsx as jsx3 } from "react/jsx-runtime";
336
+ var iconButtonVariants = cva2(
337
+ "inline-flex items-center justify-center rounded-md font-medium transition-colors disabled:opacity-50 disabled:pointer-events-none",
338
+ {
339
+ variants: {
340
+ variant: {
341
+ primary: "bg-primary text-primary-foreground hover:bg-primary/90",
342
+ secondary: "bg-surface text-foreground hover:bg-surface-hover",
343
+ ghost: "hover:bg-surface-hover text-foreground-muted",
344
+ danger: "bg-danger text-white hover:bg-danger/90",
345
+ outline: "border border-border bg-transparent hover:bg-surface-hover"
346
+ },
347
+ size: {
348
+ sm: "h-7 w-7",
349
+ md: "h-8 w-8",
350
+ lg: "h-9 w-9"
351
+ }
352
+ },
353
+ defaultVariants: {
354
+ variant: "primary",
355
+ size: "sm"
356
+ }
357
+ }
358
+ );
359
+ var IconButton = forwardRef2(
360
+ ({ className, variant, size, icon: Icon, ...props }, ref) => {
361
+ return /* @__PURE__ */ jsx3(
362
+ "button",
363
+ {
364
+ ref,
365
+ className: cn(iconButtonVariants({ variant, size }), className),
366
+ ...props,
367
+ children: /* @__PURE__ */ jsx3(Icon, { className: "h-4 w-4" })
368
+ }
369
+ );
370
+ }
371
+ );
372
+ IconButton.displayName = "IconButton";
373
+
374
+ // src/primitives/badge.tsx
375
+ import { forwardRef as forwardRef3 } from "react";
376
+ import { cva as cva3 } from "class-variance-authority";
377
+ import { jsx as jsx4 } from "react/jsx-runtime";
378
+ var badgeVariants = cva3(
379
+ "inline-flex items-center rounded-full text-xs font-medium px-2 py-0.5",
380
+ {
381
+ variants: {
382
+ variant: {
383
+ default: "bg-surface-hover text-foreground",
384
+ success: "bg-success/15 text-success",
385
+ warning: "bg-warning/15 text-warning",
386
+ danger: "bg-danger/15 text-danger",
387
+ info: "bg-info/15 text-info"
388
+ },
389
+ styleVariant: {
390
+ solid: "",
391
+ outline: "border bg-transparent"
392
+ }
393
+ },
394
+ defaultVariants: {
395
+ variant: "default",
396
+ styleVariant: "solid"
397
+ }
398
+ }
399
+ );
400
+ var Badge = forwardRef3(
401
+ ({ className, variant, style, ...props }, ref) => {
402
+ return /* @__PURE__ */ jsx4(
403
+ "span",
404
+ {
405
+ ref,
406
+ className: cn(badgeVariants({ variant, styleVariant: style }), className),
407
+ ...props
408
+ }
409
+ );
410
+ }
411
+ );
412
+ Badge.displayName = "Badge";
413
+
414
+ // src/primitives/card.tsx
415
+ import { forwardRef as forwardRef4 } from "react";
416
+ import { cva as cva4 } from "class-variance-authority";
417
+ import { jsx as jsx5 } from "react/jsx-runtime";
418
+ var cardVariants = cva4("rounded-lg p-3", {
419
+ variants: {
420
+ variant: {
421
+ flat: "bg-surface",
422
+ bordered: "bg-surface border border-border"
423
+ }
424
+ },
425
+ defaultVariants: {
426
+ variant: "bordered"
427
+ }
428
+ });
429
+ var Card = forwardRef4(
430
+ ({ className, variant, ...props }, ref) => {
431
+ return /* @__PURE__ */ jsx5(
432
+ "div",
433
+ {
434
+ ref,
435
+ className: cn(cardVariants({ variant }), className),
436
+ ...props
437
+ }
438
+ );
439
+ }
440
+ );
441
+ Card.displayName = "Card";
442
+
443
+ // src/primitives/label.tsx
444
+ import { forwardRef as forwardRef5 } from "react";
445
+ import { jsx as jsx6 } from "react/jsx-runtime";
446
+ var Label = forwardRef5(
447
+ ({ className, ...props }, ref) => {
448
+ return /* @__PURE__ */ jsx6(
449
+ "label",
450
+ {
451
+ ref,
452
+ className: cn(
453
+ "text-[10px] uppercase tracking-wider text-foreground-muted font-medium",
454
+ className
455
+ ),
456
+ ...props
457
+ }
458
+ );
459
+ }
460
+ );
461
+ Label.displayName = "Label";
462
+
463
+ // src/primitives/separator.tsx
464
+ import { forwardRef as forwardRef6 } from "react";
465
+ import { cva as cva5 } from "class-variance-authority";
466
+ import { jsx as jsx7 } from "react/jsx-runtime";
467
+ var separatorVariants = cva5("", {
468
+ variants: {
469
+ orientation: {
470
+ horizontal: "h-px w-full bg-border-subtle",
471
+ vertical: "w-px h-full bg-border-subtle"
472
+ }
473
+ },
474
+ defaultVariants: {
475
+ orientation: "horizontal"
476
+ }
477
+ });
478
+ var Separator = forwardRef6(
479
+ ({ className, orientation, ...props }, ref) => {
480
+ return /* @__PURE__ */ jsx7(
481
+ "div",
482
+ {
483
+ ref,
484
+ role: "separator",
485
+ className: cn(separatorVariants({ orientation }), className),
486
+ ...props
487
+ }
488
+ );
489
+ }
490
+ );
491
+ Separator.displayName = "Separator";
492
+
493
+ // src/primitives/skeleton.tsx
494
+ import { forwardRef as forwardRef7 } from "react";
495
+ import { jsx as jsx8 } from "react/jsx-runtime";
496
+ var Skeleton = forwardRef7(
497
+ ({ className, ...props }, ref) => {
498
+ return /* @__PURE__ */ jsx8(
499
+ "div",
500
+ {
501
+ ref,
502
+ className: cn("animate-pulse bg-surface-hover rounded-md h-4 w-full", className),
503
+ ...props
504
+ }
505
+ );
506
+ }
507
+ );
508
+ Skeleton.displayName = "Skeleton";
509
+
510
+ // src/primitives/input.tsx
511
+ import { forwardRef as forwardRef8 } from "react";
512
+ import { cva as cva6 } from "class-variance-authority";
513
+ import { jsx as jsx9, jsxs } from "react/jsx-runtime";
514
+ var inputVariants = cva6(
515
+ "h-7 w-full px-2.5 text-xs bg-surface border rounded-md text-foreground placeholder:text-foreground-subtle focus:outline-none focus:ring-1 focus:ring-primary transition-colors",
516
+ {
517
+ variants: {
518
+ state: {
519
+ default: "border-border",
520
+ error: "border-danger"
521
+ }
522
+ },
523
+ defaultVariants: {
524
+ state: "default"
525
+ }
526
+ }
527
+ );
528
+ var Input = forwardRef8(
529
+ ({ className, state, leftSlot, rightSlot, ...props }, ref) => {
530
+ if (leftSlot || rightSlot) {
531
+ return /* @__PURE__ */ jsxs("div", { className: "relative flex items-center w-full", children: [
532
+ leftSlot && /* @__PURE__ */ jsx9("div", { className: "absolute left-2.5 flex items-center pointer-events-none", children: leftSlot }),
533
+ /* @__PURE__ */ jsx9(
534
+ "input",
535
+ {
536
+ ref,
537
+ className: cn(
538
+ inputVariants({ state }),
539
+ leftSlot ? "pl-7" : "",
540
+ rightSlot ? "pr-7" : "",
541
+ className
542
+ ),
543
+ ...props
544
+ }
545
+ ),
546
+ rightSlot && /* @__PURE__ */ jsx9("div", { className: "absolute right-2.5 flex items-center pointer-events-none", children: rightSlot })
547
+ ] });
548
+ }
549
+ return /* @__PURE__ */ jsx9(
550
+ "input",
551
+ {
552
+ ref,
553
+ className: cn(inputVariants({ state }), className),
554
+ ...props
555
+ }
556
+ );
557
+ }
558
+ );
559
+ Input.displayName = "Input";
560
+
561
+ // src/primitives/select.tsx
562
+ import { forwardRef as forwardRef9 } from "react";
563
+ import { ChevronDown } from "lucide-react";
564
+ import { jsx as jsx10, jsxs as jsxs2 } from "react/jsx-runtime";
565
+ var Select = forwardRef9(
566
+ ({ className, options, ...props }, ref) => {
567
+ return /* @__PURE__ */ jsxs2("div", { className: "relative w-full", children: [
568
+ /* @__PURE__ */ jsx10(
569
+ "select",
570
+ {
571
+ ref,
572
+ className: cn(
573
+ "h-7 w-full px-2.5 text-xs bg-surface border border-border rounded-md text-foreground appearance-none pr-7 focus:outline-none focus:ring-1 focus:ring-primary",
574
+ className
575
+ ),
576
+ ...props,
577
+ children: options.map((option) => /* @__PURE__ */ jsx10("option", { value: option.value, children: option.label }, option.value))
578
+ }
579
+ ),
580
+ /* @__PURE__ */ jsx10("div", { className: "absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none text-foreground-muted", children: /* @__PURE__ */ jsx10(ChevronDown, { className: "h-3.5 w-3.5" }) })
581
+ ] });
582
+ }
583
+ );
584
+ Select.displayName = "Select";
585
+
586
+ // src/primitives/checkbox.tsx
587
+ import { forwardRef as forwardRef10 } from "react";
588
+ import { jsx as jsx11 } from "react/jsx-runtime";
589
+ var Checkbox = forwardRef10(
590
+ ({ className, ...props }, ref) => {
591
+ return /* @__PURE__ */ jsx11(
592
+ "input",
593
+ {
594
+ ref,
595
+ type: "checkbox",
596
+ className: cn(
597
+ "w-3.5 h-3.5 rounded-sm border border-border accent-primary",
598
+ className
599
+ ),
600
+ ...props
601
+ }
602
+ );
603
+ }
604
+ );
605
+ Checkbox.displayName = "Checkbox";
606
+
607
+ // src/primitives/switch.tsx
608
+ import { forwardRef as forwardRef11 } from "react";
609
+ import { jsx as jsx12, jsxs as jsxs3 } from "react/jsx-runtime";
610
+ var Switch = forwardRef11(
611
+ ({ className, checked, onCheckedChange, label, ...props }, ref) => {
612
+ return /* @__PURE__ */ jsxs3(
613
+ "button",
614
+ {
615
+ ref,
616
+ role: "switch",
617
+ "aria-checked": checked,
618
+ type: "button",
619
+ onClick: () => onCheckedChange(!checked),
620
+ className: cn("inline-flex items-center gap-2", className),
621
+ ...props,
622
+ children: [
623
+ /* @__PURE__ */ jsx12(
624
+ "span",
625
+ {
626
+ className: cn(
627
+ "w-8 h-4 rounded-full transition-colors",
628
+ checked ? "bg-primary" : "bg-surface-hover"
629
+ ),
630
+ children: /* @__PURE__ */ jsx12(
631
+ "span",
632
+ {
633
+ className: cn(
634
+ "block w-3.5 h-3.5 rounded-full bg-white transition-transform",
635
+ checked ? "translate-x-4" : "translate-x-0.5"
636
+ )
637
+ }
638
+ )
639
+ }
640
+ ),
641
+ label && /* @__PURE__ */ jsx12("span", { className: "text-xs text-foreground", children: label })
642
+ ]
643
+ }
644
+ );
645
+ }
646
+ );
647
+ Switch.displayName = "Switch";
648
+
649
+ // src/primitives/dialog.tsx
650
+ import {
651
+ createContext as createContext2,
652
+ forwardRef as forwardRef12,
653
+ useCallback as useCallback2,
654
+ useContext as useContext2,
655
+ useEffect as useEffect2,
656
+ useId,
657
+ useRef,
658
+ useState as useState2
659
+ } from "react";
660
+ import { cva as cva7 } from "class-variance-authority";
661
+ import { jsx as jsx13 } from "react/jsx-runtime";
662
+ var DialogContext = createContext2(null);
663
+ function useDialogContext() {
664
+ const ctx = useContext2(DialogContext);
665
+ if (!ctx) throw new Error("Dialog compound components must be used within <Dialog>");
666
+ return ctx;
667
+ }
668
+ function Dialog({ children, open: controlledOpen, onOpenChange }) {
669
+ const [uncontrolledOpen, setUncontrolledOpen] = useState2(false);
670
+ const open = controlledOpen ?? uncontrolledOpen;
671
+ const contentId = useId();
672
+ const setOpen = useCallback2(
673
+ (next) => {
674
+ onOpenChange?.(next);
675
+ if (controlledOpen === void 0) setUncontrolledOpen(next);
676
+ },
677
+ [controlledOpen, onOpenChange]
678
+ );
679
+ return /* @__PURE__ */ jsx13(DialogContext.Provider, { value: { open, setOpen, contentId }, children });
680
+ }
681
+ function DialogTrigger({ children, ...props }) {
682
+ const { setOpen } = useDialogContext();
683
+ return /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => setOpen(true), ...props, children });
684
+ }
685
+ var contentVariants = cva7(
686
+ "bg-background-elevated border border-border rounded-lg p-4 backdrop:bg-black/50 backdrop:backdrop-blur-sm",
687
+ {
688
+ variants: {
689
+ width: {
690
+ sm: "max-w-sm",
691
+ md: "max-w-md",
692
+ lg: "max-w-lg"
693
+ }
694
+ },
695
+ defaultVariants: { width: "md" }
696
+ }
697
+ );
698
+ var DialogContent = forwardRef12(
699
+ ({ className, width, children, ...props }, ref) => {
700
+ const { open, setOpen, contentId } = useDialogContext();
701
+ const innerRef = useRef(null);
702
+ const dialogRef = ref ?? innerRef;
703
+ useEffect2(() => {
704
+ const el = dialogRef.current;
705
+ if (!el) return;
706
+ if (open && !el.open) el.showModal();
707
+ if (!open && el.open) el.close();
708
+ }, [open, dialogRef]);
709
+ const handleClick = (e) => {
710
+ if (e.target === e.currentTarget) setOpen(false);
711
+ };
712
+ return /* @__PURE__ */ jsx13(
713
+ "dialog",
714
+ {
715
+ ref: dialogRef,
716
+ id: contentId,
717
+ className: cn(contentVariants({ width }), "w-full", className),
718
+ onClick: handleClick,
719
+ onClose: () => setOpen(false),
720
+ ...props,
721
+ children
722
+ }
723
+ );
724
+ }
725
+ );
726
+ DialogContent.displayName = "DialogContent";
727
+ function DialogHeader({ className, ...props }) {
728
+ return /* @__PURE__ */ jsx13("div", { className: cn("flex flex-col gap-1 mb-3", className), ...props });
729
+ }
730
+ function DialogFooter({ className, ...props }) {
731
+ return /* @__PURE__ */ jsx13("div", { className: cn("flex justify-end gap-2 mt-4", className), ...props });
732
+ }
733
+ function DialogTitle({ className, ...props }) {
734
+ return /* @__PURE__ */ jsx13("h2", { className: cn("text-sm font-semibold text-foreground", className), ...props });
735
+ }
736
+ function DialogDescription({ className, ...props }) {
737
+ return /* @__PURE__ */ jsx13("p", { className: cn("text-xs text-foreground-muted", className), ...props });
738
+ }
739
+
740
+ // src/primitives/dropdown.tsx
741
+ import {
742
+ createContext as createContext3,
743
+ useCallback as useCallback3,
744
+ useContext as useContext3,
745
+ useEffect as useEffect3,
746
+ useId as useId2,
747
+ useRef as useRef2,
748
+ useState as useState3
749
+ } from "react";
750
+ import { jsx as jsx14, jsxs as jsxs4 } from "react/jsx-runtime";
751
+ var DropdownContext = createContext3(null);
752
+ function useDropdownContext() {
753
+ const ctx = useContext3(DropdownContext);
754
+ if (!ctx) throw new Error("Dropdown compound components must be used within <Dropdown>");
755
+ return ctx;
756
+ }
757
+ function Dropdown({ children }) {
758
+ const [open, setOpen] = useState3(false);
759
+ const triggerId = useId2();
760
+ const contentId = useId2();
761
+ return /* @__PURE__ */ jsx14(DropdownContext.Provider, { value: { open, setOpen, triggerId, contentId }, children: /* @__PURE__ */ jsx14("div", { className: "relative inline-block", children }) });
762
+ }
763
+ function DropdownTrigger({ children, ...props }) {
764
+ const { open, setOpen, triggerId, contentId } = useDropdownContext();
765
+ return /* @__PURE__ */ jsx14(
766
+ "button",
767
+ {
768
+ type: "button",
769
+ id: triggerId,
770
+ "aria-haspopup": "menu",
771
+ "aria-expanded": open,
772
+ "aria-controls": open ? contentId : void 0,
773
+ onClick: () => setOpen(!open),
774
+ ...props,
775
+ children
776
+ }
777
+ );
778
+ }
779
+ function DropdownContent({ className, children, ...props }) {
780
+ const { open, setOpen, contentId, triggerId } = useDropdownContext();
781
+ const ref = useRef2(null);
782
+ useEffect3(() => {
783
+ if (!open) return;
784
+ const handler = (e) => {
785
+ const el = ref.current;
786
+ const trigger = document.getElementById(triggerId);
787
+ if (el && !el.contains(e.target) && !trigger?.contains(e.target)) {
788
+ setOpen(false);
789
+ }
790
+ };
791
+ const escHandler = (e) => {
792
+ if (e.key === "Escape") setOpen(false);
793
+ };
794
+ document.addEventListener("mousedown", handler);
795
+ document.addEventListener("keydown", escHandler);
796
+ return () => {
797
+ document.removeEventListener("mousedown", handler);
798
+ document.removeEventListener("keydown", escHandler);
799
+ };
800
+ }, [open, setOpen, triggerId]);
801
+ if (!open) return null;
802
+ return /* @__PURE__ */ jsx14(
803
+ "div",
804
+ {
805
+ ref,
806
+ id: contentId,
807
+ role: "menu",
808
+ "aria-labelledby": triggerId,
809
+ className: cn(
810
+ "absolute left-0 top-full z-50 mt-1 bg-background-elevated border border-border rounded-md shadow-lg py-1 min-w-[160px]",
811
+ className
812
+ ),
813
+ ...props,
814
+ children
815
+ }
816
+ );
817
+ }
818
+ function DropdownItem({
819
+ className,
820
+ icon: Icon,
821
+ variant = "default",
822
+ children,
823
+ onClick,
824
+ ...props
825
+ }) {
826
+ const { setOpen } = useDropdownContext();
827
+ const handleClick = useCallback3(
828
+ (e) => {
829
+ onClick?.(e);
830
+ setOpen(false);
831
+ },
832
+ [onClick, setOpen]
833
+ );
834
+ return /* @__PURE__ */ jsxs4(
835
+ "button",
836
+ {
837
+ type: "button",
838
+ role: "menuitem",
839
+ className: cn(
840
+ "h-7 text-xs px-2 w-full text-left flex items-center gap-2",
841
+ variant === "danger" ? "text-danger hover:bg-danger/10" : "text-foreground hover:bg-surface-hover",
842
+ className
843
+ ),
844
+ onClick: handleClick,
845
+ ...props,
846
+ children: [
847
+ Icon && /* @__PURE__ */ jsx14(Icon, { size: 14 }),
848
+ children
849
+ ]
850
+ }
851
+ );
852
+ }
853
+
854
+ // src/primitives/tooltip.tsx
855
+ import {
856
+ createContext as createContext4,
857
+ useContext as useContext4,
858
+ useId as useId3,
859
+ useRef as useRef3,
860
+ useState as useState4
861
+ } from "react";
862
+ import { jsx as jsx15 } from "react/jsx-runtime";
863
+ var TooltipContext = createContext4(null);
864
+ function useTooltipContext() {
865
+ const ctx = useContext4(TooltipContext);
866
+ if (!ctx) throw new Error("Tooltip compound components must be used within <Tooltip>");
867
+ return ctx;
868
+ }
869
+ function Tooltip({ children }) {
870
+ const [open, setOpen] = useState4(false);
871
+ const timerRef = useRef3(null);
872
+ const tooltipId = useId3();
873
+ const show = () => {
874
+ timerRef.current = setTimeout(() => setOpen(true), 300);
875
+ };
876
+ const hide = () => {
877
+ if (timerRef.current) clearTimeout(timerRef.current);
878
+ setOpen(false);
879
+ };
880
+ return /* @__PURE__ */ jsx15(TooltipContext.Provider, { value: { open, show, hide, tooltipId }, children: /* @__PURE__ */ jsx15("div", { className: "relative inline-block", children }) });
881
+ }
882
+ function TooltipTrigger({ children, ...props }) {
883
+ const { show, hide, tooltipId, open } = useTooltipContext();
884
+ return /* @__PURE__ */ jsx15(
885
+ "div",
886
+ {
887
+ onMouseEnter: show,
888
+ onMouseLeave: hide,
889
+ onFocus: show,
890
+ onBlur: hide,
891
+ "aria-describedby": open ? tooltipId : void 0,
892
+ ...props,
893
+ children
894
+ }
895
+ );
896
+ }
897
+ function TooltipContent({ className, children, ...props }) {
898
+ const { open, tooltipId } = useTooltipContext();
899
+ return /* @__PURE__ */ jsx15(
900
+ "div",
901
+ {
902
+ id: tooltipId,
903
+ role: "tooltip",
904
+ className: cn(
905
+ "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 bg-foreground text-background text-xs px-2 py-1 rounded-md shadow-md whitespace-nowrap pointer-events-none transition-opacity duration-150",
906
+ open ? "opacity-100" : "opacity-0",
907
+ className
908
+ ),
909
+ ...props,
910
+ children
911
+ }
912
+ );
913
+ }
914
+
915
+ // src/primitives/popover.tsx
916
+ import {
917
+ createContext as createContext5,
918
+ useCallback as useCallback4,
919
+ useContext as useContext5,
920
+ useEffect as useEffect4,
921
+ useId as useId4,
922
+ useRef as useRef4,
923
+ useState as useState5
924
+ } from "react";
925
+ import { jsx as jsx16 } from "react/jsx-runtime";
926
+ var PopoverContext = createContext5(null);
927
+ function usePopoverContext() {
928
+ const ctx = useContext5(PopoverContext);
929
+ if (!ctx) throw new Error("Popover compound components must be used within <Popover>");
930
+ return ctx;
931
+ }
932
+ function Popover({ children, open: controlledOpen, onOpenChange }) {
933
+ const [uncontrolledOpen, setUncontrolledOpen] = useState5(false);
934
+ const open = controlledOpen ?? uncontrolledOpen;
935
+ const triggerId = useId4();
936
+ const contentId = useId4();
937
+ const setOpen = useCallback4(
938
+ (next) => {
939
+ onOpenChange?.(next);
940
+ if (controlledOpen === void 0) setUncontrolledOpen(next);
941
+ },
942
+ [controlledOpen, onOpenChange]
943
+ );
944
+ return /* @__PURE__ */ jsx16(PopoverContext.Provider, { value: { open, setOpen, triggerId, contentId }, children: /* @__PURE__ */ jsx16("div", { className: "relative inline-block", children }) });
945
+ }
946
+ function PopoverTrigger({ children, ...props }) {
947
+ const { open, setOpen, triggerId, contentId } = usePopoverContext();
948
+ return /* @__PURE__ */ jsx16(
949
+ "button",
950
+ {
951
+ type: "button",
952
+ id: triggerId,
953
+ "aria-haspopup": "dialog",
954
+ "aria-expanded": open,
955
+ "aria-controls": open ? contentId : void 0,
956
+ onClick: () => setOpen(!open),
957
+ ...props,
958
+ children
959
+ }
960
+ );
961
+ }
962
+ function PopoverContent({ className, children, ...props }) {
963
+ const { open, setOpen, contentId, triggerId } = usePopoverContext();
964
+ const ref = useRef4(null);
965
+ useEffect4(() => {
966
+ if (!open) return;
967
+ const handler = (e) => {
968
+ const el = ref.current;
969
+ const trigger = document.getElementById(triggerId);
970
+ if (el && !el.contains(e.target) && !trigger?.contains(e.target)) {
971
+ setOpen(false);
972
+ }
973
+ };
974
+ const escHandler = (e) => {
975
+ if (e.key === "Escape") setOpen(false);
976
+ };
977
+ document.addEventListener("mousedown", handler);
978
+ document.addEventListener("keydown", escHandler);
979
+ return () => {
980
+ document.removeEventListener("mousedown", handler);
981
+ document.removeEventListener("keydown", escHandler);
982
+ };
983
+ }, [open, setOpen, triggerId]);
984
+ if (!open) return null;
985
+ return /* @__PURE__ */ jsx16(
986
+ "div",
987
+ {
988
+ ref,
989
+ id: contentId,
990
+ role: "dialog",
991
+ "aria-labelledby": triggerId,
992
+ className: cn(
993
+ "absolute left-0 top-full z-50 mt-1 bg-background-elevated border border-border rounded-lg shadow-lg p-3",
994
+ className
995
+ ),
996
+ ...props,
997
+ children
998
+ }
999
+ );
1000
+ }
1001
+
1002
+ // src/primitives/tabs.tsx
1003
+ import {
1004
+ createContext as createContext6,
1005
+ useCallback as useCallback5,
1006
+ useContext as useContext6,
1007
+ useState as useState6
1008
+ } from "react";
1009
+ import { jsx as jsx17 } from "react/jsx-runtime";
1010
+ var TabsContext = createContext6(null);
1011
+ function useTabsContext() {
1012
+ const ctx = useContext6(TabsContext);
1013
+ if (!ctx) throw new Error("Tabs compound components must be used within <Tabs>");
1014
+ return ctx;
1015
+ }
1016
+ function Tabs({
1017
+ value: controlledValue,
1018
+ onValueChange,
1019
+ defaultValue = "",
1020
+ className,
1021
+ ...props
1022
+ }) {
1023
+ const [uncontrolledValue, setUncontrolledValue] = useState6(defaultValue);
1024
+ const value = controlledValue ?? uncontrolledValue;
1025
+ const setValue = useCallback5(
1026
+ (next) => {
1027
+ onValueChange?.(next);
1028
+ if (controlledValue === void 0) setUncontrolledValue(next);
1029
+ },
1030
+ [controlledValue, onValueChange]
1031
+ );
1032
+ return /* @__PURE__ */ jsx17(TabsContext.Provider, { value: { value, setValue }, children: /* @__PURE__ */ jsx17("div", { className, ...props }) });
1033
+ }
1034
+ function TabsList({ className, ...props }) {
1035
+ return /* @__PURE__ */ jsx17(
1036
+ "div",
1037
+ {
1038
+ role: "tablist",
1039
+ className: cn("flex flex-row border-b border-border-subtle", className),
1040
+ ...props
1041
+ }
1042
+ );
1043
+ }
1044
+ function TabsTrigger({ value, className, ...props }) {
1045
+ const { value: activeValue, setValue } = useTabsContext();
1046
+ const isActive = value === activeValue;
1047
+ const panelId = `tabpanel-${value}`;
1048
+ return /* @__PURE__ */ jsx17(
1049
+ "button",
1050
+ {
1051
+ type: "button",
1052
+ role: "tab",
1053
+ "aria-selected": isActive,
1054
+ "aria-controls": panelId,
1055
+ tabIndex: isActive ? 0 : -1,
1056
+ className: cn(
1057
+ "h-7 text-xs px-3 transition-colors",
1058
+ isActive ? "border-b-2 border-primary text-foreground font-medium" : "text-foreground-muted hover:text-foreground",
1059
+ className
1060
+ ),
1061
+ onClick: () => setValue(value),
1062
+ ...props
1063
+ }
1064
+ );
1065
+ }
1066
+ function TabsContent({ value, className, ...props }) {
1067
+ const { value: activeValue } = useTabsContext();
1068
+ if (value !== activeValue) return null;
1069
+ return /* @__PURE__ */ jsx17(
1070
+ "div",
1071
+ {
1072
+ role: "tabpanel",
1073
+ id: `tabpanel-${value}`,
1074
+ className: cn("pt-3", className),
1075
+ ...props
1076
+ }
1077
+ );
1078
+ }
1079
+
1080
+ // src/primitives/scroll-area.tsx
1081
+ import { forwardRef as forwardRef13 } from "react";
1082
+ import { jsx as jsx18 } from "react/jsx-runtime";
1083
+ var ScrollArea = forwardRef13(
1084
+ ({ className, ...props }, ref) => {
1085
+ return /* @__PURE__ */ jsx18(
1086
+ "div",
1087
+ {
1088
+ ref,
1089
+ className: cn(
1090
+ "overflow-auto [&::-webkit-scrollbar]:w-1 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:bg-surface-hover [&::-webkit-scrollbar-thumb]:rounded-full",
1091
+ className
1092
+ ),
1093
+ ...props
1094
+ }
1095
+ );
1096
+ }
1097
+ );
1098
+ ScrollArea.displayName = "ScrollArea";
1099
+
1100
+ // src/primitives/floating-panel.tsx
1101
+ import { useRef as useRef5, useState as useState7, useCallback as useCallback6, useEffect as useEffect5 } from "react";
1102
+ import { X, Minimize2, Maximize2, GripHorizontal } from "lucide-react";
1103
+ import { jsx as jsx19, jsxs as jsxs5 } from "react/jsx-runtime";
1104
+ function FloatingPanel({
1105
+ title,
1106
+ onClose,
1107
+ children,
1108
+ defaultWidth = 360,
1109
+ defaultHeight = 280,
1110
+ minWidth = 280,
1111
+ minHeight = 160,
1112
+ offsetIndex = 0,
1113
+ className
1114
+ }) {
1115
+ const [pos, setPos] = useState7({ x: 80 + offsetIndex * 30, y: 80 + offsetIndex * 30 });
1116
+ const [size, setSize] = useState7({ w: defaultWidth, h: defaultHeight });
1117
+ const [minimized, setMinimized] = useState7(false);
1118
+ const dragging = useRef5(false);
1119
+ const resizing = useRef5(false);
1120
+ const offset = useRef5({ x: 0, y: 0 });
1121
+ const onDragStart = useCallback6((e) => {
1122
+ e.preventDefault();
1123
+ dragging.current = true;
1124
+ offset.current = { x: e.clientX - pos.x, y: e.clientY - pos.y };
1125
+ }, [pos]);
1126
+ const onResizeStart = useCallback6((e) => {
1127
+ e.preventDefault();
1128
+ e.stopPropagation();
1129
+ resizing.current = true;
1130
+ offset.current = { x: e.clientX, y: e.clientY };
1131
+ }, []);
1132
+ useEffect5(() => {
1133
+ const onMouseMove = (e) => {
1134
+ if (dragging.current) setPos({ x: e.clientX - offset.current.x, y: e.clientY - offset.current.y });
1135
+ if (resizing.current) {
1136
+ const dx = e.clientX - offset.current.x;
1137
+ const dy = e.clientY - offset.current.y;
1138
+ offset.current = { x: e.clientX, y: e.clientY };
1139
+ setSize((prev) => ({ w: Math.max(minWidth, prev.w + dx), h: Math.max(minHeight, prev.h + dy) }));
1140
+ }
1141
+ };
1142
+ const onMouseUp = () => {
1143
+ dragging.current = false;
1144
+ resizing.current = false;
1145
+ };
1146
+ window.addEventListener("mousemove", onMouseMove);
1147
+ window.addEventListener("mouseup", onMouseUp);
1148
+ return () => {
1149
+ window.removeEventListener("mousemove", onMouseMove);
1150
+ window.removeEventListener("mouseup", onMouseUp);
1151
+ };
1152
+ }, [minWidth, minHeight]);
1153
+ return /* @__PURE__ */ jsxs5(
1154
+ "div",
1155
+ {
1156
+ className: cn(
1157
+ "fixed z-50 rounded-lg border border-border bg-background-elevated shadow-2xl flex flex-col overflow-hidden",
1158
+ className
1159
+ ),
1160
+ style: { left: pos.x, top: pos.y, width: minimized ? 280 : size.w, height: minimized ? "auto" : size.h },
1161
+ children: [
1162
+ /* @__PURE__ */ jsxs5(
1163
+ "div",
1164
+ {
1165
+ onMouseDown: onDragStart,
1166
+ className: "flex items-center justify-between gap-2 px-3 py-2 border-b border-border cursor-move select-none shrink-0 bg-surface",
1167
+ children: [
1168
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 min-w-0", children: [
1169
+ /* @__PURE__ */ jsx19(GripHorizontal, { size: 12, className: "text-foreground-subtle shrink-0" }),
1170
+ /* @__PURE__ */ jsx19("span", { className: "text-[11px] font-medium truncate", children: title })
1171
+ ] }),
1172
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1 shrink-0", children: [
1173
+ /* @__PURE__ */ jsx19(
1174
+ "button",
1175
+ {
1176
+ onClick: () => setMinimized(!minimized),
1177
+ className: "p-0.5 rounded hover:bg-surface-hover text-foreground-muted transition-colors",
1178
+ title: minimized ? "Restore" : "Minimize",
1179
+ children: minimized ? /* @__PURE__ */ jsx19(Maximize2, { size: 12 }) : /* @__PURE__ */ jsx19(Minimize2, { size: 12 })
1180
+ }
1181
+ ),
1182
+ /* @__PURE__ */ jsx19(
1183
+ "button",
1184
+ {
1185
+ onClick: onClose,
1186
+ className: "p-0.5 rounded hover:bg-danger/20 text-foreground-muted hover:text-danger transition-colors",
1187
+ title: "Close",
1188
+ children: /* @__PURE__ */ jsx19(X, { size: 12 })
1189
+ }
1190
+ )
1191
+ ] })
1192
+ ]
1193
+ }
1194
+ ),
1195
+ !minimized && /* @__PURE__ */ jsxs5("div", { className: "flex-1 min-h-0 overflow-y-auto relative", children: [
1196
+ children,
1197
+ /* @__PURE__ */ jsx19(
1198
+ "div",
1199
+ {
1200
+ onMouseDown: onResizeStart,
1201
+ className: "absolute bottom-0 right-0 w-4 h-4 cursor-nwse-resize",
1202
+ style: { background: "linear-gradient(135deg, transparent 50%, var(--color-foreground-subtle) 50%)", opacity: 0.4 }
1203
+ }
1204
+ )
1205
+ ] })
1206
+ ]
1207
+ }
1208
+ );
1209
+ }
1210
+
1211
+ // src/composites/status-badge.tsx
1212
+ import { jsx as jsx20, jsxs as jsxs6 } from "react/jsx-runtime";
1213
+ var statusConfig = {
1214
+ online: { colorClass: "bg-success", label: "Online" },
1215
+ offline: { colorClass: "bg-danger", label: "Offline" },
1216
+ degraded: { colorClass: "bg-warning", label: "Degraded" },
1217
+ unknown: { colorClass: "bg-foreground-subtle", label: "Unknown" }
1218
+ };
1219
+ function StatusBadge({
1220
+ status,
1221
+ showDot = true,
1222
+ showLabel = true,
1223
+ size = "sm",
1224
+ className
1225
+ }) {
1226
+ const config = statusConfig[status];
1227
+ return /* @__PURE__ */ jsxs6(
1228
+ "span",
1229
+ {
1230
+ className: cn(
1231
+ "inline-flex items-center gap-1.5",
1232
+ size === "sm" ? "text-xs" : "text-sm",
1233
+ className
1234
+ ),
1235
+ children: [
1236
+ showDot && /* @__PURE__ */ jsx20(
1237
+ "span",
1238
+ {
1239
+ className: cn("h-1.5 w-1.5 shrink-0 rounded-full", config.colorClass),
1240
+ "aria-hidden": "true"
1241
+ }
1242
+ ),
1243
+ showLabel && /* @__PURE__ */ jsx20("span", { className: "text-foreground", children: config.label })
1244
+ ]
1245
+ }
1246
+ );
1247
+ }
1248
+
1249
+ // src/composites/provider-badge.tsx
1250
+ import { jsx as jsx21, jsxs as jsxs7 } from "react/jsx-runtime";
1251
+ var providerConfig = {
1252
+ frigate: { colorClass: "bg-provider-frigate", label: "Frigate" },
1253
+ scrypted: { colorClass: "bg-provider-scrypted", label: "Scrypted" },
1254
+ reolink: { colorClass: "bg-provider-reolink", label: "Reolink" },
1255
+ homeAssistant: { colorClass: "bg-provider-homeAssistant", label: "Home Assistant" },
1256
+ rtsp: { colorClass: "bg-provider-rtsp", label: "RTSP" }
1257
+ };
1258
+ function ProviderBadge({
1259
+ provider,
1260
+ showLabel = true,
1261
+ className
1262
+ }) {
1263
+ const config = providerConfig[provider];
1264
+ return /* @__PURE__ */ jsxs7("span", { className: cn("inline-flex items-center gap-1.5 text-xs", className), children: [
1265
+ /* @__PURE__ */ jsx21(
1266
+ "span",
1267
+ {
1268
+ className: cn("h-1.5 w-1.5 shrink-0 rounded-sm", config.colorClass),
1269
+ "aria-hidden": "true"
1270
+ }
1271
+ ),
1272
+ showLabel && /* @__PURE__ */ jsx21("span", { className: "text-foreground", children: config.label })
1273
+ ] });
1274
+ }
1275
+
1276
+ // src/composites/form-field.tsx
1277
+ import { jsx as jsx22, jsxs as jsxs8 } from "react/jsx-runtime";
1278
+ function FormField({
1279
+ label,
1280
+ description,
1281
+ error,
1282
+ required,
1283
+ children,
1284
+ orientation = "vertical",
1285
+ className
1286
+ }) {
1287
+ const isHorizontal = orientation === "horizontal";
1288
+ return /* @__PURE__ */ jsxs8(
1289
+ "div",
1290
+ {
1291
+ className: cn(
1292
+ "flex gap-2",
1293
+ isHorizontal ? "flex-row items-center justify-between" : "flex-col",
1294
+ className
1295
+ ),
1296
+ children: [
1297
+ /* @__PURE__ */ jsxs8("div", { className: cn(isHorizontal ? "flex-1" : ""), children: [
1298
+ /* @__PURE__ */ jsxs8(Label, { children: [
1299
+ label,
1300
+ required && /* @__PURE__ */ jsx22("span", { className: "text-danger ml-0.5", children: "*" })
1301
+ ] }),
1302
+ description && /* @__PURE__ */ jsx22("p", { className: "text-foreground-subtle text-xs mt-0.5", children: description })
1303
+ ] }),
1304
+ /* @__PURE__ */ jsx22("div", { className: cn(isHorizontal ? "shrink-0" : ""), children }),
1305
+ error && /* @__PURE__ */ jsx22("p", { className: "text-danger text-xs", children: error })
1306
+ ]
1307
+ }
1308
+ );
1309
+ }
1310
+
1311
+ // src/composites/page-header.tsx
1312
+ import { jsx as jsx23, jsxs as jsxs9 } from "react/jsx-runtime";
1313
+ function PageHeader({ title, subtitle, actions, className }) {
1314
+ return /* @__PURE__ */ jsxs9("div", { className: cn("flex items-center justify-between mb-3", className), children: [
1315
+ /* @__PURE__ */ jsxs9("div", { children: [
1316
+ /* @__PURE__ */ jsx23("h1", { className: "text-sm font-semibold text-foreground", children: title }),
1317
+ subtitle && /* @__PURE__ */ jsx23("p", { className: "text-foreground-subtle text-xs", children: subtitle })
1318
+ ] }),
1319
+ actions && /* @__PURE__ */ jsx23("div", { className: "flex items-center gap-2", children: actions })
1320
+ ] });
1321
+ }
1322
+
1323
+ // src/composites/empty-state.tsx
1324
+ import { jsx as jsx24, jsxs as jsxs10 } from "react/jsx-runtime";
1325
+ function EmptyState({
1326
+ icon: Icon,
1327
+ title,
1328
+ description,
1329
+ action,
1330
+ className
1331
+ }) {
1332
+ return /* @__PURE__ */ jsxs10("div", { className: cn("flex flex-col items-center justify-center gap-3 py-12", className), children: [
1333
+ Icon && /* @__PURE__ */ jsx24(Icon, { className: "h-12 w-12 text-foreground-subtle", "aria-hidden": "true" }),
1334
+ /* @__PURE__ */ jsxs10("div", { className: "flex flex-col items-center gap-1 text-center", children: [
1335
+ /* @__PURE__ */ jsx24("p", { className: "text-foreground-muted text-sm font-medium", children: title }),
1336
+ description && /* @__PURE__ */ jsx24("p", { className: "text-foreground-subtle text-xs max-w-xs", children: description })
1337
+ ] }),
1338
+ action && /* @__PURE__ */ jsx24("div", { className: "mt-1", children: action })
1339
+ ] });
1340
+ }
1341
+
1342
+ // src/composites/confirm-dialog.tsx
1343
+ import { jsx as jsx25, jsxs as jsxs11 } from "react/jsx-runtime";
1344
+ function ConfirmDialog({
1345
+ title,
1346
+ message,
1347
+ confirmLabel = "Confirm",
1348
+ cancelLabel = "Cancel",
1349
+ onConfirm,
1350
+ onCancel,
1351
+ variant = "default",
1352
+ open,
1353
+ onOpenChange
1354
+ }) {
1355
+ return /* @__PURE__ */ jsx25(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs11(DialogContent, { children: [
1356
+ /* @__PURE__ */ jsxs11(DialogHeader, { children: [
1357
+ /* @__PURE__ */ jsx25(DialogTitle, { children: title }),
1358
+ /* @__PURE__ */ jsx25(DialogDescription, { children: message })
1359
+ ] }),
1360
+ /* @__PURE__ */ jsxs11(DialogFooter, { children: [
1361
+ /* @__PURE__ */ jsx25(Button, { variant: "ghost", onClick: onCancel, children: cancelLabel }),
1362
+ /* @__PURE__ */ jsx25(
1363
+ Button,
1364
+ {
1365
+ variant: variant === "danger" ? "danger" : "primary",
1366
+ onClick: onConfirm,
1367
+ children: confirmLabel
1368
+ }
1369
+ )
1370
+ ] })
1371
+ ] }) });
1372
+ }
1373
+
1374
+ // src/composites/stat-card.tsx
1375
+ import { TrendingUp, TrendingDown } from "lucide-react";
1376
+ import { jsx as jsx26, jsxs as jsxs12 } from "react/jsx-runtime";
1377
+ function StatCard({ value, label, trend, className }) {
1378
+ return /* @__PURE__ */ jsxs12(Card, { className: cn("flex flex-col gap-1", className), children: [
1379
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-baseline gap-2", children: [
1380
+ /* @__PURE__ */ jsx26("span", { className: "text-2xl font-semibold text-foreground", children: value }),
1381
+ trend && /* @__PURE__ */ jsxs12(
1382
+ "span",
1383
+ {
1384
+ className: cn(
1385
+ "inline-flex items-center gap-0.5 text-xs font-medium",
1386
+ trend.direction === "up" ? "text-success" : "text-danger"
1387
+ ),
1388
+ children: [
1389
+ trend.direction === "up" ? /* @__PURE__ */ jsx26(TrendingUp, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx26(TrendingDown, { className: "h-3 w-3" }),
1390
+ trend.value,
1391
+ "%"
1392
+ ]
1393
+ }
1394
+ )
1395
+ ] }),
1396
+ /* @__PURE__ */ jsx26("span", { className: "text-xs text-foreground-muted", children: label })
1397
+ ] });
1398
+ }
1399
+
1400
+ // src/composites/key-value-list.tsx
1401
+ import { jsx as jsx27, jsxs as jsxs13 } from "react/jsx-runtime";
1402
+ function KeyValueList({ items, className }) {
1403
+ return /* @__PURE__ */ jsx27("dl", { className: cn("flex flex-col", className), children: items.map((item) => /* @__PURE__ */ jsxs13(
1404
+ "div",
1405
+ {
1406
+ className: "flex items-center h-7",
1407
+ children: [
1408
+ /* @__PURE__ */ jsx27("dt", { className: "text-foreground-subtle text-xs w-1/3 shrink-0", children: item.key }),
1409
+ /* @__PURE__ */ jsx27("dd", { className: "text-foreground text-xs", children: item.value })
1410
+ ]
1411
+ },
1412
+ item.key
1413
+ )) });
1414
+ }
1415
+
1416
+ // src/composites/code-block.tsx
1417
+ import { useCallback as useCallback7, useState as useState8 } from "react";
1418
+ import { Copy, Check } from "lucide-react";
1419
+ import { jsx as jsx28, jsxs as jsxs14 } from "react/jsx-runtime";
1420
+ function CodeBlock({ children, maxHeight = 300, className }) {
1421
+ const [copied, setCopied] = useState8(false);
1422
+ const handleCopy = useCallback7(() => {
1423
+ navigator.clipboard.writeText(children).then(() => {
1424
+ setCopied(true);
1425
+ setTimeout(() => setCopied(false), 2e3);
1426
+ });
1427
+ }, [children]);
1428
+ return /* @__PURE__ */ jsxs14("div", { className: cn("relative group", className), children: [
1429
+ /* @__PURE__ */ jsx28(ScrollArea, { style: { maxHeight }, children: /* @__PURE__ */ jsx28("pre", { className: "font-mono text-xs bg-surface p-3 rounded-md border border-border-subtle", children: /* @__PURE__ */ jsx28("code", { children }) }) }),
1430
+ /* @__PURE__ */ jsx28("div", { className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsx28(
1431
+ IconButton,
1432
+ {
1433
+ icon: copied ? Check : Copy,
1434
+ "aria-label": "Copy code",
1435
+ variant: "ghost",
1436
+ size: "sm",
1437
+ onClick: handleCopy
1438
+ }
1439
+ ) })
1440
+ ] });
1441
+ }
1442
+
1443
+ // src/composites/filter-bar.tsx
1444
+ import { Search } from "lucide-react";
1445
+ import { jsx as jsx29 } from "react/jsx-runtime";
1446
+ function FilterBar({ filters, values, onChange, className }) {
1447
+ const handleChange = (key, value) => {
1448
+ onChange({ ...values, [key]: value });
1449
+ };
1450
+ return /* @__PURE__ */ jsx29("div", { className: cn("flex items-center gap-2 flex-wrap", className), children: filters.map((filter) => {
1451
+ switch (filter.type) {
1452
+ case "search":
1453
+ return /* @__PURE__ */ jsx29(
1454
+ Input,
1455
+ {
1456
+ placeholder: filter.placeholder ?? "Search...",
1457
+ value: values[filter.key] ?? "",
1458
+ onChange: (e) => handleChange(filter.key, e.target.value),
1459
+ leftSlot: /* @__PURE__ */ jsx29(Search, { className: "h-3 w-3 text-foreground-subtle" }),
1460
+ className: "w-48"
1461
+ },
1462
+ filter.key
1463
+ );
1464
+ case "select":
1465
+ return /* @__PURE__ */ jsx29(
1466
+ Select,
1467
+ {
1468
+ options: filter.options,
1469
+ value: values[filter.key] ?? "",
1470
+ onChange: (e) => handleChange(filter.key, e.target.value),
1471
+ className: "w-36"
1472
+ },
1473
+ filter.key
1474
+ );
1475
+ case "badge-toggle":
1476
+ return /* @__PURE__ */ jsx29("div", { className: "flex items-center gap-1", children: filter.options.map((option) => {
1477
+ const currentValue = values[filter.key];
1478
+ const isActive = currentValue === option.value;
1479
+ return /* @__PURE__ */ jsx29(
1480
+ "button",
1481
+ {
1482
+ type: "button",
1483
+ onClick: () => handleChange(
1484
+ filter.key,
1485
+ isActive ? void 0 : option.value
1486
+ ),
1487
+ children: /* @__PURE__ */ jsx29(
1488
+ Badge,
1489
+ {
1490
+ variant: isActive ? "info" : "default",
1491
+ className: "cursor-pointer",
1492
+ children: option.label
1493
+ }
1494
+ )
1495
+ },
1496
+ option.value
1497
+ );
1498
+ }) }, filter.key);
1499
+ default:
1500
+ return null;
1501
+ }
1502
+ }) });
1503
+ }
1504
+
1505
+ // src/composites/app-shell/sidebar-item.tsx
1506
+ import { jsx as jsx30, jsxs as jsxs15 } from "react/jsx-runtime";
1507
+ function SidebarItem({
1508
+ label,
1509
+ icon: Icon,
1510
+ href,
1511
+ badge,
1512
+ active = false,
1513
+ className
1514
+ }) {
1515
+ return /* @__PURE__ */ jsxs15(
1516
+ "a",
1517
+ {
1518
+ href,
1519
+ className: cn(
1520
+ "flex items-center gap-2 h-7 px-2 text-[11px] transition-colors",
1521
+ active ? "border-l-2 border-primary bg-primary/[0.08] text-foreground rounded-r-md" : "text-foreground-subtle hover:bg-surface-hover rounded-md",
1522
+ className
1523
+ ),
1524
+ children: [
1525
+ /* @__PURE__ */ jsx30(Icon, { className: "h-3.5 w-3.5 shrink-0" }),
1526
+ /* @__PURE__ */ jsx30("span", { className: "truncate flex-1", children: label }),
1527
+ badge !== void 0 && /* @__PURE__ */ jsx30(Badge, { className: "ml-auto text-[10px] px-1.5 py-0", children: badge })
1528
+ ]
1529
+ }
1530
+ );
1531
+ }
1532
+
1533
+ // src/composites/app-shell/sidebar.tsx
1534
+ import { jsx as jsx31, jsxs as jsxs16 } from "react/jsx-runtime";
1535
+ function Sidebar({ logo, sections, footer, className }) {
1536
+ return /* @__PURE__ */ jsxs16(
1537
+ "nav",
1538
+ {
1539
+ className: cn(
1540
+ "w-44 bg-surface border-r border-border h-full flex flex-col",
1541
+ className
1542
+ ),
1543
+ children: [
1544
+ logo && /* @__PURE__ */ jsx31("div", { className: "px-3 py-2 shrink-0", children: logo }),
1545
+ /* @__PURE__ */ jsx31("div", { className: "flex-1 overflow-auto px-1 py-1", children: sections.map((section, sectionIndex) => /* @__PURE__ */ jsxs16("div", { className: cn(sectionIndex > 0 ? "mt-3" : ""), children: [
1546
+ section.label && /* @__PURE__ */ jsx31("span", { className: "text-[10px] text-foreground-disabled uppercase tracking-wider px-2 mb-1 block", children: section.label }),
1547
+ /* @__PURE__ */ jsx31("div", { className: "flex flex-col gap-0.5", children: section.items.map((item) => /* @__PURE__ */ jsx31(SidebarItem, { ...item }, item.href)) })
1548
+ ] }, sectionIndex)) }),
1549
+ footer && footer.length > 0 && /* @__PURE__ */ jsxs16("div", { className: "shrink-0 px-1 pb-1", children: [
1550
+ /* @__PURE__ */ jsx31(Separator, { className: "mb-1" }),
1551
+ /* @__PURE__ */ jsx31("div", { className: "flex flex-col gap-0.5", children: footer.map((item) => /* @__PURE__ */ jsx31(SidebarItem, { ...item }, item.href)) })
1552
+ ] })
1553
+ ]
1554
+ }
1555
+ );
1556
+ }
1557
+
1558
+ // src/composites/app-shell/app-shell.tsx
1559
+ import { ChevronRight } from "lucide-react";
1560
+ import { jsx as jsx32, jsxs as jsxs17 } from "react/jsx-runtime";
1561
+ function AppShell({ sidebar, header, children, className }) {
1562
+ return /* @__PURE__ */ jsxs17("div", { className: cn("flex h-screen", className), children: [
1563
+ /* @__PURE__ */ jsx32(Sidebar, { ...sidebar }),
1564
+ /* @__PURE__ */ jsxs17("div", { className: "flex flex-1 flex-col min-w-0", children: [
1565
+ header && /* @__PURE__ */ jsxs17("header", { className: "flex items-center h-10 border-b border-border px-4 shrink-0", children: [
1566
+ header.breadcrumbs && header.breadcrumbs.length > 0 && /* @__PURE__ */ jsx32("nav", { className: "flex items-center gap-1 text-xs flex-1 min-w-0", children: header.breadcrumbs.map((crumb, index) => {
1567
+ const isLast = index === header.breadcrumbs.length - 1;
1568
+ return /* @__PURE__ */ jsxs17("span", { className: "flex items-center gap-1", children: [
1569
+ index > 0 && /* @__PURE__ */ jsx32(ChevronRight, { className: "h-3 w-3 text-foreground-subtle shrink-0" }),
1570
+ crumb.href && !isLast ? /* @__PURE__ */ jsx32(
1571
+ "a",
1572
+ {
1573
+ href: crumb.href,
1574
+ className: "text-foreground-subtle hover:text-foreground transition-colors truncate",
1575
+ children: crumb.label
1576
+ }
1577
+ ) : /* @__PURE__ */ jsx32("span", { className: "text-foreground truncate", children: crumb.label })
1578
+ ] }, index);
1579
+ }) }),
1580
+ header.actions && /* @__PURE__ */ jsx32("div", { className: "flex items-center gap-2 ml-auto shrink-0", children: header.actions })
1581
+ ] }),
1582
+ /* @__PURE__ */ jsx32("main", { className: "flex-1 overflow-auto p-4", children })
1583
+ ] })
1584
+ ] });
1585
+ }
1586
+
1587
+ // src/composites/data-table/data-table.tsx
1588
+ import { useMemo as useMemo2 } from "react";
1589
+ import {
1590
+ useReactTable,
1591
+ getCoreRowModel,
1592
+ getSortedRowModel,
1593
+ getFilteredRowModel,
1594
+ getPaginationRowModel,
1595
+ flexRender
1596
+ } from "@tanstack/react-table";
1597
+
1598
+ // src/composites/data-table/data-table-header.tsx
1599
+ import { ArrowUpDown, ArrowUp, ArrowDown } from "lucide-react";
1600
+ import { jsx as jsx33, jsxs as jsxs18 } from "react/jsx-runtime";
1601
+ function DataTableHeader({
1602
+ headerGroups,
1603
+ onSortingChange,
1604
+ stickyHeader,
1605
+ flexRender: render
1606
+ }) {
1607
+ return /* @__PURE__ */ jsx33(
1608
+ "thead",
1609
+ {
1610
+ className: cn(
1611
+ stickyHeader && "sticky top-0 z-10 bg-background"
1612
+ ),
1613
+ children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx33("tr", { className: "h-6", children: headerGroup.headers.map((header) => /* @__PURE__ */ jsx33(
1614
+ HeaderCell,
1615
+ {
1616
+ header,
1617
+ sortable: header.column.getCanSort() && !!onSortingChange,
1618
+ flexRender: render
1619
+ },
1620
+ header.id
1621
+ )) }, headerGroup.id))
1622
+ }
1623
+ );
1624
+ }
1625
+ function HeaderCell({ header, sortable, flexRender: render }) {
1626
+ const sorted = header.column.getIsSorted();
1627
+ const SortIcon = sorted === "asc" ? ArrowUp : sorted === "desc" ? ArrowDown : ArrowUpDown;
1628
+ return /* @__PURE__ */ jsx33(
1629
+ "th",
1630
+ {
1631
+ className: cn(
1632
+ "px-2 py-1 text-left text-[10px] text-foreground-subtle uppercase tracking-wider font-medium",
1633
+ sortable && "cursor-pointer select-none"
1634
+ ),
1635
+ onClick: sortable ? header.column.getToggleSortingHandler() : void 0,
1636
+ children: /* @__PURE__ */ jsxs18("span", { className: "inline-flex items-center gap-1", children: [
1637
+ header.isPlaceholder ? null : render(header.column.columnDef.header, header.getContext()),
1638
+ sortable && /* @__PURE__ */ jsx33(SortIcon, { className: "h-3 w-3" })
1639
+ ] })
1640
+ }
1641
+ );
1642
+ }
1643
+
1644
+ // src/composites/data-table/data-table-row.tsx
1645
+ import { MoreHorizontal } from "lucide-react";
1646
+ import { jsx as jsx34, jsxs as jsxs19 } from "react/jsx-runtime";
1647
+ function DataTableRow({
1648
+ row,
1649
+ onRowClick,
1650
+ rowActions,
1651
+ flexRender: render
1652
+ }) {
1653
+ const actions = rowActions ? rowActions(row.original) : [];
1654
+ return /* @__PURE__ */ jsxs19(
1655
+ "tr",
1656
+ {
1657
+ className: cn(
1658
+ "h-7 border-b border-border/50",
1659
+ onRowClick && "cursor-pointer",
1660
+ "hover:bg-surface-hover"
1661
+ ),
1662
+ onClick: onRowClick ? () => onRowClick(row.original) : void 0,
1663
+ children: [
1664
+ row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx34(DataTableCell, { cell, flexRender: render }, cell.id)),
1665
+ actions.length > 0 && /* @__PURE__ */ jsx34("td", { className: "px-2 py-1.5 w-8", children: /* @__PURE__ */ jsxs19(Dropdown, { children: [
1666
+ /* @__PURE__ */ jsx34(
1667
+ DropdownTrigger,
1668
+ {
1669
+ className: "p-0.5 rounded hover:bg-surface-hover",
1670
+ onClick: (e) => e.stopPropagation(),
1671
+ children: /* @__PURE__ */ jsx34(MoreHorizontal, { className: "h-3.5 w-3.5 text-foreground-muted" })
1672
+ }
1673
+ ),
1674
+ /* @__PURE__ */ jsx34(DropdownContent, { className: "right-0 left-auto", children: actions.map((action) => /* @__PURE__ */ jsx34(
1675
+ DropdownItem,
1676
+ {
1677
+ icon: action.icon,
1678
+ variant: action.variant,
1679
+ onClick: (e) => {
1680
+ e.stopPropagation();
1681
+ action.onClick();
1682
+ },
1683
+ children: action.label
1684
+ },
1685
+ action.label
1686
+ )) })
1687
+ ] }) })
1688
+ ]
1689
+ }
1690
+ );
1691
+ }
1692
+ function DataTableCell({ cell, flexRender: render }) {
1693
+ return /* @__PURE__ */ jsx34("td", { className: "px-2 py-1.5 text-xs text-foreground", children: render(cell.column.columnDef.cell, cell.getContext()) });
1694
+ }
1695
+
1696
+ // src/composites/data-table/data-table-pagination.tsx
1697
+ import { ChevronLeft, ChevronRight as ChevronRight2 } from "lucide-react";
1698
+ import { jsx as jsx35, jsxs as jsxs20 } from "react/jsx-runtime";
1699
+ var PAGE_SIZE_OPTIONS = [
1700
+ { value: "10", label: "10" },
1701
+ { value: "25", label: "25" },
1702
+ { value: "50", label: "50" },
1703
+ { value: "100", label: "100" }
1704
+ ];
1705
+ function DataTablePagination({
1706
+ page,
1707
+ pageSize,
1708
+ total,
1709
+ onPaginationChange
1710
+ }) {
1711
+ const totalPages = Math.max(1, Math.ceil(total / pageSize));
1712
+ const currentPage = page + 1;
1713
+ return /* @__PURE__ */ jsxs20("div", { className: "flex items-center justify-between px-2 py-2 text-xs text-foreground-muted", children: [
1714
+ /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2", children: [
1715
+ /* @__PURE__ */ jsx35("span", { children: "Rows per page" }),
1716
+ /* @__PURE__ */ jsx35("div", { className: "w-16", children: /* @__PURE__ */ jsx35(
1717
+ Select,
1718
+ {
1719
+ options: PAGE_SIZE_OPTIONS,
1720
+ value: String(pageSize),
1721
+ onChange: (e) => onPaginationChange?.({
1722
+ pageIndex: 0,
1723
+ pageSize: Number(e.target.value)
1724
+ })
1725
+ }
1726
+ ) })
1727
+ ] }),
1728
+ /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2", children: [
1729
+ /* @__PURE__ */ jsxs20("span", { children: [
1730
+ "Page ",
1731
+ currentPage,
1732
+ " of ",
1733
+ totalPages
1734
+ ] }),
1735
+ /* @__PURE__ */ jsx35(
1736
+ IconButton,
1737
+ {
1738
+ icon: ChevronLeft,
1739
+ "aria-label": "Previous page",
1740
+ variant: "ghost",
1741
+ size: "sm",
1742
+ disabled: page <= 0,
1743
+ onClick: () => onPaginationChange?.({ pageIndex: page - 1, pageSize })
1744
+ }
1745
+ ),
1746
+ /* @__PURE__ */ jsx35(
1747
+ IconButton,
1748
+ {
1749
+ icon: ChevronRight2,
1750
+ "aria-label": "Next page",
1751
+ variant: "ghost",
1752
+ size: "sm",
1753
+ disabled: currentPage >= totalPages,
1754
+ onClick: () => onPaginationChange?.({ pageIndex: page + 1, pageSize })
1755
+ }
1756
+ )
1757
+ ] })
1758
+ ] });
1759
+ }
1760
+
1761
+ // src/composites/data-table/data-table.tsx
1762
+ import { Fragment, jsx as jsx36, jsxs as jsxs21 } from "react/jsx-runtime";
1763
+ function DataTable({
1764
+ data,
1765
+ columns: userColumns,
1766
+ sorting,
1767
+ onSortingChange,
1768
+ filtering,
1769
+ onFilteringChange,
1770
+ pagination,
1771
+ onPaginationChange,
1772
+ loading = false,
1773
+ emptyState,
1774
+ rowActions,
1775
+ onRowClick,
1776
+ selectable = false,
1777
+ compact = true,
1778
+ stickyHeader = false,
1779
+ className
1780
+ }) {
1781
+ const columns = useMemo2(() => {
1782
+ if (!selectable) return userColumns;
1783
+ const selectColumn = {
1784
+ id: "__select",
1785
+ header: ({ table: table2 }) => /* @__PURE__ */ jsx36(
1786
+ Checkbox,
1787
+ {
1788
+ checked: table2.getIsAllPageRowsSelected(),
1789
+ onChange: table2.getToggleAllPageRowsSelectedHandler(),
1790
+ "aria-label": "Select all"
1791
+ }
1792
+ ),
1793
+ cell: ({ row }) => /* @__PURE__ */ jsx36(
1794
+ Checkbox,
1795
+ {
1796
+ checked: row.getIsSelected(),
1797
+ onChange: row.getToggleSelectedHandler(),
1798
+ "aria-label": "Select row"
1799
+ }
1800
+ ),
1801
+ enableSorting: false
1802
+ };
1803
+ return [selectColumn, ...userColumns];
1804
+ }, [userColumns, selectable]);
1805
+ const table = useReactTable({
1806
+ data,
1807
+ columns,
1808
+ state: {
1809
+ ...sorting !== void 0 && { sorting },
1810
+ ...filtering !== void 0 && { columnFilters: filtering },
1811
+ ...pagination !== void 0 && {
1812
+ pagination: { pageIndex: pagination.page, pageSize: pagination.pageSize }
1813
+ }
1814
+ },
1815
+ onSortingChange: onSortingChange ? (updater) => {
1816
+ const next = typeof updater === "function" ? updater(sorting ?? []) : updater;
1817
+ onSortingChange(next);
1818
+ } : void 0,
1819
+ onColumnFiltersChange: onFilteringChange ? (updater) => {
1820
+ const next = typeof updater === "function" ? updater(filtering ?? []) : updater;
1821
+ onFilteringChange(next);
1822
+ } : void 0,
1823
+ getCoreRowModel: getCoreRowModel(),
1824
+ getSortedRowModel: getSortedRowModel(),
1825
+ getFilteredRowModel: getFilteredRowModel(),
1826
+ getPaginationRowModel: pagination ? getPaginationRowModel() : void 0,
1827
+ manualPagination: pagination !== void 0,
1828
+ pageCount: pagination ? Math.ceil(pagination.total / pagination.pageSize) : void 0
1829
+ });
1830
+ const hasActions = !!rowActions;
1831
+ return /* @__PURE__ */ jsxs21("div", { className: cn("overflow-auto", className), children: [
1832
+ /* @__PURE__ */ jsxs21("table", { className: "w-full border-collapse", children: [
1833
+ /* @__PURE__ */ jsx36(
1834
+ DataTableHeader,
1835
+ {
1836
+ headerGroups: table.getHeaderGroups(),
1837
+ onSortingChange,
1838
+ stickyHeader,
1839
+ flexRender
1840
+ }
1841
+ ),
1842
+ /* @__PURE__ */ jsx36("tbody", { children: loading ? /* @__PURE__ */ jsx36(LoadingRows, { colSpan: columns.length + (hasActions ? 1 : 0), compact }) : table.getRowModel().rows.length === 0 ? /* @__PURE__ */ jsx36("tr", { children: /* @__PURE__ */ jsx36(
1843
+ "td",
1844
+ {
1845
+ colSpan: columns.length + (hasActions ? 1 : 0),
1846
+ className: "text-center py-8 text-xs text-foreground-muted",
1847
+ children: emptyState ?? "No data"
1848
+ }
1849
+ ) }) : table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx36(
1850
+ DataTableRow,
1851
+ {
1852
+ row,
1853
+ onRowClick,
1854
+ rowActions,
1855
+ flexRender
1856
+ },
1857
+ row.id
1858
+ )) })
1859
+ ] }),
1860
+ pagination && /* @__PURE__ */ jsx36(
1861
+ DataTablePagination,
1862
+ {
1863
+ page: pagination.page,
1864
+ pageSize: pagination.pageSize,
1865
+ total: pagination.total,
1866
+ onPaginationChange
1867
+ }
1868
+ )
1869
+ ] });
1870
+ }
1871
+ function LoadingRows({ colSpan, compact }) {
1872
+ return /* @__PURE__ */ jsx36(Fragment, { children: Array.from({ length: 5 }).map((_, rowIdx) => /* @__PURE__ */ jsx36("tr", { className: compact ? "h-7" : "h-9", children: Array.from({ length: colSpan }).map((_2, colIdx) => /* @__PURE__ */ jsx36("td", { className: "px-2 py-1.5", children: /* @__PURE__ */ jsx36(Skeleton, { className: "h-3 w-full" }) }, colIdx)) }, rowIdx)) });
1873
+ }
1874
+
1875
+ // src/composites/device-card.tsx
1876
+ import { jsx as jsx37, jsxs as jsxs22 } from "react/jsx-runtime";
1877
+ var STATUS_COLORS = {
1878
+ online: "bg-success",
1879
+ offline: "bg-danger",
1880
+ warning: "bg-warning",
1881
+ unknown: "bg-foreground-subtle"
1882
+ };
1883
+ function DeviceCard({
1884
+ title,
1885
+ subtitle,
1886
+ status,
1887
+ selected,
1888
+ onClick,
1889
+ badges,
1890
+ actions,
1891
+ offlineAction,
1892
+ className
1893
+ }) {
1894
+ const isOffline = status === "offline";
1895
+ return /* @__PURE__ */ jsxs22(
1896
+ "div",
1897
+ {
1898
+ onClick,
1899
+ className: cn(
1900
+ "w-full rounded-lg border p-3 text-left transition-colors",
1901
+ onClick && "cursor-pointer",
1902
+ selected ? "border-primary bg-primary/10" : "border-border bg-surface hover:bg-surface-hover",
1903
+ isOffline && !selected && "opacity-50",
1904
+ className
1905
+ ),
1906
+ children: [
1907
+ /* @__PURE__ */ jsxs22("div", { className: "flex items-center justify-between mb-2", children: [
1908
+ /* @__PURE__ */ jsx37("span", { className: "text-sm font-medium truncate", children: title }),
1909
+ status && /* @__PURE__ */ jsx37("span", { className: cn("h-2 w-2 rounded-full shrink-0", STATUS_COLORS[status]) })
1910
+ ] }),
1911
+ subtitle && /* @__PURE__ */ jsx37("div", { className: "text-[11px] text-foreground-muted", children: subtitle }),
1912
+ badges && badges.length > 0 && /* @__PURE__ */ jsx37("div", { className: "flex flex-wrap gap-1 mt-2", children: badges.map((badge, i) => {
1913
+ const cls = cn(
1914
+ "rounded px-1.5 py-0.5 text-[10px] flex items-center gap-0.5",
1915
+ selected ? "bg-primary/20" : "bg-surface-hover",
1916
+ badge.onClick && "hover:opacity-80 transition-opacity cursor-pointer"
1917
+ );
1918
+ return badge.onClick ? /* @__PURE__ */ jsxs22(
1919
+ "button",
1920
+ {
1921
+ onClick: (e) => {
1922
+ e.stopPropagation();
1923
+ badge.onClick();
1924
+ },
1925
+ className: cls,
1926
+ children: [
1927
+ badge.icon,
1928
+ badge.label
1929
+ ]
1930
+ },
1931
+ i
1932
+ ) : /* @__PURE__ */ jsxs22("span", { className: cls, children: [
1933
+ badge.icon,
1934
+ badge.label
1935
+ ] }, i);
1936
+ }) }),
1937
+ !isOffline && actions && actions.length > 0 && /* @__PURE__ */ jsx37("div", { className: "flex items-center gap-0.5 mt-2 -mb-1", children: actions.map((action, i) => /* @__PURE__ */ jsx37(
1938
+ "button",
1939
+ {
1940
+ onClick: (e) => {
1941
+ e.stopPropagation();
1942
+ action.onClick();
1943
+ },
1944
+ className: "p-1 rounded hover:bg-surface-hover text-foreground-subtle hover:text-foreground transition-colors",
1945
+ title: action.label,
1946
+ "aria-label": action.label,
1947
+ children: action.icon
1948
+ },
1949
+ i
1950
+ )) }),
1951
+ isOffline && offlineAction && /* @__PURE__ */ jsx37("div", { className: "mt-2", onClick: (e) => e.stopPropagation(), children: offlineAction })
1952
+ ]
1953
+ }
1954
+ );
1955
+ }
1956
+
1957
+ // src/composites/device-grid.tsx
1958
+ import { jsx as jsx38 } from "react/jsx-runtime";
1959
+ function DeviceGrid({
1960
+ children,
1961
+ minCardWidth = 220,
1962
+ gap = 3,
1963
+ className
1964
+ }) {
1965
+ return /* @__PURE__ */ jsx38(
1966
+ "div",
1967
+ {
1968
+ className: cn(
1969
+ "p-4 overflow-y-auto flex-1 content-start",
1970
+ className
1971
+ ),
1972
+ style: {
1973
+ display: "grid",
1974
+ gridTemplateColumns: `repeat(auto-fill, minmax(${minCardWidth}px, 1fr))`,
1975
+ gap: `${gap * 4}px`
1976
+ },
1977
+ children
1978
+ }
1979
+ );
1980
+ }
1981
+ export {
1982
+ AppShell,
1983
+ Badge,
1984
+ Button,
1985
+ Card,
1986
+ Checkbox,
1987
+ CodeBlock,
1988
+ ConfirmDialog,
1989
+ DataTable,
1990
+ DeviceCard,
1991
+ DeviceGrid,
1992
+ Dialog,
1993
+ DialogContent,
1994
+ DialogDescription,
1995
+ DialogFooter,
1996
+ DialogHeader,
1997
+ DialogTitle,
1998
+ DialogTrigger,
1999
+ Dropdown,
2000
+ DropdownContent,
2001
+ DropdownItem,
2002
+ DropdownTrigger,
2003
+ EmptyState,
2004
+ FilterBar,
2005
+ FloatingPanel,
2006
+ FormField,
2007
+ IconButton,
2008
+ Input,
2009
+ KeyValueList,
2010
+ Label,
2011
+ PageHeader,
2012
+ Popover,
2013
+ PopoverContent,
2014
+ PopoverTrigger,
2015
+ ProviderBadge,
2016
+ ScrollArea,
2017
+ Select,
2018
+ Separator,
2019
+ Sidebar,
2020
+ SidebarItem,
2021
+ Skeleton,
2022
+ StatCard,
2023
+ StatusBadge,
2024
+ Switch,
2025
+ Tabs,
2026
+ TabsContent,
2027
+ TabsList,
2028
+ TabsTrigger,
2029
+ ThemeProvider,
2030
+ Tooltip,
2031
+ TooltipContent,
2032
+ TooltipTrigger,
2033
+ cn,
2034
+ createTheme,
2035
+ darkColors,
2036
+ defaultTheme,
2037
+ lightColors,
2038
+ providerIcons,
2039
+ statusIcons,
2040
+ themeToCss,
2041
+ useThemeMode
2042
+ };
2043
+ //# sourceMappingURL=index.js.map