@memelabui/ui 0.1.1 → 0.3.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.cjs CHANGED
@@ -1,9 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var react = require('react');
3
+ var React = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var reactDom = require('react-dom');
6
6
 
7
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
+
9
+ var React__default = /*#__PURE__*/_interopDefault(React);
10
+
7
11
  // src/utils/cn.ts
8
12
  function toClassName(out, value) {
9
13
  if (!value) return;
@@ -58,21 +62,157 @@ function focusSafely(el) {
58
62
  } catch {
59
63
  }
60
64
  }
61
- var base = "inline-flex items-center justify-center gap-2 rounded-xl font-semibold leading-none transition-[transform,background-color,box-shadow,opacity] select-none [-webkit-tap-highlight-color:transparent] active:translate-y-[0.5px] disabled:opacity-60 disabled:pointer-events-none focus-visible:ring-2 focus-visible:ring-primary/40 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent";
65
+ function useClipboard(timeout = 2e3) {
66
+ const [copied, setCopied] = React.useState(false);
67
+ const timerRef = React.useRef(null);
68
+ React.useEffect(() => {
69
+ return () => {
70
+ if (timerRef.current !== null) {
71
+ clearTimeout(timerRef.current);
72
+ }
73
+ };
74
+ }, []);
75
+ const copy = React.useCallback(
76
+ async (text) => {
77
+ try {
78
+ await navigator.clipboard.writeText(text);
79
+ } catch {
80
+ return;
81
+ }
82
+ if (timerRef.current !== null) {
83
+ clearTimeout(timerRef.current);
84
+ }
85
+ setCopied(true);
86
+ timerRef.current = setTimeout(() => {
87
+ setCopied(false);
88
+ timerRef.current = null;
89
+ }, timeout);
90
+ },
91
+ [timeout]
92
+ );
93
+ return { copy, copied };
94
+ }
95
+ function useDisclosure(defaultOpen = false) {
96
+ const [isOpen, setIsOpen] = React.useState(defaultOpen);
97
+ const open = React.useCallback(() => setIsOpen(true), []);
98
+ const close = React.useCallback(() => setIsOpen(false), []);
99
+ const toggle = React.useCallback(() => setIsOpen((prev) => !prev), []);
100
+ return { isOpen, open, close, toggle };
101
+ }
102
+ function useMediaQuery(query) {
103
+ const [matches, setMatches] = React.useState(() => {
104
+ if (typeof window === "undefined") return false;
105
+ return window.matchMedia(query).matches;
106
+ });
107
+ React.useEffect(() => {
108
+ if (typeof window === "undefined") return;
109
+ const mediaQueryList = window.matchMedia(query);
110
+ setMatches(mediaQueryList.matches);
111
+ const handler = (event) => {
112
+ setMatches(event.matches);
113
+ };
114
+ mediaQueryList.addEventListener("change", handler);
115
+ return () => {
116
+ mediaQueryList.removeEventListener("change", handler);
117
+ };
118
+ }, [query]);
119
+ return matches;
120
+ }
121
+ function useDebounce(value, delayMs = 300) {
122
+ const [debouncedValue, setDebouncedValue] = React.useState(value);
123
+ React.useEffect(() => {
124
+ const timer = setTimeout(() => {
125
+ setDebouncedValue(value);
126
+ }, delayMs);
127
+ return () => {
128
+ clearTimeout(timer);
129
+ };
130
+ }, [value, delayMs]);
131
+ return debouncedValue;
132
+ }
133
+
134
+ // src/tokens/colors.ts
135
+ var colors = {
136
+ bg: "#0a0a0f",
137
+ fg: "#f9fafb",
138
+ surface: {
139
+ 0: "#0a0a0f",
140
+ 50: "#0f0f18",
141
+ 100: "#141420",
142
+ 200: "#1a1a2e",
143
+ 300: "#24243a",
144
+ 400: "#2a2a4a"
145
+ },
146
+ primary: { DEFAULT: "#8b5cf6", light: "#a78bfa", dark: "#7c3aed" },
147
+ accent: { DEFAULT: "#667eea", light: "#8b9cf7", dark: "#4c5fd4" },
148
+ glow: { purple: "#764ba2", pink: "#f093fb" },
149
+ success: "#10b981",
150
+ warning: "#f59e0b",
151
+ danger: "#f43f5e"
152
+ };
62
153
  var sizeClass = {
154
+ sm: "w-8 h-8 text-xs",
155
+ md: "w-10 h-10 text-sm",
156
+ lg: "w-12 h-12 text-base",
157
+ xl: "w-16 h-16 text-lg"
158
+ };
159
+ function getInitials(name) {
160
+ if (!name) return "";
161
+ const parts = name.trim().split(/\s+/);
162
+ const first = parts[0]?.[0] ?? "";
163
+ const second = parts[1]?.[0] ?? "";
164
+ return (first + second).toUpperCase();
165
+ }
166
+ var Avatar = React.forwardRef(function Avatar2({ src, alt, name, size = "md", className }, ref) {
167
+ const [imgError, setImgError] = React.useState(false);
168
+ const showFallback = !src || imgError;
169
+ const initials = getInitials(name);
170
+ return /* @__PURE__ */ jsxRuntime.jsxs(
171
+ "div",
172
+ {
173
+ ref,
174
+ className: cn(
175
+ "relative inline-flex items-center justify-center shrink-0 rounded-full overflow-hidden ring-1 ring-white/10",
176
+ sizeClass[size],
177
+ className
178
+ ),
179
+ children: [
180
+ !showFallback && /* @__PURE__ */ jsxRuntime.jsx(
181
+ "img",
182
+ {
183
+ src,
184
+ alt: alt ?? name ?? "",
185
+ onError: () => setImgError(true),
186
+ className: "w-full h-full object-cover"
187
+ }
188
+ ),
189
+ showFallback && /* @__PURE__ */ jsxRuntime.jsx(
190
+ "span",
191
+ {
192
+ "aria-label": name,
193
+ className: "w-full h-full inline-flex items-center justify-center bg-gradient-to-br from-primary to-accent font-semibold text-white leading-none select-none",
194
+ children: initials
195
+ }
196
+ )
197
+ ]
198
+ }
199
+ );
200
+ });
201
+ var base = "inline-flex items-center justify-center gap-2 rounded-xl font-semibold leading-none transition-[transform,background-color,box-shadow,opacity] select-none [-webkit-tap-highlight-color:transparent] active:translate-y-[0.5px] disabled:opacity-60 disabled:pointer-events-none focus-visible:ring-2 focus-visible:ring-primary/40 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent";
202
+ var sizeClass2 = {
63
203
  sm: "px-3 py-2 text-sm",
64
204
  md: "px-3.5 py-2.5 text-sm",
65
205
  lg: "px-4 py-3 text-base"
66
206
  };
67
207
  var variantClass = {
68
- primary: "bg-gradient-to-r from-violet-600 to-purple-600 text-white shadow-[0_0_25px_rgba(139,92,246,0.35)] hover:shadow-[0_0_35px_rgba(139,92,246,0.5)] hover:scale-[1.02]",
69
- success: "bg-emerald-600 text-white shadow-[0_10px_18px_rgba(16,185,129,0.22)] hover:bg-emerald-700",
70
- warning: "bg-amber-600 text-white shadow-[0_10px_18px_rgba(245,158,11,0.22)] hover:bg-amber-700",
71
- danger: "bg-rose-600 text-white shadow-[0_10px_18px_rgba(244,63,94,0.22)] hover:bg-rose-700",
208
+ primary: "bg-gradient-to-r from-violet-600 to-purple-600 text-white shadow-glow hover:shadow-glow-lg hover:scale-[1.02]",
209
+ success: "bg-emerald-600 text-white shadow-lg shadow-emerald-600/20 hover:bg-emerald-700",
210
+ warning: "bg-amber-600 text-white shadow-lg shadow-amber-600/20 hover:bg-amber-700",
211
+ danger: "bg-rose-600 text-white shadow-lg shadow-rose-600/20 hover:bg-rose-700",
72
212
  secondary: "text-white bg-white/5 ring-1 ring-white/10 hover:bg-white/10 hover:ring-white/20 backdrop-blur-sm",
73
213
  ghost: "text-white/70 hover:text-white hover:bg-white/5"
74
214
  };
75
- var Button = react.forwardRef(function Button2({ variant = "secondary", size = "md", leftIcon, rightIcon, loading, className, disabled, children, type, ...props }, ref) {
215
+ var Button = React.forwardRef(function Button2({ variant = "secondary", size = "md", leftIcon, rightIcon, loading, className, disabled, children, type, ...props }, ref) {
76
216
  return /* @__PURE__ */ jsxRuntime.jsx(
77
217
  "button",
78
218
  {
@@ -80,7 +220,8 @@ var Button = react.forwardRef(function Button2({ variant = "secondary", size = "
80
220
  type: type === "submit" ? "submit" : type === "reset" ? "reset" : "button",
81
221
  ...props,
82
222
  disabled: disabled || loading,
83
- className: cn(base, sizeClass[size], variantClass[variant], className),
223
+ ...loading ? { "aria-busy": true } : {},
224
+ className: cn(base, sizeClass2[size], variantClass[variant], className),
84
225
  children: loading ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center justify-center gap-2", children: [
85
226
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" }),
86
227
  children
@@ -93,20 +234,20 @@ var Button = react.forwardRef(function Button2({ variant = "secondary", size = "
93
234
  );
94
235
  });
95
236
  var base2 = "inline-flex items-center justify-center rounded-xl font-semibold leading-none transition-[transform,background-color,box-shadow,opacity] select-none [-webkit-tap-highlight-color:transparent] active:translate-y-[0.5px] disabled:opacity-60 disabled:pointer-events-none focus-visible:ring-2 focus-visible:ring-primary/40 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent";
96
- var sizeClass2 = {
237
+ var sizeClass3 = {
97
238
  sm: "p-1.5 w-7 h-7",
98
239
  md: "p-2 w-9 h-9",
99
240
  lg: "p-2.5 w-11 h-11"
100
241
  };
101
242
  var variantClass2 = {
102
243
  primary: "bg-primary text-white shadow-glow hover:brightness-[0.98]",
103
- success: "bg-emerald-600 text-white shadow-[0_10px_18px_rgba(16,185,129,0.22)] hover:bg-emerald-700",
104
- warning: "bg-amber-600 text-white shadow-[0_10px_18px_rgba(245,158,11,0.22)] hover:bg-amber-700",
105
- danger: "bg-rose-600 text-white shadow-[0_10px_18px_rgba(244,63,94,0.22)] hover:bg-rose-700",
244
+ success: "bg-emerald-600 text-white shadow-lg shadow-emerald-600/20 hover:bg-emerald-700",
245
+ warning: "bg-amber-600 text-white shadow-lg shadow-amber-600/20 hover:bg-amber-700",
246
+ danger: "bg-rose-600 text-white shadow-lg shadow-rose-600/20 hover:bg-rose-700",
106
247
  secondary: "text-white bg-white/10 shadow-sm ring-1 ring-white/10",
107
248
  ghost: "text-white hover:bg-white/10"
108
249
  };
109
- var IconButton = react.forwardRef(function IconButton2({ icon, variant = "ghost", size = "md", className, disabled, type, ...props }, ref) {
250
+ var IconButton = React.forwardRef(function IconButton2({ icon, variant = "ghost", size = "md", className, disabled, type, ...props }, ref) {
110
251
  return /* @__PURE__ */ jsxRuntime.jsx(
111
252
  "button",
112
253
  {
@@ -114,78 +255,284 @@ var IconButton = react.forwardRef(function IconButton2({ icon, variant = "ghost"
114
255
  type: type === "submit" ? "submit" : type === "reset" ? "reset" : "button",
115
256
  ...props,
116
257
  disabled,
117
- className: cn(base2, sizeClass2[size], variantClass2[variant], className),
258
+ className: cn(base2, sizeClass3[size], variantClass2[variant], className),
118
259
  children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: icon })
119
260
  }
120
261
  );
121
262
  });
122
- var inputBase = "w-full rounded-xl px-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none placeholder-white/30 focus:ring-2 focus:ring-primary/40 transition-shadow";
123
- var Input = react.forwardRef(function Input2({ hasError, label, error, helperText, className, id: externalId, ...props }, ref) {
124
- const generatedId = react.useId();
263
+ var inputBase = "w-full rounded-xl px-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none placeholder-white/30 focus-visible:ring-2 focus-visible:ring-primary/40 transition-shadow";
264
+ var Input = React.forwardRef(function Input2({ hasError, label, error, helperText, className, id: externalId, ...props }, ref) {
265
+ const generatedId = React.useId();
125
266
  const inputId = externalId || generatedId;
126
267
  const showError = hasError || !!error;
268
+ const errorId = error ? `${inputId}-error` : void 0;
269
+ const helperId = helperText && !error ? `${inputId}-helper` : void 0;
127
270
  const input = /* @__PURE__ */ jsxRuntime.jsx(
128
271
  "input",
129
272
  {
273
+ ...props,
130
274
  ref,
131
275
  id: inputId,
132
- ...props,
276
+ "aria-invalid": showError || void 0,
277
+ "aria-describedby": errorId || helperId || void 0,
133
278
  className: cn(inputBase, showError && "ring-2 ring-rose-500/40 focus:ring-rose-500/40", className)
134
279
  }
135
280
  );
136
281
  if (!label && !error && !helperText) return input;
137
282
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
138
- label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: inputId, className: "block text-sm text-white/50 mb-1.5", children: label }),
283
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: inputId, className: "block text-sm text-white/70 mb-1.5", children: label }),
139
284
  input,
140
- error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-rose-400 text-xs mt-1", children: error }),
141
- helperText && !error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-white/40 text-xs mt-1", children: helperText })
285
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", children: error }),
286
+ helperText && !error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: helperId, className: "text-white/40 text-xs mt-1", children: helperText })
287
+ ] });
288
+ });
289
+ var inputBase2 = "w-full rounded-xl pl-10 pr-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none placeholder-white/30 focus-visible:ring-2 focus-visible:ring-primary/40 transition-shadow";
290
+ var SearchInput = React.forwardRef(function SearchInput2({ label, onClear, className, id: externalId, placeholder = "Search...", value, ...props }, ref) {
291
+ const generatedId = React.useId();
292
+ const inputId = externalId || generatedId;
293
+ const hasValue = value !== void 0 && value !== "";
294
+ const wrapper = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
295
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-white/40", children: /* @__PURE__ */ jsxRuntime.jsxs(
296
+ "svg",
297
+ {
298
+ xmlns: "http://www.w3.org/2000/svg",
299
+ width: "16",
300
+ height: "16",
301
+ viewBox: "0 0 24 24",
302
+ fill: "none",
303
+ stroke: "currentColor",
304
+ strokeWidth: "2",
305
+ strokeLinecap: "round",
306
+ strokeLinejoin: "round",
307
+ "aria-hidden": "true",
308
+ children: [
309
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "8" }),
310
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" })
311
+ ]
312
+ }
313
+ ) }),
314
+ /* @__PURE__ */ jsxRuntime.jsx(
315
+ "input",
316
+ {
317
+ ref,
318
+ id: inputId,
319
+ type: "search",
320
+ value,
321
+ placeholder,
322
+ ...props,
323
+ className: cn(inputBase2, hasValue && "pr-9", className)
324
+ }
325
+ ),
326
+ hasValue && onClear && /* @__PURE__ */ jsxRuntime.jsx(
327
+ "button",
328
+ {
329
+ type: "button",
330
+ onClick: onClear,
331
+ "aria-label": "Clear search",
332
+ className: "absolute right-3 top-1/2 -translate-y-1/2 text-white/40 hover:text-white transition-colors",
333
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
334
+ "svg",
335
+ {
336
+ xmlns: "http://www.w3.org/2000/svg",
337
+ width: "14",
338
+ height: "14",
339
+ viewBox: "0 0 24 24",
340
+ fill: "none",
341
+ stroke: "currentColor",
342
+ strokeWidth: "2",
343
+ strokeLinecap: "round",
344
+ strokeLinejoin: "round",
345
+ "aria-hidden": "true",
346
+ children: [
347
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
348
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
349
+ ]
350
+ }
351
+ )
352
+ }
353
+ )
354
+ ] });
355
+ if (!label) return wrapper;
356
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
357
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: inputId, className: "block text-sm text-white/70 mb-1.5", children: label }),
358
+ wrapper
142
359
  ] });
