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