@yenio-pld/blocks 0.0.0-stage → 0.0.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.
Files changed (103) hide show
  1. package/README.md +100 -2
  2. package/dist/avatar.cjs +88 -0
  3. package/dist/avatar.cjs.map +1 -0
  4. package/dist/avatar.d.cts +18 -0
  5. package/dist/avatar.d.ts +18 -0
  6. package/dist/avatar.js +84 -0
  7. package/dist/avatar.js.map +1 -0
  8. package/dist/consent.cjs +85 -0
  9. package/dist/consent.cjs.map +1 -0
  10. package/dist/consent.d.cts +20 -0
  11. package/dist/consent.d.ts +20 -0
  12. package/dist/consent.js +73 -0
  13. package/dist/consent.js.map +1 -0
  14. package/dist/index.cjs +4053 -0
  15. package/dist/index.cjs.map +1 -0
  16. package/dist/index.d.cts +859 -0
  17. package/dist/index.d.ts +859 -0
  18. package/dist/index.js +3947 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/theme-init.cjs +12 -0
  21. package/dist/theme-init.cjs.map +1 -0
  22. package/dist/theme-init.d.cts +11 -0
  23. package/dist/theme-init.d.ts +11 -0
  24. package/dist/theme-init.js +8 -0
  25. package/dist/theme-init.js.map +1 -0
  26. package/dist/ui/alert-dialog.d.ts +15 -0
  27. package/dist/ui/alert-dialog.js +137 -0
  28. package/dist/ui/alert-dialog.js.map +1 -0
  29. package/dist/ui/alert.d.ts +11 -0
  30. package/dist/ui/alert.js +57 -0
  31. package/dist/ui/alert.js.map +1 -0
  32. package/dist/ui/avatar.d.ts +13 -0
  33. package/dist/ui/avatar.js +105 -0
  34. package/dist/ui/avatar.js.map +1 -0
  35. package/dist/ui/badge.d.ts +13 -0
  36. package/dist/ui/badge.js +33 -0
  37. package/dist/ui/badge.js.map +1 -0
  38. package/dist/ui/breadcrumb.d.ts +12 -0
  39. package/dist/ui/breadcrumb.js +121 -0
  40. package/dist/ui/breadcrumb.js.map +1 -0
  41. package/dist/ui/bubble.d.ts +19 -0
  42. package/dist/ui/bubble.js +120 -0
  43. package/dist/ui/bubble.js.map +1 -0
  44. package/dist/ui/button.d.ts +12 -0
  45. package/dist/ui/button.js +59 -0
  46. package/dist/ui/button.js.map +1 -0
  47. package/dist/ui/card.d.ts +10 -0
  48. package/dist/ui/card.js +55 -0
  49. package/dist/ui/card.js.map +1 -0
  50. package/dist/ui/collapsible.d.ts +8 -0
  51. package/dist/ui/collapsible.js +17 -0
  52. package/dist/ui/collapsible.js.map +1 -0
  53. package/dist/ui/dialog.d.ts +16 -0
  54. package/dist/ui/dialog.js +125 -0
  55. package/dist/ui/dialog.js.map +1 -0
  56. package/dist/ui/dropdown-menu.d.ts +31 -0
  57. package/dist/ui/dropdown-menu.js +244 -0
  58. package/dist/ui/dropdown-menu.js.map +1 -0
  59. package/dist/ui/input-otp.d.ts +39 -0
  60. package/dist/ui/input-otp.js +53 -0
  61. package/dist/ui/input-otp.js.map +1 -0
  62. package/dist/ui/input.d.ts +5 -0
  63. package/dist/ui/input.js +28 -0
  64. package/dist/ui/input.js.map +1 -0
  65. package/dist/ui/label.d.ts +7 -0
  66. package/dist/ui/label.js +29 -0
  67. package/dist/ui/label.js.map +1 -0
  68. package/dist/ui/message.d.ts +12 -0
  69. package/dist/ui/message.js +93 -0
  70. package/dist/ui/message.js.map +1 -0
  71. package/dist/ui/progress.d.ts +9 -0
  72. package/dist/ui/progress.js +41 -0
  73. package/dist/ui/progress.js.map +1 -0
  74. package/dist/ui/separator.d.ts +6 -0
  75. package/dist/ui/separator.js +31 -0
  76. package/dist/ui/separator.js.map +1 -0
  77. package/dist/ui/sheet.d.ts +16 -0
  78. package/dist/ui/sheet.js +127 -0
  79. package/dist/ui/sheet.js.map +1 -0
  80. package/dist/ui/sidebar.d.ts +69 -0
  81. package/dist/ui/sidebar.js +624 -0
  82. package/dist/ui/sidebar.js.map +1 -0
  83. package/dist/ui/skeleton.d.ts +5 -0
  84. package/dist/ui/skeleton.js +26 -0
  85. package/dist/ui/skeleton.js.map +1 -0
  86. package/dist/ui/sonner.d.ts +6 -0
  87. package/dist/ui/sonner.js +25 -0
  88. package/dist/ui/sonner.js.map +1 -0
  89. package/dist/ui/switch.d.ts +8 -0
  90. package/dist/ui/switch.js +38 -0
  91. package/dist/ui/switch.js.map +1 -0
  92. package/dist/ui/table.d.ts +11 -0
  93. package/dist/ui/table.js +85 -0
  94. package/dist/ui/table.js.map +1 -0
  95. package/dist/ui/tooltip.d.ts +9 -0
  96. package/dist/ui/tooltip.js +67 -0
  97. package/dist/ui/tooltip.js.map +1 -0
  98. package/dist/ui/use-mobile.d.ts +3 -0
  99. package/dist/ui/use-mobile.js +24 -0
  100. package/dist/ui/use-mobile.js.map +1 -0
  101. package/package.json +281 -5
  102. package/scripts/check-theme.mjs +74 -0
  103. package/src/theme.css +191 -0