143
360
  });
144
- var selectBase = "w-full rounded-xl px-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none focus:ring-2 focus:ring-primary/40 transition-shadow";
145
- var Select = react.forwardRef(function Select2({ hasError, label, error, className, id: externalId, children, ...props }, ref) {
146
- const generatedId = react.useId();
361
+ var selectBase = "w-full rounded-xl px-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none focus-visible:ring-2 focus-visible:ring-primary/40 transition-shadow";
362
+ var Select = React.forwardRef(function Select2({ hasError, label, error, helperText, className, id: externalId, children, ...props }, ref) {
363
+ const generatedId = React.useId();
147
364
  const selectId = externalId || generatedId;
148
365
  const showError = hasError || !!error;
366
+ const errorId = error ? `${selectId}-error` : void 0;
367
+ const helperId = helperText && !error ? `${selectId}-helper` : void 0;
149
368
  const select = /* @__PURE__ */ jsxRuntime.jsx(
150
369
  "select",
151
370
  {
371
+ ...props,
152
372
  ref,
153
373
  id: selectId,
154
- ...props,
374
+ "aria-invalid": showError || void 0,
375
+ "aria-describedby": errorId || helperId || void 0,
155
376
  className: cn(selectBase, showError && "ring-2 ring-rose-500/40 focus:ring-rose-500/40", className),
156
377
  children
157
378
  }
158
379
  );
159
- if (!label && !error) return select;
380
+ if (!label && !error && !helperText) return select;
160
381
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
161
- label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: selectId, className: "block text-sm text-white/50 mb-1.5", children: label }),
382
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: selectId, className: "block text-sm text-white/70 mb-1.5", children: label }),
162
383
  select,
163
- error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-rose-400 text-xs mt-1", children: error })
384
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", children: error }),
385
+ helperText && !error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: helperId, className: "text-white/40 text-xs mt-1", children: helperText })
164
386
  ] });
165
387
  });
166
- var textareaBase = "w-full rounded-xl px-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none placeholder-white/30 focus:ring-2 focus:ring-primary/40 transition-shadow resize-y";
167
- var Textarea = react.forwardRef(function Textarea2({ hasError, label, error, className, id: externalId, ...props }, ref) {
168
- const generatedId = react.useId();
388
+ var textareaBase = "w-full rounded-xl px-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none placeholder-white/30 focus-visible:ring-2 focus-visible:ring-primary/40 transition-shadow resize-y";
389
+ var Textarea = React.forwardRef(function Textarea2({ hasError, label, error, helperText, className, id: externalId, ...props }, ref) {
390
+ const generatedId = React.useId();
169
391
  const textareaId = externalId || generatedId;
170
392
  const showError = hasError || !!error;
393
+ const errorId = error ? `${textareaId}-error` : void 0;
394
+ const helperId = helperText && !error ? `${textareaId}-helper` : void 0;
171
395
  const textarea = /* @__PURE__ */ jsxRuntime.jsx(
172
396
  "textarea",
173
397
  {
398
+ ...props,
174
399
  ref,
175
400
  id: textareaId,
176
- ...props,
401
+ "aria-invalid": showError || void 0,
402
+ "aria-describedby": errorId || helperId || void 0,
177
403
  className: cn(textareaBase, showError && "ring-2 ring-rose-500/40 focus:ring-rose-500/40", className)
178
404
  }
179
405
  );
180
- if (!label && !error) return textarea;
406
+ if (!label && !error && !helperText) return textarea;
181
407
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
182
- label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: textareaId, className: "block text-sm text-white/50 mb-1.5", children: label }),
408
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: textareaId, className: "block text-sm text-white/70 mb-1.5", children: label }),
183
409
  textarea,
184
- error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-rose-400 text-xs mt-1", children: error })
410
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", children: error }),
411
+ helperText && !error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: helperId, className: "text-white/40 text-xs mt-1", children: helperText })
185
412
  ] });
186
413
  });
414
+ function TagInput({
415
+ value,
416
+ onChange,
417
+ placeholder,
418
+ disabled = false,
419
+ label,
420
+ error,
421
+ maxTags,
422
+ className,
423
+ id: externalId
424
+ }) {
425
+ const generatedId = React.useId();
426
+ const inputId = externalId ?? generatedId;
427
+ const errorId = error ? `${inputId}-error` : void 0;
428
+ const inputRef = React.useRef(null);
429
+ const atMax = maxTags !== void 0 && value.length >= maxTags;
430
+ function addTag(raw) {
431
+ const tag = raw.trim();
432
+ if (!tag || value.includes(tag) || atMax) return;
433
+ onChange([...value, tag]);
434
+ }
435
+ function removeTag(index) {
436
+ onChange(value.filter((_, i) => i !== index));
437
+ }
438
+ function handleKeyDown(e) {
439
+ const input = inputRef.current;
440
+ if (!input) return;
441
+ if (e.key === "Enter" || e.key === "," || e.key === "Tab") {
442
+ if (input.value) {
443
+ e.preventDefault();
444
+ addTag(input.value);
445
+ input.value = "";
446
+ }
447
+ return;
448
+ }
449
+ if (e.key === "Backspace" && !input.value && value.length > 0) {
450
+ removeTag(value.length - 1);
451
+ }
452
+ }
453
+ function handlePaste(e) {
454
+ const pasted = e.clipboardData.getData("text");
455
+ if (!pasted.includes(",")) return;
456
+ e.preventDefault();
457
+ const parts = pasted.split(",");
458
+ const next = [...value];
459
+ for (const part of parts) {
460
+ const tag = part.trim();
461
+ if (tag && !next.includes(tag)) {
462
+ if (maxTags !== void 0 && next.length >= maxTags) break;
463
+ next.push(tag);
464
+ }
465
+ }
466
+ onChange(next);
467
+ if (inputRef.current) inputRef.current.value = "";
468
+ }
469
+ const wrapper = /* @__PURE__ */ jsxRuntime.jsxs(
470
+ "div",
471
+ {
472
+ className: cn(
473
+ "flex flex-wrap items-center gap-1.5 min-h-[42px] w-full rounded-xl px-3 py-2 bg-white/10 ring-1 ring-white/10 transition-shadow focus-within:ring-2 focus-within:ring-primary/40",
474
+ error && "ring-2 ring-rose-500/40 focus-within:ring-rose-500/40",
475
+ disabled && "opacity-50 cursor-not-allowed",
476
+ className
477
+ ),
478
+ onClick: () => inputRef.current?.focus(),
479
+ children: [
480
+ value.map((tag, i) => /* @__PURE__ */ jsxRuntime.jsxs(
481
+ "span",
482
+ {
483
+ className: "inline-flex items-center gap-1 bg-white/10 text-white/90 rounded-full text-xs px-2.5 py-1 ring-1 ring-white/10",
484
+ children: [
485
+ tag,
486
+ !disabled && /* @__PURE__ */ jsxRuntime.jsx(
487
+ "button",
488
+ {
489
+ type: "button",
490
+ "aria-label": `Remove ${tag}`,
491
+ onClick: (e) => {
492
+ e.stopPropagation();
493
+ removeTag(i);
494
+ },
495
+ className: "text-white/50 hover:text-white/90 transition-colors leading-none",
496
+ children: "\u2715"
497
+ }
498
+ )
499
+ ]
500
+ },
501
+ `${tag}-${i}`
502
+ )),
503
+ /* @__PURE__ */ jsxRuntime.jsx(
504
+ "input",
505
+ {
506
+ ref: inputRef,
507
+ id: inputId,
508
+ type: "text",
509
+ disabled: disabled || atMax,
510
+ placeholder: value.length === 0 ? placeholder : void 0,
511
+ "aria-invalid": !!error || void 0,
512
+ "aria-describedby": errorId,
513
+ onKeyDown: handleKeyDown,
514
+ onPaste: handlePaste,
515
+ onBlur: (e) => {
516
+ if (e.target.value) {
517
+ addTag(e.target.value);
518
+ e.target.value = "";
519
+ }
520
+ },
521
+ className: "flex-1 min-w-[120px] bg-transparent text-sm text-white outline-none placeholder-white/30 disabled:cursor-not-allowed"
522
+ }
523
+ )
524
+ ]
525
+ }
526
+ );
527
+ if (!label && !error) return wrapper;
528
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
529
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: inputId, className: "block text-sm text-white/70 mb-1.5", children: label }),
530
+ wrapper,
531
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", children: error })
532
+ ] });
533
+ }
187
534
  var base3 = "inline-flex items-center justify-center rounded-full font-semibold ring-1 ring-white/10";
188
- var sizeClass3 = {
535
+ var sizeClass4 = {
189
536
  sm: "text-xs px-2.5 py-1",
190
537
  md: "text-sm px-3 py-1.5"
191
538
  };
@@ -199,8 +546,8 @@ var variantClass3 = {
199
546
  dangerSolid: "bg-rose-600 text-white ring-0",
200
547
  accent: "bg-accent/15 text-accent-light ring-accent/20"
201
548
  };
202
- var Badge = react.forwardRef(function Badge2({ children, variant = "neutral", size = "sm", className, ...props }, ref) {
203
- return /* @__PURE__ */ jsxRuntime.jsx("span", { ref, ...props, className: cn(base3, sizeClass3[size], variantClass3[variant], className), children });
549
+ var Badge = React.forwardRef(function Badge2({ children, variant = "neutral", size = "sm", className, ...props }, ref) {
550
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { ref, ...props, className: cn(base3, sizeClass4[size], variantClass3[variant], className), children });
204
551
  });
205
552
  var Pill = Badge;
206
553
  var trackSize = {
@@ -211,11 +558,15 @@ var thumbSize = {
211
558
  sm: { base: "w-4 h-4", translate: "translate-x-4" },
212
559
  md: { base: "w-5 h-5", translate: "translate-x-5" }
213
560
  };
214
- function Toggle({ checked, onChange, disabled, label, size = "md", "aria-label": ariaLabel }) {
561
+ var Toggle = React.forwardRef(function Toggle2({ checked, onChange, disabled, label, size = "md", id: externalId, "aria-label": ariaLabel }, ref) {
562
+ const generatedId = React.useId();
563
+ const toggleId = externalId || generatedId;
215
564
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
216
565
  /* @__PURE__ */ jsxRuntime.jsx(
217
566
  "button",
218
567
  {
568
+ ref,
569
+ id: toggleId,
219
570
  type: "button",
220
571
  role: "switch",
221
572
  "aria-checked": checked,
@@ -240,20 +591,415 @@ function Toggle({ checked, onChange, disabled, label, size = "md", "aria-label":
240
591
  )
241
592
  }
242
593
  ),
243
- label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-white/60", children: label })
594
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: toggleId, className: "text-sm text-white/60 cursor-pointer", children: label })
595
+ ] });
596
+ });
597
+ var Slider = React.forwardRef(function Slider2({
598
+ label,
599
+ showValue = false,
600
+ formatValue,
601
+ onChange,
602
+ disabled,
603
+ min = 0,
604
+ max = 100,
605
+ step = 1,
606
+ value,
607
+ defaultValue,
608
+ className,
609
+ id: externalId,
610
+ ...props
611
+ }, ref) {
612
+ const generatedId = React.useId();
613
+ const sliderId = externalId || generatedId;
614
+ const numericValue = React.useMemo(() => {
615
+ const raw = value !== void 0 ? value : defaultValue;
616
+ return raw !== void 0 ? Number(raw) : Number(min);
617
+ }, [value, defaultValue, min]);
618
+ const percentage = React.useMemo(() => {
619
+ const numMin = Number(min);
620
+ const numMax = Number(max);
621
+ if (numMax === numMin) return 0;
622
+ return Math.max(0, Math.min(100, (numericValue - numMin) / (numMax - numMin) * 100));
623
+ }, [numericValue, min, max]);
624
+ const trackGradient = `linear-gradient(to right, var(--ml-primary, #8b5cf6) ${percentage}%, rgba(255,255,255,0.1) ${percentage}%)`;
625
+ const displayValue = formatValue ? formatValue(numericValue) : String(numericValue);
626
+ const handleChange = (e) => {
627
+ onChange?.(Number(e.target.value));
628
+ };
629
+ const inputEl = /* @__PURE__ */ jsxRuntime.jsx(
630
+ "input",
631
+ {
632
+ ...props,
633
+ ref,
634
+ id: sliderId,
635
+ type: "range",
636
+ min,
637
+ max,
638
+ step,
639
+ value,
640
+ defaultValue: value === void 0 ? defaultValue ?? Number(min) : void 0,
641
+ disabled,
642
+ onChange: handleChange,
643
+ style: { background: trackGradient },
644
+ className: cn(
645
+ // Layout + reset
646
+ "w-full h-2 appearance-none rounded-full outline-none cursor-pointer",
647
+ // bg is set via inline style (trackGradient); keep transparent fallback for SSR
648
+ "bg-white/10",
649
+ // Webkit thumb
650
+ "[&::-webkit-slider-thumb]:appearance-none",
651
+ "[&::-webkit-slider-thumb]:w-4",
652
+ "[&::-webkit-slider-thumb]:h-4",
653
+ "[&::-webkit-slider-thumb]:rounded-full",
654
+ "[&::-webkit-slider-thumb]:bg-white",
655
+ "[&::-webkit-slider-thumb]:border-2",
656
+ "[&::-webkit-slider-thumb]:border-[var(--ml-primary,#8b5cf6)]",
657
+ "[&::-webkit-slider-thumb]:transition-shadow",
658
+ "[&::-webkit-slider-thumb]:duration-150",
659
+ // Moz thumb
660
+ "[&::-moz-range-thumb]:w-4",
661
+ "[&::-moz-range-thumb]:h-4",
662
+ "[&::-moz-range-thumb]:rounded-full",
663
+ "[&::-moz-range-thumb]:bg-white",
664
+ "[&::-moz-range-thumb]:border-2",
665
+ "[&::-moz-range-thumb]:border-[var(--ml-primary,#8b5cf6)]",
666
+ "[&::-moz-range-thumb]:transition-shadow",
667
+ "[&::-moz-range-thumb]:duration-150",
668
+ // Moz track — transparent so the gradient on the element shows through
669
+ "[&::-moz-range-track]:bg-transparent",
670
+ "[&::-moz-range-track]:rounded-full",
671
+ // Focus ring on thumb
672
+ "focus-visible:outline-none",
673
+ "focus-visible:[&::-webkit-slider-thumb]:ring-2",
674
+ "focus-visible:[&::-webkit-slider-thumb]:ring-primary/40",
675
+ "focus-visible:[&::-webkit-slider-thumb]:ring-offset-2",
676
+ "focus-visible:[&::-webkit-slider-thumb]:ring-offset-surface",
677
+ "focus-visible:[&::-moz-range-thumb]:ring-2",
678
+ "focus-visible:[&::-moz-range-thumb]:ring-primary/40",
679
+ // Disabled
680
+ disabled && "opacity-50 cursor-not-allowed",
681
+ className
682
+ )
683
+ }
684
+ );
685
+ if (!label && !showValue) return inputEl;
686
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full space-y-1.5", children: [
687
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
688
+ label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: sliderId, className: "text-sm text-white/70", children: label }),
689
+ showValue && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-white/90 tabular-nums ml-auto", children: displayValue })
690
+ ] }),
691
+ inputEl
692
+ ] });
693
+ });
694
+ var ColorInput = React.forwardRef(function ColorInput2({ value, onChange, label, disabled, className, id: idProp }, ref) {
695
+ const autoId = React.useId();
696
+ const id = idProp ?? autoId;
697
+ const nativePickerRef = React.useRef(null);
698
+ function handleSwatchClick() {
699
+ if (!disabled) {
700
+ nativePickerRef.current?.click();
701
+ }
702
+ }
703
+ function handleTextChange(e) {
704
+ onChange(e.target.value);
705
+ }
706
+ function handleNativeChange(e) {
707
+ onChange(e.target.value);
708
+ }
709
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-1.5", className), children: [
710
+ label ? /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: "block text-sm text-white/70", children: label }) : null,
711
+ /* @__PURE__ */ jsxRuntime.jsxs(
712
+ "div",
713
+ {
714
+ className: cn(
715
+ "flex items-center gap-2 bg-white/10 rounded-xl ring-1 ring-white/10 px-3 py-2 transition-shadow",
716
+ "focus-within:ring-2 focus-within:ring-primary/40",
717
+ disabled && "opacity-60 pointer-events-none"
718
+ ),
719
+ children: [
720
+ /* @__PURE__ */ jsxRuntime.jsx(
721
+ "input",
722
+ {
723
+ type: "color",
724
+ ref: nativePickerRef,
725
+ value,
726
+ onChange: handleNativeChange,
727
+ disabled,
728
+ "aria-hidden": "true",
729
+ tabIndex: -1,
730
+ className: "sr-only"
731
+ }
732
+ ),
733
+ /* @__PURE__ */ jsxRuntime.jsx(
734
+ "button",
735
+ {
736
+ type: "button",
737
+ onClick: handleSwatchClick,
738
+ disabled,
739
+ "aria-label": "Open color picker",
740
+ className: cn(
741
+ "w-8 h-8 rounded-lg border border-white/20 flex-shrink-0 transition-transform hover:scale-105 active:scale-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60"
742
+ ),
743
+ style: { backgroundColor: value }
744
+ }
745
+ ),
746
+ /* @__PURE__ */ jsxRuntime.jsx(
747
+ "input",
748
+ {
749
+ ref,
750
+ id,
751
+ type: "text",
752
+ value,
753
+ onChange: handleTextChange,
754
+ disabled,
755
+ spellCheck: false,
756
+ className: "flex-1 min-w-0 bg-transparent text-sm text-white placeholder:text-white/30 outline-none font-mono",
757
+ placeholder: "#000000"
758
+ }
759
+ )
760
+ ]
761
+ }
762
+ )
763
+ ] });
764
+ });
765
+ var Checkbox = React.forwardRef(function Checkbox2({ label, error, indeterminate, className, id: externalId, disabled, checked, onChange, ...props }, ref) {
766
+ const generatedId = React.useId();
767
+ const inputId = externalId || generatedId;
768
+ const errorId = error ? `${inputId}-error` : void 0;
769
+ const internalRef = React.useRef(null);
770
+ React.useEffect(() => {
771
+ const el = internalRef.current;
772
+ if (el) {
773
+ el.indeterminate = indeterminate ?? false;
774
+ }
775
+ }, [indeterminate]);
776
+ const setRefs = (el) => {
777
+ internalRef.current = el;
778
+ if (typeof ref === "function") {
779
+ ref(el);
780
+ } else if (ref) {
781
+ ref.current = el;
782
+ }
783
+ };
784
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("inline-flex flex-col gap-1", className), children: [
785
+ /* @__PURE__ */ jsxRuntime.jsxs(
786
+ "label",
787
+ {
788
+ htmlFor: inputId,
789
+ className: cn(
790
+ "inline-flex items-center gap-2.5",
791
+ disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"
792
+ ),
793
+ children: [
794
+ /* @__PURE__ */ jsxRuntime.jsx(
795
+ "input",
796
+ {
797
+ ...props,
798
+ ref: setRefs,
799
+ id: inputId,
800
+ type: "checkbox",
801
+ checked,
802
+ onChange,
803
+ disabled,
804
+ "aria-invalid": !!error || void 0,
805
+ "aria-describedby": errorId,
806
+ className: "sr-only peer"
807
+ }
808
+ ),
809
+ /* @__PURE__ */ jsxRuntime.jsxs(
810
+ "span",
811
+ {
812
+ className: cn(
813
+ "relative flex items-center justify-center w-5 h-5 rounded-md",
814
+ "bg-white/10 border border-white/20",
815
+ "transition-colors duration-150",
816
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-primary/40 peer-focus-visible:ring-offset-1 peer-focus-visible:ring-offset-transparent",
817
+ checked && !indeterminate && "bg-primary border-primary",
818
+ indeterminate && "bg-primary border-primary"
819
+ ),
820
+ "aria-hidden": "true",
821
+ children: [
822
+ checked && !indeterminate && /* @__PURE__ */ jsxRuntime.jsx(
823
+ "svg",
824
+ {
825
+ viewBox: "0 0 12 10",
826
+ fill: "none",
827
+ stroke: "white",
828
+ strokeWidth: "1.8",
829
+ strokeLinecap: "round",
830
+ strokeLinejoin: "round",
831
+ className: "w-3 h-2.5",
832
+ children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "1,5 4.5,8.5 11,1" })
833
+ }
834
+ ),
835
+ indeterminate && /* @__PURE__ */ jsxRuntime.jsx(
836
+ "svg",
837
+ {
838
+ viewBox: "0 0 10 2",
839
+ fill: "none",
840
+ stroke: "white",
841
+ strokeWidth: "2",
842
+ strokeLinecap: "round",
843
+ className: "w-2.5 h-0.5",
844
+ children: /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "0", y1: "1", x2: "10", y2: "1" })
845
+ }
846
+ )
847
+ ]
848
+ }
849
+ ),
850
+ label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-white/80 select-none", children: label })
851
+ ]
852
+ }
853
+ ),
854
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-rose-400 text-xs ml-7", children: error })
244
855
  ] });
856
+ });
857
+ var RadioGroupContext = React.createContext(null);
858
+ function useRadioGroup() {
859
+ const ctx = React.useContext(RadioGroupContext);
860
+ if (!ctx) {
861
+ throw new Error("RadioItem must be used inside a RadioGroup");
862
+ }
863
+ return ctx;
245
864
  }
246
- var sizeClass4 = {
865
+ function RadioGroup({
866
+ value: controlledValue,
867
+ defaultValue,
868
+ onValueChange,
869
+ name: externalName,
870
+ disabled = false,
871
+ orientation = "vertical",
872
+ label,
873
+ error,
874
+ children,
875
+ className
876
+ }) {
877
+ const groupId = React.useId();
878
+ const generatedName = React.useId();
879
+ const name = externalName ?? generatedName;
880
+ const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue);
881
+ const isControlled = controlledValue !== void 0;
882
+ const currentValue = isControlled ? controlledValue : uncontrolledValue;
883
+ const handleChange = React.useCallback(
884
+ (val) => {
885
+ if (!isControlled) setUncontrolledValue(val);
886
+ onValueChange?.(val);
887
+ },
888
+ [isControlled, onValueChange]
889
+ );
890
+ const labelId = label ? `${groupId}-label` : void 0;
891
+ const errorId = error ? `${groupId}-error` : void 0;
892
+ const ctxValue = React.useMemo(
893
+ () => ({ value: currentValue, onChange: handleChange, name, disabled, groupId }),
894
+ [currentValue, handleChange, name, disabled, groupId]
895
+ );
896
+ return /* @__PURE__ */ jsxRuntime.jsx(RadioGroupContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
897
+ "fieldset",
898
+ {
899
+ role: "radiogroup",
900
+ "aria-labelledby": labelId,
901
+ "aria-describedby": errorId,
902
+ "aria-disabled": disabled || void 0,
903
+ className: cn("border-none p-0 m-0", className),
904
+ children: [
905
+ label && /* @__PURE__ */ jsxRuntime.jsx("legend", { id: labelId, className: "text-sm text-white/50 mb-2 float-none p-0", children: label }),
906
+ /* @__PURE__ */ jsxRuntime.jsx(
907
+ "div",
908
+ {
909
+ className: cn(
910
+ "flex gap-3",
911
+ orientation === "vertical" ? "flex-col" : "flex-row flex-wrap"
912
+ ),
913
+ children
914
+ }
915
+ ),
916
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1.5", children: error })
917
+ ]
918
+ }
919
+ ) });
920
+ }
921
+ function RadioItem({ value, disabled: itemDisabled, children, className }) {
922
+ const { value: groupValue, onChange, name, disabled: groupDisabled, groupId } = useRadioGroup();
923
+ const inputId = React.useId();
924
+ const inputRef = React.useRef(null);
925
+ const isDisabled = groupDisabled || itemDisabled;
926
+ const isSelected = groupValue === value;
927
+ const handleKeyDown = (e) => {
928
+ if (e.key !== "ArrowDown" && e.key !== "ArrowUp" && e.key !== "ArrowRight" && e.key !== "ArrowLeft") {
929
+ return;
930
+ }
931
+ e.preventDefault();
932
+ const fieldset = inputRef.current?.closest("fieldset");
933
+ if (!fieldset) return;
934
+ const inputs = Array.from(
935
+ fieldset.querySelectorAll(`input[type="radio"][name="${name}"]:not(:disabled)`)
936
+ );
937
+ const currentIndex = inputs.indexOf(inputRef.current);
938
+ if (currentIndex === -1) return;
939
+ const forward = e.key === "ArrowDown" || e.key === "ArrowRight";
940
+ const nextIndex = forward ? (currentIndex + 1) % inputs.length : (currentIndex - 1 + inputs.length) % inputs.length;
941
+ const nextInput = inputs[nextIndex];
942
+ nextInput.focus();
943
+ onChange(nextInput.value);
944
+ };
945
+ return /* @__PURE__ */ jsxRuntime.jsxs(
946
+ "label",
947
+ {
948
+ htmlFor: inputId,
949
+ className: cn(
950
+ "inline-flex items-center gap-2.5",
951
+ isDisabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer",
952
+ className
953
+ ),
954
+ children: [
955
+ /* @__PURE__ */ jsxRuntime.jsx(
956
+ "input",
957
+ {
958
+ ref: inputRef,
959
+ id: inputId,
960
+ type: "radio",
961
+ name,
962
+ value,
963
+ checked: isSelected,
964
+ disabled: isDisabled,
965
+ onChange: () => onChange(value),
966
+ onKeyDown: handleKeyDown,
967
+ className: "sr-only peer",
968
+ "aria-describedby": void 0
969
+ }
970
+ ),
971
+ /* @__PURE__ */ jsxRuntime.jsx(
972
+ "span",
973
+ {
974
+ className: cn(
975
+ "relative flex items-center justify-center w-5 h-5 rounded-full",
976
+ "bg-white/10 border border-white/20",
977
+ "transition-colors duration-150",
978
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-primary/40 peer-focus-visible:ring-offset-1 peer-focus-visible:ring-offset-transparent",
979
+ isSelected && "border-primary"
980
+ ),
981
+ "aria-hidden": "true",
982
+ children: isSelected && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-2.5 h-2.5 rounded-full bg-primary" })
983
+ }
984
+ ),
985
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-white/80 select-none", children })
986
+ ]
987
+ }
988
+ );
989
+ }
990
+ var sizeClass5 = {
247
991
  sm: "h-3 w-3 border",
248
992
  md: "h-4 w-4 border-2",
249
993
  lg: "h-6 w-6 border-2"
250
994
  };
251
- function Spinner({ className, size = "md" }) {
995
+ function Spinner({ className, size = "md", label }) {
252
996
  return /* @__PURE__ */ jsxRuntime.jsx(
253
997
  "span",
254
998
  {
255
- className: cn("inline-block rounded-full border-white/15 border-t-primary animate-spin", sizeClass4[size], className),
256
- "aria-hidden": "true"
999
+ className: cn("inline-block rounded-full border-white/15 border-t-primary animate-spin", sizeClass5[size], className),
1000
+ role: label ? "status" : void 0,
1001
+ "aria-hidden": label ? void 0 : "true",
1002
+ "aria-label": label || void 0
257
1003
  }
258
1004
  );
259
1005
  }
@@ -266,14 +1012,170 @@ function Skeleton({ className, circle }) {
266
1012
  }
267
1013
  );
268
1014
  }