package/dist/index.js ADDED
@@ -0,0 +1,3947 @@
1
+ "use client";
2
+ import * as React from 'react';
3
+ import { createContext, useContext, useState, useRef, useId, useEffect, useCallback, useLayoutEffect } from 'react';
4
+ import { Loader2, AlertTriangle, Inbox, Bell, Moon, Sun, Monitor, Languages, Upload, Move, ZoomOut, ZoomIn, RotateCcw, RotateCw, Grid3X3, RefreshCw, X, User, Pencil, Check, Pause, Play, BarChart3, XIcon } from 'lucide-react';
5
+ import { clsx } from 'clsx';
6
+ import { twMerge } from 'tailwind-merge';
7
+ import { cva } from 'class-variance-authority';
8
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
+ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
10
+ import { Slot } from '@radix-ui/react-slot';
11
+ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
12
+ import { useTheme } from 'next-themes';
13
+ import * as SliderPrimitive from '@radix-ui/react-slider';
14
+ import * as TogglePrimitive from '@radix-ui/react-toggle';
15
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
16
+ import { Drawer as Drawer$1 } from 'vaul';
17
+ import { AnimatePresence, motion } from 'framer-motion';
18
+
19
+ var LanguageContext = createContext(null);
20
+ function useLanguageContext() {
21
+ return useContext(LanguageContext);
22
+ }
23
+ function cn(...inputs) {
24
+ return twMerge(clsx(inputs));
25
+ }
26
+ function Empty({ className, ...props }) {
27
+ return /* @__PURE__ */ jsx(
28
+ "div",
29
+ {
30
+ "data-slot": "empty",
31
+ className: cn(
32
+ "flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg p-6 text-center text-balance md:p-12",
33
+ className
34
+ ),
35
+ ...props
36
+ }
37
+ );
38
+ }
39
+ function EmptyHeader({ className, ...props }) {
40
+ return /* @__PURE__ */ jsx(
41
+ "div",
42
+ {
43
+ "data-slot": "empty-header",
44
+ className: cn(
45
+ "flex max-w-sm flex-col items-center gap-2 text-center",
46
+ className
47
+ ),
48
+ ...props
49
+ }
50
+ );
51
+ }
52
+ var emptyMediaVariants = cva(
53
+ "mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
54
+ {
55
+ variants: {
56
+ variant: {
57
+ default: "bg-transparent",
58
+ icon: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6"
59
+ }
60
+ },
61
+ defaultVariants: {
62
+ variant: "default"
63
+ }
64
+ }
65
+ );
66
+ function EmptyMedia({
67
+ className,
68
+ variant = "default",
69
+ ...props
70
+ }) {
71
+ return /* @__PURE__ */ jsx(
72
+ "div",
73
+ {
74
+ "data-slot": "empty-icon",
75
+ "data-variant": variant,
76
+ className: cn(emptyMediaVariants({ variant, className })),
77
+ ...props
78
+ }
79
+ );
80
+ }
81
+ function EmptyTitle({ className, ...props }) {
82
+ return /* @__PURE__ */ jsx(
83
+ "div",
84
+ {
85
+ "data-slot": "empty-title",
86
+ className: cn("text-lg font-medium tracking-tight", className),
87
+ ...props
88
+ }
89
+ );
90
+ }
91
+ function EmptyDescription({ className, ...props }) {
92
+ return /* @__PURE__ */ jsx(
93
+ "div",
94
+ {
95
+ "data-slot": "empty-description",
96
+ className: cn(
97
+ "text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
98
+ className
99
+ ),
100
+ ...props
101
+ }
102
+ );
103
+ }
104
+ function EmptyContent({ className, ...props }) {
105
+ return /* @__PURE__ */ jsx(
106
+ "div",
107
+ {
108
+ "data-slot": "empty-content",
109
+ className: cn(
110
+ "flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance",
111
+ className
112
+ ),
113
+ ...props
114
+ }
115
+ );
116
+ }
117
+ function LoadingState({
118
+ label = "Loading\u2026",
119
+ className
120
+ }) {
121
+ return /* @__PURE__ */ jsxs(
122
+ "div",
123
+ {
124
+ role: "status",
125
+ "aria-live": "polite",
126
+ className: cn(
127
+ "flex items-center justify-center gap-2 py-12 text-sm text-muted-foreground",
128
+ className
129
+ ),
130
+ children: [
131
+ /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": "true" }),
132
+ /* @__PURE__ */ jsx("span", { children: label })
133
+ ]
134
+ }
135
+ );
136
+ }
137
+ function ErrorState({
138
+ title = "Something went wrong",
139
+ description,
140
+ action,
141
+ className
142
+ }) {
143
+ return /* @__PURE__ */ jsxs(
144
+ "div",
145
+ {
146
+ role: "alert",
147
+ className: cn(
148
+ "flex flex-col items-center gap-3 rounded-xl border border-dashed border-destructive/40 bg-destructive/5 py-12 text-center",
149
+ className
150
+ ),
151
+ children: [
152
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "size-6 text-destructive", "aria-hidden": "true" }),
153
+ /* @__PURE__ */ jsxs("div", { children: [
154
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: title }),
155
+ description ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
156
+ ] }),
157
+ action
158
+ ]
159
+ }
160
+ );
161
+ }
162
+ function EmptyState({
163
+ title,
164
+ description,
165
+ action,
166
+ icon: Icon = Inbox,
167
+ variant = "default",
168
+ className
169
+ }) {
170
+ const isCompact = variant === "compact";
171
+ return /* @__PURE__ */ jsxs(
172
+ Empty,
173
+ {
174
+ className: cn(
175
+ "gap-3",
176
+ isCompact ? "flex-none rounded-none px-4 py-6" : "flex-none rounded-xl border border-dashed px-6 py-12",
177
+ className
178
+ ),
179
+ children: [
180
+ /* @__PURE__ */ jsxs(EmptyHeader, { className: "gap-3", children: [
181
+ /* @__PURE__ */ jsx(
182
+ EmptyMedia,
183
+ {
184
+ variant: "icon",
185
+ className: cn(
186
+ "mb-0 bg-transparent text-muted-foreground",
187
+ isCompact ? "size-auto [&_svg:not([class*='size-'])]:size-8 [&_svg]:opacity-50" : "size-auto [&_svg:not([class*='size-'])]:size-6"
188
+ ),
189
+ children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" })
190
+ }
191
+ ),
192
+ /* @__PURE__ */ jsx(
193
+ EmptyTitle,
194
+ {
195
+ className: cn(
196
+ "font-medium tracking-normal",
197
+ isCompact ? "text-sm" : "text-base"
198
+ ),
199
+ children: title
200
+ }
201
+ ),
202
+ description ? /* @__PURE__ */ jsx(EmptyDescription, { className: "mt-0", children: description }) : null
203
+ ] }),
204
+ action ? /* @__PURE__ */ jsx(EmptyContent, { className: "gap-0", children: action }) : null
205
+ ]
206
+ }
207
+ );
208
+ }
209
+ var buttonVariants = cva(
210
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
211
+ {
212
+ variants: {
213
+ variant: {
214
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
215
+ destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
216
+ outline: "border bg-background text-foreground shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:text-foreground dark:hover:bg-muted dark:hover:text-foreground",
217
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
218
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50 dark:hover:text-foreground",
219
+ link: "text-primary underline-offset-4 hover:underline"
220
+ },
221
+ size: {
222
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
223
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
224
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
225
+ icon: "size-9",
226
+ "icon-sm": "size-8",
227
+ "icon-lg": "size-10"
228
+ }
229
+ },
230
+ defaultVariants: {
231
+ variant: "default",
232
+ size: "default"
233
+ }
234
+ }
235
+ );
236
+ function Button({
237
+ className,
238
+ variant = "default",
239
+ size = "default",
240
+ asChild = false,
241
+ ...props
242
+ }) {
243
+ const Comp = asChild ? Slot : "button";
244
+ return /* @__PURE__ */ jsx(
245
+ Comp,
246
+ {
247
+ "data-slot": "button",
248
+ "data-variant": variant,
249
+ "data-size": size,
250
+ className: cn(buttonVariants({ variant, size, className })),
251
+ ...props
252
+ }
253
+ );
254
+ }
255
+ function AlertDialog({
256
+ ...props
257
+ }) {
258
+ return /* @__PURE__ */ jsx(AlertDialogPrimitive.Root, { "data-slot": "alert-dialog", ...props });
259
+ }
260
+ function AlertDialogPortal({
261
+ ...props
262
+ }) {
263
+ return /* @__PURE__ */ jsx(AlertDialogPrimitive.Portal, { "data-slot": "alert-dialog-portal", ...props });
264
+ }
265
+ function AlertDialogOverlay({
266
+ className,
267
+ ...props
268
+ }) {
269
+ return /* @__PURE__ */ jsx(
270
+ AlertDialogPrimitive.Overlay,
271
+ {
272
+ "data-slot": "alert-dialog-overlay",
273
+ className: cn(
274
+ "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
275
+ className
276
+ ),
277
+ ...props
278
+ }
279
+ );
280
+ }
281
+ function AlertDialogContent({
282
+ className,
283
+ size = "default",
284
+ ...props
285
+ }) {
286
+ return /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [
287
+ /* @__PURE__ */ jsx(AlertDialogOverlay, {}),
288
+ /* @__PURE__ */ jsx(
289
+ AlertDialogPrimitive.Content,
290
+ {
291
+ "data-slot": "alert-dialog-content",
292
+ "data-size": size,
293
+ className: cn(
294
+ "group/alert-dialog-content fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-[size=sm]:max-w-xs data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[size=default]:sm:max-w-lg",
295
+ className
296
+ ),
297
+ ...props
298
+ }
299
+ )
300
+ ] });
301
+ }
302
+ function AlertDialogHeader({
303
+ className,
304
+ ...props
305
+ }) {
306
+ return /* @__PURE__ */ jsx(
307
+ "div",
308
+ {
309
+ "data-slot": "alert-dialog-header",
310
+ className: cn(
311
+ "flex flex-col gap-1.5 text-center sm:text-left",
312
+ className
313
+ ),
314
+ ...props
315
+ }
316
+ );
317
+ }
318
+ function AlertDialogFooter({
319
+ className,
320
+ ...props
321
+ }) {
322
+ return /* @__PURE__ */ jsx(
323
+ "div",
324
+ {
325
+ "data-slot": "alert-dialog-footer",
326
+ className: cn(
327
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
328
+ className
329
+ ),
330
+ ...props
331
+ }
332
+ );
333
+ }
334
+ function AlertDialogTitle({
335
+ className,
336
+ ...props
337
+ }) {
338
+ return /* @__PURE__ */ jsx(
339
+ AlertDialogPrimitive.Title,
340
+ {
341
+ "data-slot": "alert-dialog-title",
342
+ className: cn("text-lg font-semibold", className),
343
+ ...props
344
+ }
345
+ );
346
+ }
347
+ function AlertDialogDescription({
348
+ className,
349
+ ...props
350
+ }) {
351
+ return /* @__PURE__ */ jsx(
352
+ AlertDialogPrimitive.Description,
353
+ {
354
+ "data-slot": "alert-dialog-description",
355
+ className: cn("text-sm text-muted-foreground", className),
356
+ ...props
357
+ }
358
+ );
359
+ }
360
+ function AlertDialogAction({
361
+ className,
362
+ variant = "default",
363
+ size = "default",
364
+ ...props
365
+ }) {
366
+ return /* @__PURE__ */ jsx(Button, { variant, size, asChild: true, children: /* @__PURE__ */ jsx(
367
+ AlertDialogPrimitive.Action,
368
+ {
369
+ "data-slot": "alert-dialog-action",
370
+ className: cn(className),
371
+ ...props
372
+ }
373
+ ) });
374
+ }
375
+ function AlertDialogCancel({
376
+ className,
377
+ variant = "outline",
378
+ size = "default",
379
+ ...props
380
+ }) {
381
+ return /* @__PURE__ */ jsx(Button, { variant, size, asChild: true, children: /* @__PURE__ */ jsx(
382
+ AlertDialogPrimitive.Cancel,
383
+ {
384
+ "data-slot": "alert-dialog-cancel",
385
+ className: cn(className),
386
+ ...props
387
+ }
388
+ ) });
389
+ }
390
+ function ConfirmDestructiveDialog({
391
+ open,
392
+ onOpenChange,
393
+ title,
394
+ description,
395
+ confirmLabel = "Delete",
396
+ cancelLabel = "Cancel",
397
+ onConfirm
398
+ }) {
399
+ return /* @__PURE__ */ jsx(AlertDialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(AlertDialogContent, { size: "sm", children: [
400
+ /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
401
+ /* @__PURE__ */ jsx(AlertDialogTitle, { children: title }),
402
+ /* @__PURE__ */ jsx(AlertDialogDescription, { children: description })
403
+ ] }),
404
+ /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
405
+ /* @__PURE__ */ jsx(AlertDialogCancel, { children: cancelLabel }),
406
+ /* @__PURE__ */ jsx(
407
+ AlertDialogAction,
408
+ {
409
+ variant: "destructive",
410
+ onClick: () => {
411
+ onConfirm();
412
+ onOpenChange(false);
413
+ },
414
+ children: confirmLabel
415
+ }
416
+ )
417
+ ] })
418
+ ] }) });
419
+ }
420
+ var miniMenuContentClassName = "max-h-none overflow-x-visible overflow-y-visible";
421
+ var miniMenuContentDefaults = {
422
+ sideOffset: 8,
423
+ collisionPadding: 12
424
+ };
425
+ function DropdownMenu({
426
+ ...props
427
+ }) {
428
+ return /* @__PURE__ */ jsx(DropdownMenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
429
+ }
430
+ function DropdownMenuTrigger({
431
+ ...props
432
+ }) {
433
+ return /* @__PURE__ */ jsx(
434
+ DropdownMenuPrimitive.Trigger,
435
+ {
436
+ "data-slot": "dropdown-menu-trigger",
437
+ ...props
438
+ }
439
+ );
440
+ }
441
+ function DropdownMenuContent({
442
+ className,
443
+ sideOffset = 4,
444
+ ...props
445
+ }) {
446
+ return /* @__PURE__ */ jsx(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx(
447
+ DropdownMenuPrimitive.Content,
448
+ {
449
+ "data-slot": "dropdown-menu-content",
450
+ sideOffset,
451
+ className: cn(
452
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-[200] max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
453
+ className
454
+ ),
455
+ ...props
456
+ }
457
+ ) });
458
+ }
459
+ function DropdownMenuItem({
460
+ className,
461
+ inset,
462
+ variant = "default",
463
+ ...props
464
+ }) {
465
+ return /* @__PURE__ */ jsx(
466
+ DropdownMenuPrimitive.Item,
467
+ {
468
+ "data-slot": "dropdown-menu-item",
469
+ "data-inset": inset,
470
+ "data-variant": variant,
471
+ className: cn(
472
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[highlighted]:[&_svg:not([class*='text-'])]:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:[&_svg:not([class*='text-'])]:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:data-[highlighted]:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 dark:data-[variant=destructive]:data-[highlighted]:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:data-[highlighted]:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
473
+ className
474
+ ),
475
+ ...props
476
+ }
477
+ );
478
+ }
479
+ var DEFAULT_NOTIFICATION_BELL_LABELS = {
480
+ title: "Notifications",
481
+ empty: "You're all caught up.",
482
+ viewAll: "View all"
483
+ };
484
+ function formatUnreadBadge(count) {
485
+ return count > 9 ? "9+" : String(count);
486
+ }
487
+ function defaultFormatTime(iso) {
488
+ const date = new Date(iso);
489
+ if (Number.isNaN(date.getTime())) return iso;
490
+ return date.toLocaleString(void 0, {
491
+ dateStyle: "short",
492
+ timeStyle: "short"
493
+ });
494
+ }
495
+ function NotificationListSkeleton({ rows = 3 }) {
496
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-col", "aria-hidden": "true", children: Array.from({ length: rows }, (_, index) => /* @__PURE__ */ jsxs("div", { children: [
497
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2 px-4 py-3", children: [
498
+ /* @__PURE__ */ jsx(
499
+ "div",
500
+ {
501
+ "data-slot": "skeleton",
502
+ className: "bg-muted h-4 w-36 animate-pulse rounded-md motion-reduce:animate-none"
503
+ }
504
+ ),
505
+ /* @__PURE__ */ jsx(
506
+ "div",
507
+ {
508
+ "data-slot": "skeleton",
509
+ className: "bg-muted h-3 w-full animate-pulse rounded-md motion-reduce:animate-none"
510
+ }
511
+ ),
512
+ /* @__PURE__ */ jsx(
513
+ "div",
514
+ {
515
+ "data-slot": "skeleton",
516
+ className: "bg-muted h-3 w-20 animate-pulse rounded-md motion-reduce:animate-none"
517
+ }
518
+ )
519
+ ] }),
520
+ index < rows - 1 ? /* @__PURE__ */ jsx("div", { className: "bg-border h-px" }) : null
521
+ ] }, index)) });
522
+ }
523
+ function NotificationBell({
524
+ items,
525
+ unreadCount,
526
+ isLoading = false,
527
+ labels: labelOverrides,
528
+ viewAllHref,
529
+ formatTime = defaultFormatTime,
530
+ onOpenChange,
531
+ align = "end",
532
+ className
533
+ }) {
534
+ const labels = { ...DEFAULT_NOTIFICATION_BELL_LABELS, ...labelOverrides };
535
+ return /* @__PURE__ */ jsxs(DropdownMenu, { modal: false, onOpenChange, children: [
536
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
537
+ Button,
538
+ {
539
+ type: "button",
540
+ variant: "ghost",
541
+ size: "icon",
542
+ className: cn("relative size-9", className),
543
+ "aria-label": labels.title,
544
+ children: [
545
+ /* @__PURE__ */ jsx(Bell, { className: "size-4" }),
546
+ unreadCount > 0 ? /* @__PURE__ */ jsx("span", { className: "bg-destructive text-destructive-foreground absolute -top-0.5 -right-0.5 flex size-4 items-center justify-center rounded-full text-[10px] leading-none font-medium tabular-nums", children: formatUnreadBadge(unreadCount) }) : null
547
+ ]
548
+ }
549
+ ) }),
550
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align, className: "w-80 p-0", children: [
551
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between border-b px-4 py-3", children: /* @__PURE__ */ jsx("h4", { className: "font-semibold", children: labels.title }) }),
552
+ /* @__PURE__ */ jsx("div", { className: "max-h-80 overflow-y-auto", children: isLoading ? /* @__PURE__ */ jsx(NotificationListSkeleton, {}) : items.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground px-4 py-8 text-center text-sm", children: labels.empty }) : /* @__PURE__ */ jsx("div", { className: "flex flex-col", children: items.map((item, index) => {
553
+ const body = /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-1", children: [
554
+ /* @__PURE__ */ jsx("p", { className: "text-sm leading-none font-medium", children: item.title }),
555
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground line-clamp-2 text-xs", children: item.message }),
556
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground/70 text-xs", children: formatTime(item.createdAt) })
557
+ ] });
558
+ return /* @__PURE__ */ jsxs("div", { children: [
559
+ item.href ? /* @__PURE__ */ jsx(
560
+ "a",
561
+ {
562
+ href: item.href,
563
+ className: cn(
564
+ "hover:bg-muted/50 flex gap-3 px-4 py-3 motion-safe:transition-colors",
565
+ !item.read && "bg-muted/30"
566
+ ),
567
+ children: body
568
+ }
569
+ ) : /* @__PURE__ */ jsx(
570
+ "div",
571
+ {
572
+ className: cn(
573
+ "flex gap-3 px-4 py-3",
574
+ !item.read && "bg-muted/30"
575
+ ),
576
+ children: body
577
+ }
578
+ ),
579
+ index < items.length - 1 ? /* @__PURE__ */ jsx("div", { className: "bg-border h-px" }) : null
580
+ ] }, item.id);
581
+ }) }) }),
582
+ /* @__PURE__ */ jsx("div", { className: "border-t px-4 py-2", children: /* @__PURE__ */ jsx(
583
+ "a",
584
+ {
585
+ href: viewAllHref,
586
+ className: "text-primary block text-center text-sm font-medium hover:underline",
587
+ children: labels.viewAll
588
+ }
589
+ ) })
590
+ ] })
591
+ ] });
592
+ }
593
+
594
+ // src/components/editable/types.ts
595
+ var DEFAULT_EDITABLE_SECTION_LABELS = {
596
+ edit: "Edit",
597
+ save: "Save",
598
+ saving: "Saving\u2026",
599
+ cancel: "Cancel",
600
+ saved: "Saved",
601
+ discardTitle: "Discard changes?",
602
+ discardDescription: "If you leave now, unsaved changes will be lost.",
603
+ discardConfirm: "Discard",
604
+ discardCancel: "Keep editing"
605
+ };
606
+
607
+ // src/components/editable/useEditableSection.ts
608
+ function focusFirstField(container) {
609
+ if (!container) return;
610
+ const target = container.querySelector(
611
+ "[data-autofocus], input:not([type='hidden']):not([disabled]), select:not([disabled]), textarea:not([disabled]), [contenteditable='true']"
612
+ );
613
+ target?.focus();
614
+ }
615
+ function useEditableSection({
616
+ onSave,
617
+ onDiscard,
618
+ isDirty = false,
619
+ labelledBy,
620
+ title,
621
+ labels,
622
+ stableHeight = false
623
+ }) {
624
+ const resolvedLabels = {
625
+ ...DEFAULT_EDITABLE_SECTION_LABELS,
626
+ ...labels
627
+ };
628
+ const [mode, setMode] = useState("reading");
629
+ const [status, setStatus] = useState(null);
630
+ const [confirmOpen, setConfirmOpen] = useState(false);
631
+ const [minHeightPx, setMinHeightPx] = useState(void 0);
632
+ const contentRef = useRef(null);
633
+ const editButtonRef = useRef(null);
634
+ const measuredHeightRef = useRef(0);
635
+ const shouldFocusEditRef = useRef(false);
636
+ const shouldFocusFirstFieldRef = useRef(false);
637
+ const editButtonId = useId();
638
+ const contentFormId = useId();
639
+ const isEditing = mode === "editing" || mode === "saving";
640
+ const isSaving = mode === "saving";
641
+ useEffect(() => {
642
+ if (mode === "reading" && shouldFocusEditRef.current) {
643
+ shouldFocusEditRef.current = false;
644
+ editButtonRef.current?.focus();
645
+ return;
646
+ }
647
+ if (mode === "editing" && shouldFocusFirstFieldRef.current) {
648
+ shouldFocusFirstFieldRef.current = false;
649
+ const invalid = contentRef.current?.querySelector(
650
+ "[aria-invalid='true']"
651
+ );
652
+ if (invalid) {
653
+ invalid.focus();
654
+ return;
655
+ }
656
+ focusFirstField(contentRef.current);
657
+ }
658
+ }, [mode]);
659
+ const exitToReading = useCallback(() => {
660
+ setConfirmOpen(false);
661
+ shouldFocusEditRef.current = true;
662
+ setMode("reading");
663
+ onDiscard?.();
664
+ }, [onDiscard]);
665
+ const startEdit = useCallback(() => {
666
+ setStatus(null);
667
+ shouldFocusFirstFieldRef.current = true;
668
+ setMode("editing");
669
+ }, []);
670
+ const requestCancel = useCallback(() => {
671
+ if (isSaving) return;
672
+ if (isDirty) {
673
+ setConfirmOpen(true);
674
+ return;
675
+ }
676
+ exitToReading();
677
+ }, [exitToReading, isDirty, isSaving]);
678
+ const handleSubmit = useCallback(
679
+ (event) => {
680
+ event.preventDefault();
681
+ if (isSaving) return;
682
+ void (async () => {
683
+ setMode("saving");
684
+ setStatus(null);
685
+ try {
686
+ await onSave();
687
+ setStatus(resolvedLabels.saved);
688
+ shouldFocusEditRef.current = true;
689
+ setMode("reading");
690
+ } catch {
691
+ shouldFocusFirstFieldRef.current = true;
692
+ setMode("editing");
693
+ }
694
+ })();
695
+ },
696
+ [isSaving, onSave, resolvedLabels.saved]
697
+ );
698
+ const handleFormKeyDown = useCallback(
699
+ (event) => {
700
+ if (event.key !== "Escape") return;
701
+ event.preventDefault();
702
+ requestCancel();
703
+ },
704
+ [requestCancel]
705
+ );
706
+ useEffect(() => {
707
+ if (!stableHeight || !isEditing) return;
708
+ const el = contentRef.current;
709
+ if (!el) return;
710
+ const measure = () => {
711
+ const height = el.getBoundingClientRect().height;
712
+ if (height > measuredHeightRef.current) {
713
+ measuredHeightRef.current = height;
714
+ setMinHeightPx(height);
715
+ }
716
+ };
717
+ measure();
718
+ const observer = new ResizeObserver(measure);
719
+ observer.observe(el);
720
+ return () => observer.disconnect();
721
+ }, [isEditing, stableHeight]);
722
+ const editButtonNameProps = labelledBy != null ? { "aria-labelledby": `${labelledBy} ${editButtonId}` } : title != null && title.length > 0 ? { "aria-label": `${resolvedLabels.edit} ${title}` } : { "aria-label": resolvedLabels.edit };
723
+ return {
724
+ mode,
725
+ isEditing,
726
+ isSaving,
727
+ startEdit,
728
+ requestCancel,
729
+ formProps: {
730
+ onSubmit: handleSubmit,
731
+ onKeyDown: handleFormKeyDown,
732
+ noValidate: true
733
+ },
734
+ contentRef,
735
+ editButtonRef,
736
+ contentFormId,
737
+ editButtonProps: {
738
+ type: "button",
739
+ id: editButtonId,
740
+ onClick: startEdit,
741
+ ref: editButtonRef,
742
+ ...editButtonNameProps
743
+ },
744
+ saveButtonProps: {
745
+ type: "submit",
746
+ disabled: isSaving
747
+ },
748
+ cancelButtonProps: {
749
+ type: "button",
750
+ onClick: requestCancel,
751
+ disabled: isSaving
752
+ },
753
+ status,
754
+ statusProps: {
755
+ role: "status",
756
+ "aria-live": "polite",
757
+ className: "sr-only"
758
+ },
759
+ confirmDiscard: {
760
+ open: confirmOpen,
761
+ onOpenChange: setConfirmOpen,
762
+ title: resolvedLabels.discardTitle,
763
+ description: resolvedLabels.discardDescription,
764
+ confirmLabel: resolvedLabels.discardConfirm,
765
+ cancelLabel: resolvedLabels.discardCancel,
766
+ onConfirm: exitToReading
767
+ },
768
+ resolvedLabels,
769
+ contentStyle: stableHeight && minHeightPx != null ? { minHeight: minHeightPx } : void 0,
770
+ actionsClassName: "flex min-h-9 min-w-[11rem] flex-wrap items-center justify-end gap-2"
771
+ };
772
+ }
773
+ function EditableSection({
774
+ readView,
775
+ editView,
776
+ children,
777
+ className,
778
+ ...options
779
+ }) {
780
+ const section = useEditableSection(options);
781
+ const {
782
+ isEditing,
783
+ isSaving,
784
+ formProps,
785
+ contentRef,
786
+ contentFormId,
787
+ editButtonProps,
788
+ saveButtonProps,
789
+ cancelButtonProps,
790
+ status,
791
+ statusProps,
792
+ confirmDiscard,
793
+ resolvedLabels,
794
+ contentStyle,
795
+ actionsClassName,
796
+ mode
797
+ } = section;
798
+ const actionButtons = !isEditing ? /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", ...editButtonProps, children: resolvedLabels.edit }) : /* @__PURE__ */ jsxs(Fragment, { children: [
799
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", ...cancelButtonProps, children: resolvedLabels.cancel }),
800
+ /* @__PURE__ */ jsx(
801
+ Button,
802
+ {
803
+ size: "sm",
804
+ type: "submit",
805
+ form: contentFormId,
806
+ disabled: saveButtonProps.disabled,
807
+ children: isSaving ? resolvedLabels.saving : resolvedLabels.save
808
+ }
809
+ )
810
+ ] });
811
+ const actions = /* @__PURE__ */ jsx("div", { "data-slot": "editable-section-actions", className: actionsClassName, children: actionButtons });
812
+ const content = isEditing ? /* @__PURE__ */ jsx(
813
+ "form",
814
+ {
815
+ id: contentFormId,
816
+ ref: contentRef,
817
+ "data-slot": "editable-section-content",
818
+ className: cn("yenio-editable-section-content space-y-4"),
819
+ style: contentStyle,
820
+ ...formProps,
821
+ children: editView()
822
+ }
823
+ ) : /* @__PURE__ */ jsx(
824
+ "div",
825
+ {
826
+ ref: contentRef,
827
+ "data-slot": "editable-section-content",
828
+ className: "yenio-editable-section-content",
829
+ style: contentStyle,
830
+ children: readView()
831
+ }
832
+ );
833
+ const statusNode = /* @__PURE__ */ jsx("span", { ...statusProps, "data-slot": "editable-section-status", children: status });
834
+ const dialog = /* @__PURE__ */ jsx(
835
+ ConfirmDestructiveDialog,
836
+ {
837
+ open: confirmDiscard.open,
838
+ onOpenChange: confirmDiscard.onOpenChange,
839
+ title: confirmDiscard.title,
840
+ description: confirmDiscard.description,
841
+ confirmLabel: confirmDiscard.confirmLabel,
842
+ cancelLabel: confirmDiscard.cancelLabel,
843
+ onConfirm: confirmDiscard.onConfirm
844
+ }
845
+ );
846
+ if (children) {
847
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
848
+ children({
849
+ actions,
850
+ content,
851
+ status: statusNode,
852
+ mode,
853
+ isSaving,
854
+ isEditing
855
+ }),
856
+ dialog
857
+ ] });
858
+ }
859
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "editable-section", className: cn("space-y-4", className), children: [
860
+ actions,
861
+ content,
862
+ statusNode,
863
+ dialog
864
+ ] });
865
+ }
866
+ function DataList({ className, ...props }) {
867
+ return /* @__PURE__ */ jsx(
868
+ "dl",
869
+ {
870
+ "data-slot": "data-list",
871
+ className: cn("grid gap-4 sm:grid-cols-2", className),
872
+ ...props
873
+ }
874
+ );
875
+ }
876
+ function DataListItem({
877
+ label,
878
+ children,
879
+ empty = "\u2014",
880
+ className,
881
+ labelClassName,
882
+ valueClassName
883
+ }) {
884
+ const isEmpty = children == null || children === false || typeof children === "string" && children.trim().length === 0;
885
+ return /* @__PURE__ */ jsxs(
886
+ "div",
887
+ {
888
+ "data-slot": "data-list-item",
889
+ className: cn("flex flex-col gap-1.5", className),
890
+ children: [
891
+ /* @__PURE__ */ jsx(
892
+ "dt",
893
+ {
894
+ className: cn(
895
+ "text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
896
+ labelClassName
897
+ ),
898
+ children: label
899
+ }
900
+ ),
901
+ /* @__PURE__ */ jsx(
902
+ "dd",
903
+ {
904
+ className: cn(
905
+ "text-sm wrap-break-word",
906
+ isEmpty && "text-muted-foreground",
907
+ valueClassName
908
+ ),
909
+ children: isEmpty ? empty : children
910
+ }
911
+ )
912
+ ]
913
+ }
914
+ );
915
+ }
916
+ function FieldMessage({
917
+ id,
918
+ children,
919
+ className,
920
+ ...props
921
+ }) {
922
+ const hasMessage = children != null && children !== false && !(typeof children === "string" && children.trim().length === 0);
923
+ return /* @__PURE__ */ jsx(
924
+ "p",
925
+ {
926
+ id,
927
+ role: hasMessage ? "alert" : void 0,
928
+ "data-slot": "field-message",
929
+ className: cn("min-h-5 text-xs text-destructive", className),
930
+ ...props,
931
+ children: hasMessage ? children : "\xA0"
932
+ }
933
+ );
934
+ }
935
+ function isBrokenImage(img) {
936
+ return img.complete && img.naturalWidth === 0;
937
+ }
938
+ function SafeImage({
939
+ src,
940
+ fallback,
941
+ alt = "",
942
+ onError,
943
+ ...imgProps
944
+ }) {
945
+ const [failed, setFailed] = useState(false);
946
+ const imgRef = useRef(null);
947
+ const resolvedSrc = typeof src === "string" && src.length > 0 ? src : null;
948
+ useLayoutEffect(() => {
949
+ setFailed(false);
950
+ }, [resolvedSrc]);
951
+ useLayoutEffect(() => {
952
+ const img = imgRef.current;
953
+ if (img && isBrokenImage(img)) {
954
+ setFailed(true);
955
+ }
956
+ }, [resolvedSrc, failed]);
957
+ if (!resolvedSrc || failed) {
958
+ return /* @__PURE__ */ jsx(Fragment, { children: fallback });
959
+ }
960
+ return /* @__PURE__ */ jsx(
961
+ "img",
962
+ {
963
+ ...imgProps,
964
+ ref: imgRef,
965
+ src: resolvedSrc,
966
+ alt,
967
+ onError: (event) => {
968
+ setFailed(true);
969
+ onError?.(event);
970
+ }
971
+ }
972
+ );
973
+ }
974
+ var defaultLabels = {
975
+ theme: "Theme",
976
+ system: "System",
977
+ light: "Light",
978
+ dark: "Dark"
979
+ };
980
+ var sizeClasses = {
981
+ sm: { button: "size-8", icon: "size-4" },
982
+ md: { button: "size-8", icon: "size-4" },
983
+ lg: { button: "size-9", icon: "size-4" }
984
+ };
985
+ var shapeClasses = {
986
+ rounded: "rounded-md",
987
+ pill: "rounded-full"
988
+ };
989
+ var iconTriggerClassName = "border border-border bg-transparent text-muted-foreground shadow-none hover:bg-accent hover:text-foreground dark:border-border dark:bg-transparent dark:text-muted-foreground dark:hover:bg-accent/50";
990
+ var themeIcons = {
991
+ system: Monitor,
992
+ light: Sun,
993
+ dark: Moon
994
+ };
995
+ var themes = [
996
+ { key: "system", icon: themeIcons.system },
997
+ { key: "light", icon: themeIcons.light },
998
+ { key: "dark", icon: themeIcons.dark }
999
+ ];
1000
+ function storedTheme(value) {
1001
+ if (value === "system" || value === "light" || value === "dark") return value;
1002
+ return void 0;
1003
+ }
1004
+ function ThemeSwitcher({
1005
+ className,
1006
+ size = "sm",
1007
+ shape = "rounded",
1008
+ align = "end",
1009
+ side = "bottom",
1010
+ labels: userLabels
1011
+ }) {
1012
+ const { theme, setTheme } = useTheme();
1013
+ const [mounted, setMounted] = useState(false);
1014
+ const sizes = sizeClasses[size];
1015
+ const shapeClass = shapeClasses[shape];
1016
+ const labels = { ...defaultLabels, ...userLabels };
1017
+ const handleThemeClick = useCallback(
1018
+ (themeKey) => {
1019
+ setTheme(themeKey);
1020
+ },
1021
+ [setTheme]
1022
+ );
1023
+ useEffect(() => {
1024
+ setMounted(true);
1025
+ }, []);
1026
+ if (!mounted) {
1027
+ return /* @__PURE__ */ jsx(
1028
+ "div",
1029
+ {
1030
+ "aria-hidden": true,
1031
+ className: cn(
1032
+ sizes.button,
1033
+ shapeClass,
1034
+ iconTriggerClassName,
1035
+ className
1036
+ )
1037
+ }
1038
+ );
1039
+ }
1040
+ const currentTheme = storedTheme(theme);
1041
+ const TriggerIcon = currentTheme ? themeIcons[currentTheme] : Monitor;
1042
+ return /* @__PURE__ */ jsxs(DropdownMenu, { modal: false, children: [
1043
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
1044
+ Button,
1045
+ {
1046
+ "data-testid": "theme-switcher",
1047
+ variant: "outline",
1048
+ size: "icon",
1049
+ "aria-label": labels.theme,
1050
+ className: cn(
1051
+ sizes.button,
1052
+ shapeClass,
1053
+ iconTriggerClassName,
1054
+ className
1055
+ ),
1056
+ children: /* @__PURE__ */ jsx(TriggerIcon, { className: sizes.icon })
1057
+ }
1058
+ ) }),
1059
+ /* @__PURE__ */ jsx(
1060
+ DropdownMenuContent,
1061
+ {
1062
+ side,
1063
+ align,
1064
+ sideOffset: miniMenuContentDefaults.sideOffset,
1065
+ collisionPadding: miniMenuContentDefaults.collisionPadding,
1066
+ className: miniMenuContentClassName,
1067
+ children: themes.map(({ key, icon: Icon }) => /* @__PURE__ */ jsxs(
1068
+ DropdownMenuItem,
1069
+ {
1070
+ onClick: () => handleThemeClick(key),
1071
+ className: cn(
1072
+ "gap-2 cursor-pointer",
1073
+ currentTheme === key && "bg-accent text-accent-foreground [&_svg]:text-accent-foreground"
1074
+ ),
1075
+ children: [
1076
+ /* @__PURE__ */ jsx(Icon, { className: "size-4" }),
1077
+ /* @__PURE__ */ jsx("span", { children: labels[key] })
1078
+ ]
1079
+ },
1080
+ key
1081
+ ))
1082
+ }
1083
+ )
1084
+ ] });
1085
+ }
1086
+ var defaultLanguages = [
1087
+ { key: "es-MX", label: "ES", nativeName: "Espa\xF1ol (M\xE9xico)" },
1088
+ { key: "en", label: "EN", nativeName: "English" }
1089
+ ];
1090
+ var sizeClasses2 = {
1091
+ sm: { button: "size-8", icon: "size-4" },
1092
+ md: { button: "size-8", icon: "size-4" },
1093
+ lg: { button: "size-9", icon: "size-4" }
1094
+ };
1095
+ var shapeClasses2 = {
1096
+ rounded: "rounded-md",
1097
+ squared: "rounded-sm",
1098
+ pill: "rounded-full"
1099
+ };
1100
+ var iconTriggerClassName2 = "border border-border bg-transparent text-muted-foreground shadow-none hover:bg-accent hover:text-foreground dark:border-border dark:bg-transparent dark:text-muted-foreground dark:hover:bg-accent/50";
1101
+ function LanguageSwitcher({
1102
+ languages: propLanguages,
1103
+ currentLanguage: propCurrentLanguage,
1104
+ onLanguageChange: propOnLanguageChange,
1105
+ labels = {},
1106
+ className,
1107
+ size = "sm",
1108
+ shape = "rounded",
1109
+ align = "end",
1110
+ side = "bottom"
1111
+ }) {
1112
+ const context = useLanguageContext();
1113
+ const contextLanguages = context?.languages?.map((lang) => ({
1114
+ key: lang.code,
1115
+ label: lang.code.toUpperCase(),
1116
+ nativeName: lang.name
1117
+ }));
1118
+ const languages = propLanguages ?? contextLanguages ?? defaultLanguages;
1119
+ const currentLanguage = propCurrentLanguage ?? context?.language ?? "en";
1120
+ const onLanguageChange = propOnLanguageChange ?? context?.setLanguage;
1121
+ const sizes = sizeClasses2[size];
1122
+ const shapeClass = shapeClasses2[shape];
1123
+ const accessibleName = labels.language ?? "Language";
1124
+ return /* @__PURE__ */ jsxs(DropdownMenu, { modal: false, children: [
1125
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
1126
+ Button,
1127
+ {
1128
+ variant: "outline",
1129
+ size: "icon",
1130
+ "aria-label": accessibleName,
1131
+ className: cn(
1132
+ sizes.button,
1133
+ shapeClass,
1134
+ iconTriggerClassName2,
1135
+ className
1136
+ ),
1137
+ children: /* @__PURE__ */ jsx(Languages, { className: sizes.icon })
1138
+ }
1139
+ ) }),
1140
+ /* @__PURE__ */ jsx(
1141
+ DropdownMenuContent,
1142
+ {
1143
+ side,
1144
+ align,
1145
+ sideOffset: miniMenuContentDefaults.sideOffset,
1146
+ collisionPadding: miniMenuContentDefaults.collisionPadding,
1147
+ className: cn(miniMenuContentClassName, "min-w-0 w-auto"),
1148
+ children: languages.map(({ key, nativeName }) => /* @__PURE__ */ jsx(
1149
+ DropdownMenuItem,
1150
+ {
1151
+ onClick: () => onLanguageChange?.(key),
1152
+ className: cn(
1153
+ "justify-center text-xs font-semibold cursor-pointer px-3",
1154
+ currentLanguage === key && "bg-accent text-accent-foreground"
1155
+ ),
1156
+ children: nativeName
1157
+ },
1158
+ key
1159
+ ))
1160
+ }
1161
+ )
1162
+ ] });
1163
+ }
1164
+ function Slider({
1165
+ className,
1166
+ defaultValue,
1167
+ value,
1168
+ min = 0,
1169
+ max = 100,
1170
+ ...props
1171
+ }) {
1172
+ const _values = React.useMemo(
1173
+ () => Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min, max],
1174
+ [value, defaultValue, min, max]
1175
+ );
1176
+ return /* @__PURE__ */ jsxs(
1177
+ SliderPrimitive.Root,
1178
+ {
1179
+ "data-slot": "slider",
1180
+ defaultValue,
1181
+ value,
1182
+ min,
1183
+ max,
1184
+ className: cn(
1185
+ "relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
1186
+ className
1187
+ ),
1188
+ ...props,
1189
+ children: [
1190
+ /* @__PURE__ */ jsx(
1191
+ SliderPrimitive.Track,
1192
+ {
1193
+ "data-slot": "slider-track",
1194
+ className: cn(
1195
+ "bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5"
1196
+ ),
1197
+ children: /* @__PURE__ */ jsx(
1198
+ SliderPrimitive.Range,
1199
+ {
1200
+ "data-slot": "slider-range",
1201
+ className: cn(
1202
+ "bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
1203
+ )
1204
+ }
1205
+ )
1206
+ }
1207
+ ),
1208
+ Array.from({ length: _values.length }, (_, index) => /* @__PURE__ */ jsx(
1209
+ SliderPrimitive.Thumb,
1210
+ {
1211
+ "data-slot": "slider-thumb",
1212
+ className: "border-primary ring-ring/50 block size-4 shrink-0 rounded-full border bg-white shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
1213
+ },
1214
+ index
1215
+ ))
1216
+ ]
1217
+ }
1218
+ );
1219
+ }
1220
+ var toggleVariants = cva(
1221
+ "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",
1222
+ {
1223
+ variants: {
1224
+ variant: {
1225
+ default: "bg-transparent",
1226
+ outline: "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground"
1227
+ },
1228
+ size: {
1229
+ default: "h-9 px-2 min-w-9",
1230
+ sm: "h-8 px-1.5 min-w-8",
1231
+ lg: "h-10 px-2.5 min-w-10"
1232
+ }
1233
+ },
1234
+ defaultVariants: {
1235
+ variant: "default",
1236
+ size: "default"
1237
+ }
1238
+ }
1239
+ );
1240
+ function Toggle({
1241
+ className,
1242
+ variant,
1243
+ size,
1244
+ ...props
1245
+ }) {
1246
+ return /* @__PURE__ */ jsx(
1247
+ TogglePrimitive.Root,
1248
+ {
1249
+ "data-slot": "toggle",
1250
+ className: cn(toggleVariants({ variant, size, className })),
1251
+ ...props
1252
+ }
1253
+ );
1254
+ }
1255
+ var DEFAULT_LABELS = {
1256
+ uploadAria: "Upload image",
1257
+ uploadHint: "Click to upload",
1258
+ uploadFormats: "PNG, JPG up to 10MB",
1259
+ dragging: "Dragging",
1260
+ zoomOut: "Zoom out",
1261
+ zoomIn: "Zoom in",
1262
+ zoomLevel: "Zoom level",
1263
+ rotateLeft: "Rotate left 15 degrees",
1264
+ rotateRight: "Rotate right 15 degrees",
1265
+ toggleGrid: "Toggle grid overlay",
1266
+ reset: "Reset all transforms",
1267
+ replace: "Upload new image",
1268
+ discard: "Discard image"
1269
+ };
1270
+ function AvatarEditor({
1271
+ value: _value,
1272
+ onChange,
1273
+ size = 280,
1274
+ showGrid: initialShowGrid = false,
1275
+ outputSize = 256,
1276
+ outputFormat = "png",
1277
+ outputQuality = 0.92,
1278
+ className,
1279
+ controlSize = "default",
1280
+ labels
1281
+ }) {
1282
+ const t = { ...DEFAULT_LABELS, ...labels };
1283
+ const canvasRef = useRef(null);
1284
+ const fileInputRef = useRef(null);
1285
+ const containerRef = useRef(null);
1286
+ const [image, setImage] = useState(null);
1287
+ const [imageLoaded, setImageLoaded] = useState(false);
1288
+ const [zoom, setZoom] = useState(1);
1289
+ const [rotation, setRotation] = useState(0);
1290
+ const [position, setPosition] = useState({ x: 0, y: 0 });
1291
+ const [isDragging, setIsDragging] = useState(false);
1292
+ const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
1293
+ const [showGrid, setShowGrid] = useState(initialShowGrid);
1294
+ const generateOutput = useCallback(() => {
1295
+ if (!canvasRef.current || !image) return null;
1296
+ const outputCanvas = document.createElement("canvas");
1297
+ outputCanvas.width = outputSize;
1298
+ outputCanvas.height = outputSize;
1299
+ const ctx = outputCanvas.getContext("2d");
1300
+ if (!ctx) return null;
1301
+ ctx.drawImage(canvasRef.current, 0, 0, outputSize, outputSize);
1302
+ const mimeType = `image/${outputFormat}`;
1303
+ return outputCanvas.toDataURL(mimeType, outputQuality);
1304
+ }, [image, outputSize, outputFormat, outputQuality]);
1305
+ useEffect(() => {
1306
+ if (!canvasRef.current || !image || !imageLoaded) return;
1307
+ const canvas = canvasRef.current;
1308
+ const ctx = canvas.getContext("2d");
1309
+ if (!ctx) return;
1310
+ const displaySize = size;
1311
+ canvas.width = displaySize;
1312
+ canvas.height = displaySize;
1313
+ ctx.clearRect(0, 0, displaySize, displaySize);
1314
+ ctx.save();
1315
+ ctx.translate(displaySize / 2, displaySize / 2);
1316
+ ctx.rotate(rotation * Math.PI / 180);
1317
+ const scale = zoom;
1318
+ const imgAspect = image.width / image.height;
1319
+ let drawWidth, drawHeight;
1320
+ if (imgAspect > 1) {
1321
+ drawHeight = displaySize * scale;
1322
+ drawWidth = drawHeight * imgAspect;
1323
+ } else {
1324
+ drawWidth = displaySize * scale;
1325
+ drawHeight = drawWidth / imgAspect;
1326
+ }
1327
+ ctx.drawImage(
1328
+ image,
1329
+ -drawWidth / 2 + position.x,
1330
+ -drawHeight / 2 + position.y,
1331
+ drawWidth,
1332
+ drawHeight
1333
+ );
1334
+ ctx.restore();
1335
+ const dataUrl = generateOutput();
1336
+ if (dataUrl) {
1337
+ onChange?.(dataUrl);
1338
+ }
1339
+ }, [
1340
+ image,
1341
+ imageLoaded,
1342
+ zoom,
1343
+ rotation,
1344
+ position,
1345
+ size,
1346
+ generateOutput,
1347
+ onChange
1348
+ ]);
1349
+ const resetTransforms = useCallback(() => {
1350
+ setZoom(1);
1351
+ setRotation(0);
1352
+ setPosition({ x: 0, y: 0 });
1353
+ }, []);
1354
+ const handleFileSelect = useCallback(
1355
+ (e) => {
1356
+ const file = e.target.files?.[0];
1357
+ if (!file) return;
1358
+ const reader = new FileReader();
1359
+ reader.onload = (event) => {
1360
+ const img = new Image();
1361
+ img.crossOrigin = "anonymous";
1362
+ img.onload = () => {
1363
+ setImage(img);
1364
+ setImageLoaded(true);
1365
+ resetTransforms();
1366
+ };
1367
+ img.src = event.target?.result;
1368
+ };
1369
+ reader.readAsDataURL(file);
1370
+ },
1371
+ [resetTransforms]
1372
+ );
1373
+ const handleMouseDown = useCallback(
1374
+ (e) => {
1375
+ if (!imageLoaded) return;
1376
+ setIsDragging(true);
1377
+ setDragStart({ x: e.clientX - position.x, y: e.clientY - position.y });
1378
+ },
1379
+ [imageLoaded, position]
1380
+ );
1381
+ const handleMouseMove = useCallback(
1382
+ (e) => {
1383
+ if (!isDragging) return;
1384
+ setPosition({
1385
+ x: e.clientX - dragStart.x,
1386
+ y: e.clientY - dragStart.y
1387
+ });
1388
+ },
1389
+ [isDragging, dragStart]
1390
+ );
1391
+ const handleMouseUp = useCallback(() => {
1392
+ setIsDragging(false);
1393
+ }, []);
1394
+ const handleTouchStart = useCallback(
1395
+ (e) => {
1396
+ if (!imageLoaded) return;
1397
+ const touch = e.touches[0];
1398
+ setIsDragging(true);
1399
+ setDragStart({
1400
+ x: touch.clientX - position.x,
1401
+ y: touch.clientY - position.y
1402
+ });
1403
+ },
1404
+ [imageLoaded, position]
1405
+ );
1406
+ const handleTouchMove = useCallback(
1407
+ (e) => {
1408
+ if (!isDragging) return;
1409
+ const touch = e.touches[0];
1410
+ setPosition({
1411
+ x: touch.clientX - dragStart.x,
1412
+ y: touch.clientY - dragStart.y
1413
+ });
1414
+ },
1415
+ [isDragging, dragStart]
1416
+ );
1417
+ const handleDiscard = useCallback(() => {
1418
+ setImage(null);
1419
+ setImageLoaded(false);
1420
+ resetTransforms();
1421
+ if (fileInputRef.current) {
1422
+ fileInputRef.current.value = "";
1423
+ }
1424
+ onChange?.(null);
1425
+ }, [resetTransforms, onChange]);
1426
+ const handleZoomIn = () => setZoom((z) => Math.min(z + 0.1, 3));
1427
+ const handleZoomOut = () => setZoom((z) => Math.max(z - 0.1, 0.5));
1428
+ const handleRotateLeft = () => setRotation((r) => r - 15);
1429
+ const handleRotateRight = () => setRotation((r) => r + 15);
1430
+ const isLarge = controlSize === "large";
1431
+ const buttonSize = isLarge ? "h-12 w-12" : "h-8 w-8";
1432
+ const iconSize = isLarge ? "w-6 h-6" : "w-4 h-4";
1433
+ const smallIconSize = isLarge ? "w-5 h-5" : "w-3 h-3";
1434
+ const textSize = isLarge ? "text-sm" : "text-xs";
1435
+ const uploadIconSize = isLarge ? "w-12 h-12" : "w-8 h-8";
1436
+ const uploadContainerSize = isLarge ? "w-24 h-24" : "w-16 h-16";
1437
+ const gapSize = isLarge ? "gap-6" : "gap-4";
1438
+ const controlGap = isLarge ? "gap-3" : "gap-2";
1439
+ const sliderHeight = isLarge ? "[&_[role=slider]]:h-6 [&_[role=slider]]:w-6" : "";
1440
+ const guideRadius = size / 2 - 2;
1441
+ const guideCenter = size / 2;
1442
+ return /* @__PURE__ */ jsxs(
1443
+ "div",
1444
+ {
1445
+ className: cn("flex flex-col", gapSize, className),
1446
+ style: { width: size },
1447
+ children: [
1448
+ /* @__PURE__ */ jsxs(
1449
+ "div",
1450
+ {
1451
+ ref: containerRef,
1452
+ className: "relative bg-muted rounded-2xl overflow-hidden",
1453
+ style: { width: size, height: size },
1454
+ children: [
1455
+ !imageLoaded && /* @__PURE__ */ jsxs(
1456
+ "button",
1457
+ {
1458
+ onClick: () => fileInputRef.current?.click(),
1459
+ className: cn(
1460
+ "absolute inset-0 z-10 flex flex-col items-center justify-center text-muted-foreground hover:text-foreground hover:bg-muted/80 transition-colors cursor-pointer",
1461
+ isLarge ? "gap-4" : "gap-3"
1462
+ ),
1463
+ "aria-label": t.uploadAria,
1464
+ type: "button",
1465
+ children: [
1466
+ /* @__PURE__ */ jsx(
1467
+ "div",
1468
+ {
1469
+ className: cn(
1470
+ "rounded-full bg-primary/10 flex items-center justify-center",
1471
+ uploadContainerSize
1472
+ ),
1473
+ children: /* @__PURE__ */ jsx(Upload, { className: cn("text-primary", uploadIconSize) })
1474
+ }
1475
+ ),
1476
+ /* @__PURE__ */ jsx(
1477
+ "span",
1478
+ {
1479
+ className: cn("font-medium", isLarge ? "text-base" : "text-sm"),
1480
+ children: t.uploadHint
1481
+ }
1482
+ ),
1483
+ /* @__PURE__ */ jsx("span", { className: cn("text-muted-foreground", textSize), children: t.uploadFormats })
1484
+ ]
1485
+ }
1486
+ ),
1487
+ /* @__PURE__ */ jsx(
1488
+ "canvas",
1489
+ {
1490
+ ref: canvasRef,
1491
+ className: cn(
1492
+ "absolute inset-0",
1493
+ imageLoaded ? "cursor-move" : "pointer-events-none opacity-0"
1494
+ ),
1495
+ style: { width: size, height: size },
1496
+ onMouseDown: handleMouseDown,
1497
+ onMouseMove: handleMouseMove,
1498
+ onMouseUp: handleMouseUp,
1499
+ onMouseLeave: handleMouseUp,
1500
+ onTouchStart: handleTouchStart,
1501
+ onTouchMove: handleTouchMove,
1502
+ onTouchEnd: handleMouseUp
1503
+ }
1504
+ ),
1505
+ imageLoaded && /* @__PURE__ */ jsxs(
1506
+ "svg",
1507
+ {
1508
+ width: size,
1509
+ height: size,
1510
+ className: "pointer-events-none absolute inset-0",
1511
+ "aria-hidden": "true",
1512
+ children: [
1513
+ /* @__PURE__ */ jsx(
1514
+ "path",
1515
+ {
1516
+ d: `M0 0H${size}V${size}H0Z M${guideCenter - guideRadius} ${guideCenter}a${guideRadius} ${guideRadius} 0 1 0 ${guideRadius * 2} 0a${guideRadius} ${guideRadius} 0 1 0 ${-guideRadius * 2} 0`,
1517
+ fill: "rgb(0 0 0 / 0.45)",
1518
+ fillRule: "evenodd"
1519
+ }
1520
+ ),
1521
+ /* @__PURE__ */ jsx(
1522
+ "circle",
1523
+ {
1524
+ cx: guideCenter,
1525
+ cy: guideCenter,
1526
+ r: guideRadius,
1527
+ fill: "none",
1528
+ stroke: "hsl(var(--primary))",
1529
+ strokeWidth: "3"
1530
+ }
1531
+ )
1532
+ ]
1533
+ }
1534
+ ),
1535
+ showGrid && imageLoaded && /* @__PURE__ */ jsx(
1536
+ "div",
1537
+ {
1538
+ className: "absolute inset-0 pointer-events-none",
1539
+ style: { width: size, height: size },
1540
+ children: /* @__PURE__ */ jsxs("svg", { width: size, height: size, className: "opacity-30", children: [
1541
+ /* @__PURE__ */ jsx(
1542
+ "line",
1543
+ {
1544
+ x1: size / 3,
1545
+ y1: 0,
1546
+ x2: size / 3,
1547
+ y2: size,
1548
+ stroke: "white",
1549
+ strokeWidth: "1"
1550
+ }
1551
+ ),
1552
+ /* @__PURE__ */ jsx(
1553
+ "line",
1554
+ {
1555
+ x1: size * 2 / 3,
1556
+ y1: 0,
1557
+ x2: size * 2 / 3,
1558
+ y2: size,
1559
+ stroke: "white",
1560
+ strokeWidth: "1"
1561
+ }
1562
+ ),
1563
+ /* @__PURE__ */ jsx(
1564
+ "line",
1565
+ {
1566
+ x1: 0,
1567
+ y1: size / 3,
1568
+ x2: size,
1569
+ y2: size / 3,
1570
+ stroke: "white",
1571
+ strokeWidth: "1"
1572
+ }
1573
+ ),
1574
+ /* @__PURE__ */ jsx(
1575
+ "line",
1576
+ {
1577
+ x1: 0,
1578
+ y1: size * 2 / 3,
1579
+ x2: size,
1580
+ y2: size * 2 / 3,
1581
+ stroke: "white",
1582
+ strokeWidth: "1"
1583
+ }
1584
+ ),
1585
+ /* @__PURE__ */ jsx(
1586
+ "line",
1587
+ {
1588
+ x1: size / 2 - 10,
1589
+ y1: size / 2,
1590
+ x2: size / 2 + 10,
1591
+ y2: size / 2,
1592
+ stroke: "white",
1593
+ strokeWidth: "1"
1594
+ }
1595
+ ),
1596
+ /* @__PURE__ */ jsx(
1597
+ "line",
1598
+ {
1599
+ x1: size / 2,
1600
+ y1: size / 2 - 10,
1601
+ x2: size / 2,
1602
+ y2: size / 2 + 10,
1603
+ stroke: "white",
1604
+ strokeWidth: "1"
1605
+ }
1606
+ )
1607
+ ] })
1608
+ }
1609
+ ),
1610
+ imageLoaded && isDragging && /* @__PURE__ */ jsxs(
1611
+ "div",
1612
+ {
1613
+ className: cn(
1614
+ "absolute top-2 left-1/2 -translate-x-1/2 bg-black/60 rounded text-white flex items-center",
1615
+ isLarge ? "px-3 py-2 text-sm gap-2" : "px-2 py-1 text-xs gap-1"
1616
+ ),
1617
+ children: [
1618
+ /* @__PURE__ */ jsx(Move, { className: smallIconSize }),
1619
+ t.dragging
1620
+ ]
1621
+ }
1622
+ )
1623
+ ]
1624
+ }
1625
+ ),
1626
+ imageLoaded && /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", isLarge && "space-y-5"), children: [
1627
+ /* @__PURE__ */ jsxs("div", { className: cn("flex items-center", controlGap), children: [
1628
+ /* @__PURE__ */ jsx(
1629
+ Button,
1630
+ {
1631
+ variant: "ghost",
1632
+ size: "icon",
1633
+ className: cn(buttonSize, "shrink-0"),
1634
+ onClick: handleZoomOut,
1635
+ "aria-label": t.zoomOut,
1636
+ type: "button",
1637
+ children: /* @__PURE__ */ jsx(ZoomOut, { className: iconSize })
1638
+ }
1639
+ ),
1640
+ /* @__PURE__ */ jsx(
1641
+ Slider,
1642
+ {
1643
+ value: [zoom],
1644
+ onValueChange: ([v]) => setZoom(v),
1645
+ min: 0.5,
1646
+ max: 3,
1647
+ step: 0.01,
1648
+ className: cn("flex-1", sliderHeight),
1649
+ "aria-label": t.zoomLevel
1650
+ }
1651
+ ),
1652
+ /* @__PURE__ */ jsx(
1653
+ Button,
1654
+ {
1655
+ variant: "ghost",
1656
+ size: "icon",
1657
+ className: cn(buttonSize, "shrink-0"),
1658
+ onClick: handleZoomIn,
1659
+ "aria-label": t.zoomIn,
1660
+ type: "button",
1661
+ children: /* @__PURE__ */ jsx(ZoomIn, { className: iconSize })
1662
+ }
1663
+ )
1664
+ ] }),
1665
+ /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between", controlGap), children: [
1666
+ /* @__PURE__ */ jsxs(
1667
+ "div",
1668
+ {
1669
+ className: cn("flex items-center", isLarge ? "gap-2" : "gap-1"),
1670
+ children: [
1671
+ /* @__PURE__ */ jsx(
1672
+ Button,
1673
+ {
1674
+ variant: "ghost",
1675
+ size: "icon",
1676
+ className: buttonSize,
1677
+ onClick: handleRotateLeft,
1678
+ "aria-label": t.rotateLeft,
1679
+ type: "button",
1680
+ children: /* @__PURE__ */ jsx(RotateCcw, { className: iconSize })
1681
+ }
1682
+ ),
1683
+ /* @__PURE__ */ jsxs(
1684
+ "span",
1685
+ {
1686
+ className: cn(
1687
+ "text-muted-foreground text-center tabular-nums",
1688
+ textSize,
1689
+ isLarge ? "w-16" : "w-12"
1690
+ ),
1691
+ children: [
1692
+ rotation,
1693
+ "\xB0"
1694
+ ]
1695
+ }
1696
+ ),
1697
+ /* @__PURE__ */ jsx(
1698
+ Button,
1699
+ {
1700
+ variant: "ghost",
1701
+ size: "icon",
1702
+ className: buttonSize,
1703
+ onClick: handleRotateRight,
1704
+ "aria-label": t.rotateRight,
1705
+ type: "button",
1706
+ children: /* @__PURE__ */ jsx(RotateCw, { className: iconSize })
1707
+ }
1708
+ )
1709
+ ]
1710
+ }
1711
+ ),
1712
+ /* @__PURE__ */ jsxs(
1713
+ "div",
1714
+ {
1715
+ className: cn("flex items-center", isLarge ? "gap-2" : "gap-1"),
1716
+ children: [
1717
+ /* @__PURE__ */ jsx(
1718
+ Toggle,
1719
+ {
1720
+ pressed: showGrid,
1721
+ onPressedChange: setShowGrid,
1722
+ size: "sm",
1723
+ className: cn(buttonSize, "p-0"),
1724
+ "aria-label": t.toggleGrid,
1725
+ children: /* @__PURE__ */ jsx(Grid3X3, { className: iconSize })
1726
+ }
1727
+ ),
1728
+ /* @__PURE__ */ jsx(
1729
+ Button,
1730
+ {
1731
+ variant: "ghost",
1732
+ size: "icon",
1733
+ className: buttonSize,
1734
+ onClick: resetTransforms,
1735
+ "aria-label": t.reset,
1736
+ type: "button",
1737
+ children: /* @__PURE__ */ jsx(RefreshCw, { className: iconSize })
1738
+ }
1739
+ ),
1740
+ /* @__PURE__ */ jsx(
1741
+ Button,
1742
+ {
1743
+ variant: "ghost",
1744
+ size: "icon",
1745
+ className: buttonSize,
1746
+ onClick: () => fileInputRef.current?.click(),
1747
+ "aria-label": t.replace,
1748
+ type: "button",
1749
+ children: /* @__PURE__ */ jsx(Upload, { className: iconSize })
1750
+ }
1751
+ ),
1752
+ /* @__PURE__ */ jsx(
1753
+ Button,
1754
+ {
1755
+ variant: "ghost",
1756
+ size: "icon",
1757
+ className: cn(
1758
+ buttonSize,
1759
+ "text-destructive hover:text-destructive hover:bg-destructive/10"
1760
+ ),
1761
+ onClick: handleDiscard,
1762
+ "aria-label": t.discard,
1763
+ type: "button",
1764
+ children: /* @__PURE__ */ jsx(X, { className: iconSize })
1765
+ }
1766
+ )
1767
+ ]
1768
+ }
1769
+ )
1770
+ ] })
1771
+ ] }),
1772
+ /* @__PURE__ */ jsx(
1773
+ "input",
1774
+ {
1775
+ ref: fileInputRef,
1776
+ type: "file",
1777
+ accept: "image/*",
1778
+ onChange: handleFileSelect,
1779
+ className: "hidden",
1780
+ "aria-hidden": "true"
1781
+ }
1782
+ )
1783
+ ]
1784
+ }
1785
+ );
1786
+ }
1787
+ function Dialog({
1788
+ ...props
1789
+ }) {
1790
+ return /* @__PURE__ */ jsx(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
1791
+ }
1792
+ function DialogPortal({
1793
+ ...props
1794
+ }) {
1795
+ return /* @__PURE__ */ jsx(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
1796
+ }
1797
+ function DialogClose({
1798
+ ...props
1799
+ }) {
1800
+ return /* @__PURE__ */ jsx(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
1801
+ }
1802
+ function DialogOverlay({
1803
+ className,
1804
+ ...props
1805
+ }) {
1806
+ return /* @__PURE__ */ jsx(
1807
+ DialogPrimitive.Overlay,
1808
+ {
1809
+ "data-slot": "dialog-overlay",
1810
+ className: cn(
1811
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
1812
+ className
1813
+ ),
1814
+ ...props
1815
+ }
1816
+ );
1817
+ }
1818
+ function DialogContent({
1819
+ className,
1820
+ children,
1821
+ showCloseButton = true,
1822
+ ...props
1823
+ }) {
1824
+ return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
1825
+ /* @__PURE__ */ jsx(DialogOverlay, {}),
1826
+ /* @__PURE__ */ jsxs(
1827
+ DialogPrimitive.Content,
1828
+ {
1829
+ "data-slot": "dialog-content",
1830
+ className: cn(
1831
+ "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
1832
+ className
1833
+ ),
1834
+ ...props,
1835
+ children: [
1836
+ children,
1837
+ showCloseButton && /* @__PURE__ */ jsxs(
1838
+ DialogPrimitive.Close,
1839
+ {
1840
+ "data-slot": "dialog-close",
1841
+ className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1842
+ children: [
1843
+ /* @__PURE__ */ jsx(XIcon, {}),
1844
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
1845
+ ]
1846
+ }
1847
+ )
1848
+ ]
1849
+ }
1850
+ )
1851
+ ] });
1852
+ }
1853
+ function DialogHeader({ className, ...props }) {
1854
+ return /* @__PURE__ */ jsx(
1855
+ "div",
1856
+ {
1857
+ "data-slot": "dialog-header",
1858
+ className: cn("flex flex-col gap-2 text-center sm:text-left", className),
1859
+ ...props
1860
+ }
1861
+ );
1862
+ }
1863
+ function DialogFooter({ className, ...props }) {
1864
+ return /* @__PURE__ */ jsx(
1865
+ "div",
1866
+ {
1867
+ "data-slot": "dialog-footer",
1868
+ className: cn(
1869
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1870
+ className
1871
+ ),
1872
+ ...props
1873
+ }
1874
+ );
1875
+ }
1876
+ function DialogTitle({
1877
+ className,
1878
+ ...props
1879
+ }) {
1880
+ return /* @__PURE__ */ jsx(
1881
+ DialogPrimitive.Title,
1882
+ {
1883
+ "data-slot": "dialog-title",
1884
+ className: cn("text-lg leading-none font-semibold", className),
1885
+ ...props
1886
+ }
1887
+ );
1888
+ }
1889
+ function Drawer({
1890
+ ...props
1891
+ }) {
1892
+ return /* @__PURE__ */ jsx(Drawer$1.Root, { "data-slot": "drawer", ...props });
1893
+ }
1894
+ function DrawerPortal({
1895
+ ...props
1896
+ }) {
1897
+ return /* @__PURE__ */ jsx(Drawer$1.Portal, { "data-slot": "drawer-portal", ...props });
1898
+ }
1899
+ function DrawerClose({
1900
+ ...props
1901
+ }) {
1902
+ return /* @__PURE__ */ jsx(Drawer$1.Close, { "data-slot": "drawer-close", ...props });
1903
+ }
1904
+ function DrawerOverlay({
1905
+ className,
1906
+ ...props
1907
+ }) {
1908
+ return /* @__PURE__ */ jsx(
1909
+ Drawer$1.Overlay,
1910
+ {
1911
+ "data-slot": "drawer-overlay",
1912
+ className: cn(
1913
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
1914
+ className
1915
+ ),
1916
+ ...props
1917
+ }
1918
+ );
1919
+ }
1920
+ function DrawerContent({
1921
+ className,
1922
+ children,
1923
+ ...props
1924
+ }) {
1925
+ return /* @__PURE__ */ jsxs(DrawerPortal, { "data-slot": "drawer-portal", children: [
1926
+ /* @__PURE__ */ jsx(DrawerOverlay, {}),
1927
+ /* @__PURE__ */ jsxs(
1928
+ Drawer$1.Content,
1929
+ {
1930
+ "data-slot": "drawer-content",
1931
+ className: cn(
1932
+ "group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
1933
+ "data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
1934
+ "data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t",
1935
+ "data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
1936
+ "data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
1937
+ className
1938
+ ),
1939
+ ...props,
1940
+ children: [
1941
+ /* @__PURE__ */ jsx("div", { className: "bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }),
1942
+ children
1943
+ ]
1944
+ }
1945
+ )
1946
+ ] });
1947
+ }
1948
+ function DrawerHeader({ className, ...props }) {
1949
+ return /* @__PURE__ */ jsx(
1950
+ "div",
1951
+ {
1952
+ "data-slot": "drawer-header",
1953
+ className: cn(
1954
+ "flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left",
1955
+ className
1956
+ ),
1957
+ ...props
1958
+ }
1959
+ );
1960
+ }
1961
+ function DrawerFooter({ className, ...props }) {
1962
+ return /* @__PURE__ */ jsx(
1963
+ "div",
1964
+ {
1965
+ "data-slot": "drawer-footer",
1966
+ className: cn("mt-auto flex flex-col gap-2 p-4", className),
1967
+ ...props
1968
+ }
1969
+ );
1970
+ }
1971
+ function DrawerTitle({
1972
+ className,
1973
+ ...props
1974
+ }) {
1975
+ return /* @__PURE__ */ jsx(
1976
+ Drawer$1.Title,
1977
+ {
1978
+ "data-slot": "drawer-title",
1979
+ className: cn("text-foreground font-semibold", className),
1980
+ ...props
1981
+ }
1982
+ );
1983
+ }
1984
+ function useMediaQuery(query) {
1985
+ const [matches, setMatches] = useState(false);
1986
+ useEffect(() => {
1987
+ const media = window.matchMedia(query);
1988
+ if (media.matches !== matches) {
1989
+ setMatches(media.matches);
1990
+ }
1991
+ const listener = () => setMatches(media.matches);
1992
+ media.addEventListener("change", listener);
1993
+ return () => media.removeEventListener("change", listener);
1994
+ }, [matches, query]);
1995
+ return matches;
1996
+ }
1997
+ function AvatarEditorDialog({
1998
+ value,
1999
+ onChange,
2000
+ onSave,
2001
+ displaySize = 120,
2002
+ editorSize = 280,
2003
+ outputSize = 256,
2004
+ placeholder = "Add Photo",
2005
+ editLabel = "Edit avatar",
2006
+ dialogTitle = "Edit Avatar",
2007
+ acceptText = "Accept",
2008
+ cancelText = "Cancel",
2009
+ savingText = "Saving...",
2010
+ savedText = "Saved!",
2011
+ successMessage = "Avatar saved successfully!",
2012
+ errorMessage = "Failed to save avatar. Please try again.",
2013
+ className,
2014
+ editorLabels
2015
+ }) {
2016
+ const [isOpen, setIsOpen] = useState(false);
2017
+ const [editedValue, setEditedValue] = useState(value ?? null);
2018
+ const [displaySrc, setDisplaySrc] = useState(value ?? null);
2019
+ const [isSaving, setIsSaving] = useState(false);
2020
+ const [feedback, setFeedback] = useState(null);
2021
+ useEffect(() => {
2022
+ setDisplaySrc(value ?? null);
2023
+ }, [value]);
2024
+ const isMobile = useMediaQuery("(max-width: 640px)");
2025
+ const caption = (placeholder ?? "").trim();
2026
+ const handleOpenChange = useCallback(
2027
+ (open) => {
2028
+ if (open) {
2029
+ setEditedValue(value ?? null);
2030
+ setFeedback(null);
2031
+ }
2032
+ setIsOpen(open);
2033
+ },
2034
+ [value]
2035
+ );
2036
+ const handleAccept = useCallback(async () => {
2037
+ if (!editedValue) return;
2038
+ setIsSaving(true);
2039
+ setFeedback(null);
2040
+ try {
2041
+ if (onSave) {
2042
+ const result = await onSave(editedValue);
2043
+ if (result) {
2044
+ setDisplaySrc(editedValue);
2045
+ onChange?.(editedValue);
2046
+ setFeedback({ type: "success", message: successMessage });
2047
+ setTimeout(() => {
2048
+ setIsOpen(false);
2049
+ setFeedback(null);
2050
+ }, 1500);
2051
+ } else {
2052
+ setFeedback({ type: "error", message: errorMessage });
2053
+ }
2054
+ } else {
2055
+ setDisplaySrc(editedValue);
2056
+ onChange?.(editedValue);
2057
+ setFeedback({ type: "success", message: successMessage });
2058
+ setTimeout(() => {
2059
+ setIsOpen(false);
2060
+ setFeedback(null);
2061
+ }, 1500);
2062
+ }
2063
+ } catch {
2064
+ setFeedback({ type: "error", message: errorMessage });
2065
+ } finally {
2066
+ setIsSaving(false);
2067
+ }
2068
+ }, [editedValue, onChange, onSave, successMessage, errorMessage]);
2069
+ const AvatarDisplay = /* @__PURE__ */ jsxs(
2070
+ "div",
2071
+ {
2072
+ className: cn("relative group", className),
2073
+ style: { width: displaySize, height: displaySize },
2074
+ children: [
2075
+ /* @__PURE__ */ jsx(
2076
+ "div",
2077
+ {
2078
+ className: "relative w-full h-full rounded-full overflow-hidden bg-muted border-2 border-border flex items-center justify-center",
2079
+ style: { width: displaySize, height: displaySize },
2080
+ children: /* @__PURE__ */ jsx(
2081
+ SafeImage,
2082
+ {
2083
+ src: displaySrc,
2084
+ alt: "Avatar",
2085
+ className: "absolute inset-0 h-full w-full object-cover",
2086
+ fallback: /* @__PURE__ */ jsxs(
2087
+ "div",
2088
+ {
2089
+ className: cn(
2090
+ "flex flex-col items-center justify-center text-muted-foreground",
2091
+ caption ? "gap-1" : "gap-0"
2092
+ ),
2093
+ children: [
2094
+ /* @__PURE__ */ jsx(
2095
+ User,
2096
+ {
2097
+ className: "h-1/2 w-1/2 max-h-[55%] max-w-[55%] shrink-0",
2098
+ "aria-hidden": true
2099
+ }
2100
+ ),
2101
+ caption ? /* @__PURE__ */ jsx("span", { className: "text-xs text-center px-1", children: caption }) : null
2102
+ ]
2103
+ }
2104
+ )
2105
+ }
2106
+ )
2107
+ }
2108
+ ),
2109
+ /* @__PURE__ */ jsx(
2110
+ "button",
2111
+ {
2112
+ onClick: () => handleOpenChange(true),
2113
+ className: "absolute bottom-0 right-0 w-8 h-8 rounded-full bg-primary text-primary-foreground flex items-center justify-center shadow-lg hover:bg-primary/90 transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2",
2114
+ "aria-label": editLabel,
2115
+ type: "button",
2116
+ children: /* @__PURE__ */ jsx(Pencil, { className: "w-4 h-4" })
2117
+ }
2118
+ )
2119
+ ]
2120
+ }
2121
+ );
2122
+ const mobileEditorSize = isMobile ? Math.min(editorSize + 40, window.innerWidth - 48) : editorSize;
2123
+ const EditorContent = /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4", children: [
2124
+ /* @__PURE__ */ jsx(
2125
+ AvatarEditor,
2126
+ {
2127
+ value: editedValue,
2128
+ onChange: setEditedValue,
2129
+ size: mobileEditorSize,
2130
+ outputSize,
2131
+ controlSize: isMobile ? "large" : "default",
2132
+ labels: editorLabels
2133
+ }
2134
+ ),
2135
+ /* @__PURE__ */ jsx(AnimatePresence, { children: feedback && /* @__PURE__ */ jsxs(
2136
+ motion.div,
2137
+ {
2138
+ initial: { opacity: 0, y: -10 },
2139
+ animate: { opacity: 1, y: 0 },
2140
+ exit: { opacity: 0, y: -10 },
2141
+ className: cn(
2142
+ "flex items-center gap-2 rounded-lg font-medium",
2143
+ isMobile ? "px-5 py-3 text-base" : "px-4 py-2 text-sm",
2144
+ feedback.type === "success" ? "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400" : "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400"
2145
+ ),
2146
+ children: [
2147
+ feedback.type === "success" ? /* @__PURE__ */ jsx(Check, { className: isMobile ? "w-5 h-5" : "w-4 h-4" }) : /* @__PURE__ */ jsx(X, { className: isMobile ? "w-5 h-5" : "w-4 h-4" }),
2148
+ feedback.message
2149
+ ]
2150
+ }
2151
+ ) })
2152
+ ] });
2153
+ const FooterButtons = /* @__PURE__ */ jsxs(Fragment, { children: [
2154
+ isMobile ? /* @__PURE__ */ jsx(DrawerClose, { asChild: true, children: /* @__PURE__ */ jsx(
2155
+ Button,
2156
+ {
2157
+ variant: "outline",
2158
+ disabled: isSaving,
2159
+ size: "lg",
2160
+ className: "flex-1 bg-transparent",
2161
+ children: cancelText
2162
+ }
2163
+ ) }) : /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", disabled: isSaving, children: cancelText }) }),
2164
+ /* @__PURE__ */ jsx(
2165
+ Button,
2166
+ {
2167
+ onClick: handleAccept,
2168
+ disabled: !editedValue || isSaving || feedback?.type === "success",
2169
+ size: isMobile ? "lg" : "default",
2170
+ className: isMobile ? "flex-1" : "",
2171
+ children: isSaving ? /* @__PURE__ */ jsxs(Fragment, { children: [
2172
+ /* @__PURE__ */ jsx(
2173
+ Loader2,
2174
+ {
2175
+ className: cn(
2176
+ "mr-2 animate-spin",
2177
+ isMobile ? "w-5 h-5" : "w-4 h-4"
2178
+ )
2179
+ }
2180
+ ),
2181
+ savingText
2182
+ ] }) : feedback?.type === "success" ? /* @__PURE__ */ jsxs(Fragment, { children: [
2183
+ /* @__PURE__ */ jsx(Check, { className: cn("mr-2", isMobile ? "w-5 h-5" : "w-4 h-4") }),
2184
+ savedText
2185
+ ] }) : acceptText
2186
+ }
2187
+ )
2188
+ ] });
2189
+ if (isMobile) {
2190
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2191
+ AvatarDisplay,
2192
+ /* @__PURE__ */ jsx(Drawer, { open: isOpen, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxs(DrawerContent, { className: "max-h-[96vh]", children: [
2193
+ /* @__PURE__ */ jsx(DrawerHeader, { className: "text-center", children: /* @__PURE__ */ jsx(DrawerTitle, { className: "text-xl", children: dialogTitle }) }),
2194
+ /* @__PURE__ */ jsx("div", { className: "px-6 pb-6 overflow-y-auto", children: EditorContent }),
2195
+ /* @__PURE__ */ jsx(DrawerFooter, { className: "flex-row gap-3 px-6 pb-8", children: FooterButtons })
2196
+ ] }) })
2197
+ ] });
2198
+ }
2199
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2200
+ AvatarDisplay,
2201
+ /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
2202
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: dialogTitle }) }),
2203
+ /* @__PURE__ */ jsx("div", { className: "flex justify-center py-4", children: EditorContent }),
2204
+ /* @__PURE__ */ jsx(DialogFooter, { className: "sm:gap-3", children: FooterButtons })
2205
+ ] }) })
2206
+ ] });
2207
+ }
2208
+
2209
+ // src/components/upload/part-math.ts
2210
+ function partRanges(fileSize, partSize) {
2211
+ if (partSize <= 0) {
2212
+ throw new Error("partSize must be greater than 0");
2213
+ }
2214
+ if (fileSize <= 0) {
2215
+ return [{ partNumber: 1, start: 0, end: 0 }];
2216
+ }
2217
+ const count = Math.ceil(fileSize / partSize);
2218
+ return Array.from({ length: count }, (_, index) => {
2219
+ const start = index * partSize;
2220
+ return {
2221
+ partNumber: index + 1,
2222
+ start,
2223
+ end: Math.min(fileSize, start + partSize)
2224
+ };
2225
+ });
2226
+ }
2227
+ var BYTE_UNITS = ["B", "KB", "MB", "GB", "TB"];
2228
+ function formatUploadBytes(bytes) {
2229
+ if (!Number.isFinite(bytes) || bytes <= 0) {
2230
+ return "0 B";
2231
+ }
2232
+ let value = bytes;
2233
+ let unit = 0;
2234
+ while (value >= 1024 && unit < BYTE_UNITS.length - 1) {
2235
+ value /= 1024;
2236
+ unit += 1;
2237
+ }
2238
+ if (unit === 0 || value >= 10) {
2239
+ return `${Math.round(value)} ${BYTE_UNITS[unit]}`;
2240
+ }
2241
+ const tenths = Math.round(value * 10) / 10;
2242
+ const label = Number.isInteger(tenths) ? String(tenths) : tenths.toFixed(1);
2243
+ return `${label} ${BYTE_UNITS[unit]}`;
2244
+ }
2245
+ function progressFromBytes(loaded, total) {
2246
+ const safeTotal = Math.max(0, total);
2247
+ const safeLoaded = Math.min(Math.max(0, loaded), safeTotal);
2248
+ const percent = safeTotal > 0 ? Math.round(safeLoaded / safeTotal * 100) : 0;
2249
+ return { loaded: safeLoaded, total: safeTotal, percent };
2250
+ }
2251
+ function fileMatchesAccept(file, accept) {
2252
+ if (!accept?.trim()) return true;
2253
+ const type = file.type.toLowerCase();
2254
+ const name = file.name.toLowerCase();
2255
+ return accept.split(",").some((token) => {
2256
+ const trimmed = token.trim().toLowerCase();
2257
+ if (!trimmed) return false;
2258
+ if (trimmed.endsWith("/*")) {
2259
+ return type.startsWith(trimmed.slice(0, -1));
2260
+ }
2261
+ if (trimmed.startsWith(".")) {
2262
+ return name.endsWith(trimmed);
2263
+ }
2264
+ return type === trimmed;
2265
+ });
2266
+ }
2267
+
2268
+ // src/components/upload/types.ts
2269
+ var DEFAULT_FILE_DROPZONE_LABELS = {
2270
+ idle: "Drop a file here or browse",
2271
+ hint: "Click or press Enter to choose a file",
2272
+ browse: "Browse",
2273
+ rejectType: "This file type is not allowed",
2274
+ rejectSize: "This file is too large"
2275
+ };
2276
+ var DEFAULT_FILE_UPLOAD_TRAY_LABELS = {
2277
+ title: "Uploads",
2278
+ pause: "Pause",
2279
+ resume: "Resume",
2280
+ cancel: "Cancel",
2281
+ uploading: "Uploading",
2282
+ paused: "Paused",
2283
+ completing: "Finishing",
2284
+ completed: "Uploaded",
2285
+ cancelled: "Cancelled",
2286
+ error: "Failed"
2287
+ };
2288
+ var DEFAULT_FILE_UPLOAD_LABELS = {
2289
+ dropzone: DEFAULT_FILE_DROPZONE_LABELS,
2290
+ tray: DEFAULT_FILE_UPLOAD_TRAY_LABELS
2291
+ };
2292
+ function FileDropzone({
2293
+ accept,
2294
+ maxSize,
2295
+ disabled = false,
2296
+ labels: labelOverrides,
2297
+ onFile,
2298
+ onReject,
2299
+ className
2300
+ }) {
2301
+ const labels = { ...DEFAULT_FILE_DROPZONE_LABELS, ...labelOverrides };
2302
+ const inputId = useId();
2303
+ const inputRef = useRef(null);
2304
+ const [dragging, setDragging] = useState(false);
2305
+ const [message, setMessage] = useState(null);
2306
+ function takeFile(file) {
2307
+ if (!file || disabled) return;
2308
+ if (!fileMatchesAccept(file, accept)) {
2309
+ setMessage(labels.rejectType);
2310
+ onReject?.("type");
2311
+ return;
2312
+ }
2313
+ if (maxSize != null && file.size > maxSize) {
2314
+ setMessage(labels.rejectSize);
2315
+ onReject?.("size");
2316
+ return;
2317
+ }
2318
+ setMessage(null);
2319
+ onFile(file);
2320
+ }
2321
+ function onDrop(event) {
2322
+ event.preventDefault();
2323
+ setDragging(false);
2324
+ takeFile(event.dataTransfer.files[0]);
2325
+ }
2326
+ function onKeyDown(event) {
2327
+ if (disabled) return;
2328
+ if (event.key === "Enter" || event.key === " ") {
2329
+ event.preventDefault();
2330
+ inputRef.current?.click();
2331
+ }
2332
+ }
2333
+ return /* @__PURE__ */ jsxs(
2334
+ "div",
2335
+ {
2336
+ className: cn("relative flex w-full min-w-0 flex-col gap-2", className),
2337
+ children: [
2338
+ /* @__PURE__ */ jsxs(
2339
+ "div",
2340
+ {
2341
+ role: "button",
2342
+ tabIndex: disabled ? -1 : 0,
2343
+ "aria-disabled": disabled,
2344
+ "aria-describedby": message ? `${inputId}-message` : `${inputId}-hint`,
2345
+ "aria-controls": inputId,
2346
+ "data-dragging": dragging || void 0,
2347
+ className: cn(
2348
+ "border-input bg-background focus-visible:border-ring focus-visible:ring-ring/50 w-full min-w-0 rounded-lg border border-dashed px-4 py-8 text-center outline-none focus-visible:ring-[3px]",
2349
+ dragging && "border-primary bg-primary/5",
2350
+ disabled && "pointer-events-none opacity-50"
2351
+ ),
2352
+ onClick: () => {
2353
+ if (!disabled) inputRef.current?.click();
2354
+ },
2355
+ onKeyDown,
2356
+ onDragEnter: (event) => {
2357
+ event.preventDefault();
2358
+ if (!disabled) setDragging(true);
2359
+ },
2360
+ onDragOver: (event) => {
2361
+ event.preventDefault();
2362
+ },
2363
+ onDragLeave: (event) => {
2364
+ if (event.currentTarget.contains(event.relatedTarget)) {
2365
+ return;
2366
+ }
2367
+ setDragging(false);
2368
+ },
2369
+ onDrop,
2370
+ children: [
2371
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: labels.idle }),
2372
+ /* @__PURE__ */ jsx(
2373
+ "p",
2374
+ {
2375
+ id: `${inputId}-hint`,
2376
+ className: "text-muted-foreground mt-1 text-xs",
2377
+ children: labels.hint
2378
+ }
2379
+ ),
2380
+ /* @__PURE__ */ jsx("span", { className: "text-primary mt-3 inline-flex text-sm font-medium underline-offset-4", children: labels.browse })
2381
+ ]
2382
+ }
2383
+ ),
2384
+ /* @__PURE__ */ jsx(
2385
+ "input",
2386
+ {
2387
+ id: inputId,
2388
+ ref: inputRef,
2389
+ type: "file",
2390
+ accept,
2391
+ disabled,
2392
+ "aria-label": labels.browse,
2393
+ className: "sr-only",
2394
+ onChange: (event) => {
2395
+ takeFile(event.target.files?.[0]);
2396
+ event.target.value = "";
2397
+ }
2398
+ }
2399
+ ),
2400
+ message ? /* @__PURE__ */ jsx(
2401
+ "p",
2402
+ {
2403
+ id: `${inputId}-message`,
2404
+ role: "alert",
2405
+ className: "text-destructive text-sm",
2406
+ children: message
2407
+ }
2408
+ ) : null
2409
+ ]
2410
+ }
2411
+ );
2412
+ }
2413
+ function statusLabel(status, labels) {
2414
+ switch (status) {
2415
+ case "uploading":
2416
+ return labels.uploading;
2417
+ case "paused":
2418
+ return labels.paused;
2419
+ case "completing":
2420
+ return labels.completing;
2421
+ case "completed":
2422
+ return labels.completed;
2423
+ case "cancelled":
2424
+ return labels.cancelled;
2425
+ case "error":
2426
+ return labels.error;
2427
+ default: {
2428
+ const _exhaustive = status;
2429
+ return _exhaustive;
2430
+ }
2431
+ }
2432
+ }
2433
+ function progressLabel(job) {
2434
+ return `${job.percent}% \xB7 ${formatUploadBytes(job.loaded)} / ${formatUploadBytes(job.total)}`;
2435
+ }
2436
+ function FileUploadTray({
2437
+ jobs,
2438
+ labels: labelOverrides,
2439
+ onPause,
2440
+ onResume,
2441
+ onCancel,
2442
+ className
2443
+ }) {
2444
+ const labels = { ...DEFAULT_FILE_UPLOAD_TRAY_LABELS, ...labelOverrides };
2445
+ const visible = jobs.filter((job) => job.status !== "cancelled");
2446
+ if (visible.length === 0) return null;
2447
+ return /* @__PURE__ */ jsx(
2448
+ "section",
2449
+ {
2450
+ "aria-label": labels.title,
2451
+ className: cn("grid min-w-0 gap-3", className),
2452
+ children: /* @__PURE__ */ jsx("ul", { className: "grid min-w-0 gap-3", children: visible.map((job) => {
2453
+ const active = job.status === "uploading" || job.status === "completing";
2454
+ const canPause = job.status === "uploading";
2455
+ const canResume = job.status === "paused";
2456
+ const canCancel = job.status === "uploading" || job.status === "paused" || job.status === "error";
2457
+ return /* @__PURE__ */ jsxs("li", { className: "grid min-w-0 gap-1.5", children: [
2458
+ /* @__PURE__ */ jsx(
2459
+ "p",
2460
+ {
2461
+ className: "min-w-0 line-clamp-3 text-sm leading-5 font-medium break-all",
2462
+ title: job.name,
2463
+ children: job.name
2464
+ }
2465
+ ),
2466
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
2467
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground shrink-0 text-xs", children: statusLabel(job.status, labels) }),
2468
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground shrink-0 text-xs tabular-nums", children: progressLabel(job) }),
2469
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1" }),
2470
+ canPause ? /* @__PURE__ */ jsx(
2471
+ Button,
2472
+ {
2473
+ type: "button",
2474
+ variant: "ghost",
2475
+ size: "icon-sm",
2476
+ className: "shrink-0",
2477
+ "aria-label": `${labels.pause} ${job.name}`,
2478
+ onClick: () => onPause(job.id),
2479
+ children: /* @__PURE__ */ jsx(Pause, {})
2480
+ }
2481
+ ) : null,
2482
+ canResume ? /* @__PURE__ */ jsx(
2483
+ Button,
2484
+ {
2485
+ type: "button",
2486
+ variant: "ghost",
2487
+ size: "icon-sm",
2488
+ className: "shrink-0",
2489
+ "aria-label": `${labels.resume} ${job.name}`,
2490
+ onClick: () => onResume(job.id),
2491
+ children: /* @__PURE__ */ jsx(Play, {})
2492
+ }
2493
+ ) : null,
2494
+ canCancel ? /* @__PURE__ */ jsx(
2495
+ Button,
2496
+ {
2497
+ type: "button",
2498
+ variant: "ghost",
2499
+ size: "icon-sm",
2500
+ className: "shrink-0",
2501
+ "aria-label": `${labels.cancel} ${job.name}`,
2502
+ onClick: () => onCancel(job.id),
2503
+ children: /* @__PURE__ */ jsx(X, {})
2504
+ }
2505
+ ) : null
2506
+ ] }),
2507
+ /* @__PURE__ */ jsx(
2508
+ "div",
2509
+ {
2510
+ role: "progressbar",
2511
+ "aria-valuemin": 0,
2512
+ "aria-valuemax": 100,
2513
+ "aria-valuenow": job.percent,
2514
+ "aria-label": job.name,
2515
+ className: "bg-muted h-2 overflow-hidden rounded-full",
2516
+ children: /* @__PURE__ */ jsx(
2517
+ "div",
2518
+ {
2519
+ className: cn(
2520
+ "bg-primary h-full",
2521
+ active && "motion-safe:transition-[width]"
2522
+ ),
2523
+ style: { width: `${job.percent}%` }
2524
+ }
2525
+ )
2526
+ }
2527
+ ),
2528
+ job.error ? /* @__PURE__ */ jsx("p", { role: "alert", className: "text-destructive text-xs", children: job.error }) : null
2529
+ ] }, job.id);
2530
+ }) })
2531
+ }
2532
+ );
2533
+ }
2534
+ function formatActiveBadge(count) {
2535
+ return count > 9 ? "9+" : String(count);
2536
+ }
2537
+ function isActiveStatus(status) {
2538
+ return status === "uploading" || status === "paused" || status === "completing";
2539
+ }
2540
+ function FileUploadMenu({
2541
+ jobs,
2542
+ labels: labelOverrides,
2543
+ onPause,
2544
+ onResume,
2545
+ onCancel,
2546
+ open,
2547
+ onOpenChange,
2548
+ align = "end",
2549
+ className
2550
+ }) {
2551
+ const labels = { ...DEFAULT_FILE_UPLOAD_TRAY_LABELS, ...labelOverrides };
2552
+ const visible = jobs.filter((job) => job.status !== "cancelled");
2553
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
2554
+ const isControlled = open !== void 0;
2555
+ const resolvedOpen = isControlled ? open : uncontrolledOpen;
2556
+ function setResolvedOpen(next) {
2557
+ if (!isControlled) setUncontrolledOpen(next);
2558
+ onOpenChange?.(next);
2559
+ }
2560
+ if (visible.length === 0) return null;
2561
+ const activeCount = visible.filter(
2562
+ (job) => isActiveStatus(job.status)
2563
+ ).length;
2564
+ return /* @__PURE__ */ jsxs(
2565
+ DropdownMenu,
2566
+ {
2567
+ modal: false,
2568
+ open: resolvedOpen,
2569
+ onOpenChange: setResolvedOpen,
2570
+ children: [
2571
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
2572
+ Button,
2573
+ {
2574
+ type: "button",
2575
+ variant: "ghost",
2576
+ size: "icon",
2577
+ className: cn("relative size-9", className),
2578
+ "aria-label": labels.title,
2579
+ children: [
2580
+ /* @__PURE__ */ jsx(Upload, { className: "size-4" }),
2581
+ activeCount > 0 ? /* @__PURE__ */ jsx("span", { className: "bg-destructive text-destructive-foreground absolute -top-0.5 -right-0.5 flex size-4 items-center justify-center rounded-full text-[10px] leading-none font-medium tabular-nums", children: formatActiveBadge(activeCount) }) : null
2582
+ ]
2583
+ }
2584
+ ) }),
2585
+ /* @__PURE__ */ jsxs(
2586
+ DropdownMenuContent,
2587
+ {
2588
+ align,
2589
+ className: "w-80 min-w-0 max-w-[calc(100vw-2rem)] overflow-x-hidden p-0",
2590
+ children: [
2591
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between border-b px-4 py-3", children: /* @__PURE__ */ jsx("h4", { className: "font-semibold", children: labels.title }) }),
2592
+ /* @__PURE__ */ jsx("div", { className: "max-h-80 min-w-0 overflow-x-hidden overflow-y-auto p-3", children: /* @__PURE__ */ jsx(
2593
+ FileUploadTray,
2594
+ {
2595
+ jobs: visible,
2596
+ labels,
2597
+ onPause,
2598
+ onResume,
2599
+ onCancel
2600
+ }
2601
+ ) })
2602
+ ]
2603
+ }
2604
+ )
2605
+ ]
2606
+ }
2607
+ );
2608
+ }
2609
+
2610
+ // src/components/upload/createMultipartUploadController.ts
2611
+ var PART_ATTEMPTS = 3;
2612
+ var PART_RETRY_BACKOFF_MS = [500, 1500];
2613
+ function defaultSleep(ms) {
2614
+ return new Promise((resolve) => {
2615
+ setTimeout(resolve, ms);
2616
+ });
2617
+ }
2618
+ function isAbortError(error) {
2619
+ return error instanceof DOMException && error.name === "AbortError";
2620
+ }
2621
+ function isMissingEtagError(error) {
2622
+ return error instanceof Error && error.message.includes("missing ETag");
2623
+ }
2624
+ function isRetryableStatus(status) {
2625
+ return status === 0 || status === 403 || status === 408 || status === 429 || status >= 500;
2626
+ }
2627
+ function putPartFailure(partNumber, status) {
2628
+ return new Error(`Upload failed (part ${partNumber}, status ${status})`);
2629
+ }
2630
+ function parsePutStatus(error) {
2631
+ if (!(error instanceof Error)) return null;
2632
+ const match = error.message.match(/status (\d+)\)$/);
2633
+ if (!match?.[1]) return null;
2634
+ return Number(match[1]);
2635
+ }
2636
+ function isRetryablePutError(error) {
2637
+ if (isAbortError(error) || isMissingEtagError(error)) return false;
2638
+ const status = parsePutStatus(error);
2639
+ if (status === null) return false;
2640
+ return isRetryableStatus(status);
2641
+ }
2642
+ function readEtag(xhr) {
2643
+ const raw = xhr.getResponseHeader("ETag") ?? xhr.getResponseHeader("etag") ?? "";
2644
+ const etag = raw.trim();
2645
+ if (!etag) {
2646
+ throw new Error("Upload part missing ETag");
2647
+ }
2648
+ return etag;
2649
+ }
2650
+ function putPart(input) {
2651
+ return new Promise((resolve, reject) => {
2652
+ input.xhr.open("PUT", input.url);
2653
+ input.xhr.upload.onprogress = (event) => {
2654
+ input.onProgress(event.loaded);
2655
+ };
2656
+ input.xhr.onload = () => {
2657
+ if (input.xhr.status >= 200 && input.xhr.status < 300) {
2658
+ try {
2659
+ resolve(readEtag(input.xhr));
2660
+ } catch (error) {
2661
+ reject(error);
2662
+ }
2663
+ return;
2664
+ }
2665
+ reject(putPartFailure(input.partNumber, input.xhr.status));
2666
+ };
2667
+ input.xhr.onerror = () => {
2668
+ reject(putPartFailure(input.partNumber, 0));
2669
+ };
2670
+ input.xhr.onabort = () => {
2671
+ reject(new DOMException("The operation was aborted.", "AbortError"));
2672
+ };
2673
+ input.xhr.send(input.body);
2674
+ });
2675
+ }
2676
+ function createMultipartUploadController(options) {
2677
+ const ranges = partRanges(options.file.size, options.partSize);
2678
+ const sleep = options.sleep ?? defaultSleep;
2679
+ const state = { status: "idle" };
2680
+ const seeded = /* @__PURE__ */ new Map();
2681
+ for (const part of options.completedParts ?? []) {
2682
+ if (part.partNumber < 1 || part.partNumber > ranges.length) continue;
2683
+ seeded.set(part.partNumber, part);
2684
+ }
2685
+ const completed = [...seeded.values()].sort(
2686
+ (left, right) => left.partNumber - right.partNumber
2687
+ );
2688
+ const done = new Set(completed.map((part) => part.partNumber));
2689
+ let currentXhr = null;
2690
+ let run = null;
2691
+ let resumeIndex = ranges.findIndex((range) => !done.has(range.partNumber));
2692
+ if (resumeIndex < 0) resumeIndex = ranges.length;
2693
+ function currentStatus() {
2694
+ return state.status;
2695
+ }
2696
+ function isStopped() {
2697
+ const status = currentStatus();
2698
+ return status === "paused" || status === "cancelled";
2699
+ }
2700
+ function emitProgress(currentPartLoaded = 0) {
2701
+ const completedBytes = completed.reduce((sum, part) => {
2702
+ const range = ranges[part.partNumber - 1];
2703
+ return sum + (range ? range.end - range.start : 0);
2704
+ }, 0);
2705
+ options.onProgress?.(
2706
+ progressFromBytes(completedBytes + currentPartLoaded, options.file.size)
2707
+ );
2708
+ }
2709
+ async function putPartWithRetry(partNumber, body) {
2710
+ let lastError;
2711
+ for (let attempt = 1; attempt <= PART_ATTEMPTS; attempt += 1) {
2712
+ if (state.status !== "uploading") {
2713
+ throw lastError ?? new DOMException("The operation was aborted.", "AbortError");
2714
+ }
2715
+ const url = await options.adapter.requestPartUrl(partNumber);
2716
+ if (state.status !== "uploading") {
2717
+ throw new DOMException("The operation was aborted.", "AbortError");
2718
+ }
2719
+ const xhr = options.xhrFactory ? options.xhrFactory() : new XMLHttpRequest();
2720
+ currentXhr = xhr;
2721
+ try {
2722
+ return await putPart({
2723
+ url,
2724
+ body,
2725
+ xhr,
2726
+ partNumber,
2727
+ onProgress: (loaded) => emitProgress(loaded)
2728
+ });
2729
+ } catch (error) {
2730
+ lastError = error;
2731
+ if (currentXhr === xhr) currentXhr = null;
2732
+ if (isStopped() || !isRetryablePutError(error) || attempt === PART_ATTEMPTS) {
2733
+ throw error;
2734
+ }
2735
+ const delay = PART_RETRY_BACKOFF_MS[attempt - 1] ?? PART_RETRY_BACKOFF_MS[PART_RETRY_BACKOFF_MS.length - 1];
2736
+ await sleep(delay);
2737
+ } finally {
2738
+ if (currentXhr === xhr) currentXhr = null;
2739
+ }
2740
+ }
2741
+ throw lastError;
2742
+ }
2743
+ async function uploadFrom(startIndex) {
2744
+ state.status = "uploading";
2745
+ for (let index = startIndex; index < ranges.length; index += 1) {
2746
+ if (state.status !== "uploading") return;
2747
+ resumeIndex = index;
2748
+ const range = ranges[index];
2749
+ if (!range) continue;
2750
+ if (done.has(range.partNumber)) continue;
2751
+ const body = options.file.slice(range.start, range.end);
2752
+ try {
2753
+ const etag = await putPartWithRetry(range.partNumber, body);
2754
+ if (state.status !== "uploading") return;
2755
+ completed.push({ partNumber: range.partNumber, etag });
2756
+ done.add(range.partNumber);
2757
+ emitProgress(0);
2758
+ } catch (error) {
2759
+ currentXhr = null;
2760
+ if (isStopped()) return;
2761
+ state.status = "error";
2762
+ throw error;
2763
+ }
2764
+ }
2765
+ if (state.status !== "uploading") return;
2766
+ state.status = "completing";
2767
+ await options.adapter.complete([...completed]);
2768
+ state.status = "completed";
2769
+ emitProgress(0);
2770
+ }
2771
+ async function start() {
2772
+ if (run) return run;
2773
+ emitProgress(0);
2774
+ run = uploadFrom(resumeIndex).catch((error) => {
2775
+ if (isStopped()) return;
2776
+ state.status = "error";
2777
+ throw error;
2778
+ });
2779
+ return run;
2780
+ }
2781
+ function pause() {
2782
+ if (state.status !== "uploading") return;
2783
+ state.status = "paused";
2784
+ currentXhr?.abort();
2785
+ }
2786
+ async function resume() {
2787
+ if (state.status !== "paused") return;
2788
+ run = uploadFrom(resumeIndex).catch((error) => {
2789
+ if (isStopped()) return;
2790
+ state.status = "error";
2791
+ throw error;
2792
+ });
2793
+ return run;
2794
+ }
2795
+ async function cancel() {
2796
+ if (state.status === "completed" || state.status === "cancelled" || state.status === "idle") {
2797
+ state.status = "cancelled";
2798
+ return;
2799
+ }
2800
+ state.status = "cancelled";
2801
+ currentXhr?.abort();
2802
+ await options.adapter.abort?.();
2803
+ }
2804
+ return {
2805
+ start,
2806
+ pause,
2807
+ resume,
2808
+ cancel,
2809
+ getStatus: () => state.status
2810
+ };
2811
+ }
2812
+ function useMediaQuery2(query) {
2813
+ const [matches, setMatches] = useState(false);
2814
+ useEffect(() => {
2815
+ if (typeof window === "undefined" || !window.matchMedia) return;
2816
+ const mql = window.matchMedia(query);
2817
+ const onChange = () => setMatches(mql.matches);
2818
+ onChange();
2819
+ mql.addEventListener("change", onChange);
2820
+ return () => mql.removeEventListener("change", onChange);
2821
+ }, [query]);
2822
+ return matches;
2823
+ }
2824
+
2825
+ // src/lib/avatar.ts
2826
+ var EMPTY_COLORS = {
2827
+ backgroundColor: "#9ca3af",
2828
+ color: "#111111"
2829
+ };
2830
+ var AVATAR_PALETTE = [
2831
+ "#4f6d8c",
2832
+ // slate blue
2833
+ "#3d7a6a",
2834
+ // teal
2835
+ "#6b5b8a",
2836
+ // muted purple
2837
+ "#8a5a4a",
2838
+ // warm brown
2839
+ "#4a7a8a",
2840
+ // steel cyan
2841
+ "#7a6b4a",
2842
+ // olive
2843
+ "#5a6b8a",
2844
+ // periwinkle
2845
+ "#8a4a5a",
2846
+ // rose
2847
+ "#4a8a6b",
2848
+ // green
2849
+ "#6b4a7a",
2850
+ // plum
2851
+ "#8a6b4a",
2852
+ // amber brown
2853
+ "#4a5a8a",
2854
+ // indigo
2855
+ "#7a4a6b",
2856
+ // mauve
2857
+ "#5a8a4a",
2858
+ // leaf
2859
+ "#8a5a7a",
2860
+ // dusty pink
2861
+ "#3d6b7a"
2862
+ // deep teal
2863
+ ];
2864
+ function hashString(input) {
2865
+ let hash = 2166136261;
2866
+ for (let i = 0; i < input.length; i++) {
2867
+ hash ^= input.charCodeAt(i);
2868
+ hash = Math.imul(hash, 16777619);
2869
+ }
2870
+ return hash >>> 0;
2871
+ }
2872
+ function srgbToLinear(channel) {
2873
+ const c = channel / 255;
2874
+ return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
2875
+ }
2876
+ function relativeLuminance(hex) {
2877
+ const normalized = hex.replace("#", "");
2878
+ const r = Number.parseInt(normalized.slice(0, 2), 16);
2879
+ const g = Number.parseInt(normalized.slice(2, 4), 16);
2880
+ const b = Number.parseInt(normalized.slice(4, 6), 16);
2881
+ return 0.2126 * srgbToLinear(r) + 0.7152 * srgbToLinear(g) + 0.0722 * srgbToLinear(b);
2882
+ }
2883
+ function contrastingTextColor(backgroundHex) {
2884
+ return relativeLuminance(backgroundHex) > 0.179 ? "#111111" : "#ffffff";
2885
+ }
2886
+ function getInitials(name, max = 2) {
2887
+ const parts = name.trim().split(/\s+/).filter(Boolean);
2888
+ if (parts.length === 0) return "?";
2889
+ return parts.slice(0, Math.max(1, max)).map((part) => part.charAt(0)).join("").toUpperCase();
2890
+ }
2891
+ function getAvatarColors(name) {
2892
+ const normalized = name.trim().toLowerCase();
2893
+ if (!normalized) {
2894
+ return { ...EMPTY_COLORS };
2895
+ }
2896
+ const backgroundColor = AVATAR_PALETTE[hashString(normalized) % AVATAR_PALETTE.length];
2897
+ return {
2898
+ backgroundColor,
2899
+ color: contrastingTextColor(backgroundColor)
2900
+ };
2901
+ }
2902
+ function avatarFallbackStyle(name) {
2903
+ return getAvatarColors(name);
2904
+ }
2905
+ function StatHighlightCard({
2906
+ label,
2907
+ value,
2908
+ hint,
2909
+ delta,
2910
+ deltaTone = "neutral",
2911
+ className
2912
+ }) {
2913
+ return /* @__PURE__ */ jsxs(
2914
+ "div",
2915
+ {
2916
+ className: cn(
2917
+ "bg-card text-card-foreground rounded-xl border p-6 shadow-sm",
2918
+ className
2919
+ ),
2920
+ children: [
2921
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: label }),
2922
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-2xl font-semibold tracking-tight", children: value }),
2923
+ delta ? /* @__PURE__ */ jsx(
2924
+ "p",
2925
+ {
2926
+ className: cn(
2927
+ "mt-1 text-sm",
2928
+ deltaTone === "up" && "text-emerald-600 dark:text-emerald-400",
2929
+ deltaTone === "down" && "text-destructive",
2930
+ deltaTone === "neutral" && "text-muted-foreground"
2931
+ ),
2932
+ children: delta
2933
+ }
2934
+ ) : null,
2935
+ hint ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground mt-1 text-xs", children: hint }) : null
2936
+ ]
2937
+ }
2938
+ );
2939
+ }
2940
+ function MetricChartCard({
2941
+ title,
2942
+ description,
2943
+ action,
2944
+ children,
2945
+ className
2946
+ }) {
2947
+ return /* @__PURE__ */ jsxs(
2948
+ "div",
2949
+ {
2950
+ className: cn(
2951
+ "bg-card text-card-foreground rounded-xl border shadow-sm",
2952
+ className
2953
+ ),
2954
+ children: [
2955
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 p-6 pb-2", children: [
2956
+ /* @__PURE__ */ jsxs("div", { children: [
2957
+ /* @__PURE__ */ jsx("h3", { className: "leading-none font-semibold tracking-tight", children: title }),
2958
+ description ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground mt-1.5 text-sm", children: description }) : null
2959
+ ] }),
2960
+ action
2961
+ ] }),
2962
+ /* @__PURE__ */ jsx("div", { className: "p-6 pt-4", children })
2963
+ ]
2964
+ }
2965
+ );
2966
+ }
2967
+ function polyline(points) {
2968
+ if (points.length === 0) return "";
2969
+ const min = Math.min(...points);
2970
+ const max = Math.max(...points);
2971
+ const span = max - min || 1;
2972
+ return points.map((point, index) => {
2973
+ const x = points.length === 1 ? 50 : index / (points.length - 1) * 100;
2974
+ const y = 28 - (point - min) / span * 24;
2975
+ return `${x.toFixed(2)},${y.toFixed(2)}`;
2976
+ }).join(" ");
2977
+ }
2978
+ function SparklineStat({
2979
+ label,
2980
+ value,
2981
+ points,
2982
+ className
2983
+ }) {
2984
+ const reduced = useMediaQuery2("(prefers-reduced-motion: reduce)");
2985
+ const path = polyline(points);
2986
+ return /* @__PURE__ */ jsxs(
2987
+ "div",
2988
+ {
2989
+ className: cn(
2990
+ "bg-card text-card-foreground flex items-end justify-between gap-3 rounded-xl border p-4 shadow-sm",
2991
+ className
2992
+ ),
2993
+ children: [
2994
+ /* @__PURE__ */ jsxs("div", { children: [
2995
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: label }),
2996
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-lg font-semibold tracking-tight", children: value })
2997
+ ] }),
2998
+ path ? /* @__PURE__ */ jsx(
2999
+ "svg",
3000
+ {
3001
+ viewBox: "0 0 100 32",
3002
+ className: cn(
3003
+ "text-primary h-8 w-24",
3004
+ !reduced && "motion-safe:transition-opacity"
3005
+ ),
3006
+ "aria-hidden": "true",
3007
+ children: /* @__PURE__ */ jsx(
3008
+ "polyline",
3009
+ {
3010
+ fill: "none",
3011
+ stroke: "currentColor",
3012
+ strokeWidth: "2",
3013
+ strokeLinejoin: "round",
3014
+ strokeLinecap: "round",
3015
+ points: path
3016
+ }
3017
+ )
3018
+ }
3019
+ ) : null
3020
+ ]
3021
+ }
3022
+ );
3023
+ }
3024
+ var TIME_RANGE_OPTIONS = ["day", "week", "month", "year"];
3025
+ var DEFAULT_LABELS2 = {
3026
+ day: "Day",
3027
+ week: "Week",
3028
+ month: "Month",
3029
+ year: "Year"
3030
+ };
3031
+ function TimeRangePicker({
3032
+ value,
3033
+ onChange,
3034
+ labels,
3035
+ className
3036
+ }) {
3037
+ const copy = { ...DEFAULT_LABELS2, ...labels };
3038
+ return /* @__PURE__ */ jsx(
3039
+ "div",
3040
+ {
3041
+ role: "group",
3042
+ "aria-label": "Time range",
3043
+ className: cn("flex flex-wrap gap-1", className),
3044
+ children: TIME_RANGE_OPTIONS.map((option) => {
3045
+ const selected = option === value;
3046
+ return /* @__PURE__ */ jsx(
3047
+ Button,
3048
+ {
3049
+ type: "button",
3050
+ size: "sm",
3051
+ variant: selected ? "default" : "outline",
3052
+ "aria-pressed": selected,
3053
+ onClick: () => onChange(option),
3054
+ children: copy[option]
3055
+ },
3056
+ option
3057
+ );
3058
+ })
3059
+ }
3060
+ );
3061
+ }
3062
+ function DimensionBreakdownTable({
3063
+ title,
3064
+ rows,
3065
+ empty,
3066
+ className
3067
+ }) {
3068
+ return /* @__PURE__ */ jsxs(
3069
+ "div",
3070
+ {
3071
+ className: cn(
3072
+ "bg-card text-card-foreground overflow-hidden rounded-xl border shadow-sm",
3073
+ className
3074
+ ),
3075
+ children: [
3076
+ title ? /* @__PURE__ */ jsx("h3", { className: "px-6 pt-6 text-sm font-semibold tracking-tight", children: title }) : null,
3077
+ rows.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-6", children: empty }) : /* @__PURE__ */ jsx("table", { className: "w-full text-sm", children: /* @__PURE__ */ jsx("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs("tr", { className: "border-t first:border-t-0", children: [
3078
+ /* @__PURE__ */ jsx("th", { scope: "row", className: "px-6 py-3 text-left font-medium", children: row.label }),
3079
+ /* @__PURE__ */ jsx("td", { className: "px-6 py-3 text-right tabular-nums", children: row.value }),
3080
+ row.share ? /* @__PURE__ */ jsx("td", { className: "text-muted-foreground px-6 py-3 text-right tabular-nums", children: row.share }) : null
3081
+ ] }, row.id)) }) })
3082
+ ]
3083
+ }
3084
+ );
3085
+ }
3086
+ function CostBreakdownCard({
3087
+ title,
3088
+ total,
3089
+ items,
3090
+ className
3091
+ }) {
3092
+ return /* @__PURE__ */ jsxs(
3093
+ "div",
3094
+ {
3095
+ className: cn(
3096
+ "bg-card text-card-foreground rounded-xl border p-6 shadow-sm",
3097
+ className
3098
+ ),
3099
+ children: [
3100
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-3", children: [
3101
+ /* @__PURE__ */ jsx("h3", { className: "font-semibold tracking-tight", children: title }),
3102
+ total ? /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold tabular-nums", children: total }) : null
3103
+ ] }),
3104
+ /* @__PURE__ */ jsx("ul", { className: "mt-4 space-y-2", children: items.map((item) => /* @__PURE__ */ jsxs(
3105
+ "li",
3106
+ {
3107
+ className: "flex items-baseline justify-between gap-3 text-sm",
3108
+ children: [
3109
+ /* @__PURE__ */ jsxs("div", { children: [
3110
+ /* @__PURE__ */ jsx("p", { children: item.label }),
3111
+ item.basis ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: item.basis }) : null
3112
+ ] }),
3113
+ /* @__PURE__ */ jsx("p", { className: "tabular-nums", children: item.amount })
3114
+ ]
3115
+ },
3116
+ item.id
3117
+ )) })
3118
+ ]
3119
+ }
3120
+ );
3121
+ }
3122
+ function MetricEmptyState({
3123
+ title,
3124
+ description,
3125
+ action,
3126
+ icon = BarChart3,
3127
+ className
3128
+ }) {
3129
+ return /* @__PURE__ */ jsx(
3130
+ EmptyState,
3131
+ {
3132
+ title,
3133
+ description,
3134
+ action,
3135
+ icon,
3136
+ className
3137
+ }
3138
+ );
3139
+ }
3140
+ function MetricSkeleton({ className, bars = 4 }) {
3141
+ const reduced = useMediaQuery2("(prefers-reduced-motion: reduce)");
3142
+ const count = Math.max(1, bars);
3143
+ return /* @__PURE__ */ jsxs(
3144
+ "div",
3145
+ {
3146
+ role: "status",
3147
+ "aria-live": "polite",
3148
+ className: cn("bg-card rounded-xl border p-6 shadow-sm", className),
3149
+ children: [
3150
+ /* @__PURE__ */ jsx(
3151
+ "div",
3152
+ {
3153
+ className: cn(
3154
+ "bg-muted mb-4 h-4 w-32 rounded",
3155
+ !reduced && "animate-pulse"
3156
+ )
3157
+ }
3158
+ ),
3159
+ /* @__PURE__ */ jsx("div", { className: "flex h-32 items-end gap-2", children: Array.from({ length: count }, (_, index) => /* @__PURE__ */ jsx(
3160
+ "div",
3161
+ {
3162
+ className: cn(
3163
+ "bg-muted flex-1 rounded-t",
3164
+ !reduced && "animate-pulse"
3165
+ ),
3166
+ style: { height: `${40 + index * 17 % 50}%` }
3167
+ },
3168
+ index
3169
+ )) }),
3170
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading metrics" })
3171
+ ]
3172
+ }
3173
+ );
3174
+ }
3175
+
3176
+ // src/lib/cookie-consent.ts
3177
+ var COOKIE_CONSENT_NAME = "yenio-cookie-consent";
3178
+ var COOKIE_CONSENT_VALUES = ["necessary", "all"];
3179
+ var COOKIE_CONSENT_MAX_AGE = 180 * 24 * 60 * 60;
3180
+ function isCookieConsentValue(value) {
3181
+ return typeof value === "string" && COOKIE_CONSENT_VALUES.includes(value);
3182
+ }
3183
+ function cookieConsentAllowsReplay(value) {
3184
+ return value === "all";
3185
+ }
3186
+ function cookieDomainFromHostname(hostname) {
3187
+ const host = hostname.toLowerCase();
3188
+ if (host === "localhost" || host === "127.0.0.1") return void 0;
3189
+ if (host.endsWith(".yenio.mx") || host === "yenio.mx") return ".yenio.mx";
3190
+ if (host.endsWith(".yenio-pld.workers.dev") || host === "yenio-pld.workers.dev") {
3191
+ return ".yenio-pld.workers.dev";
3192
+ }
3193
+ return void 0;
3194
+ }
3195
+ function readCookieMap(cookieSource) {
3196
+ const map = /* @__PURE__ */ new Map();
3197
+ for (const part of cookieSource.split(";")) {
3198
+ const trimmed = part.trim();
3199
+ if (!trimmed) continue;
3200
+ const eq = trimmed.indexOf("=");
3201
+ if (eq < 0) continue;
3202
+ const name = trimmed.slice(0, eq).trim();
3203
+ const raw = trimmed.slice(eq + 1).trim();
3204
+ map.set(name, decodeURIComponent(raw));
3205
+ }
3206
+ return map;
3207
+ }
3208
+ function parseCookieConsent(cookieSource) {
3209
+ const raw = readCookieMap(cookieSource).get(COOKIE_CONSENT_NAME);
3210
+ return isCookieConsentValue(raw) ? raw : null;
3211
+ }
3212
+ function readCookieConsent() {
3213
+ if (typeof document === "undefined") return null;
3214
+ return parseCookieConsent(document.cookie);
3215
+ }
3216
+ function serializeCookieConsent(value, options = {}) {
3217
+ const maxAge = options.maxAge ?? COOKIE_CONSENT_MAX_AGE;
3218
+ const secure = options.secure ?? (typeof window !== "undefined" && window.location.protocol === "https:");
3219
+ const parts = [
3220
+ `${COOKIE_CONSENT_NAME}=${encodeURIComponent(value)}`,
3221
+ "Path=/",
3222
+ `Max-Age=${maxAge}`,
3223
+ "SameSite=Lax"
3224
+ ];
3225
+ if (options.domain) {
3226
+ parts.push(`Domain=${options.domain}`);
3227
+ }
3228
+ if (secure) {
3229
+ parts.push("Secure");
3230
+ }
3231
+ return parts.join("; ");
3232
+ }
3233
+ function writeCookieConsent(value, options = {}) {
3234
+ if (typeof document === "undefined") return;
3235
+ document.cookie = serializeCookieConsent(value, options);
3236
+ }
3237
+ function isEmbeddedWindow() {
3238
+ if (typeof window === "undefined") return false;
3239
+ try {
3240
+ return window.self !== window.top;
3241
+ } catch {
3242
+ return true;
3243
+ }
3244
+ }
3245
+ var DEFAULT_COOKIE_CONSENT_BANNER_LABELS = {
3246
+ message: "We use necessary cookies to run Yenio. Extra cookies can be used for diagnostics if you opt in.",
3247
+ acceptAll: "Accept all",
3248
+ necessaryOnly: "Necessary only",
3249
+ policy: "Cookies policy"
3250
+ };
3251
+ function CookieConsentBanner({
3252
+ domain,
3253
+ policyHref,
3254
+ labels,
3255
+ onChoice,
3256
+ variant = "default",
3257
+ className
3258
+ }) {
3259
+ const copy = { ...DEFAULT_COOKIE_CONSENT_BANNER_LABELS, ...labels };
3260
+ const [consent, setConsent] = useState(
3261
+ "pending"
3262
+ );
3263
+ useEffect(() => {
3264
+ if (variant === "embed" || isEmbeddedWindow()) {
3265
+ setConsent("necessary");
3266
+ return;
3267
+ }
3268
+ setConsent(readCookieConsent());
3269
+ }, [variant]);
3270
+ if (variant === "embed") return null;
3271
+ if (consent === "pending" || consent) return null;
3272
+ const choose = (value) => {
3273
+ const resolvedDomain = domain ?? (typeof window === "undefined" ? void 0 : cookieDomainFromHostname(window.location.hostname));
3274
+ writeCookieConsent(value, { domain: resolvedDomain });
3275
+ setConsent(value);
3276
+ onChoice?.(value);
3277
+ };
3278
+ return /* @__PURE__ */ jsx(
3279
+ "div",
3280
+ {
3281
+ role: "dialog",
3282
+ "aria-label": copy.message,
3283
+ className: cn(
3284
+ "fixed inset-x-0 bottom-0 z-50 border-t bg-background/95 p-4 shadow-lg backdrop-blur-sm",
3285
+ className
3286
+ ),
3287
+ children: /* @__PURE__ */ jsxs("div", { className: "mx-auto flex w-full max-w-5xl flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [
3288
+ /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
3289
+ copy.message,
3290
+ policyHref ? /* @__PURE__ */ jsxs(Fragment, { children: [
3291
+ " ",
3292
+ /* @__PURE__ */ jsx(
3293
+ "a",
3294
+ {
3295
+ href: policyHref,
3296
+ className: "text-foreground underline underline-offset-4",
3297
+ children: copy.policy
3298
+ }
3299
+ )
3300
+ ] }) : null
3301
+ ] }),
3302
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-wrap gap-2", children: [
3303
+ /* @__PURE__ */ jsx(
3304
+ Button,
3305
+ {
3306
+ type: "button",
3307
+ variant: "outline",
3308
+ onClick: () => choose("necessary"),
3309
+ children: copy.necessaryOnly
3310
+ }
3311
+ ),
3312
+ /* @__PURE__ */ jsx(Button, { type: "button", onClick: () => choose("all"), children: copy.acceptAll })
3313
+ ] })
3314
+ ] })
3315
+ }
3316
+ );
3317
+ }
3318
+ function LegalDocLink({
3319
+ href,
3320
+ children
3321
+ }) {
3322
+ return /* @__PURE__ */ jsx(
3323
+ "a",
3324
+ {
3325
+ href,
3326
+ target: "_blank",
3327
+ rel: "noopener noreferrer",
3328
+ className: "underline underline-offset-4",
3329
+ children
3330
+ }
3331
+ );
3332
+ }
3333
+ function wrapPhrases(label, terms, privacy, termsHref, privacyHref) {
3334
+ const hits = [];
3335
+ if (terms) {
3336
+ const start = label.indexOf(terms);
3337
+ if (start >= 0) {
3338
+ hits.push({
3339
+ start,
3340
+ end: start + terms.length,
3341
+ href: termsHref,
3342
+ text: terms
3343
+ });
3344
+ }
3345
+ }
3346
+ if (privacy) {
3347
+ const start = label.indexOf(privacy);
3348
+ if (start >= 0) {
3349
+ hits.push({
3350
+ start,
3351
+ end: start + privacy.length,
3352
+ href: privacyHref,
3353
+ text: privacy
3354
+ });
3355
+ }
3356
+ }
3357
+ hits.sort((a, b) => a.start - b.start || b.end - b.start - (a.end - a.start));
3358
+ const kept = [];
3359
+ for (const hit of hits) {
3360
+ const last = kept[kept.length - 1];
3361
+ if (last && hit.start < last.end) {
3362
+ if (hit.end - hit.start > last.end - last.start) {
3363
+ kept[kept.length - 1] = hit;
3364
+ }
3365
+ continue;
3366
+ }
3367
+ kept.push(hit);
3368
+ }
3369
+ if (kept.length === 0) {
3370
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3371
+ label,
3372
+ " ",
3373
+ /* @__PURE__ */ jsx(LegalDocLink, { href: termsHref, children: terms }),
3374
+ " \xB7 ",
3375
+ /* @__PURE__ */ jsx(LegalDocLink, { href: privacyHref, children: privacy })
3376
+ ] });
3377
+ }
3378
+ const nodes = [];
3379
+ let cursor = 0;
3380
+ kept.forEach((hit, index) => {
3381
+ if (hit.start > cursor) {
3382
+ nodes.push(label.slice(cursor, hit.start));
3383
+ }
3384
+ nodes.push(
3385
+ /* @__PURE__ */ jsx(LegalDocLink, { href: hit.href, children: hit.text }, `${hit.text}-${index}`)
3386
+ );
3387
+ cursor = hit.end;
3388
+ });
3389
+ if (cursor < label.length) {
3390
+ nodes.push(label.slice(cursor));
3391
+ }
3392
+ return nodes;
3393
+ }
3394
+ function renderLegalAcceptLabel(label, terms, privacy, termsHref, privacyHref) {
3395
+ if (label.includes("{terms}") || label.includes("{privacy}")) {
3396
+ return label.split(/(\{terms\}|\{privacy\})/g).map((part, index) => {
3397
+ if (part === "{terms}") {
3398
+ return /* @__PURE__ */ jsx(LegalDocLink, { href: termsHref, children: terms }, `terms-${index}`);
3399
+ }
3400
+ if (part === "{privacy}") {
3401
+ return /* @__PURE__ */ jsx(LegalDocLink, { href: privacyHref, children: privacy }, `privacy-${index}`);
3402
+ }
3403
+ return part;
3404
+ });
3405
+ }
3406
+ return wrapPhrases(label, terms, privacy, termsHref, privacyHref);
3407
+ }
3408
+ var DEFAULT_LEGAL_ACCEPT_DIALOG_LABELS = {
3409
+ title: "Updated terms",
3410
+ description: "We updated the Terms of Service and Privacy Policy. Please accept to continue.",
3411
+ accept: "Accept",
3412
+ terms: "Terms of Service",
3413
+ privacy: "Privacy Policy"
3414
+ };
3415
+ function LegalAcceptDialog({
3416
+ open,
3417
+ termsHref,
3418
+ privacyHref,
3419
+ summary,
3420
+ accepting = false,
3421
+ labels,
3422
+ onAccept
3423
+ }) {
3424
+ const copy = { ...DEFAULT_LEGAL_ACCEPT_DIALOG_LABELS, ...labels };
3425
+ return /* @__PURE__ */ jsx(
3426
+ AlertDialog,
3427
+ {
3428
+ open,
3429
+ onOpenChange: (next) => {
3430
+ if (next) return;
3431
+ },
3432
+ children: /* @__PURE__ */ jsxs(
3433
+ AlertDialogContent,
3434
+ {
3435
+ onEscapeKeyDown: (event) => {
3436
+ event.preventDefault();
3437
+ },
3438
+ children: [
3439
+ /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
3440
+ /* @__PURE__ */ jsx(AlertDialogTitle, { children: copy.title }),
3441
+ /* @__PURE__ */ jsxs(AlertDialogDescription, { children: [
3442
+ renderLegalAcceptLabel(
3443
+ copy.description,
3444
+ copy.terms,
3445
+ copy.privacy,
3446
+ termsHref,
3447
+ privacyHref
3448
+ ),
3449
+ summary ? ` ${summary}` : ""
3450
+ ] })
3451
+ ] }),
3452
+ /* @__PURE__ */ jsx(AlertDialogFooter, { children: /* @__PURE__ */ jsx(Button, { type: "button", disabled: accepting, onClick: onAccept, children: copy.accept }) })
3453
+ ]
3454
+ }
3455
+ )
3456
+ }
3457
+ );
3458
+ }
3459
+ var DEFAULT_LEGAL_ACCEPT_CHECKBOX_LABELS = {
3460
+ label: "I agree to the {terms} and {privacy}",
3461
+ terms: "Terms of Service",
3462
+ privacy: "Privacy Policy"
3463
+ };
3464
+ function LegalAcceptCheckbox({
3465
+ id = "legal-accept",
3466
+ checked,
3467
+ onCheckedChange,
3468
+ termsHref,
3469
+ privacyHref,
3470
+ labels,
3471
+ disabled = false,
3472
+ className
3473
+ }) {
3474
+ const copy = { ...DEFAULT_LEGAL_ACCEPT_CHECKBOX_LABELS, ...labels };
3475
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex items-start gap-3", className), children: [
3476
+ /* @__PURE__ */ jsx(
3477
+ "input",
3478
+ {
3479
+ id,
3480
+ type: "checkbox",
3481
+ checked,
3482
+ disabled,
3483
+ onChange: (event) => onCheckedChange(event.target.checked),
3484
+ className: "mt-1 size-4 shrink-0 accent-primary"
3485
+ }
3486
+ ),
3487
+ /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-sm leading-5", children: renderLegalAcceptLabel(
3488
+ copy.label,
3489
+ copy.terms,
3490
+ copy.privacy,
3491
+ termsHref,
3492
+ privacyHref
3493
+ ) })
3494
+ ] });
3495
+ }
3496
+ function sanitizeHref(href) {
3497
+ const trimmed = href.trim();
3498
+ if (/^https?:\/\//i.test(trimmed) || /^mailto:/i.test(trimmed)) {
3499
+ return trimmed;
3500
+ }
3501
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
3502
+ return trimmed;
3503
+ }
3504
+ return null;
3505
+ }
3506
+ function renderInline(text) {
3507
+ const nodes = [];
3508
+ const pattern = /(\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`|\[[^\]]+\]\([^)]+\))/g;
3509
+ let last = 0;
3510
+ let match;
3511
+ let key = 0;
3512
+ while ((match = pattern.exec(text)) !== null) {
3513
+ if (match.index > last) {
3514
+ nodes.push(text.slice(last, match.index));
3515
+ }
3516
+ const token = match[0];
3517
+ if (token.startsWith("**")) {
3518
+ nodes.push(/* @__PURE__ */ jsx("strong", { children: token.slice(2, -2) }, key));
3519
+ } else if (token.startsWith("*")) {
3520
+ nodes.push(/* @__PURE__ */ jsx("em", { children: token.slice(1, -1) }, key));
3521
+ } else if (token.startsWith("`")) {
3522
+ nodes.push(
3523
+ /* @__PURE__ */ jsx("code", { className: "rounded bg-muted px-1 py-0.5 text-[0.9em]", children: token.slice(1, -1) }, key)
3524
+ );
3525
+ } else {
3526
+ const link = token.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
3527
+ const href = link ? sanitizeHref(link[2] ?? "") : null;
3528
+ if (link && href) {
3529
+ nodes.push(
3530
+ /* @__PURE__ */ jsx(
3531
+ "a",
3532
+ {
3533
+ href,
3534
+ className: "underline underline-offset-4",
3535
+ rel: "noopener noreferrer",
3536
+ children: link[1]
3537
+ },
3538
+ key
3539
+ )
3540
+ );
3541
+ } else {
3542
+ nodes.push(token);
3543
+ }
3544
+ }
3545
+ key += 1;
3546
+ last = match.index + token.length;
3547
+ }
3548
+ if (last < text.length) {
3549
+ nodes.push(text.slice(last));
3550
+ }
3551
+ return nodes;
3552
+ }
3553
+ function headingTag(line) {
3554
+ if (line.startsWith("### ")) return { Tag: "h3", text: line.slice(4) };
3555
+ if (line.startsWith("## ")) return { Tag: "h2", text: line.slice(3) };
3556
+ if (line.startsWith("# ")) return { Tag: "h1", text: line.slice(2) };
3557
+ return null;
3558
+ }
3559
+ function isUnorderedItem(line) {
3560
+ return /^[-*] /.test(line);
3561
+ }
3562
+ function isOrderedItem(line) {
3563
+ return /^\d+\. /.test(line);
3564
+ }
3565
+ function LegalMarkdown({ markdown, className }) {
3566
+ const blocks = markdown.replace(/\r\n/g, "\n").trim().split(/\n{2,}/);
3567
+ return /* @__PURE__ */ jsx(
3568
+ "div",
3569
+ {
3570
+ className: cn(
3571
+ "legal-markdown space-y-4 text-sm leading-6 [&_h1]:text-3xl [&_h1]:font-semibold [&_h2]:text-xl [&_h2]:font-semibold [&_h3]:text-lg [&_h3]:font-semibold [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:list-decimal [&_ol]:pl-5",
3572
+ className
3573
+ ),
3574
+ children: blocks.map((block, index) => {
3575
+ const lines = block.split("\n");
3576
+ const heading = headingTag(lines[0] ?? "");
3577
+ if (heading && lines.length === 1) {
3578
+ const { Tag, text } = heading;
3579
+ return /* @__PURE__ */ jsx(Tag, { children: renderInline(text) }, index);
3580
+ }
3581
+ if (lines.every(isUnorderedItem)) {
3582
+ return /* @__PURE__ */ jsx("ul", { children: lines.map((line, itemIndex) => /* @__PURE__ */ jsx("li", { children: renderInline(line.replace(/^[-*] /, "")) }, itemIndex)) }, index);
3583
+ }
3584
+ if (lines.every(isOrderedItem)) {
3585
+ return /* @__PURE__ */ jsx("ol", { children: lines.map((line, itemIndex) => /* @__PURE__ */ jsx("li", { children: renderInline(line.replace(/^\d+\. /, "")) }, itemIndex)) }, index);
3586
+ }
3587
+ return /* @__PURE__ */ jsx("p", { children: renderInline(lines.join(" ")) }, index);
3588
+ })
3589
+ }
3590
+ );
3591
+ }
3592
+
3593
+ // src/theme-init.ts
3594
+ var THEME_STORAGE_KEY = "theme";
3595
+ var THEME_INIT_SCRIPT_ID = "yenio-theme-init";
3596
+ var THEME_INIT_SCRIPT = `(function(){var t="light";var s;try{s=localStorage.getItem("${THEME_STORAGE_KEY}")}catch(e){}if(s==="dark"||s==="light"){t=s}else{try{if(window.matchMedia("(prefers-color-scheme: dark)").matches)t="dark"}catch(e){}}var r=document.documentElement;r.classList.add(t);r.style.colorScheme=t})();`;
3597
+ var DEFAULT_AUDIT_TIMELINE_LABELS = {
3598
+ emptyTitle: "No events",
3599
+ emptyDescription: "Audited activity will appear here.",
3600
+ actor: "Actor",
3601
+ subject: "Subject"
3602
+ };
3603
+ function formatOccurredAt(occurredAt) {
3604
+ const date = new Date(occurredAt);
3605
+ if (Number.isNaN(date.getTime())) return String(occurredAt);
3606
+ return new Intl.DateTimeFormat("en", {
3607
+ dateStyle: "medium",
3608
+ timeStyle: "short",
3609
+ timeZone: "America/Mexico_City"
3610
+ }).format(date);
3611
+ }
3612
+ function actorLine(actor) {
3613
+ const role = actor.organizationRole ? ` (${actor.organizationRole})` : "";
3614
+ return `${actor.kind} ${actor.id}${role}`;
3615
+ }
3616
+ function AuditTimeline({
3617
+ items,
3618
+ labels,
3619
+ emptyAction,
3620
+ className,
3621
+ formatOccurredAt: format = formatOccurredAt
3622
+ }) {
3623
+ const copy = { ...DEFAULT_AUDIT_TIMELINE_LABELS, ...labels };
3624
+ if (items.length === 0) {
3625
+ return /* @__PURE__ */ jsx(
3626
+ EmptyState,
3627
+ {
3628
+ className,
3629
+ title: copy.emptyTitle,
3630
+ description: copy.emptyDescription,
3631
+ action: emptyAction
3632
+ }
3633
+ );
3634
+ }
3635
+ return /* @__PURE__ */ jsx("ol", { className: cn("flex flex-col gap-3", className), children: items.map((item) => {
3636
+ const occurred = new Date(item.occurredAt);
3637
+ const iso = Number.isNaN(occurred.getTime()) ? void 0 : occurred.toISOString();
3638
+ return /* @__PURE__ */ jsxs(
3639
+ "li",
3640
+ {
3641
+ className: "rounded-xl border border-border bg-card p-4 text-card-foreground",
3642
+ children: [
3643
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline justify-between gap-2", children: [
3644
+ /* @__PURE__ */ jsx("p", { className: "font-mono text-sm font-medium", children: item.type }),
3645
+ /* @__PURE__ */ jsx("time", { className: "text-xs text-muted-foreground", dateTime: iso, children: format(item.occurredAt) })
3646
+ ] }),
3647
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm", children: item.summary }),
3648
+ /* @__PURE__ */ jsxs("p", { className: "mt-2 text-xs text-muted-foreground", children: [
3649
+ copy.actor,
3650
+ ": ",
3651
+ actorLine(item.actor)
3652
+ ] }),
3653
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
3654
+ copy.subject,
3655
+ ": ",
3656
+ item.subject.type,
3657
+ " ",
3658
+ item.subject.id
3659
+ ] })
3660
+ ]
3661
+ },
3662
+ item.id
3663
+ );
3664
+ }) });
3665
+ }
3666
+ var DEFAULT_RISK_BADGE_LABELS = {
3667
+ low: "Low",
3668
+ medium: "Medium",
3669
+ high: "High",
3670
+ unverifiedRule: "Unverified rule"
3671
+ };
3672
+ var tone = {
3673
+ low: "bg-muted text-muted-foreground",
3674
+ medium: "bg-accent text-accent-foreground",
3675
+ high: "bg-destructive/15 text-destructive"
3676
+ };
3677
+ function severityLabel(severity, labels) {
3678
+ switch (severity) {
3679
+ case "low":
3680
+ return labels.low;
3681
+ case "medium":
3682
+ return labels.medium;
3683
+ case "high":
3684
+ return labels.high;
3685
+ default: {
3686
+ const exhaustive = severity;
3687
+ return exhaustive;
3688
+ }
3689
+ }
3690
+ }
3691
+ function RiskBadge({
3692
+ severity,
3693
+ smeVerified,
3694
+ labels,
3695
+ className
3696
+ }) {
3697
+ const copy = { ...DEFAULT_RISK_BADGE_LABELS, ...labels };
3698
+ return /* @__PURE__ */ jsxs("span", { className: cn("inline-flex flex-wrap items-center gap-2", className), children: [
3699
+ /* @__PURE__ */ jsx(
3700
+ "span",
3701
+ {
3702
+ className: cn(
3703
+ "inline-flex items-center rounded-md px-2 py-0.5 text-xs font-medium",
3704
+ tone[severity]
3705
+ ),
3706
+ children: severityLabel(severity, copy)
3707
+ }
3708
+ ),
3709
+ smeVerified === false ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: copy.unverifiedRule }) : null
3710
+ ] });
3711
+ }
3712
+ var DEFAULT_ENTITY_GRAPH_LABELS = {
3713
+ emptyTitle: "No beneficial owners",
3714
+ emptyDescription: "Ownership and control relationships will appear here.",
3715
+ ownership: "Ownership",
3716
+ otherMeans: "Other means",
3717
+ seniorManager: "Senior manager"
3718
+ };
3719
+ function controlLabel(controlKind, labels) {
3720
+ switch (controlKind) {
3721
+ case "ownership":
3722
+ return labels.ownership;
3723
+ case "other_means":
3724
+ return labels.otherMeans;
3725
+ case "senior_manager":
3726
+ return labels.seniorManager;
3727
+ default: {
3728
+ const exhaustive = controlKind;
3729
+ return exhaustive;
3730
+ }
3731
+ }
3732
+ }
3733
+ function formatOwnership(bps) {
3734
+ const percent = bps / 100;
3735
+ const text = Number.isInteger(percent) ? String(percent) : percent.toFixed(2);
3736
+ return `${text}% (${bps} bps)`;
3737
+ }
3738
+ function nodeLabel(nodes, id) {
3739
+ return nodes.find((node) => node.id === id)?.label ?? id;
3740
+ }
3741
+ function EntityGraph({
3742
+ nodes,
3743
+ edges,
3744
+ labels,
3745
+ emptyAction,
3746
+ className
3747
+ }) {
3748
+ const copy = { ...DEFAULT_ENTITY_GRAPH_LABELS, ...labels };
3749
+ if (nodes.length === 0 && edges.length === 0) {
3750
+ return /* @__PURE__ */ jsx(
3751
+ EmptyState,
3752
+ {
3753
+ className,
3754
+ title: copy.emptyTitle,
3755
+ description: copy.emptyDescription,
3756
+ action: emptyAction
3757
+ }
3758
+ );
3759
+ }
3760
+ const nodeIds = new Set(nodes.map((node) => node.id));
3761
+ const unattached = edges.filter((edge) => !nodeIds.has(edge.from));
3762
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-3", className), children: [
3763
+ nodes.map((node) => {
3764
+ const outgoing = edges.filter((edge) => edge.from === node.id);
3765
+ return /* @__PURE__ */ jsxs(
3766
+ "section",
3767
+ {
3768
+ className: "rounded-xl border border-border bg-card p-4 text-card-foreground",
3769
+ children: [
3770
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: node.label }),
3771
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: node.kind }),
3772
+ outgoing.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "mt-3 flex flex-col gap-1 text-sm", children: outgoing.map((edge) => /* @__PURE__ */ jsxs("li", { children: [
3773
+ node.label,
3774
+ " \u2192 ",
3775
+ nodeLabel(nodes, edge.to),
3776
+ ":",
3777
+ " ",
3778
+ controlLabel(edge.controlKind, copy),
3779
+ typeof edge.ownershipBps === "number" ? ` ${formatOwnership(edge.ownershipBps)}` : null
3780
+ ] }, `${edge.from}-${edge.to}-${edge.controlKind}`)) }) : null
3781
+ ]
3782
+ },
3783
+ node.id
3784
+ );
3785
+ }),
3786
+ unattached.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-1 text-sm", children: unattached.map((edge) => /* @__PURE__ */ jsxs("li", { children: [
3787
+ nodeLabel(nodes, edge.from),
3788
+ " \u2192 ",
3789
+ nodeLabel(nodes, edge.to),
3790
+ ":",
3791
+ " ",
3792
+ controlLabel(edge.controlKind, copy),
3793
+ typeof edge.ownershipBps === "number" ? ` ${formatOwnership(edge.ownershipBps)}` : null
3794
+ ] }, `${edge.from}-${edge.to}-${edge.controlKind}`)) }) : null
3795
+ ] });
3796
+ }
3797
+ var DEFAULT_MATCH_REVIEW_LABELS = {
3798
+ emptyTitle: "No match",
3799
+ emptyDescription: "There is no hit to review.",
3800
+ source: "Source",
3801
+ score: "Score",
3802
+ matchedFields: "Matched fields",
3803
+ noMatchedFields: "None",
3804
+ status: "Status",
3805
+ unverified: "Unverified",
3806
+ reason: "Reason",
3807
+ reasonPlaceholder: "Why this decision",
3808
+ confirm: "Confirm",
3809
+ dismiss: "Dismiss"
3810
+ };
3811
+ function sourceUrlIsHttps(url) {
3812
+ try {
3813
+ return new URL(url).protocol === "https:";
3814
+ } catch {
3815
+ return false;
3816
+ }
3817
+ }
3818
+ function matchClaimIsUnverified(hit) {
3819
+ if (hit.unverified === true) return true;
3820
+ return (hit.sources ?? []).some((source) => !sourceUrlIsHttps(source.url));
3821
+ }
3822
+ function appearanceOf(hit) {
3823
+ if (matchClaimIsUnverified(hit)) return "unverified";
3824
+ return hit.status;
3825
+ }
3826
+ var appearanceClass = {
3827
+ unverified: "border-dashed border-border bg-muted/40",
3828
+ pending: "border-border bg-card",
3829
+ confirmed: "border-primary bg-primary/5",
3830
+ dismissed: "border-border bg-muted/30"
3831
+ };
3832
+ function MatchReview({
3833
+ hit,
3834
+ onResolve,
3835
+ labels,
3836
+ sourceLabels,
3837
+ emptyAction,
3838
+ className
3839
+ }) {
3840
+ const copy = { ...DEFAULT_MATCH_REVIEW_LABELS, ...labels };
3841
+ const reasonId = useId();
3842
+ const [reason, setReason] = useState("");
3843
+ const trimmed = reason.trim();
3844
+ if (!hit) {
3845
+ return /* @__PURE__ */ jsx(
3846
+ EmptyState,
3847
+ {
3848
+ className,
3849
+ title: copy.emptyTitle,
3850
+ description: copy.emptyDescription,
3851
+ action: emptyAction
3852
+ }
3853
+ );
3854
+ }
3855
+ const appearance = appearanceOf(hit);
3856
+ const sourceLabel = sourceLabels?.[hit.source] ?? hit.source;
3857
+ function resolve(status) {
3858
+ if (trimmed.length === 0) return;
3859
+ onResolve(status, trimmed);
3860
+ }
3861
+ return /* @__PURE__ */ jsxs(
3862
+ "article",
3863
+ {
3864
+ "data-appearance": appearance,
3865
+ className: cn(
3866
+ "flex flex-col gap-3 rounded-xl border p-4 text-card-foreground motion-reduce:transition-none",
3867
+ appearanceClass[appearance],
3868
+ className
3869
+ ),
3870
+ children: [
3871
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
3872
+ /* @__PURE__ */ jsxs("p", { className: "text-sm font-medium", children: [
3873
+ copy.source,
3874
+ ": ",
3875
+ sourceLabel
3876
+ ] }),
3877
+ appearance === "unverified" ? /* @__PURE__ */ jsx("span", { className: "rounded-md bg-muted px-2 py-0.5 text-xs text-muted-foreground", children: copy.unverified }) : null
3878
+ ] }),
3879
+ /* @__PURE__ */ jsxs("dl", { className: "grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-sm", children: [
3880
+ typeof hit.score === "number" ? /* @__PURE__ */ jsxs(Fragment, { children: [
3881
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: copy.score }),
3882
+ /* @__PURE__ */ jsx("dd", { children: hit.score })
3883
+ ] }) : null,
3884
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: copy.matchedFields }),
3885
+ /* @__PURE__ */ jsx("dd", { children: hit.matchedFields.length > 0 ? hit.matchedFields.join(", ") : copy.noMatchedFields }),
3886
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: copy.status }),
3887
+ /* @__PURE__ */ jsx("dd", { children: hit.status })
3888
+ ] }),
3889
+ hit.sources && hit.sources.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-1 text-sm", children: hit.sources.map(
3890
+ (source) => sourceUrlIsHttps(source.url) ? /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
3891
+ "a",
3892
+ {
3893
+ className: "underline underline-offset-2",
3894
+ href: source.url,
3895
+ rel: "noreferrer",
3896
+ target: "_blank",
3897
+ children: source.title
3898
+ }
3899
+ ) }, `${source.title}-${source.url}`) : /* @__PURE__ */ jsxs("li", { children: [
3900
+ source.title,
3901
+ " (",
3902
+ copy.unverified,
3903
+ ")"
3904
+ ] }, `${source.title}-${source.url}`)
3905
+ ) }) : null,
3906
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
3907
+ /* @__PURE__ */ jsx("label", { className: "text-sm font-medium", htmlFor: reasonId, children: copy.reason }),
3908
+ /* @__PURE__ */ jsx(
3909
+ "textarea",
3910
+ {
3911
+ id: reasonId,
3912
+ className: "min-h-20 rounded-md border border-input bg-background px-3 py-2 text-sm",
3913
+ value: reason,
3914
+ placeholder: copy.reasonPlaceholder,
3915
+ onChange: (event) => setReason(event.target.value)
3916
+ }
3917
+ )
3918
+ ] }),
3919
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
3920
+ /* @__PURE__ */ jsx(
3921
+ Button,
3922
+ {
3923
+ type: "button",
3924
+ disabled: trimmed.length === 0,
3925
+ onClick: () => resolve("confirmed"),
3926
+ children: copy.confirm
3927
+ }
3928
+ ),
3929
+ /* @__PURE__ */ jsx(
3930
+ Button,
3931
+ {
3932
+ type: "button",
3933
+ variant: "outline",
3934
+ disabled: trimmed.length === 0,
3935
+ onClick: () => resolve("dismissed"),
3936
+ children: copy.dismiss
3937
+ }
3938
+ )
3939
+ ] })
3940
+ ]
3941
+ }
3942
+ );
3943
+ }
3944
+
3945
+ export { AuditTimeline, AvatarEditor, AvatarEditorDialog, COOKIE_CONSENT_MAX_AGE, COOKIE_CONSENT_NAME, COOKIE_CONSENT_VALUES, ConfirmDestructiveDialog, CookieConsentBanner, CostBreakdownCard, DEFAULT_AUDIT_TIMELINE_LABELS, DEFAULT_COOKIE_CONSENT_BANNER_LABELS, DEFAULT_EDITABLE_SECTION_LABELS, DEFAULT_ENTITY_GRAPH_LABELS, DEFAULT_FILE_DROPZONE_LABELS, DEFAULT_FILE_UPLOAD_LABELS, DEFAULT_FILE_UPLOAD_TRAY_LABELS, DEFAULT_LEGAL_ACCEPT_CHECKBOX_LABELS, DEFAULT_LEGAL_ACCEPT_DIALOG_LABELS, DEFAULT_MATCH_REVIEW_LABELS, DEFAULT_NOTIFICATION_BELL_LABELS, DEFAULT_RISK_BADGE_LABELS, DataList, DataListItem, DimensionBreakdownTable, EditableSection, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, EntityGraph, ErrorState, FieldMessage, FileDropzone, FileUploadMenu, FileUploadTray, LanguageContext, LanguageSwitcher, LegalAcceptCheckbox, LegalAcceptDialog, LegalMarkdown, LoadingState, MatchReview, MetricChartCard, MetricEmptyState, MetricSkeleton, NotificationBell, RiskBadge, SafeImage, SparklineStat, StatHighlightCard, THEME_INIT_SCRIPT, THEME_INIT_SCRIPT_ID, THEME_STORAGE_KEY, TIME_RANGE_OPTIONS, ThemeSwitcher, TimeRangePicker, avatarFallbackStyle, cn, cookieConsentAllowsReplay, cookieDomainFromHostname, createMultipartUploadController, defaultLanguages, fileMatchesAccept, getAvatarColors, getInitials, isCookieConsentValue, isEmbeddedWindow, matchClaimIsUnverified, parseCookieConsent, partRanges, progressFromBytes, readCookieConsent, serializeCookieConsent, sourceUrlIsHttps, useEditableSection, useLanguageContext, useMediaQuery2 as useMediaQuery, writeCookieConsent };
3946
+ //# sourceMappingURL=index.js.map
3947
+ //# sourceMappingURL=index.js.map