269
- function Card({ hoverable, variant = "surface", className, ...props }) {
1015
+ var TabsContext = React.createContext(null);
1016
+ function useTabsContext() {
1017
+ const ctx = React.useContext(TabsContext);
1018
+ if (!ctx) throw new Error("Tabs sub-components must be used inside <Tabs>");
1019
+ return ctx;
1020
+ }
1021
+ function Tabs({
1022
+ defaultValue = "",
1023
+ value,
1024
+ onValueChange,
1025
+ variant = "underline",
1026
+ children,
1027
+ className
1028
+ }) {
1029
+ const baseId = React.useId();
1030
+ const isControlled = value !== void 0;
1031
+ const [internalValue, setInternalValue] = React.useState(defaultValue);
1032
+ const activeValue = isControlled ? value ?? "" : internalValue;
1033
+ const setActiveValue = React.useCallback(
1034
+ (next) => {
1035
+ if (!isControlled) setInternalValue(next);
1036
+ onValueChange?.(next);
1037
+ },
1038
+ [isControlled, onValueChange]
1039
+ );
1040
+ const ctxValue = React.useMemo(
1041
+ () => ({ activeValue, setActiveValue, variant, baseId }),
1042
+ [activeValue, setActiveValue, variant, baseId]
1043
+ );
1044
+ return /* @__PURE__ */ jsxRuntime.jsx(TabsContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col", className), children }) });
1045
+ }
1046
+ function TabList({ children, className }) {
1047
+ const { variant } = useTabsContext();
1048
+ const listRef = React.useRef(null);
1049
+ const handleKeyDown = (e) => {
1050
+ const list = listRef.current;
1051
+ if (!list) return;
1052
+ const tabs = Array.from(list.querySelectorAll('[role="tab"]:not([disabled])'));
1053
+ if (tabs.length === 0) return;
1054
+ const focused = document.activeElement;
1055
+ if (!focused || !list.contains(focused)) return;
1056
+ const currentIndex = tabs.indexOf(focused);
1057
+ let nextIndex = currentIndex;
1058
+ switch (e.key) {
1059
+ case "ArrowRight":
1060
+ e.preventDefault();
1061
+ nextIndex = currentIndex < tabs.length - 1 ? currentIndex + 1 : 0;
1062
+ break;
1063
+ case "ArrowLeft":
1064
+ e.preventDefault();
1065
+ nextIndex = currentIndex > 0 ? currentIndex - 1 : tabs.length - 1;
1066
+ break;
1067
+ case "Home":
1068
+ e.preventDefault();
1069
+ nextIndex = 0;
1070
+ break;
1071
+ case "End":
1072
+ e.preventDefault();
1073
+ nextIndex = tabs.length - 1;
1074
+ break;
1075
+ case "Enter":
1076
+ case " ":
1077
+ e.preventDefault();
1078
+ focused?.click();
1079
+ return;
1080
+ default:
1081
+ return;
1082
+ }
1083
+ tabs[nextIndex]?.focus();
1084
+ tabs[nextIndex]?.click();
1085
+ };
1086
+ return /* @__PURE__ */ jsxRuntime.jsx(
1087
+ "div",
1088
+ {
1089
+ ref: listRef,
1090
+ role: "tablist",
1091
+ onKeyDown: handleKeyDown,
1092
+ className: cn(
1093
+ "flex",
1094
+ variant === "underline" && "border-b border-white/10",
1095
+ variant === "pill" && "gap-1 p-1 rounded-xl bg-white/5 ring-1 ring-white/10 w-fit",
1096
+ className
1097
+ ),
1098
+ children
1099
+ }
1100
+ );
1101
+ }
1102
+ function Tab({ value, disabled = false, children, className }) {
1103
+ const { activeValue, setActiveValue, variant, baseId } = useTabsContext();
1104
+ const isActive = activeValue === value;
1105
+ return /* @__PURE__ */ jsxRuntime.jsx(
1106
+ "button",
1107
+ {
1108
+ type: "button",
1109
+ role: "tab",
1110
+ id: `${baseId}-tab-${value}`,
1111
+ "aria-selected": isActive,
1112
+ "aria-controls": `${baseId}-panel-${value}`,
1113
+ disabled,
1114
+ tabIndex: isActive ? 0 : -1,
1115
+ onClick: () => {
1116
+ if (!disabled) setActiveValue(value);
1117
+ },
1118
+ className: cn(
1119
+ "relative px-4 py-2.5 text-sm font-medium transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 focus-visible:ring-offset-transparent",
1120
+ // underline variant
1121
+ variant === "underline" && [
1122
+ "border-b-2 -mb-px",
1123
+ isActive ? "border-primary text-white" : "border-transparent text-white/40 hover:text-white/70 hover:border-white/20",
1124
+ disabled && "opacity-40 cursor-not-allowed hover:text-white/40 hover:border-transparent"
1125
+ ],
1126
+ // pill variant
1127
+ variant === "pill" && [
1128
+ "rounded-lg",
1129
+ isActive ? "bg-white/10 text-white shadow-sm" : "text-white/50 hover:text-white/80 hover:bg-white/5",
1130
+ disabled && "opacity-40 cursor-not-allowed hover:bg-transparent hover:text-white/50"
1131
+ ],
1132
+ className
1133
+ ),
1134
+ children
1135
+ }
1136
+ );
1137
+ }
1138
+ function TabPanel({ value, children, className }) {
1139
+ const { activeValue, baseId } = useTabsContext();
1140
+ if (activeValue !== value) return null;
1141
+ return /* @__PURE__ */ jsxRuntime.jsx(
1142
+ "div",
1143
+ {
1144
+ role: "tabpanel",
1145
+ id: `${baseId}-panel-${value}`,
1146
+ "aria-labelledby": `${baseId}-tab-${value}`,
1147
+ tabIndex: 0,
1148
+ className: cn("mt-4 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary", className),
1149
+ children
1150
+ }
1151
+ );
1152
+ }
1153
+ var Card = React.forwardRef(function Card2({ hoverable, variant = "surface", className, ...props }, ref) {
270
1154
  return /* @__PURE__ */ jsxRuntime.jsx(
271
1155
  "div",
272
1156
  {
1157
+ ref,
273
1158
  ...props,
274
1159
  className: cn(variant === "glass" ? "glass" : "surface", hoverable && "surface-hover", className)
275
1160
  }
276
1161
  );
1162
+ });
1163
+ var scrollLockCount = 0;
1164
+ var savedOverflow = "";
1165
+ function lockScroll() {
1166
+ if (typeof document === "undefined") return;
1167
+ if (scrollLockCount === 0) {
1168
+ savedOverflow = document.body.style.overflow;
1169
+ document.body.style.overflow = "hidden";
1170
+ }
1171
+ scrollLockCount++;
1172
+ }
1173
+ function unlockScroll() {
1174
+ if (typeof document === "undefined") return;
1175
+ scrollLockCount = Math.max(0, scrollLockCount - 1);
1176
+ if (scrollLockCount === 0) {
1177
+ document.body.style.overflow = savedOverflow;
1178
+ }
277
1179
  }
278
1180
  function Modal({
279
1181
  isOpen,
@@ -288,9 +1190,9 @@ function Modal({
288
1190
  contentClassName,
289
1191
  zIndexClassName = "z-50"
290
1192
  }) {
291
- const dialogRef = react.useRef(null);
292
- const lastActiveElementRef = react.useRef(null);
293
- react.useEffect(() => {
1193
+ const dialogRef = React.useRef(null);
1194
+ const lastActiveElementRef = React.useRef(null);
1195
+ React.useEffect(() => {
294
1196
  if (!isOpen) return;
295
1197
  lastActiveElementRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
296
1198
  const raf = window.requestAnimationFrame(() => {
@@ -306,6 +1208,11 @@ function Modal({
306
1208
  if (lastActive?.isConnected) focusSafely(lastActive);
307
1209
  };
308
1210
  }, [isOpen]);
1211
+ React.useEffect(() => {
1212
+ if (!isOpen) return;
1213
+ lockScroll();
1214
+ return () => unlockScroll();
1215
+ }, [isOpen]);
309
1216
  if (!isOpen) return null;
310
1217
  return /* @__PURE__ */ jsxRuntime.jsx(
311
1218
  "div",
@@ -331,7 +1238,7 @@ function Modal({
331
1238
  ref: dialogRef,
332
1239
  className: cn(
333
1240
  "w-full rounded-t-3xl sm:rounded-2xl shadow-xl ring-1 ring-white/10 animate-modal-pop focus:outline-none focus-visible:ring-2 focus-visible:ring-primary",
334
- useGlass && "glass bg-[#0f0f18]/80",
1241
+ useGlass && "glass bg-surface-50/80",
335
1242
  contentClassName
336
1243
  ),
337
1244
  onMouseDown: (e) => e.stopPropagation(),
@@ -394,7 +1301,7 @@ function ConfirmDialog({
394
1301
  variant = "danger",
395
1302
  isLoading = false
396
1303
  }) {
397
- const titleId = react.useId();
1304
+ const titleId = React.useId();
398
1305
  return /* @__PURE__ */ jsxRuntime.jsxs(
399
1306
  Modal,
400
1307
  {
@@ -439,22 +1346,22 @@ function ConfirmDialog({
439
1346
  );
440
1347
  }
441
1348
  function Tooltip({ content, delayMs = 500, placement = "top", className, children }) {
442
- const tooltipId = react.useId();
443
- const openTimerRef = react.useRef(null);
444
- const anchorRef = react.useRef(null);
445
- const [open, setOpen] = react.useState(false);
446
- const [pos, setPos] = react.useState(null);
447
- const contentStr = react.useMemo(() => {
1349
+ const tooltipId = React.useId();
1350
+ const openTimerRef = React.useRef(null);
1351
+ const anchorRef = React.useRef(null);
1352
+ const [open, setOpen] = React.useState(false);
1353
+ const [pos, setPos] = React.useState(null);
1354
+ React.useMemo(() => {
448
1355
  if (typeof content === "string") return content.trim();
449
1356
  return "";
450
1357
  }, [content]);
451
- function clearTimer() {
1358
+ const clearTimer = React.useCallback(() => {
452
1359
  if (openTimerRef.current !== null) {
453
1360
  window.clearTimeout(openTimerRef.current);
454
1361
  openTimerRef.current = null;
455
1362
  }
456
- }
457
- function updatePosition() {
1363
+ }, []);
1364
+ const updatePosition = React.useCallback(() => {
458
1365
  const el = anchorRef.current;
459
1366
  if (!el) return;
460
1367
  const r = el.getBoundingClientRect();
@@ -469,18 +1376,18 @@ function Tooltip({ content, delayMs = 500, placement = "top", className, childre
469
1376
  top: Math.round(effPlacement === "top" ? topY : bottomY),
470
1377
  placement: effPlacement
471
1378
  });
472
- }
473
- function scheduleOpen() {
1379
+ }, [placement]);
1380
+ const scheduleOpen = React.useCallback(() => {
474
1381
  clearTimer();
475
1382
  openTimerRef.current = window.setTimeout(() => {
476
1383
  setOpen(true);
477
1384
  }, Math.max(0, delayMs));
478
- }
479
- function close() {
1385
+ }, [clearTimer, delayMs]);
1386
+ const close = React.useCallback(() => {
480
1387
  clearTimer();
481
1388
  setOpen(false);
482
- }
483
- react.useEffect(() => {
1389
+ }, [clearTimer]);
1390
+ React.useEffect(() => {
484
1391
  if (!open) return;
485
1392
  updatePosition();
486
1393
  const onScroll = () => updatePosition();
@@ -491,45 +1398,52 @@ function Tooltip({ content, delayMs = 500, placement = "top", className, childre
491
1398
  window.removeEventListener("scroll", onScroll, true);
492
1399
  window.removeEventListener("resize", onResize);
493
1400
  };
494
- }, [open]);
495
- react.useEffect(() => {
1401
+ }, [open, updatePosition]);
1402
+ React.useEffect(() => {
496
1403
  return () => clearTimer();
497
- }, []);
498
- const child = react.cloneElement(children, {
1404
+ }, [clearTimer]);
1405
+ if (!React.isValidElement(children)) return children;
1406
+ const child = React.cloneElement(children, {
499
1407
  ref: (node) => {
500
1408
  anchorRef.current = node;
501
- const prevRef = children.ref;
1409
+ const childProps = children.props;
1410
+ const prevRef = childProps.ref;
502
1411
  if (typeof prevRef === "function") prevRef(node);
503
1412
  else if (prevRef && typeof prevRef === "object") prevRef.current = node;
504
1413
  },
505
1414
  onMouseEnter: (e) => {
506
- children.props.onMouseEnter?.(e);
1415
+ const childProps = children.props;
1416
+ if (typeof childProps.onMouseEnter === "function") childProps.onMouseEnter(e);
507
1417
  scheduleOpen();
508
1418
  },
509
1419
  onMouseLeave: (e) => {
510
- children.props.onMouseLeave?.(e);
1420
+ const childProps = children.props;
1421
+ if (typeof childProps.onMouseLeave === "function") childProps.onMouseLeave(e);
511
1422
  close();
512
1423
  },
513
1424
  onFocus: (e) => {
514
- children.props.onFocus?.(e);
1425
+ const childProps = children.props;
1426
+ if (typeof childProps.onFocus === "function") childProps.onFocus(e);
515
1427
  scheduleOpen();
516
1428
  },
517
1429
  onBlur: (e) => {
518
- children.props.onBlur?.(e);
1430
+ const childProps = children.props;
1431
+ if (typeof childProps.onBlur === "function") childProps.onBlur(e);
519
1432
  close();
520
1433
  },
521
- ...contentStr ? { "aria-describedby": tooltipId } : {}
1434
+ ...open ? { "aria-describedby": tooltipId } : {}
522
1435
  });
523
1436
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
524
1437
  child,
525
- open && content ? reactDom.createPortal(
1438
+ open && content && typeof document !== "undefined" ? reactDom.createPortal(
526
1439
  /* @__PURE__ */ jsxRuntime.jsx(
527
1440
  "div",
528
1441
  {
529
1442
  id: tooltipId,
530
1443
  role: "tooltip",
531
1444
  className: cn(
532
- "fixed z-[9999] max-w-[320px] rounded-lg bg-gray-900 px-3 py-2 text-xs leading-snug text-white shadow-xl pointer-events-none",
1445
+ "fixed z-[9999] max-w-[320px] rounded-lg px-3 py-2 text-xs leading-snug shadow-xl pointer-events-none",
1446
+ "bg-surface-100 text-white ring-1 ring-white/10",
533
1447
  className
534
1448
  ),
535
1449
  style: pos ? {
@@ -554,15 +1468,19 @@ function EmptyState({ icon: Icon, title, description, actionLabel, onAction, chi
554
1468
  ] });
555
1469
  }
556
1470
  function CollapsibleSection({ title, defaultOpen = true, children, right, className }) {
557
- const [isOpen, setIsOpen] = react.useState(defaultOpen);
1471
+ const [isOpen, setIsOpen] = React.useState(defaultOpen);
1472
+ const contentId = React.useId();
1473
+ const titleId = `${contentId}-title`;
558
1474
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("rounded-xl ring-1 ring-white/10 bg-white/5 overflow-hidden", className), children: [
559
1475
  /* @__PURE__ */ jsxRuntime.jsxs(
560
1476
  "button",
561
1477
  {
562
1478
  type: "button",
1479
+ id: titleId,
563
1480
  onClick: () => setIsOpen((prev) => !prev),
564
1481
  className: "flex w-full items-center justify-between gap-3 px-4 py-3 text-left hover:bg-white/[0.03] transition-colors",
565
1482
  "aria-expanded": isOpen,
1483
+ "aria-controls": contentId,
566
1484
  children: [
567
1485
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
568
1486
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -586,6 +1504,9 @@ function CollapsibleSection({ title, defaultOpen = true, children, right, classN
586
1504
  /* @__PURE__ */ jsxRuntime.jsx(
587
1505
  "div",
588
1506
  {
1507
+ id: contentId,
1508
+ role: "region",
1509
+ "aria-labelledby": titleId,
589
1510
  className: cn(
590
1511
  "grid transition-[grid-template-rows] duration-200 ease-out",
591
1512
  isOpen ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
@@ -595,6 +1516,747 @@ function CollapsibleSection({ title, defaultOpen = true, children, right, classN
595
1516
  )
596
1517
  ] });
597
1518
  }
1519
+ var DropdownContext = React.createContext(null);
1520
+ function useDropdownContext(component) {
1521
+ const ctx = React.useContext(DropdownContext);
1522
+ if (!ctx) {
1523
+ throw new Error(`<${component}> must be rendered inside <Dropdown>`);
1524
+ }
1525
+ return ctx;
1526
+ }
1527
+ function Dropdown({ children, className }) {
1528
+ const [open, setOpen] = React.useState(false);
1529
+ const triggerId = React.useId();
1530
+ const menuId = React.useId();
1531
+ const triggerRef = React.useRef(null);
1532
+ const toggleOpen = React.useCallback(() => setOpen((prev) => !prev), []);
1533
+ const ctxValue = React.useMemo(
1534
+ () => ({ open, setOpen, toggleOpen, triggerId, menuId, triggerRef }),
1535
+ [open, setOpen, toggleOpen, triggerId, menuId, triggerRef]
1536
+ );
1537
+ return /* @__PURE__ */ jsxRuntime.jsx(DropdownContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("relative inline-block", className), children }) });
1538
+ }
1539
+ function DropdownTrigger({ children, className }) {
1540
+ const { open, toggleOpen, triggerId, menuId, triggerRef } = useDropdownContext("DropdownTrigger");
1541
+ if (!React.isValidElement(children)) return children;
1542
+ return React.cloneElement(children, {
1543
+ id: triggerId,
1544
+ "aria-haspopup": "menu",
1545
+ "aria-expanded": open,
1546
+ "aria-controls": open ? menuId : void 0,
1547
+ className: cn(children.props.className, className),
1548
+ ref: (node) => {
1549
+ triggerRef.current = node;
1550
+ const childRef = children.ref;
1551
+ if (typeof childRef === "function") childRef(node);
1552
+ else if (childRef && typeof childRef === "object") childRef.current = node;
1553
+ },
1554
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1555
+ onClick: (e) => {
1556
+ const childProps = children.props;
1557
+ if (typeof childProps.onClick === "function") childProps.onClick(e);
1558
+ toggleOpen();
1559
+ }
1560
+ });
1561
+ }
1562
+ function DropdownMenu({ children, className, align = "left" }) {
1563
+ const { open, setOpen, menuId, triggerId, triggerRef } = useDropdownContext("DropdownMenu");
1564
+ const menuRef = React.useRef(null);
1565
+ const [pos, setPos] = React.useState(null);
1566
+ const [visible, setVisible] = React.useState(false);
1567
+ const updatePosition = React.useCallback(() => {
1568
+ const trigger = triggerRef.current;
1569
+ if (!trigger) return;
1570
+ const rect = trigger.getBoundingClientRect();
1571
+ const left = align === "right" ? rect.right : rect.left;
1572
+ setPos({
1573
+ top: Math.round(rect.bottom + 6),
1574
+ left: Math.round(left)
1575
+ });
1576
+ }, [align, triggerRef]);
1577
+ React.useEffect(() => {
1578
+ if (!open) {
1579
+ setVisible(false);
1580
+ return;
1581
+ }
1582
+ updatePosition();
1583
+ const raf = window.requestAnimationFrame(() => {
1584
+ setVisible(true);
1585
+ const menuEl = menuRef.current;
1586
+ if (!menuEl) return;
1587
+ const items = getFocusableElements(menuEl);
1588
+ focusSafely(items[0] ?? menuEl);
1589
+ });
1590
+ return () => window.cancelAnimationFrame(raf);
1591
+ }, [open, updatePosition]);
1592
+ React.useEffect(() => {
1593
+ if (!open) return;
1594
+ function handlePointerDown(e) {
1595
+ const target = e.target;
1596
+ if (menuRef.current?.contains(target)) return;
1597
+ if (triggerRef.current?.contains(target)) return;
1598
+ setOpen(false);
1599
+ }
1600
+ document.addEventListener("pointerdown", handlePointerDown, true);
1601
+ return () => document.removeEventListener("pointerdown", handlePointerDown, true);
1602
+ }, [open, setOpen, triggerRef]);
1603
+ React.useEffect(() => {
1604
+ if (!open) return;
1605
+ const onScroll = () => updatePosition();
1606
+ const onResize = () => updatePosition();
1607
+ window.addEventListener("scroll", onScroll, true);
1608
+ window.addEventListener("resize", onResize);
1609
+ return () => {
1610
+ window.removeEventListener("scroll", onScroll, true);
1611
+ window.removeEventListener("resize", onResize);
1612
+ };
1613
+ }, [open, updatePosition]);
1614
+ if (!open) return null;
1615
+ const panel = /* @__PURE__ */ jsxRuntime.jsx(
1616
+ "div",
1617
+ {
1618
+ ref: menuRef,
1619
+ id: menuId,
1620
+ role: "menu",
1621
+ "aria-labelledby": triggerId,
1622
+ tabIndex: -1,
1623
+ className: cn(
1624
+ // Base glass panel
1625
+ "fixed z-[9999] min-w-[10rem] py-1",
1626
+ "rounded-xl shadow-xl ring-1 ring-white/10",
1627
+ "bg-surface-50/90 backdrop-blur-md",
1628
+ // Animation
1629
+ "transition-[opacity,transform] duration-150 ease-out",
1630
+ visible ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-1",
1631
+ className
1632
+ ),
1633
+ style: pos ? {
1634
+ top: pos.top,
1635
+ ...align === "right" ? { right: `calc(100vw - ${pos.left}px)` } : { left: pos.left }
1636
+ } : { top: 0, left: 0, visibility: "hidden" },
1637
+ onKeyDown: (e) => {
1638
+ const menuEl = menuRef.current;
1639
+ if (!menuEl) return;
1640
+ if (e.key === "Escape" || e.key === "Tab") {
1641
+ if (e.key === "Escape") {
1642
+ e.preventDefault();
1643
+ e.stopPropagation();
1644
+ focusSafely(triggerRef.current);
1645
+ }
1646
+ setOpen(false);
1647
+ return;
1648
+ }
1649
+ const items = getFocusableElements(menuEl).filter(
1650
+ (el) => el.getAttribute("role") === "menuitem" && el.getAttribute("aria-disabled") !== "true"
1651
+ );
1652
+ if (items.length === 0) return;
1653
+ const active = document.activeElement;
1654
+ const currentIndex = items.findIndex((el) => el === active);
1655
+ if (e.key === "ArrowDown") {
1656
+ e.preventDefault();
1657
+ const next = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
1658
+ focusSafely(items[next]);
1659
+ } else if (e.key === "ArrowUp") {
1660
+ e.preventDefault();
1661
+ const prev = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
1662
+ focusSafely(items[prev]);
1663
+ } else if (e.key === "Home") {
1664
+ e.preventDefault();
1665
+ focusSafely(items[0]);
1666
+ } else if (e.key === "End") {
1667
+ e.preventDefault();
1668
+ focusSafely(items[items.length - 1]);
1669
+ }
1670
+ },
1671
+ children
1672
+ }
1673
+ );
1674
+ if (typeof document === "undefined") return null;
1675
+ return reactDom.createPortal(panel, document.body);
1676
+ }
1677
+ function DropdownItem({ onSelect, disabled = false, children, className }) {
1678
+ const { setOpen, triggerRef } = useDropdownContext("DropdownItem");
1679
+ const handleSelect = React.useCallback(() => {
1680
+ if (disabled) return;
1681
+ setOpen(false);
1682
+ focusSafely(triggerRef.current);
1683
+ onSelect?.();
1684
+ }, [disabled, onSelect, setOpen, triggerRef]);
1685
+ return /* @__PURE__ */ jsxRuntime.jsx(
1686
+ "div",
1687
+ {
1688
+ role: "menuitem",
1689
+ tabIndex: disabled ? -1 : 0,
1690
+ "aria-disabled": disabled ? "true" : void 0,
1691
+ className: cn(
1692
+ "flex items-center gap-2 w-full px-3 py-2 text-sm text-left cursor-pointer select-none",
1693
+ "text-white/80 transition-colors duration-100",
1694
+ "focus:outline-none focus-visible:bg-white/10",
1695
+ disabled ? "opacity-40 pointer-events-none cursor-not-allowed" : "hover:bg-white/10 hover:text-white focus-visible:text-white",
1696
+ className
1697
+ ),
1698
+ onClick: handleSelect,
1699
+ onKeyDown: (e) => {
1700
+ if (e.key === "Enter" || e.key === " ") {
1701
+ e.preventDefault();
1702
+ handleSelect();
1703
+ }
1704
+ },
1705
+ children
1706
+ }
1707
+ );
1708
+ }
1709
+ function DropdownSeparator({ className }) {
1710
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "separator", className: cn("border-t border-white/10 my-1", className) });
1711
+ }
1712
+ function matchesAccept(file, accept) {
1713
+ const types = accept.split(",").map((t) => t.trim().toLowerCase());
1714
+ const fileName = file.name.toLowerCase();
1715
+ const mimeType = file.type.toLowerCase();
1716
+ return types.some((t) => {
1717
+ if (t.startsWith(".")) return fileName.endsWith(t);
1718
+ if (t.endsWith("/*")) return mimeType.startsWith(t.slice(0, -1));
1719
+ return mimeType === t;
1720
+ });
1721
+ }
1722
+ function DropZone({
1723
+ onFilesDropped,
1724
+ accept,
1725
+ maxFiles,
1726
+ maxSize,
1727
+ disabled = false,
1728
+ children,
1729
+ className,
1730
+ "aria-label": ariaLabel = "File drop zone"
1731
+ }) {
1732
+ const [isDragging, setIsDragging] = React.useState(false);
1733
+ const inputRef = React.useRef(null);
1734
+ const dragCounterRef = React.useRef(0);
1735
+ const processFiles = React.useCallback(
1736
+ (fileList) => {
1737
+ if (!fileList || disabled) return;
1738
+ let files = Array.from(fileList);
1739
+ if (accept) {
1740
+ files = files.filter((f) => matchesAccept(f, accept));
1741
+ }
1742
+ if (maxSize !== void 0) {
1743
+ files = files.filter((f) => f.size <= maxSize);
1744
+ }
1745
+ if (maxFiles !== void 0) {
1746
+ files = files.slice(0, maxFiles);
1747
+ }
1748
+ onFilesDropped(files);
1749
+ },
1750
+ [accept, disabled, maxFiles, maxSize, onFilesDropped]
1751
+ );
1752
+ const handleDragEnter = React.useCallback(
1753
+ (e) => {
1754
+ e.preventDefault();
1755
+ dragCounterRef.current++;
1756
+ if (!disabled) setIsDragging(true);
1757
+ },
1758
+ [disabled]
1759
+ );
1760
+ const handleDragOver = React.useCallback(
1761
+ (e) => {
1762
+ e.preventDefault();
1763
+ },
1764
+ []
1765
+ );
1766
+ const handleDragLeave = React.useCallback((e) => {
1767
+ e.preventDefault();
1768
+ dragCounterRef.current--;
1769
+ if (dragCounterRef.current === 0) setIsDragging(false);
1770
+ }, []);
1771
+ const handleDrop = React.useCallback(
1772
+ (e) => {
1773
+ e.preventDefault();
1774
+ dragCounterRef.current = 0;
1775
+ setIsDragging(false);
1776
+ if (disabled) return;
1777
+ processFiles(e.dataTransfer.files);
1778
+ },
1779
+ [disabled, processFiles]
1780
+ );
1781
+ const handleClick = React.useCallback(() => {
1782
+ if (!disabled) inputRef.current?.click();
1783
+ }, [disabled]);
1784
+ const handleKeyDown = React.useCallback(
1785
+ (e) => {
1786
+ if (disabled) return;
1787
+ if (e.key === "Enter" || e.key === " ") {
1788
+ e.preventDefault();
1789
+ inputRef.current?.click();
1790
+ }
1791
+ },
1792
+ [disabled]
1793
+ );
1794
+ const handleInputChange = React.useCallback(() => {
1795
+ processFiles(inputRef.current?.files ?? null);
1796
+ if (inputRef.current) inputRef.current.value = "";
1797
+ }, [processFiles]);
1798
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1799
+ "div",
1800
+ {
1801
+ role: "button",
1802
+ tabIndex: disabled ? -1 : 0,
1803
+ "aria-label": ariaLabel,
1804
+ "aria-disabled": disabled,
1805
+ onClick: handleClick,
1806
+ onKeyDown: handleKeyDown,
1807
+ onDragEnter: handleDragEnter,
1808
+ onDragOver: handleDragOver,
1809
+ onDragLeave: handleDragLeave,
1810
+ onDrop: handleDrop,
1811
+ className: cn(
1812
+ "flex flex-col items-center justify-center gap-3 rounded-2xl border-2 border-dashed px-6 py-10 text-center transition-colors cursor-pointer select-none outline-none",
1813
+ "border-white/20 bg-white/5 backdrop-blur-sm",
1814
+ "hover:border-primary/50 hover:bg-white/[0.08]",
1815
+ "focus-visible:ring-2 focus-visible:ring-primary/40 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent",
1816
+ isDragging && "border-primary bg-primary/10",
1817
+ disabled && "pointer-events-none opacity-50",
1818
+ className
1819
+ ),
1820
+ children: [
1821
+ /* @__PURE__ */ jsxRuntime.jsx(
1822
+ "input",
1823
+ {
1824
+ ref: inputRef,
1825
+ type: "file",
1826
+ tabIndex: -1,
1827
+ className: "sr-only",
1828
+ accept,
1829
+ multiple: maxFiles !== 1,
1830
+ onChange: handleInputChange,
1831
+ onClick: (e) => e.stopPropagation(),
1832
+ "aria-hidden": "true"
1833
+ }
1834
+ ),
1835
+ children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1836
+ /* @__PURE__ */ jsxRuntime.jsx(
1837
+ "svg",
1838
+ {
1839
+ xmlns: "http://www.w3.org/2000/svg",
1840
+ className: "h-10 w-10 text-white/40",
1841
+ fill: "none",
1842
+ viewBox: "0 0 24 24",
1843
+ stroke: "currentColor",
1844
+ strokeWidth: 1.5,
1845
+ "aria-hidden": "true",
1846
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1847
+ "path",
1848
+ {
1849
+ strokeLinecap: "round",
1850
+ strokeLinejoin: "round",
1851
+ d: "M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5"
1852
+ }
1853
+ )
1854
+ }
1855
+ ),
1856
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-white/50", children: "Drop files here or click to browse" })
1857
+ ] })
1858
+ ]
1859
+ }
1860
+ );
1861
+ }
1862
+ function FormField({ label, error, helperText, children, className, id: idProp }) {
1863
+ const autoId = React.useId();
1864
+ const id = idProp ?? autoId;
1865
+ const descIds = [];
1866
+ const errorId = error ? `${id}-error` : void 0;
1867
+ const helperId = helperText ? `${id}-helper` : void 0;
1868
+ if (errorId) descIds.push(errorId);
1869
+ if (helperId) descIds.push(helperId);
1870
+ const describedBy = descIds.length > 0 ? descIds.join(" ") : void 0;
1871
+ const child = React.cloneElement(children, {
1872
+ id,
1873
+ ...describedBy ? { "aria-describedby": describedBy } : {},
1874
+ ...error ? { "aria-invalid": true } : {}
1875
+ });
1876
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col", className), children: [
1877
+ label ? /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: "block text-sm text-white/70 mb-1.5", children: label }) : null,
1878
+ child,
1879
+ error ? /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-rose-400 text-xs mt-1", role: "alert", children: error }) : null,
1880
+ helperText && !error ? /* @__PURE__ */ jsxRuntime.jsx("p", { id: helperId, className: "text-white/40 text-xs mt-1", children: helperText }) : null
1881
+ ] });
1882
+ }
1883
+ function Divider({ orientation = "horizontal", label, className }) {
1884
+ if (orientation === "vertical") {
1885
+ return /* @__PURE__ */ jsxRuntime.jsx(
1886
+ "div",
1887
+ {
1888
+ role: "separator",
1889
+ "aria-orientation": "vertical",
1890
+ className: cn("border-l border-white/10 h-full mx-3 self-stretch", className)
1891
+ }
1892
+ );
1893
+ }
1894
+ if (label) {
1895
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1896
+ "div",
1897
+ {
1898
+ role: "separator",
1899
+ "aria-orientation": "horizontal",
1900
+ className: cn("flex items-center gap-3 my-3 w-full", className),
1901
+ children: [
1902
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 border-t border-white/10" }),
1903
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-white/40 leading-none select-none", children: label }),
1904
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 border-t border-white/10" })
1905
+ ]
1906
+ }
1907
+ );
1908
+ }
1909
+ return /* @__PURE__ */ jsxRuntime.jsx(
1910
+ "hr",
1911
+ {
1912
+ role: "separator",
1913
+ "aria-orientation": "horizontal",
1914
+ className: cn("border-t border-white/10 w-full my-3", className)
1915
+ }
1916
+ );
1917
+ }
1918
+ var Table = React.forwardRef(({ children, className }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl ring-1 ring-white/10 overflow-hidden bg-white/5", children: /* @__PURE__ */ jsxRuntime.jsx("table", { ref, className: cn("w-full text-sm text-white", className), children }) }));
1919
+ Table.displayName = "Table";
1920
+ var TableHeader = React.forwardRef(
1921
+ ({ children, className }, ref) => /* @__PURE__ */ jsxRuntime.jsx("thead", { ref, className: cn("bg-white/5 border-b border-white/10", className), children })
1922
+ );
1923
+ TableHeader.displayName = "TableHeader";
1924
+ var TableBody = React.forwardRef(
1925
+ ({ children, className }, ref) => /* @__PURE__ */ jsxRuntime.jsx("tbody", { ref, className: cn("divide-y divide-white/[0.06]", className), children })
1926
+ );
1927
+ TableBody.displayName = "TableBody";
1928
+ var TableRow = React.forwardRef(
1929
+ ({ children, className, hoverable }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
1930
+ "tr",
1931
+ {
1932
+ ref,
1933
+ className: cn(hoverable && "hover:bg-white/[0.03] transition-colors", className),
1934
+ children
1935
+ }
1936
+ )
1937
+ );
1938
+ TableRow.displayName = "TableRow";
1939
+ var TableHead = React.forwardRef(
1940
+ ({ children, className }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
1941
+ "th",
1942
+ {
1943
+ ref,
1944
+ className: cn(
1945
+ "px-4 py-3 text-left text-xs font-semibold text-white/50 uppercase tracking-wider",
1946
+ className
1947
+ ),
1948
+ children
1949
+ }
1950
+ )
1951
+ );
1952
+ TableHead.displayName = "TableHead";
1953
+ var alignClass = {
1954
+ left: "text-left",
1955
+ center: "text-center",
1956
+ right: "text-right"
1957
+ };
1958
+ var TableCell = React.forwardRef(
1959
+ ({ children, className, align = "left" }, ref) => /* @__PURE__ */ jsxRuntime.jsx("td", { ref, className: cn("px-4 py-3 text-white/80", alignClass[align], className), children })
1960
+ );
1961
+ TableCell.displayName = "TableCell";
1962
+ function TrendIndicator({ trend }) {
1963
+ if (trend.value > 0) {
1964
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-emerald-400", children: [
1965
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { "aria-hidden": "true", className: "w-3 h-3 shrink-0", fill: "none", viewBox: "0 0 12 12", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 9V3M3 6l3-3 3 3" }) }),
1966
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1967
+ "+",
1968
+ trend.value,
1969
+ trend.label ? ` ${trend.label}` : ""
1970
+ ] })
1971
+ ] });
1972
+ }
1973
+ if (trend.value < 0) {
1974
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-rose-400", children: [
1975
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { "aria-hidden": "true", className: "w-3 h-3 shrink-0", fill: "none", viewBox: "0 0 12 12", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 3v6M3 6l3 3 3-3" }) }),
1976
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1977
+ trend.value,
1978
+ trend.label ? ` ${trend.label}` : ""
1979
+ ] })
1980
+ ] });
1981
+ }
1982
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-white/40", children: [
1983
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { "aria-hidden": "true", className: "w-3 h-3 shrink-0", fill: "none", viewBox: "0 0 12 12", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M2 6h8" }) }),
1984
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1985
+ trend.value,
1986
+ trend.label ? ` ${trend.label}` : ""
1987
+ ] })
1988
+ ] });
1989
+ }
1990
+ function StatCard({ value, label, icon, trend, className }) {
1991
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1992
+ "div",
1993
+ {
1994
+ className: cn(
1995
+ "bg-white/5 ring-1 ring-white/10 rounded-xl p-4 flex items-start gap-3",
1996
+ className
1997
+ ),
1998
+ children: [
1999
+ icon != null && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-lg bg-white/10 flex items-center justify-center text-primary shrink-0", children: icon }),
2000
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [
2001
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-2xl font-bold text-white tabular-nums leading-none", children: value }),
2002
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-white/50 truncate", children: label }),
2003
+ trend != null && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1", children: /* @__PURE__ */ jsxRuntime.jsx(TrendIndicator, { trend }) })
2004
+ ] })
2005
+ ]
2006
+ }
2007
+ );
2008
+ }
2009
+ function getPageRange(page, totalPages, siblingCount) {
2010
+ if (totalPages <= 1) return [1];
2011
+ const siblingStart = Math.max(2, page - siblingCount);
2012
+ const siblingEnd = Math.min(totalPages - 1, page + siblingCount);
2013
+ const showLeftEllipsis = siblingStart > 2;
2014
+ const showRightEllipsis = siblingEnd < totalPages - 1;
2015
+ const items = [1];
2016
+ if (showLeftEllipsis) {
2017
+ items.push("...");
2018
+ } else {
2019
+ for (let i = 2; i < siblingStart; i++) {
2020
+ items.push(i);
2021
+ }
2022
+ }
2023
+ for (let i = siblingStart; i <= siblingEnd; i++) {
2024
+ items.push(i);
2025
+ }
2026
+ if (showRightEllipsis) {
2027
+ items.push("...");
2028
+ } else {
2029
+ for (let i = siblingEnd + 1; i < totalPages; i++) {
2030
+ items.push(i);
2031
+ }
2032
+ }
2033
+ if (totalPages > 1) {
2034
+ items.push(totalPages);
2035
+ }
2036
+ return items;
2037
+ }
2038
+ var buttonBase = "inline-flex items-center justify-center rounded-lg px-3 py-2 text-sm font-medium transition-[background-color,opacity] select-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40";
2039
+ var pageButton = cn(buttonBase, "bg-white/5 ring-1 ring-white/10 text-white/80 hover:bg-white/10 hover:text-white");
2040
+ var activeButton = cn(buttonBase, "bg-primary text-white ring-1 ring-primary/20");
2041
+ var navButton = cn(buttonBase, "bg-white/5 ring-1 ring-white/10 text-white/80 hover:bg-white/10 hover:text-white");
2042
+ var ChevronLeft = () => /* @__PURE__ */ jsxRuntime.jsx(
2043
+ "svg",
2044
+ {
2045
+ xmlns: "http://www.w3.org/2000/svg",
2046
+ width: "16",
2047
+ height: "16",
2048
+ viewBox: "0 0 24 24",
2049
+ fill: "none",
2050
+ stroke: "currentColor",
2051
+ strokeWidth: "2",
2052
+ strokeLinecap: "round",
2053
+ strokeLinejoin: "round",
2054
+ "aria-hidden": "true",
2055
+ children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "15 18 9 12 15 6" })
2056
+ }
2057
+ );
2058
+ var ChevronRight = () => /* @__PURE__ */ jsxRuntime.jsx(
2059
+ "svg",
2060
+ {
2061
+ xmlns: "http://www.w3.org/2000/svg",
2062
+ width: "16",
2063
+ height: "16",
2064
+ viewBox: "0 0 24 24",
2065
+ fill: "none",
2066
+ stroke: "currentColor",
2067
+ strokeWidth: "2",
2068
+ strokeLinecap: "round",
2069
+ strokeLinejoin: "round",
2070
+ "aria-hidden": "true",
2071
+ children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "9 18 15 12 9 6" })
2072
+ }
2073
+ );
2074
+ function Pagination({
2075
+ page,
2076
+ totalPages,
2077
+ onPageChange,
2078
+ siblingCount = 1,
2079
+ className
2080
+ }) {
2081
+ if (totalPages <= 1) return null;
2082
+ const items = getPageRange(page, totalPages, siblingCount);
2083
+ const isPrevDisabled = page <= 1;
2084
+ const isNextDisabled = page >= totalPages;
2085
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2086
+ "nav",
2087
+ {
2088
+ role: "navigation",
2089
+ "aria-label": "Pagination",
2090
+ className: cn("flex items-center gap-1", className),
2091
+ children: [
2092
+ /* @__PURE__ */ jsxRuntime.jsx(
2093
+ "button",
2094
+ {
2095
+ type: "button",
2096
+ onClick: () => onPageChange(page - 1),
2097
+ disabled: isPrevDisabled,
2098
+ "aria-label": "Previous page",
2099
+ className: cn(navButton, isPrevDisabled && "opacity-40 cursor-not-allowed pointer-events-none"),
2100
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronLeft, {})
2101
+ }
2102
+ ),
2103
+ items.map(
2104
+ (item, index) => item === "..." ? /* @__PURE__ */ jsxRuntime.jsx(
2105
+ "span",
2106
+ {
2107
+ className: "inline-flex items-center justify-center px-3 py-2 text-sm text-white/40 select-none",
2108
+ "aria-hidden": "true",
2109
+ children: "\u2026"
2110
+ },
2111
+ `ellipsis-${index}`
2112
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
2113
+ "button",
2114
+ {
2115
+ type: "button",
2116
+ onClick: () => onPageChange(item),
2117
+ "aria-label": `Page ${item}`,
2118
+ "aria-current": item === page ? "page" : void 0,
2119
+ className: item === page ? activeButton : pageButton,
2120
+ children: item
2121
+ },
2122
+ item
2123
+ )
2124
+ ),
2125
+ /* @__PURE__ */ jsxRuntime.jsx(
2126
+ "button",
2127
+ {
2128
+ type: "button",
2129
+ onClick: () => onPageChange(page + 1),
2130
+ disabled: isNextDisabled,
2131
+ "aria-label": "Next page",
2132
+ className: cn(navButton, isNextDisabled && "opacity-40 cursor-not-allowed pointer-events-none"),
2133
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRight, {})
2134
+ }
2135
+ )
2136
+ ]
2137
+ }
2138
+ );
2139
+ }
2140
+ function Stepper({ steps, activeStep, className }) {
2141
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex items-start w-full", className), children: steps.map((step, index) => {
2142
+ const isCompleted = index < activeStep;
2143
+ const isActive = index === activeStep;
2144
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
2145
+ /* @__PURE__ */ jsxRuntime.jsxs(
2146
+ "div",
2147
+ {
2148
+ className: "flex flex-col items-center",
2149
+ "aria-current": isActive ? "step" : void 0,
2150
+ children: [
2151
+ /* @__PURE__ */ jsxRuntime.jsx(
2152
+ "div",
2153
+ {
2154
+ className: cn(
2155
+ "w-8 h-8 rounded-full flex items-center justify-center text-sm font-semibold",
2156
+ isCompleted && "bg-emerald-600 text-white",
2157
+ isActive && "bg-primary text-white ring-2 ring-primary/40",
2158
+ !isCompleted && !isActive && "bg-white/10 text-white/40"
2159
+ ),
2160
+ children: isCompleted ? /* @__PURE__ */ jsxRuntime.jsx(
2161
+ "svg",
2162
+ {
2163
+ xmlns: "http://www.w3.org/2000/svg",
2164
+ viewBox: "0 0 20 20",
2165
+ fill: "currentColor",
2166
+ className: "w-4 h-4",
2167
+ "aria-hidden": "true",
2168
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2169
+ "path",
2170
+ {
2171
+ fillRule: "evenodd",
2172
+ d: "M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z",
2173
+ clipRule: "evenodd"
2174
+ }
2175
+ )
2176
+ }
2177
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: index + 1 })
2178
+ }
2179
+ ),
2180
+ /* @__PURE__ */ jsxRuntime.jsx(
2181
+ "span",
2182
+ {
2183
+ className: cn(
2184
+ "text-xs mt-2 text-center max-w-[6rem]",
2185
+ isActive ? "text-white/80" : "text-white/50"
2186
+ ),
2187
+ children: step.label
2188
+ }
2189
+ ),
2190
+ step.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs mt-0.5 text-center text-white/30 max-w-[6rem]", children: step.description })
2191
+ ]
2192
+ }
2193
+ ),
2194
+ index < steps.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
2195
+ "div",
2196
+ {
2197
+ className: cn(
2198
+ "h-0.5 flex-1 mx-2 mt-4 self-start",
2199
+ isCompleted ? "bg-emerald-600" : "bg-white/10"
2200
+ ),
2201
+ "aria-hidden": "true"
2202
+ }
2203
+ )
2204
+ ] }, index);
2205
+ }) });
2206
+ }
2207
+ var sizeClasses = {
2208
+ sm: "h-1",
2209
+ md: "h-2",
2210
+ lg: "h-3"
2211
+ };
2212
+ var variantClasses = {
2213
+ primary: "bg-primary",
2214
+ success: "bg-emerald-500",
2215
+ warning: "bg-amber-500",
2216
+ danger: "bg-rose-500"
2217
+ };
2218
+ function ProgressBar({
2219
+ value,
2220
+ max = 100,
2221
+ variant = "primary",
2222
+ label,
2223
+ showValue = false,
2224
+ size = "md",
2225
+ className
2226
+ }) {
2227
+ const percentage = Math.min(100, Math.max(0, value / max * 100));
2228
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("w-full", className), children: [
2229
+ (label || showValue) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [
2230
+ label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-white/60", children: label }),
2231
+ showValue && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-white/60 ml-auto", children: [
2232
+ Math.round(percentage),
2233
+ "%"
2234
+ ] })
2235
+ ] }),
2236
+ /* @__PURE__ */ jsxRuntime.jsx(
2237
+ "div",
2238
+ {
2239
+ className: cn("w-full rounded-full bg-white/10", sizeClasses[size]),
2240
+ role: "progressbar",
2241
+ "aria-valuenow": value,
2242
+ "aria-valuemin": 0,
2243
+ "aria-valuemax": max,
2244
+ "aria-label": label,
2245
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2246
+ "div",
2247
+ {
2248
+ className: cn(
2249
+ "rounded-full transition-[width] duration-300 ease-out",
2250
+ sizeClasses[size],
2251
+ variantClasses[variant]
2252
+ ),
2253
+ style: { width: `${percentage}%` }
2254
+ }
2255
+ )
2256
+ }
2257
+ )
2258
+ ] });
2259
+ }
598
2260
  var maxWidthClass = {
599
2261
  sm: "max-w-2xl",
600
2262
  md: "max-w-4xl",
@@ -607,7 +2269,7 @@ function defaultBackground() {
607
2269
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 overflow-hidden", children: [
608
2270
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -top-32 -left-32 h-[600px] w-[600px] rounded-full bg-violet-600/20 blur-[140px]" }),
609
2271
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-20 -right-32 h-[500px] w-[500px] rounded-full bg-purple-500/[0.12] blur-[120px]" }),
610
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0 left-1/2 h-[400px] w-[500px] -translate-x-1/2 rounded-full bg-indigo-500/[0.10] blur-[120px]" })
2272
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0 left-1/2 h-[400px] w-[500px] -translate-x-1/2 rounded-full bg-accent/[0.10] blur-[120px]" })
611
2273
  ] });
612
2274
  }
613
2275
  function PageShell({
@@ -641,11 +2303,8 @@ function PageShell({
641
2303
  }
642
2304
  function Navbar({ logo, children, className, glass = true }) {
643
2305
  return /* @__PURE__ */ jsxRuntime.jsx("header", { className: cn("fixed top-0 w-full z-50", glass && "glass", className), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-7xl mx-auto px-6 h-16 flex items-center justify-between", children: [
644
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: logo || /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
645
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-9 h-9 rounded-xl animated-gradient" }),
646
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-lg font-bold tracking-tight", children: "MemeLab" })
647
- ] }) }),
648
- children && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4", children })
2306
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: logo ?? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-9 h-9 rounded-xl animated-gradient" }) }),
2307
+ children && /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Main navigation", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4", children }) })
649
2308
  ] }) });
650
2309
  }
651
2310
  function Sidebar({ children, collapsed = false, onToggle, className }) {
@@ -683,7 +2342,15 @@ function Sidebar({ children, collapsed = false, onToggle, className }) {
683
2342
  }
684
2343
  );
685
2344
  }
686
- function DashboardLayout({ children, navbar, sidebar, className }) {
2345
+ var maxWidthClass2 = {
2346
+ sm: "max-w-2xl",
2347
+ md: "max-w-4xl",
2348
+ lg: "max-w-5xl",
2349
+ xl: "max-w-7xl",
2350
+ "2xl": "max-w-[92rem]",
2351
+ full: "max-w-full"
2352
+ };
2353
+ function DashboardLayout({ children, navbar, sidebar, className, mainClassName, maxWidth = "lg" }) {
687
2354
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("min-h-screen bg-surface relative overflow-hidden", className), children: [
688
2355
  /* @__PURE__ */ jsxRuntime.jsxs("div", { "aria-hidden": "true", className: "pointer-events-none fixed inset-0", children: [
689
2356
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "orb orb-purple w-[400px] h-[400px] -top-[150px] -left-[150px] opacity-15" }),
@@ -692,31 +2359,613 @@ function DashboardLayout({ children, navbar, sidebar, className }) {
692
2359
  navbar,
693
2360
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex", navbar && "pt-16"), children: [
694
2361
  sidebar,
695
- /* @__PURE__ */ jsxRuntime.jsx("main", { className: "flex-1 min-w-0 py-8 px-6", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-5xl mx-auto", children }) })
2362
+ /* @__PURE__ */ jsxRuntime.jsx("main", { className: cn("flex-1 min-w-0 py-8 px-6", mainClassName), children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("mx-auto", maxWidthClass2[maxWidth]), children }) })
696
2363
  ] })
697
2364
  ] });
698
2365
  }
2366
+ var variantBorderColor = {
2367
+ info: "border-blue-500",
2368
+ success: "border-emerald-500",
2369
+ warning: "border-amber-500",
2370
+ error: "border-rose-500"
2371
+ };
2372
+ var variantIconColor = {
2373
+ info: "text-blue-400",
2374
+ success: "text-emerald-400",
2375
+ warning: "text-amber-400",
2376
+ error: "text-rose-400"
2377
+ };
2378
+ function InfoIcon() {
2379
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2380
+ "svg",
2381
+ {
2382
+ "aria-hidden": "true",
2383
+ width: "18",
2384
+ height: "18",
2385
+ viewBox: "0 0 18 18",
2386
+ fill: "none",
2387
+ xmlns: "http://www.w3.org/2000/svg",
2388
+ children: [
2389
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "9", r: "7.5", stroke: "currentColor", strokeWidth: "1.5" }),
2390
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 8v5", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" }),
2391
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "5.5", r: "0.875", fill: "currentColor" })
2392
+ ]
2393
+ }
2394
+ );
2395
+ }
2396
+ function SuccessIcon() {
2397
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2398
+ "svg",
2399
+ {
2400
+ "aria-hidden": "true",
2401
+ width: "18",
2402
+ height: "18",
2403
+ viewBox: "0 0 18 18",
2404
+ fill: "none",
2405
+ xmlns: "http://www.w3.org/2000/svg",
2406
+ children: [
2407
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "9", r: "7.5", stroke: "currentColor", strokeWidth: "1.5" }),
2408
+ /* @__PURE__ */ jsxRuntime.jsx(
2409
+ "path",
2410
+ {
2411
+ d: "M5.5 9.5l2.5 2.5 4.5-5",
2412
+ stroke: "currentColor",
2413
+ strokeWidth: "1.75",
2414
+ strokeLinecap: "round",
2415
+ strokeLinejoin: "round"
2416
+ }
2417
+ )
2418
+ ]
2419
+ }
2420
+ );
2421
+ }
2422
+ function WarningIcon() {
2423
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2424
+ "svg",
2425
+ {
2426
+ "aria-hidden": "true",
2427
+ width: "18",
2428
+ height: "18",
2429
+ viewBox: "0 0 18 18",
2430
+ fill: "none",
2431
+ xmlns: "http://www.w3.org/2000/svg",
2432
+ children: [
2433
+ /* @__PURE__ */ jsxRuntime.jsx(
2434
+ "path",
2435
+ {
2436
+ d: "M7.634 2.896a1.6 1.6 0 0 1 2.732 0l5.866 10.167A1.6 1.6 0 0 1 14.866 15.5H3.134a1.6 1.6 0 0 1-1.366-2.437L7.634 2.896Z",
2437
+ stroke: "currentColor",
2438
+ strokeWidth: "1.5",
2439
+ strokeLinejoin: "round"
2440
+ }
2441
+ ),
2442
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 7v4", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" }),
2443
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "12.5", r: "0.875", fill: "currentColor" })
2444
+ ]
2445
+ }
2446
+ );
2447
+ }
2448
+ function ErrorIcon() {
2449
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2450
+ "svg",
2451
+ {
2452
+ "aria-hidden": "true",
2453
+ width: "18",
2454
+ height: "18",
2455
+ viewBox: "0 0 18 18",
2456
+ fill: "none",
2457
+ xmlns: "http://www.w3.org/2000/svg",
2458
+ children: [
2459
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "9", r: "7.5", stroke: "currentColor", strokeWidth: "1.5" }),
2460
+ /* @__PURE__ */ jsxRuntime.jsx(
2461
+ "path",
2462
+ {
2463
+ d: "M6.5 6.5l5 5M11.5 6.5l-5 5",
2464
+ stroke: "currentColor",
2465
+ strokeWidth: "1.75",
2466
+ strokeLinecap: "round"
2467
+ }
2468
+ )
2469
+ ]
2470
+ }
2471
+ );
2472
+ }
2473
+ var variantIcon = {
2474
+ info: InfoIcon,
2475
+ success: SuccessIcon,
2476
+ warning: WarningIcon,
2477
+ error: ErrorIcon
2478
+ };
2479
+ function Alert({ variant = "info", title, children, onDismiss, className }) {
2480
+ const Icon = variantIcon[variant];
2481
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2482
+ "div",
2483
+ {
2484
+ role: "alert",
2485
+ className: cn(
2486
+ "flex items-start gap-3 rounded-xl bg-white/5 ring-1 ring-white/10",
2487
+ "border-l-4 pl-4 pr-3 py-3",
2488
+ variantBorderColor[variant],
2489
+ className
2490
+ ),
2491
+ children: [
2492
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("mt-0.5 shrink-0", variantIconColor[variant]), children: /* @__PURE__ */ jsxRuntime.jsx(Icon, {}) }),
2493
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
2494
+ title && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-white leading-snug mb-0.5", children: title }),
2495
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-white/70 leading-relaxed", children })
2496
+ ] }),
2497
+ onDismiss && /* @__PURE__ */ jsxRuntime.jsx(
2498
+ "button",
2499
+ {
2500
+ type: "button",
2501
+ "aria-label": "Dismiss",
2502
+ onClick: onDismiss,
2503
+ className: cn(
2504
+ "shrink-0 flex items-center justify-center h-6 w-6 rounded-lg mt-0.5",
2505
+ "text-white/40 transition-colors hover:bg-white/10 hover:text-white/80",
2506
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40"
2507
+ ),
2508
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2509
+ "svg",
2510
+ {
2511
+ "aria-hidden": "true",
2512
+ width: "10",
2513
+ height: "10",
2514
+ viewBox: "0 0 10 10",
2515
+ fill: "none",
2516
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M1 1l8 8M9 1L1 9", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
2517
+ }
2518
+ )
2519
+ }
2520
+ )
2521
+ ]
2522
+ }
2523
+ );
2524
+ }
2525
+ function CopyIcon() {
2526
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2527
+ "svg",
2528
+ {
2529
+ "aria-hidden": "true",
2530
+ width: "16",
2531
+ height: "16",
2532
+ viewBox: "0 0 16 16",
2533
+ fill: "none",
2534
+ xmlns: "http://www.w3.org/2000/svg",
2535
+ children: [
2536
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "5", y: "5", width: "9", height: "9", rx: "1.5", stroke: "currentColor", strokeWidth: "1.5" }),
2537
+ /* @__PURE__ */ jsxRuntime.jsx(
2538
+ "path",
2539
+ {
2540
+ d: "M3.5 11H3a1.5 1.5 0 0 1-1.5-1.5V3A1.5 1.5 0 0 1 3 1.5h6.5A1.5 1.5 0 0 1 11 3v.5",
2541
+ stroke: "currentColor",
2542
+ strokeWidth: "1.5",
2543
+ strokeLinecap: "round"
2544
+ }
2545
+ )
2546
+ ]
2547
+ }
2548
+ );
2549
+ }
2550
+ function CheckIcon() {
2551
+ return /* @__PURE__ */ jsxRuntime.jsx(
2552
+ "svg",
2553
+ {
2554
+ "aria-hidden": "true",
2555
+ width: "16",
2556
+ height: "16",
2557
+ viewBox: "0 0 16 16",
2558
+ fill: "none",
2559
+ xmlns: "http://www.w3.org/2000/svg",
2560
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2561
+ "path",
2562
+ {
2563
+ d: "M3 8.5l3.5 3.5 6.5-7",
2564
+ stroke: "currentColor",
2565
+ strokeWidth: "1.75",
2566
+ strokeLinecap: "round",
2567
+ strokeLinejoin: "round"
2568
+ }
2569
+ )
2570
+ }
2571
+ );
2572
+ }
2573
+ function EyeIcon() {
2574
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2575
+ "svg",
2576
+ {
2577
+ "aria-hidden": "true",
2578
+ width: "16",
2579
+ height: "16",
2580
+ viewBox: "0 0 16 16",
2581
+ fill: "none",
2582
+ xmlns: "http://www.w3.org/2000/svg",
2583
+ children: [
2584
+ /* @__PURE__ */ jsxRuntime.jsx(
2585
+ "path",
2586
+ {
2587
+ d: "M1.5 8S3.5 3.5 8 3.5 14.5 8 14.5 8 12.5 12.5 8 12.5 1.5 8 1.5 8Z",
2588
+ stroke: "currentColor",
2589
+ strokeWidth: "1.5",
2590
+ strokeLinejoin: "round"
2591
+ }
2592
+ ),
2593
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8", cy: "8", r: "2", stroke: "currentColor", strokeWidth: "1.5" })
2594
+ ]
2595
+ }
2596
+ );
2597
+ }
2598
+ function EyeOffIcon() {
2599
+ return /* @__PURE__ */ jsxRuntime.jsx(
2600
+ "svg",
2601
+ {
2602
+ "aria-hidden": "true",
2603
+ width: "16",
2604
+ height: "16",
2605
+ viewBox: "0 0 16 16",
2606
+ fill: "none",
2607
+ xmlns: "http://www.w3.org/2000/svg",
2608
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2609
+ "path",
2610
+ {
2611
+ d: "M2 2l12 12M6.5 6.6A2 2 0 0 0 9.4 9.5M4.3 4.4C2.9 5.4 1.5 8 1.5 8S3.5 12.5 8 12.5c1.4 0 2.6-.4 3.7-1.1M6.7 3.6C7.1 3.5 7.5 3.5 8 3.5c4.5 0 6.5 4.5 6.5 4.5s-.5 1.2-1.5 2.3",
2612
+ stroke: "currentColor",
2613
+ strokeWidth: "1.5",
2614
+ strokeLinecap: "round",
2615
+ strokeLinejoin: "round"
2616
+ }
2617
+ )
2618
+ }
2619
+ );
2620
+ }
2621
+ var iconButtonBase = cn(
2622
+ "flex items-center justify-center h-7 w-7 rounded-lg shrink-0",
2623
+ "text-white/40 transition-colors hover:bg-white/10 hover:text-white/80",
2624
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40"
2625
+ );
2626
+ function CopyField({ value, label, masked = false, className, id: externalId }) {
2627
+ const generatedId = React.useId();
2628
+ const fieldId = externalId || generatedId;
2629
+ const { copy, copied } = useClipboard();
2630
+ const [revealed, setRevealed] = React.useState(false);
2631
+ const displayValue = masked && !revealed ? "\u2022".repeat(Math.min(value.length, 24)) : value;
2632
+ const field = /* @__PURE__ */ jsxRuntime.jsxs(
2633
+ "div",
2634
+ {
2635
+ className: cn(
2636
+ "flex items-center gap-1 w-full rounded-xl px-3 py-2.5",
2637
+ "bg-white/10 ring-1 ring-white/10",
2638
+ className
2639
+ ),
2640
+ children: [
2641
+ /* @__PURE__ */ jsxRuntime.jsx(
2642
+ "span",
2643
+ {
2644
+ id: fieldId,
2645
+ className: "flex-1 min-w-0 truncate text-sm text-white select-all font-mono",
2646
+ "aria-label": label,
2647
+ children: displayValue
2648
+ }
2649
+ ),
2650
+ masked && /* @__PURE__ */ jsxRuntime.jsx(
2651
+ "button",
2652
+ {
2653
+ type: "button",
2654
+ "aria-label": revealed ? "Hide value" : "Reveal value",
2655
+ onClick: () => setRevealed((v) => !v),
2656
+ className: iconButtonBase,
2657
+ children: revealed ? /* @__PURE__ */ jsxRuntime.jsx(EyeOffIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(EyeIcon, {})
2658
+ }
2659
+ ),
2660
+ /* @__PURE__ */ jsxRuntime.jsx(
2661
+ "button",
2662
+ {
2663
+ type: "button",
2664
+ "aria-label": copied ? "Copied!" : "Copy to clipboard",
2665
+ onClick: () => copy(value),
2666
+ className: cn(iconButtonBase, copied && "text-emerald-400 hover:text-emerald-400"),
2667
+ children: copied ? /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(CopyIcon, {})
2668
+ }
2669
+ )
2670
+ ]
2671
+ }
2672
+ );
2673
+ if (!label) return field;
2674
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2675
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: fieldId, className: "block text-sm text-white/70 mb-1.5", children: label }),
2676
+ field
2677
+ ] });
2678
+ }
2679
+ var ProgressButton = React.forwardRef(
2680
+ function ProgressButton2({ isLoading, loadingText, children, disabled, className, ...props }, ref) {
2681
+ return /* @__PURE__ */ jsxRuntime.jsx(
2682
+ Button,
2683
+ {
2684
+ ref,
2685
+ ...props,
2686
+ disabled: disabled || isLoading,
2687
+ "aria-busy": isLoading || void 0,
2688
+ className: cn("relative overflow-hidden", className),
2689
+ children: isLoading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2690
+ /* @__PURE__ */ jsxRuntime.jsx(
2691
+ "span",
2692
+ {
2693
+ "aria-hidden": "true",
2694
+ className: "absolute inset-0 bg-white/20 animate-[ml-shimmer_2s_ease-in-out_infinite] skew-x-[-20deg] pointer-events-none"
2695
+ }
2696
+ ),
2697
+ /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: "sm" }),
2698
+ loadingText ?? children
2699
+ ] }) : children
2700
+ }
2701
+ );
2702
+ }
2703
+ );
2704
+ function reducer(state, action) {
2705
+ switch (action.type) {
2706
+ case "ADD": {
2707
+ const next = [...state, action.toast];
2708
+ return next.length > action.maxToasts ? next.slice(-action.maxToasts) : next;
2709
+ }
2710
+ case "REMOVE":
2711
+ return state.filter((t) => t.id !== action.id);
2712
+ case "REMOVE_ALL":
2713
+ return [];
2714
+ default:
2715
+ return state;
2716
+ }
2717
+ }
2718
+ var ToastContext = React.createContext(null);
2719
+ var variantBarColor = {
2720
+ success: "bg-emerald-500",
2721
+ error: "bg-rose-500",
2722
+ warning: "bg-amber-500",
2723
+ info: "bg-primary"
2724
+ };
2725
+ var variantProgressColor = {
2726
+ success: "bg-emerald-500",
2727
+ error: "bg-rose-500",
2728
+ warning: "bg-amber-500",
2729
+ info: "bg-primary"
2730
+ };
2731
+ var variantIconLabel = {
2732
+ success: "\u2713",
2733
+ error: "\u2715",
2734
+ warning: "\u26A0",
2735
+ info: "i"
2736
+ };
2737
+ var variantIconColor2 = {
2738
+ success: "text-emerald-400",
2739
+ error: "text-rose-400",
2740
+ warning: "text-amber-400",
2741
+ info: "text-primary"
2742
+ };
2743
+ var positionClass = {
2744
+ "top-right": "top-4 right-4 items-end",
2745
+ "top-center": "top-4 left-1/2 -translate-x-1/2 items-center",
2746
+ "bottom-right": "bottom-4 right-4 items-end",
2747
+ "bottom-center": "bottom-4 left-1/2 -translate-x-1/2 items-center"
2748
+ };
2749
+ function ToastCard({ toast, onDismiss }) {
2750
+ const duration = toast.duration ?? 5e3;
2751
+ const [visible, setVisible] = React.useState(false);
2752
+ const [exiting, setExiting] = React.useState(false);
2753
+ const [started, setStarted] = React.useState(false);
2754
+ const dismissedRef = React.useRef(false);
2755
+ const timerRef = React.useRef(null);
2756
+ const exitTimerRef = React.useRef(null);
2757
+ React.useEffect(() => {
2758
+ let innerFrame;
2759
+ const frame = requestAnimationFrame(() => {
2760
+ setVisible(true);
2761
+ innerFrame = requestAnimationFrame(() => setStarted(true));
2762
+ });
2763
+ return () => {
2764
+ cancelAnimationFrame(frame);
2765
+ cancelAnimationFrame(innerFrame);
2766
+ };
2767
+ }, []);
2768
+ React.useEffect(() => {
2769
+ return () => {
2770
+ if (exitTimerRef.current !== null) clearTimeout(exitTimerRef.current);
2771
+ };
2772
+ }, []);
2773
+ const triggerDismiss = React.useCallback(() => {
2774
+ if (dismissedRef.current) return;
2775
+ dismissedRef.current = true;
2776
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
2777
+ setExiting(true);
2778
+ exitTimerRef.current = setTimeout(() => onDismiss(toast.id), 280);
2779
+ }, [onDismiss, toast.id]);
2780
+ React.useEffect(() => {
2781
+ if (duration <= 0) return;
2782
+ timerRef.current = setTimeout(triggerDismiss, duration);
2783
+ return () => {
2784
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
2785
+ };
2786
+ }, [duration, triggerDismiss]);
2787
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2788
+ "div",
2789
+ {
2790
+ role: "alert",
2791
+ "aria-live": "assertive",
2792
+ "aria-atomic": "true",
2793
+ "data-variant": toast.variant,
2794
+ className: cn(
2795
+ // Base layout
2796
+ "relative flex w-80 max-w-[calc(100vw-2rem)] overflow-hidden rounded-xl shadow-xl",
2797
+ // Glass background
2798
+ "bg-surface-100/95 backdrop-blur-md ring-1 ring-white/10",
2799
+ // Enter / exit transitions
2800
+ "transition-all duration-300",
2801
+ visible && !exiting ? "opacity-100 translate-x-0" : "opacity-0 translate-x-8",
2802
+ exiting && "scale-95"
2803
+ ),
2804
+ children: [
2805
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: cn("w-[3px] shrink-0 self-stretch", variantBarColor[toast.variant]) }),
2806
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 items-start gap-3 px-4 py-3 pr-9", children: [
2807
+ /* @__PURE__ */ jsxRuntime.jsx(
2808
+ "span",
2809
+ {
2810
+ "aria-hidden": "true",
2811
+ className: cn(
2812
+ "mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-[11px] font-bold ring-1",
2813
+ variantIconColor2[toast.variant],
2814
+ "ring-current/30"
2815
+ ),
2816
+ children: variantIconLabel[toast.variant]
2817
+ }
2818
+ ),
2819
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
2820
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold leading-snug text-white", children: toast.title }),
2821
+ toast.description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs leading-relaxed text-white/60", children: toast.description }) : null
2822
+ ] })
2823
+ ] }),
2824
+ /* @__PURE__ */ jsxRuntime.jsx(
2825
+ "button",
2826
+ {
2827
+ type: "button",
2828
+ "aria-label": "Dismiss notification",
2829
+ onClick: triggerDismiss,
2830
+ className: cn(
2831
+ "absolute right-2 top-2 flex h-6 w-6 items-center justify-center rounded-lg",
2832
+ "text-white/40 transition-colors hover:bg-white/10 hover:text-white/80",
2833
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40"
2834
+ ),
2835
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M1 1l8 8M9 1L1 9", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) })
2836
+ }
2837
+ ),
2838
+ duration > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2839
+ "span",
2840
+ {
2841
+ "aria-hidden": "true",
2842
+ className: cn(
2843
+ "absolute bottom-0 left-0 h-[2px] ease-linear",
2844
+ variantProgressColor[toast.variant],
2845
+ "opacity-60"
2846
+ ),
2847
+ style: {
2848
+ width: started ? "0%" : "100%",
2849
+ transitionProperty: "width",
2850
+ transitionDuration: started ? `${duration}ms` : "0ms"
2851
+ }
2852
+ }
2853
+ )
2854
+ ]
2855
+ }
2856
+ );
2857
+ }
2858
+ function ToastProvider({ children, position = "top-right", maxToasts = 5 }) {
2859
+ const [toasts, dispatch] = React.useReducer(reducer, []);
2860
+ const counterRef = React.useRef(0);
2861
+ const maxToastsRef = React.useRef(maxToasts);
2862
+ maxToastsRef.current = maxToasts;
2863
+ const toast = React.useCallback(
2864
+ (options) => {
2865
+ const id = `toast-${++counterRef.current}`;
2866
+ const newToast = {
2867
+ id,
2868
+ variant: options.variant,
2869
+ title: options.title,
2870
+ description: options.description,
2871
+ duration: options.duration ?? 5e3
2872
+ };
2873
+ dispatch({ type: "ADD", toast: newToast, maxToasts: maxToastsRef.current });
2874
+ return id;
2875
+ },
2876
+ []
2877
+ );
2878
+ const dismiss = React.useCallback((id) => {
2879
+ dispatch({ type: "REMOVE", id });
2880
+ }, []);
2881
+ const dismissAll = React.useCallback(() => {
2882
+ dispatch({ type: "REMOVE_ALL" });
2883
+ }, []);
2884
+ const value = React.useMemo(() => ({ toast, dismiss, dismissAll }), [toast, dismiss, dismissAll]);
2885
+ const container = typeof document !== "undefined" ? reactDom.createPortal(
2886
+ /* @__PURE__ */ jsxRuntime.jsx(
2887
+ "div",
2888
+ {
2889
+ "aria-label": "Notifications",
2890
+ className: cn("fixed z-[9999] flex flex-col gap-3 pointer-events-none", positionClass[position]),
2891
+ children: toasts.map((t) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsxRuntime.jsx(ToastCard, { toast: t, onDismiss: dismiss }) }, t.id))
2892
+ }
2893
+ ),
2894
+ document.body
2895
+ ) : null;
2896
+ return /* @__PURE__ */ jsxRuntime.jsxs(ToastContext.Provider, { value, children: [
2897
+ children,
2898
+ container
2899
+ ] });
2900
+ }
2901
+ function useToast() {
2902
+ const ctx = React.useContext(ToastContext);
2903
+ if (!ctx) {
2904
+ throw new Error("useToast must be used inside <ToastProvider>");
2905
+ }
2906
+ return ctx;
2907
+ }
699
2908
 
2909
+ exports.Alert = Alert;
2910
+ exports.Avatar = Avatar;
700
2911
  exports.Badge = Badge;
701
2912
  exports.Button = Button;
702
2913
  exports.Card = Card;
2914
+ exports.Checkbox = Checkbox;
703
2915
  exports.CollapsibleSection = CollapsibleSection;
2916
+ exports.ColorInput = ColorInput;
704
2917
  exports.ConfirmDialog = ConfirmDialog;
2918
+ exports.CopyField = CopyField;
705
2919
  exports.DashboardLayout = DashboardLayout;
2920
+ exports.Divider = Divider;
2921
+ exports.DropZone = DropZone;
2922
+ exports.Dropdown = Dropdown;
2923
+ exports.DropdownItem = DropdownItem;
2924
+ exports.DropdownMenu = DropdownMenu;
2925
+ exports.DropdownSeparator = DropdownSeparator;
2926
+ exports.DropdownTrigger = DropdownTrigger;
706
2927
  exports.EmptyState = EmptyState;
2928
+ exports.FormField = FormField;
707
2929
  exports.IconButton = IconButton;
708
2930
  exports.Input = Input;
709
2931
  exports.Modal = Modal;
710
2932
  exports.Navbar = Navbar;
711
2933
  exports.PageShell = PageShell;
2934
+ exports.Pagination = Pagination;
712
2935
  exports.Pill = Pill;
2936
+ exports.ProgressBar = ProgressBar;
2937
+ exports.ProgressButton = ProgressButton;
2938
+ exports.RadioGroup = RadioGroup;
2939
+ exports.RadioItem = RadioItem;
2940
+ exports.SearchInput = SearchInput;
713
2941
  exports.Select = Select;
714
2942
  exports.Sidebar = Sidebar;
715
2943
  exports.Skeleton = Skeleton;
2944
+ exports.Slider = Slider;
716
2945
  exports.Spinner = Spinner;
2946
+ exports.StatCard = StatCard;
2947
+ exports.Stepper = Stepper;
2948
+ exports.Tab = Tab;
2949
+ exports.TabList = TabList;
2950
+ exports.TabPanel = TabPanel;
2951
+ exports.Table = Table;
2952
+ exports.TableBody = TableBody;
2953
+ exports.TableCell = TableCell;
2954
+ exports.TableHead = TableHead;
2955
+ exports.TableHeader = TableHeader;
2956
+ exports.TableRow = TableRow;
2957
+ exports.Tabs = Tabs;
2958
+ exports.TagInput = TagInput;
717
2959
  exports.Textarea = Textarea;
2960
+ exports.ToastProvider = ToastProvider;
718
2961
  exports.Toggle = Toggle;
719
2962
  exports.Tooltip = Tooltip;
720
2963
  exports.cn = cn;
2964
+ exports.colors = colors;
721
2965
  exports.focusSafely = focusSafely;
722
2966
  exports.getFocusableElements = getFocusableElements;
2967
+ exports.useClipboard = useClipboard;
2968
+ exports.useDebounce = useDebounce;
2969
+ exports.useDisclosure = useDisclosure;
2970
+ exports.useMediaQuery = useMediaQuery;
2971
+ exports.useToast = useToast;