@olwiba/ui 0.1.13 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,13 +1,15 @@
1
1
  import { cn } from './chunk-AMWLSHD6.js';
2
2
  export { MdxContent, cn } from './chunk-AMWLSHD6.js';
3
- import * as React29 from 'react';
3
+ import * as React30 from 'react';
4
4
  import { useRef, useEffect, useState } from 'react';
5
- import { Button as Button$1, Card as Card$1, Input as Input$1, Textarea as Textarea$1, Checkbox as Checkbox$1, Switch as Switch$1, useIsMobile, UIVariantProvider, Badge as Badge$1, cn as cn$1, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Avatar, AvatarImage, AvatarFallback, useUIVariant, Separator, Accordion, AccordionItem, AccordionTrigger, AccordionContent, Label, Sheet, SheetTrigger, SheetContent, StatusIndicator, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandSeparator, CommandGroup, CommandItem, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, ContextMenu as ContextMenu$1, ContextMenuTrigger, ContextMenuContent, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, Spinner, Popover, PopoverTrigger, PopoverContent, SidebarProvider, SidebarInset, CardHeader, CardTitle, CardDescription, CardContent, ContextMenuSeparator, ContextMenuLabel, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuItem, ContextMenuShortcut, Sidebar, SidebarHeader, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarFooter, SidebarTrigger, useSidebar, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup, DropdownMenuItem } from '@olwiba/cn';
6
- export { CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@olwiba/cn';
5
+ import { Button as Button$1, Card as Card$1, Input as Input$1, Textarea as Textarea$1, Checkbox as Checkbox$1, Switch as Switch$1, useIsMobile, UIVariantProvider, Badge as Badge$1, cn as cn$1, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Avatar, AvatarImage, AvatarFallback, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, Dialog, DialogTrigger, Progress, useUIVariant, Separator, Accordion, AccordionItem, AccordionTrigger, AccordionContent, Label, Sheet, SheetTrigger, SheetContent, StatusIndicator, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandSeparator, CommandGroup, CommandItem, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, ContextMenu as ContextMenu$1, ContextMenuTrigger, ContextMenuContent, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, CommandShortcut, Spinner, Popover, PopoverTrigger, PopoverContent, SidebarProvider, SidebarInset, CardHeader, CardTitle, CardDescription, CardContent, InputOTP, InputOTPGroup, InputOTPSlot, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose, ContextMenuSeparator, ContextMenuLabel, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuItem, ContextMenuShortcut, Sidebar, SidebarHeader, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarFooter, SidebarTrigger, useSidebar, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup } from '@olwiba/cn';
6
+ export { CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Enchanted } from '@olwiba/cn';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
- import { ArrowLeft, ArrowUpRight, X, ArrowRight, ChevronLeft, ChevronRight, Sparkles, Rocket, Check, Minus, Quote, Star, Twitter, Github, Linkedin, Mail, MessageSquare, ChevronDown, Send, Menu, Search, TrendingUp, TrendingDown, Sun, Moon, Palette, AlertTriangle, Loader2, Layers, LayoutGrid, ShieldCheck, Building2, MoreVerticalIcon, CreditCardIcon, BellIcon, LogOutIcon } from 'lucide-react';
8
+ import { Search, ArrowUp, ArrowDown, ArrowUpDown, ChevronLeft, ChevronRight, MoreHorizontal, UserPlus, Download, CreditCard, Check, ArrowLeft, ArrowUpRight, X, ArrowRight, Sparkles, Rocket, Minus, Quote, Star, Twitter, Github, Linkedin, Mail, MessageSquare, ChevronDown, Send, Menu, UploadCloud, File, Loader2, CheckCircle2, AlertCircle, TrendingUp, TrendingDown, Sun, Moon, Palette, AlertTriangle, Inbox, Info, Layers, LayoutGrid, ShieldCheck, Building2, MoreVerticalIcon, CreditCardIcon, BellIcon, LogOutIcon } from 'lucide-react';
9
+ import { useReactTable, getPaginationRowModel, getSortedRowModel, getCoreRowModel, flexRender } from '@tanstack/react-table';
10
+ import { toast } from 'sonner';
9
11
 
10
- var OlwibaUIContext = React29.createContext({
12
+ var OlwibaUIContext = React30.createContext({
11
13
  isMobile: false,
12
14
  mode: "default",
13
15
  setMode: () => {
@@ -16,17 +18,17 @@ var OlwibaUIContext = React29.createContext({
16
18
  function OlwibaUIProvider({ children, isMobile: isMobileProp, mode: initialMode = "default" }) {
17
19
  const detectedMobile = useIsMobile();
18
20
  const isMobile = isMobileProp ?? detectedMobile;
19
- const [mode, setMode] = React29.useState(initialMode);
21
+ const [mode, setMode] = React30.useState(initialMode);
20
22
  const variant = mode !== "default" ? mode : void 0;
21
23
  return /* @__PURE__ */ jsx(OlwibaUIContext.Provider, { value: { isMobile, mode, setMode }, children: /* @__PURE__ */ jsx(UIVariantProvider, { mode: variant, children }) });
22
24
  }
23
25
  function useOlwibaUI() {
24
- return React29.useContext(OlwibaUIContext);
26
+ return React30.useContext(OlwibaUIContext);
25
27
  }
26
28
  function useUIMode() {
27
- return React29.useContext(OlwibaUIContext).mode;
29
+ return React30.useContext(OlwibaUIContext).mode;
28
30
  }
29
- var Button = React29.forwardRef(
31
+ var Button = React30.forwardRef(
30
32
  ({ mode, ...props }, ref) => {
31
33
  const ctxMode = useUIMode();
32
34
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -34,7 +36,7 @@ var Button = React29.forwardRef(
34
36
  }
35
37
  );
36
38
  Button.displayName = "Button";
37
- var Card = React29.forwardRef(
39
+ var Card = React30.forwardRef(
38
40
  ({ mode, ...props }, ref) => {
39
41
  const ctxMode = useUIMode();
40
42
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -45,7 +47,7 @@ Card.displayName = "Card";
45
47
  function Badge(props) {
46
48
  return /* @__PURE__ */ jsx(Badge$1, { ...props });
47
49
  }
48
- var Input = React29.forwardRef(
50
+ var Input = React30.forwardRef(
49
51
  ({ mode, ...props }, ref) => {
50
52
  const ctxMode = useUIMode();
51
53
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -53,7 +55,7 @@ var Input = React29.forwardRef(
53
55
  }
54
56
  );
55
57
  Input.displayName = "Input";
56
- var Textarea = React29.forwardRef(
58
+ var Textarea = React30.forwardRef(
57
59
  ({ mode, ...props }, ref) => {
58
60
  const ctxMode = useUIMode();
59
61
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
@@ -61,13 +63,13 @@ var Textarea = React29.forwardRef(
61
63
  }
62
64
  );
63
65
  Textarea.displayName = "Textarea";
64
- var Checkbox = React29.forwardRef(({ mode, ...props }, ref) => {
66
+ var Checkbox = React30.forwardRef(({ mode, ...props }, ref) => {
65
67
  const ctxMode = useUIMode();
66
68
  const resolvedMode = mode ?? (ctxMode !== "default" ? ctxMode : void 0);
67
69
  return /* @__PURE__ */ jsx(Checkbox$1, { mode: resolvedMode, ...props, ref });
68
70
  });
69
71
  Checkbox.displayName = "Checkbox";
70
- var Switch = React29.forwardRef(({ mode: _mode, ...props }, ref) => {
72
+ var Switch = React30.forwardRef(({ mode: _mode, ...props }, ref) => {
71
73
  return /* @__PURE__ */ jsx(Switch$1, { ...props, ref });
72
74
  });
73
75
  Switch.displayName = "Switch";
@@ -481,6 +483,13 @@ function SplitAuth({ children, panel }) {
481
483
  /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center p-6 lg:p-10", children })
482
484
  ] });
483
485
  }
486
+ var authCopy = {
487
+ signin: { title: "Sign in", description: "Enter your email and password to continue.", submit: "Sign in" },
488
+ signup: { title: "Create an account", description: "Enter your details to create your account.", submit: "Create account" },
489
+ "forgot-password": { title: "Reset your password", description: "Enter the email on your account and we\u2019ll send you a link to reset your password.", submit: "Send reset link" },
490
+ "reset-password": { title: "Choose a new password", description: "Your new password must be different from previous passwords.", submit: "Reset password" },
491
+ verify: { title: "Enter your code", description: "We sent a verification code to your email.", submit: "Verify" }
492
+ };
484
493
  function DefaultForm({
485
494
  mode = "signin",
486
495
  onSubmit,
@@ -488,8 +497,12 @@ function DefaultForm({
488
497
  signUpHref = "#",
489
498
  signInHref = "#",
490
499
  forgotPasswordHref = "#",
500
+ onResend,
501
+ destination,
502
+ codeLength = 6,
491
503
  brand,
492
504
  error,
505
+ success,
493
506
  loading,
494
507
  renderLink = defaultRenderLink2,
495
508
  footer,
@@ -497,15 +510,24 @@ function DefaultForm({
497
510
  defaultPassword
498
511
  }) {
499
512
  const isSignUp = mode === "signup";
513
+ const isForgotPassword = mode === "forgot-password";
514
+ const isResetPassword = mode === "reset-password";
515
+ const isVerify = mode === "verify";
516
+ const [code, setCode] = React30.useState("");
500
517
  const hasPrefill = !!(defaultEmail || defaultPassword);
501
518
  const prefillStyle = (active) => active ? { animation: "auth-prefill 1.6s ease-out 0.35s 1 both" } : void 0;
519
+ const copy = authCopy[mode];
502
520
  return /* @__PURE__ */ jsxs(Card, { className: "w-full", children: [
503
521
  hasPrefill && // Theme tokens hold full color values (oklch), so opacity must come from color-mix, not hsl(var()/a)
504
522
  /* @__PURE__ */ jsx("style", { children: `@keyframes auth-prefill{0%{box-shadow:0 0 0 0 transparent}40%{box-shadow:0 0 0 3px color-mix(in oklab,var(--primary) 45%,transparent),0 0 16px 2px color-mix(in oklab,var(--primary) 35%,transparent)}100%{box-shadow:0 0 0 0 transparent}}` }),
505
523
  /* @__PURE__ */ jsxs(CardHeader, { children: [
506
524
  brand && /* @__PURE__ */ jsx("div", { className: "mb-2", children: brand }),
507
- /* @__PURE__ */ jsx(CardTitle, { children: isSignUp ? "Create an account" : "Sign in" }),
508
- /* @__PURE__ */ jsx(CardDescription, { children: isSignUp ? "Enter your details to create your account." : "Enter your email and password to continue." })
525
+ /* @__PURE__ */ jsx(CardTitle, { children: copy.title }),
526
+ /* @__PURE__ */ jsx(CardDescription, { children: isVerify && destination ? /* @__PURE__ */ jsxs(Fragment, { children: [
527
+ "We sent a verification code to ",
528
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: destination }),
529
+ "."
530
+ ] }) : copy.description })
509
531
  ] }),
510
532
  /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
511
533
  /* @__PURE__ */ jsxs("form", { onSubmit, className: "space-y-4", children: [
@@ -513,7 +535,7 @@ function DefaultForm({
513
535
  /* @__PURE__ */ jsx(Label, { htmlFor: "auth-name", children: "Name" }),
514
536
  /* @__PURE__ */ jsx(Input, { id: "auth-name", name: "name", type: "text", placeholder: "Your name", autoComplete: "name" })
515
537
  ] }),
516
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
538
+ (mode === "signin" || mode === "signup" || isForgotPassword) && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
517
539
  /* @__PURE__ */ jsx(Label, { htmlFor: "auth-email", children: "Email address" }),
518
540
  /* @__PURE__ */ jsx(
519
541
  Input,
@@ -528,7 +550,7 @@ function DefaultForm({
528
550
  }
529
551
  )
530
552
  ] }),
531
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
553
+ (mode === "signin" || mode === "signup") && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
532
554
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
533
555
  /* @__PURE__ */ jsx(Label, { htmlFor: "auth-password", children: "Password" }),
534
556
  !isSignUp && forgotPasswordHref && renderLink({
@@ -550,13 +572,33 @@ function DefaultForm({
550
572
  }
551
573
  )
552
574
  ] }),
575
+ isResetPassword && /* @__PURE__ */ jsxs(Fragment, { children: [
576
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
577
+ /* @__PURE__ */ jsx(Label, { htmlFor: "auth-new-password", children: "New password" }),
578
+ /* @__PURE__ */ jsx(Input, { id: "auth-new-password", name: "password", type: "password", placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", autoComplete: "new-password" })
579
+ ] }),
580
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
581
+ /* @__PURE__ */ jsx(Label, { htmlFor: "auth-confirm-password", children: "Confirm password" }),
582
+ /* @__PURE__ */ jsx(Input, { id: "auth-confirm-password", name: "confirmPassword", type: "password", placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", autoComplete: "new-password" })
583
+ ] })
584
+ ] }),
585
+ isVerify && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
586
+ /* @__PURE__ */ jsx("input", { type: "hidden", name: "code", value: code }),
587
+ /* @__PURE__ */ jsx("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsx(InputOTP, { maxLength: codeLength, value: code, onChange: setCode, containerClassName: "justify-center", children: /* @__PURE__ */ jsx(InputOTPGroup, { children: Array.from({ length: codeLength }).map((_, i) => /* @__PURE__ */ jsx(InputOTPSlot, { index: i }, i)) }) }) })
588
+ ] }),
553
589
  error && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: error }),
590
+ success && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-primary", children: success }),
554
591
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
555
- /* @__PURE__ */ jsx(Button, { type: "submit", className: "w-full", disabled: loading, children: loading ? "Please wait\u2026" : isSignUp ? "Create account" : "Sign in" }),
556
- onSso && /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", className: "w-full", onClick: onSso, disabled: loading, children: "Use SSO" })
592
+ /* @__PURE__ */ jsx(Button, { type: "submit", className: "w-full", disabled: loading || isVerify && code.length < codeLength, children: loading ? "Please wait\u2026" : copy.submit }),
593
+ onSso && mode === "signin" && /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", className: "w-full", onClick: onSso, disabled: loading, children: "Use SSO" })
557
594
  ] })
558
595
  ] }),
559
- /* @__PURE__ */ jsx("p", { className: "text-center text-xs text-muted-foreground", children: isSignUp ? /* @__PURE__ */ jsxs(Fragment, { children: [
596
+ isVerify && onResend && /* @__PURE__ */ jsxs("p", { className: "text-center text-xs text-muted-foreground", children: [
597
+ "Didn\u2019t get a code?",
598
+ " ",
599
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: onResend, className: "text-foreground underline underline-offset-4", children: "Resend" })
600
+ ] }),
601
+ (mode === "signin" || mode === "signup") && /* @__PURE__ */ jsx("p", { className: "text-center text-xs text-muted-foreground", children: isSignUp ? /* @__PURE__ */ jsxs(Fragment, { children: [
560
602
  "Already have an account?",
561
603
  " ",
562
604
  renderLink({ href: signInHref, className: "text-foreground underline underline-offset-4", children: "Sign in" })
@@ -565,6 +607,7 @@ function DefaultForm({
565
607
  " ",
566
608
  renderLink({ href: signUpHref, className: "text-foreground underline underline-offset-4", children: "Create an account" })
567
609
  ] }) }),
610
+ (isForgotPassword || isResetPassword || isVerify) && /* @__PURE__ */ jsx("p", { className: "text-center text-xs text-muted-foreground", children: renderLink({ href: signInHref, className: "text-foreground underline underline-offset-4", children: "Back to sign in" }) }),
568
611
  footer && /* @__PURE__ */ jsx("div", { children: footer })
569
612
  ] })
570
613
  ] });
@@ -584,6 +627,386 @@ function AuthSection({
584
627
  const form = children ?? /* @__PURE__ */ jsx(DefaultForm, { ...restFormProps });
585
628
  return /* @__PURE__ */ jsx(CenteredAuth, { brand, className, children: form });
586
629
  }
630
+ function SettingsSection({ title, description, children, danger = false, className }) {
631
+ return /* @__PURE__ */ jsxs("div", { className: cn$1("grid grid-cols-1 gap-x-8 gap-y-6 py-10 sm:grid-cols-3", className), children: [
632
+ /* @__PURE__ */ jsxs("div", { children: [
633
+ /* @__PURE__ */ jsx("h2", { className: cn$1("text-base font-semibold", danger && "text-destructive"), children: title }),
634
+ description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description })
635
+ ] }),
636
+ /* @__PURE__ */ jsx("div", { className: "sm:col-span-2", children })
637
+ ] });
638
+ }
639
+ var selectColumn = {
640
+ id: "__select",
641
+ header: ({ table }) => /* @__PURE__ */ jsx(
642
+ Checkbox$1,
643
+ {
644
+ checked: table.getIsAllPageRowsSelected() || table.getIsSomePageRowsSelected() && "indeterminate",
645
+ onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value),
646
+ "aria-label": "Select all"
647
+ }
648
+ ),
649
+ cell: ({ row }) => /* @__PURE__ */ jsx(
650
+ Checkbox$1,
651
+ {
652
+ checked: row.getIsSelected(),
653
+ onCheckedChange: (value) => row.toggleSelected(!!value),
654
+ onClick: (e) => e.stopPropagation(),
655
+ "aria-label": "Select row"
656
+ }
657
+ ),
658
+ enableSorting: false
659
+ };
660
+ function DataTable({
661
+ columns,
662
+ data,
663
+ searchKey,
664
+ searchPlaceholder = "Search\u2026",
665
+ selectable = false,
666
+ onSelectionChange,
667
+ pageSize = 10,
668
+ toolbar,
669
+ onRowClick,
670
+ emptyMessage = "No results.",
671
+ className
672
+ }) {
673
+ const [sorting, setSorting] = React30.useState([]);
674
+ const [rowSelection, setRowSelection] = React30.useState({});
675
+ const [globalFilter, setGlobalFilter] = React30.useState("");
676
+ const allColumns = React30.useMemo(
677
+ () => selectable ? [selectColumn, ...columns] : columns,
678
+ [selectable, columns]
679
+ );
680
+ const table = useReactTable({
681
+ data,
682
+ columns: allColumns,
683
+ state: { sorting, rowSelection, globalFilter },
684
+ onSortingChange: setSorting,
685
+ onRowSelectionChange: setRowSelection,
686
+ onGlobalFilterChange: setGlobalFilter,
687
+ globalFilterFn: (row, _columnId, filterValue) => {
688
+ if (!searchKey) return true;
689
+ const value = row.getValue(searchKey);
690
+ return String(value ?? "").toLowerCase().includes(String(filterValue).toLowerCase());
691
+ },
692
+ getCoreRowModel: getCoreRowModel(),
693
+ getSortedRowModel: getSortedRowModel(),
694
+ ...pageSize > 0 ? { getPaginationRowModel: getPaginationRowModel() } : {},
695
+ initialState: pageSize > 0 ? { pagination: { pageSize } } : void 0,
696
+ enableRowSelection: selectable
697
+ });
698
+ React30.useEffect(() => {
699
+ if (!onSelectionChange) return;
700
+ onSelectionChange(table.getSelectedRowModel().rows.map((r) => r.original));
701
+ }, [rowSelection]);
702
+ const rows = table.getRowModel().rows;
703
+ return /* @__PURE__ */ jsxs("div", { className: cn$1("space-y-4", className), children: [
704
+ (searchKey || toolbar) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
705
+ searchKey ? /* @__PURE__ */ jsxs("div", { className: "relative max-w-sm flex-1", children: [
706
+ /* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" }),
707
+ /* @__PURE__ */ jsx(
708
+ Input,
709
+ {
710
+ value: globalFilter,
711
+ onChange: (e) => setGlobalFilter(e.target.value),
712
+ placeholder: searchPlaceholder,
713
+ className: "pl-8"
714
+ }
715
+ )
716
+ ] }) : /* @__PURE__ */ jsx("div", {}),
717
+ toolbar
718
+ ] }),
719
+ /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-lg border", children: /* @__PURE__ */ jsxs(Table, { children: [
720
+ /* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { children: headerGroup.headers.map((header) => {
721
+ const canSort = header.column.getCanSort();
722
+ const sortDir = header.column.getIsSorted();
723
+ return /* @__PURE__ */ jsx(TableHead, { children: header.isPlaceholder ? null : canSort ? /* @__PURE__ */ jsxs(
724
+ "button",
725
+ {
726
+ type: "button",
727
+ className: "flex items-center gap-1.5 font-medium hover:text-foreground",
728
+ onClick: header.column.getToggleSortingHandler(),
729
+ children: [
730
+ flexRender(header.column.columnDef.header, header.getContext()),
731
+ sortDir === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "size-3.5" }) : sortDir === "desc" ? /* @__PURE__ */ jsx(ArrowDown, { className: "size-3.5" }) : /* @__PURE__ */ jsx(ArrowUpDown, { className: "size-3.5 text-muted-foreground/50" })
732
+ ]
733
+ }
734
+ ) : flexRender(header.column.columnDef.header, header.getContext()) }, header.id);
735
+ }) }, headerGroup.id)) }),
736
+ /* @__PURE__ */ jsx(TableBody, { children: rows.length ? rows.map((row) => /* @__PURE__ */ jsx(
737
+ TableRow,
738
+ {
739
+ "data-state": row.getIsSelected() ? "selected" : void 0,
740
+ onClick: () => onRowClick?.(row.original),
741
+ className: cn$1(onRowClick && "cursor-pointer"),
742
+ children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
743
+ },
744
+ row.id
745
+ )) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: allColumns.length, className: "h-24 text-center text-muted-foreground", children: emptyMessage }) }) })
746
+ ] }) }),
747
+ pageSize > 0 && table.getPageCount() > 1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
748
+ /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
749
+ selectable && `${table.getFilteredSelectedRowModel().rows.length} of ${table.getFilteredRowModel().rows.length} selected \xB7 `,
750
+ "Page ",
751
+ table.getState().pagination.pageIndex + 1,
752
+ " of ",
753
+ table.getPageCount()
754
+ ] }),
755
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
756
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => table.previousPage(), disabled: !table.getCanPreviousPage(), children: [
757
+ /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" }),
758
+ " Previous"
759
+ ] }),
760
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => table.nextPage(), disabled: !table.getCanNextPage(), children: [
761
+ "Next ",
762
+ /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
763
+ ] })
764
+ ] })
765
+ ] })
766
+ ] });
767
+ }
768
+ function InviteDialog({ roles, onInvite }) {
769
+ const [email, setEmail] = React30.useState("");
770
+ const [role, setRole] = React30.useState(roles[roles.length - 1] ?? roles[0]);
771
+ return /* @__PURE__ */ jsxs(DialogContent, { children: [
772
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
773
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Invite a team member" }),
774
+ /* @__PURE__ */ jsx(DialogDescription, { children: "They\u2019ll get an email invite to join this workspace." })
775
+ ] }),
776
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
777
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
778
+ /* @__PURE__ */ jsx(Label, { htmlFor: "invite-email", children: "Email address" }),
779
+ /* @__PURE__ */ jsx(Input, { id: "invite-email", type: "email", placeholder: "name@company.com", value: email, onChange: (e) => setEmail(e.target.value) })
780
+ ] }),
781
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
782
+ /* @__PURE__ */ jsx(Label, { children: "Role" }),
783
+ /* @__PURE__ */ jsxs(Select, { value: role, onValueChange: setRole, children: [
784
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
785
+ /* @__PURE__ */ jsx(SelectContent, { children: roles.map((r) => /* @__PURE__ */ jsx(SelectItem, { value: r, children: r }, r)) })
786
+ ] })
787
+ ] })
788
+ ] }),
789
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
790
+ /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", children: "Cancel" }) }),
791
+ /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { disabled: !email, onClick: () => onInvite?.(email, role), children: "Send invite" }) })
792
+ ] })
793
+ ] });
794
+ }
795
+ function TeamMembersPanel({
796
+ members,
797
+ roles = ["Owner", "Admin", "Member"],
798
+ onInvite,
799
+ onRoleChange,
800
+ onRemove,
801
+ title = "Team members",
802
+ description = "Manage who has access to this workspace."
803
+ }) {
804
+ const columns = React30.useMemo(() => [
805
+ {
806
+ accessorKey: "name",
807
+ header: "Name",
808
+ cell: ({ row }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
809
+ /* @__PURE__ */ jsxs(Avatar, { className: "size-8", children: [
810
+ /* @__PURE__ */ jsx(AvatarImage, { src: row.original.avatar, alt: "" }),
811
+ /* @__PURE__ */ jsx(AvatarFallback, { children: row.original.name.slice(0, 2).toUpperCase() })
812
+ ] }),
813
+ /* @__PURE__ */ jsxs("div", { children: [
814
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium leading-none", children: row.original.name }),
815
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: row.original.email })
816
+ ] })
817
+ ] })
818
+ },
819
+ {
820
+ accessorKey: "role",
821
+ header: "Role",
822
+ cell: ({ row }) => onRoleChange ? /* @__PURE__ */ jsxs(Select, { value: row.original.role, onValueChange: (value) => onRoleChange(row.original.id, value), children: [
823
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 w-32", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
824
+ /* @__PURE__ */ jsx(SelectContent, { children: roles.map((r) => /* @__PURE__ */ jsx(SelectItem, { value: r, children: r }, r)) })
825
+ ] }) : /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: row.original.role })
826
+ },
827
+ {
828
+ id: "actions",
829
+ header: "",
830
+ cell: ({ row }) => /* @__PURE__ */ jsxs(DropdownMenu, { children: [
831
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: "size-8", children: [
832
+ /* @__PURE__ */ jsx(MoreHorizontal, { className: "size-4" }),
833
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Row actions" })
834
+ ] }) }),
835
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: /* @__PURE__ */ jsx(DropdownMenuItem, { className: "text-destructive", onClick: () => onRemove?.(row.original.id), children: "Remove member" }) })
836
+ ] }),
837
+ enableSorting: false
838
+ }
839
+ ], [roles, onRoleChange, onRemove]);
840
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
841
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
842
+ /* @__PURE__ */ jsxs("div", { children: [
843
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold", children: title }),
844
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description })
845
+ ] }),
846
+ /* @__PURE__ */ jsxs(Dialog, { children: [
847
+ /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { children: [
848
+ /* @__PURE__ */ jsx(UserPlus, { className: "size-4" }),
849
+ " Invite member"
850
+ ] }) }),
851
+ /* @__PURE__ */ jsx(InviteDialog, { roles, onInvite })
852
+ ] })
853
+ ] }),
854
+ /* @__PURE__ */ jsx(DataTable, { columns, data: members, searchKey: "name", searchPlaceholder: "Search members\u2026", pageSize: 0 })
855
+ ] });
856
+ }
857
+ var invoiceStatusVariant = {
858
+ paid: "secondary",
859
+ open: "outline",
860
+ void: "destructive"
861
+ };
862
+ function BillingPanel({
863
+ planName,
864
+ planPrice,
865
+ renewalDate,
866
+ usage,
867
+ paymentMethod,
868
+ invoices,
869
+ onManagePlan,
870
+ onUpdatePaymentMethod,
871
+ className
872
+ }) {
873
+ const invoiceColumns = React30.useMemo(() => [
874
+ { accessorKey: "date", header: "Date" },
875
+ { accessorKey: "amount", header: "Amount" },
876
+ {
877
+ accessorKey: "status",
878
+ header: "Status",
879
+ cell: ({ row }) => /* @__PURE__ */ jsx(Badge$1, { variant: invoiceStatusVariant[row.original.status], className: "capitalize", children: row.original.status })
880
+ },
881
+ {
882
+ id: "download",
883
+ header: "",
884
+ cell: ({ row }) => row.original.downloadUrl ? /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", className: "size-8", asChild: true, children: /* @__PURE__ */ jsxs("a", { href: row.original.downloadUrl, download: true, children: [
885
+ /* @__PURE__ */ jsx(Download, { className: "size-4" }),
886
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Download invoice" })
887
+ ] }) }) : null,
888
+ enableSorting: false
889
+ }
890
+ ], []);
891
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsxs("div", { className: "divide-y divide-border", children: [
892
+ /* @__PURE__ */ jsxs(SettingsSection, { title: "Current plan", description: "Your subscription and usage against plan limits.", children: [
893
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 rounded-lg border bg-card/60 p-4", children: [
894
+ /* @__PURE__ */ jsxs("div", { children: [
895
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
896
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold", children: planName }),
897
+ planPrice && /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: planPrice })
898
+ ] }),
899
+ renewalDate && /* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm text-muted-foreground", children: [
900
+ "Renews ",
901
+ renewalDate
902
+ ] })
903
+ ] }),
904
+ onManagePlan && /* @__PURE__ */ jsx(Button, { onClick: onManagePlan, children: "Manage plan" })
905
+ ] }),
906
+ usage && usage.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-4", children: usage.map((metric) => /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
907
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", children: [
908
+ /* @__PURE__ */ jsx("span", { children: metric.label }),
909
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
910
+ metric.used,
911
+ metric.unit,
912
+ " / ",
913
+ metric.limit,
914
+ metric.unit
915
+ ] })
916
+ ] }),
917
+ /* @__PURE__ */ jsx(Progress, { value: metric.used / metric.limit * 100 })
918
+ ] }, metric.label)) })
919
+ ] }),
920
+ paymentMethod && /* @__PURE__ */ jsx(SettingsSection, { title: "Payment method", description: "Used for your subscription renewal.", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border bg-card/60 p-4", children: [
921
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
922
+ /* @__PURE__ */ jsx(CreditCard, { className: "size-5 text-muted-foreground" }),
923
+ /* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
924
+ /* @__PURE__ */ jsx("span", { className: "font-medium capitalize", children: paymentMethod.brand }),
925
+ " ending in ",
926
+ paymentMethod.last4,
927
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
928
+ " \xB7 expires ",
929
+ paymentMethod.expiry
930
+ ] })
931
+ ] })
932
+ ] }),
933
+ onUpdatePaymentMethod && /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onUpdatePaymentMethod, children: "Update" })
934
+ ] }) }),
935
+ invoices && invoices.length > 0 && /* @__PURE__ */ jsx(SettingsSection, { title: "Billing history", description: "Download past invoices for your records.", children: /* @__PURE__ */ jsx(DataTable, { columns: invoiceColumns, data: invoices, pageSize: 5 }) })
936
+ ] }) });
937
+ }
938
+ function OnboardingWizard({
939
+ steps,
940
+ onComplete,
941
+ onStepChange,
942
+ step: stepProp,
943
+ onStepIndexChange,
944
+ completeLabel = "Finish",
945
+ className
946
+ }) {
947
+ const [internalStep, setInternalStep] = React30.useState(0);
948
+ const [error, setError] = React30.useState(null);
949
+ const [pending, setPending] = React30.useState(false);
950
+ const index = stepProp ?? internalStep;
951
+ const current = steps[index];
952
+ const isLast = index === steps.length - 1;
953
+ const goTo = (next) => {
954
+ setError(null);
955
+ if (stepProp === void 0) setInternalStep(next);
956
+ onStepIndexChange?.(next);
957
+ onStepChange?.(next);
958
+ };
959
+ const handleNext = async () => {
960
+ if (current.onNext) {
961
+ setPending(true);
962
+ const result = await current.onNext();
963
+ setPending(false);
964
+ if (result === false) {
965
+ setError("Please complete this step before continuing.");
966
+ return;
967
+ }
968
+ if (typeof result === "string") {
969
+ setError(result);
970
+ return;
971
+ }
972
+ }
973
+ if (isLast) {
974
+ onComplete?.();
975
+ } else {
976
+ goTo(index + 1);
977
+ }
978
+ };
979
+ const handleBack = () => {
980
+ if (index > 0) goTo(index - 1);
981
+ };
982
+ return /* @__PURE__ */ jsxs("div", { className: cn$1("w-full max-w-lg space-y-8", className), children: [
983
+ /* @__PURE__ */ jsx("ol", { className: "flex items-center", children: steps.map((step, i) => /* @__PURE__ */ jsxs("li", { className: cn$1("flex items-center", i !== steps.length - 1 && "flex-1"), children: [
984
+ /* @__PURE__ */ jsx(
985
+ "div",
986
+ {
987
+ className: cn$1(
988
+ "flex size-8 shrink-0 items-center justify-center rounded-full border text-xs font-medium",
989
+ i < index ? "border-primary bg-primary text-primary-foreground" : i === index ? "border-primary text-primary" : "border-border text-muted-foreground"
990
+ ),
991
+ children: i < index ? /* @__PURE__ */ jsx(Check, { className: "size-4" }) : i + 1
992
+ }
993
+ ),
994
+ i !== steps.length - 1 && /* @__PURE__ */ jsx("div", { className: cn$1("mx-2 h-px flex-1", i < index ? "bg-primary" : "bg-border") })
995
+ ] }, step.id)) }),
996
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
997
+ /* @__PURE__ */ jsxs("div", { children: [
998
+ /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold tracking-tight", children: current.title }),
999
+ current.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: current.description })
1000
+ ] }),
1001
+ /* @__PURE__ */ jsx("div", { children: current.content }),
1002
+ error && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: error })
1003
+ ] }),
1004
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1005
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: handleBack, disabled: index === 0 || pending, children: "Back" }),
1006
+ /* @__PURE__ */ jsx(Button, { type: "button", onClick: handleNext, disabled: pending, children: pending ? "Please wait\u2026" : isLast ? completeLabel : "Continue" })
1007
+ ] })
1008
+ ] });
1009
+ }
587
1010
  function EmptyState({ icon: Icon, title, description, action, className, ...props }) {
588
1011
  return /* @__PURE__ */ jsxs(
589
1012
  "div",
@@ -833,9 +1256,9 @@ function FadeIn({
833
1256
  style,
834
1257
  ...props
835
1258
  }) {
836
- const ref = React29.useRef(null);
837
- const [visible, setVisible] = React29.useState(false);
838
- React29.useEffect(() => {
1259
+ const ref = React30.useRef(null);
1260
+ const [visible, setVisible] = React30.useState(false);
1261
+ React30.useEffect(() => {
839
1262
  const el = ref.current;
840
1263
  if (!el) return;
841
1264
  const observer = new IntersectionObserver(
@@ -1038,9 +1461,9 @@ function StaggerChildren({
1038
1461
  className,
1039
1462
  ...props
1040
1463
  }) {
1041
- const ref = React29.useRef(null);
1042
- const [visible, setVisible] = React29.useState(false);
1043
- React29.useEffect(() => {
1464
+ const ref = React30.useRef(null);
1465
+ const [visible, setVisible] = React30.useState(false);
1466
+ React30.useEffect(() => {
1044
1467
  const el = ref.current;
1045
1468
  if (!el) return;
1046
1469
  const observer = new IntersectionObserver(
@@ -1058,7 +1481,7 @@ function StaggerChildren({
1058
1481
  return () => observer.disconnect();
1059
1482
  }, [once]);
1060
1483
  const [tx, ty] = translateMap2[direction];
1061
- return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React29.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
1484
+ return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React30.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
1062
1485
  "div",
1063
1486
  {
1064
1487
  className: "h-full",
@@ -1233,16 +1656,16 @@ function CarouselSection({
1233
1656
  features
1234
1657
  }) {
1235
1658
  const mode = useUIVariant();
1236
- const trackRef = React29.useRef(null);
1237
- const [canPrev, setCanPrev] = React29.useState(false);
1238
- const [canNext, setCanNext] = React29.useState(true);
1239
- const updateScrollState = React29.useCallback(() => {
1659
+ const trackRef = React30.useRef(null);
1660
+ const [canPrev, setCanPrev] = React30.useState(false);
1661
+ const [canNext, setCanNext] = React30.useState(true);
1662
+ const updateScrollState = React30.useCallback(() => {
1240
1663
  const track = trackRef.current;
1241
1664
  if (!track) return;
1242
1665
  setCanPrev(track.scrollLeft > 8);
1243
1666
  setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
1244
1667
  }, []);
1245
- React29.useEffect(() => {
1668
+ React30.useEffect(() => {
1246
1669
  const track = trackRef.current;
1247
1670
  if (!track) return;
1248
1671
  updateScrollState();
@@ -1454,9 +1877,9 @@ function CtaCardSection({
1454
1877
  icon,
1455
1878
  renderLink = defaultRenderLink6
1456
1879
  }) {
1457
- const ref = React29.useRef(null);
1458
- const [visible, setVisible] = React29.useState(false);
1459
- React29.useEffect(() => {
1880
+ const ref = React30.useRef(null);
1881
+ const [visible, setVisible] = React30.useState(false);
1882
+ React30.useEffect(() => {
1460
1883
  const el = ref.current;
1461
1884
  if (!el) return;
1462
1885
  const observer = new IntersectionObserver(
@@ -1603,8 +2026,8 @@ function CountdownTimer({
1603
2026
  className,
1604
2027
  compact = false
1605
2028
  }) {
1606
- const [timeLeft, setTimeLeft] = React29.useState(() => getTimeLeft(deadline));
1607
- React29.useEffect(() => {
2029
+ const [timeLeft, setTimeLeft] = React30.useState(() => getTimeLeft(deadline));
2030
+ React30.useEffect(() => {
1608
2031
  setTimeLeft(getTimeLeft(deadline));
1609
2032
  const id = setInterval(() => setTimeLeft(getTimeLeft(deadline)), 1e3);
1610
2033
  return () => clearInterval(id);
@@ -1630,7 +2053,7 @@ function CountdownTimer({
1630
2053
  ];
1631
2054
  return /* @__PURE__ */ jsxs("div", { className: cn$1("flex items-center justify-center gap-2 text-sm", className), children: [
1632
2055
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
1633
- /* @__PURE__ */ jsx("span", { className: "flex items-center gap-1 font-mono font-semibold tabular-nums", children: units.map(({ value, unit }, i) => /* @__PURE__ */ jsxs(React29.Fragment, { children: [
2056
+ /* @__PURE__ */ jsx("span", { className: "flex items-center gap-1 font-mono font-semibold tabular-nums", children: units.map(({ value, unit }, i) => /* @__PURE__ */ jsxs(React30.Fragment, { children: [
1634
2057
  /* @__PURE__ */ jsxs("span", { children: [
1635
2058
  String(value).padStart(2, "0"),
1636
2059
  /* @__PURE__ */ jsx("span", { className: "font-normal text-muted-foreground", children: unit })
@@ -1653,7 +2076,7 @@ function PricingSection({
1653
2076
  foundingDeadline,
1654
2077
  renderPlanFooter
1655
2078
  }) {
1656
- const [annual, setAnnual] = React29.useState(false);
2079
+ const [annual, setAnnual] = React30.useState(false);
1657
2080
  const isOneTime = mode === "one-time";
1658
2081
  const uiMode = useUIVariant();
1659
2082
  const sectionClasses = cn$1(
@@ -1915,10 +2338,10 @@ function CountUp({
1915
2338
  className,
1916
2339
  ...props
1917
2340
  }) {
1918
- const ref = React29.useRef(null);
1919
- const [value, setValue] = React29.useState(from);
1920
- const hasStarted = React29.useRef(false);
1921
- React29.useEffect(() => {
2341
+ const ref = React30.useRef(null);
2342
+ const [value, setValue] = React30.useState(from);
2343
+ const hasStarted = React30.useRef(false);
2344
+ React30.useEffect(() => {
1922
2345
  const el = ref.current;
1923
2346
  if (!el) return;
1924
2347
  const observer = new IntersectionObserver(
@@ -2011,8 +2434,8 @@ function NewsletterSection({
2011
2434
  successDescription = defaults.successDescription,
2012
2435
  onSubscribe
2013
2436
  } = {}) {
2014
- const [email, setEmail] = React29.useState("");
2015
- const [submitted, setSubmitted] = React29.useState(false);
2437
+ const [email, setEmail] = React30.useState("");
2438
+ const [submitted, setSubmitted] = React30.useState(false);
2016
2439
  async function handleSubmit(e) {
2017
2440
  e.preventDefault();
2018
2441
  if (!email) return;
@@ -2096,7 +2519,7 @@ function ContactSection({
2096
2519
  sendAnotherLabel = defaults2.sendAnotherLabel,
2097
2520
  onSubmit
2098
2521
  } = {}) {
2099
- const [submitted, setSubmitted] = React29.useState(false);
2522
+ const [submitted, setSubmitted] = React30.useState(false);
2100
2523
  async function handleSubmit(e) {
2101
2524
  e.preventDefault();
2102
2525
  await onSubmit?.(e);
@@ -2387,7 +2810,7 @@ function Navbar({
2387
2810
  controls,
2388
2811
  renderLink = defaultRenderLink8
2389
2812
  }) {
2390
- const [open, setOpen] = React29.useState(false);
2813
+ const [open, setOpen] = React30.useState(false);
2391
2814
  const brandHref = brand.href ?? "/";
2392
2815
  const hasRightContent = controls?.length || cta?.primary || cta?.secondary;
2393
2816
  return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsxs("nav", { className: "grid h-16 grid-cols-[1fr_auto_1fr] items-center px-6", children: [
@@ -2404,7 +2827,7 @@ function Navbar({
2404
2827
  children: link.label
2405
2828
  }) }, link.label)) }),
2406
2829
  hasRightContent && /* @__PURE__ */ jsxs("div", { className: "hidden items-center justify-end gap-2 md:flex", children: [
2407
- controls?.map((control, i) => /* @__PURE__ */ jsx(React29.Fragment, { children: control }, i)),
2830
+ controls?.map((control, i) => /* @__PURE__ */ jsx(React30.Fragment, { children: control }, i)),
2408
2831
  (cta?.secondary || cta?.primary) && controls?.length ? /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-border" }) : null,
2409
2832
  cta?.secondary && renderLink({
2410
2833
  href: cta.secondary.href,
@@ -2428,7 +2851,7 @@ function Navbar({
2428
2851
  /* @__PURE__ */ jsx("span", { children: brand.name })
2429
2852
  ] })
2430
2853
  }) }) }),
2431
- controls?.length ? /* @__PURE__ */ jsx("div", { className: "mb-4 flex w-full items-center justify-between gap-1", children: controls.map((control, i) => /* @__PURE__ */ jsx(React29.Fragment, { children: control }, i)) }) : null,
2854
+ controls?.length ? /* @__PURE__ */ jsx("div", { className: "mb-4 flex w-full items-center justify-between gap-1", children: controls.map((control, i) => /* @__PURE__ */ jsx(React30.Fragment, { children: control }, i)) }) : null,
2432
2855
  /* @__PURE__ */ jsx(Separator, {}),
2433
2856
  /* @__PURE__ */ jsx("nav", { className: "mt-4 flex flex-col gap-1", children: navLinks.map((link) => /* @__PURE__ */ jsx("span", { onClick: () => setOpen(false), children: renderLink({
2434
2857
  href: link.href,
@@ -2577,7 +3000,7 @@ function LogoStrip({
2577
3000
  ] });
2578
3001
  }
2579
3002
  function GridUnderlay({ variant, size = 24, className, ...props }) {
2580
- const id = React29.useId();
3003
+ const id = React30.useId();
2581
3004
  const pattern = variant === "dots" ? /* @__PURE__ */ jsx("circle", { cx: size / 2, cy: size / 2, r: 1, fill: "currentColor" }) : variant === "lines" ? /* @__PURE__ */ jsx("path", { d: `M ${size} 0 L 0 0 0 ${size}`, fill: "none", stroke: "currentColor", strokeWidth: 0.5 }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2582
3005
  /* @__PURE__ */ jsx("line", { x1: size / 2, y1: 0, x2: size / 2, y2: size, stroke: "currentColor", strokeWidth: 0.5 }),
2583
3006
  /* @__PURE__ */ jsx("line", { x1: 0, y1: size / 2, x2: size, y2: size / 2, stroke: "currentColor", strokeWidth: 0.5 })
@@ -2605,9 +3028,9 @@ function GlowUnderlay({
2605
3028
  style,
2606
3029
  ...props
2607
3030
  }) {
2608
- const [pos, setPos] = React29.useState({ x: "50%", y: "50%" });
2609
- const containerRef = React29.useRef(null);
2610
- React29.useEffect(() => {
3031
+ const [pos, setPos] = React30.useState({ x: "50%", y: "50%" });
3032
+ const containerRef = React30.useRef(null);
3033
+ React30.useEffect(() => {
2611
3034
  if (!followCursor) return;
2612
3035
  const handleMouseMove = (e) => {
2613
3036
  const el = containerRef.current;
@@ -2746,8 +3169,8 @@ function PageTransition({
2746
3169
  style,
2747
3170
  ...props
2748
3171
  }) {
2749
- const [mounted, setMounted] = React29.useState(false);
2750
- React29.useEffect(() => {
3172
+ const [mounted, setMounted] = React30.useState(false);
3173
+ React30.useEffect(() => {
2751
3174
  const id = requestAnimationFrame(() => setMounted(true));
2752
3175
  return () => cancelAnimationFrame(id);
2753
3176
  }, []);
@@ -2774,8 +3197,8 @@ function Spotlight({
2774
3197
  triggerClassName,
2775
3198
  shortcut = "\u2318K"
2776
3199
  }) {
2777
- const [open, setOpen] = React29.useState(false);
2778
- React29.useEffect(() => {
3200
+ const [open, setOpen] = React30.useState(false);
3201
+ React30.useEffect(() => {
2779
3202
  const down = (e) => {
2780
3203
  if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
2781
3204
  e.preventDefault();
@@ -2805,7 +3228,7 @@ function Spotlight({
2805
3228
  /* @__PURE__ */ jsx(CommandInput, { placeholder }),
2806
3229
  /* @__PURE__ */ jsxs(CommandList, { children: [
2807
3230
  /* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
2808
- groups.map((group, i) => /* @__PURE__ */ jsxs(React29.Fragment, { children: [
3231
+ groups.map((group, i) => /* @__PURE__ */ jsxs(React30.Fragment, { children: [
2809
3232
  i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
2810
3233
  /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
2811
3234
  CommandItem,
@@ -2830,8 +3253,8 @@ function Spotlight({
2830
3253
  var BASE = 40;
2831
3254
  var MAGNIFY_RADIUS = 80;
2832
3255
  function Dock({ items, iconSize = BASE, magnification = 1.7, className, ...props }) {
2833
- const [mouseX, setMouseX] = React29.useState(null);
2834
- const containerRef = React29.useRef(null);
3256
+ const [mouseX, setMouseX] = React30.useState(null);
3257
+ const containerRef = React30.useRef(null);
2835
3258
  function getScale(index) {
2836
3259
  if (mouseX === null || !containerRef.current) return 1;
2837
3260
  const container = containerRef.current;
@@ -2929,6 +3352,274 @@ function ConfirmDialog({ isOpen, options, handleConfirm, handleCancel, destructi
2929
3352
  ] })
2930
3353
  ] }) });
2931
3354
  }
3355
+ function RegisterHotkeys({ hotkeys }) {
3356
+ React30.useEffect(() => {
3357
+ const handleKeyDown = (event) => {
3358
+ for (const hotkey of hotkeys) {
3359
+ const matches = event.key.toLowerCase() === hotkey.key.toLowerCase() && !!event.ctrlKey === !!hotkey.ctrl && !!event.metaKey === !!hotkey.meta && !!event.shiftKey === !!hotkey.shift && !!event.altKey === !!hotkey.alt;
3360
+ if (matches) {
3361
+ event.preventDefault();
3362
+ hotkey.handler();
3363
+ break;
3364
+ }
3365
+ }
3366
+ };
3367
+ document.addEventListener("keydown", handleKeyDown);
3368
+ return () => document.removeEventListener("keydown", handleKeyDown);
3369
+ }, [hotkeys]);
3370
+ return null;
3371
+ }
3372
+ function useControlledOpen(defaultOpen = false) {
3373
+ const [isOpen, setIsOpen] = React30.useState(defaultOpen);
3374
+ const open = React30.useCallback(() => setIsOpen(true), []);
3375
+ const close = React30.useCallback(() => setIsOpen(false), []);
3376
+ const toggle = React30.useCallback(() => setIsOpen((prev) => !prev), []);
3377
+ return { isOpen, open, close, toggle, setIsOpen };
3378
+ }
3379
+ function CommandMenu({
3380
+ groups,
3381
+ placeholder = "Type a command or search\u2026",
3382
+ emptyMessage = "No results found.",
3383
+ open: openProp,
3384
+ onOpenChange,
3385
+ hotkey = true
3386
+ }) {
3387
+ const internal = useControlledOpen(false);
3388
+ const isOpen = openProp ?? internal.isOpen;
3389
+ const setOpen = onOpenChange ?? internal.setIsOpen;
3390
+ const runItem = (item) => {
3391
+ setOpen(false);
3392
+ item.onSelect();
3393
+ };
3394
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3395
+ hotkey && /* @__PURE__ */ jsx(
3396
+ RegisterHotkeys,
3397
+ {
3398
+ hotkeys: [
3399
+ { key: "k", meta: true, handler: () => setOpen(!isOpen) },
3400
+ { key: "k", ctrl: true, handler: () => setOpen(!isOpen) }
3401
+ ]
3402
+ }
3403
+ ),
3404
+ /* @__PURE__ */ jsxs(CommandDialog, { open: isOpen, onOpenChange: setOpen, children: [
3405
+ /* @__PURE__ */ jsx(CommandInput, { placeholder }),
3406
+ /* @__PURE__ */ jsxs(CommandList, { children: [
3407
+ /* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage }),
3408
+ groups.map((group, i) => /* @__PURE__ */ jsxs(React30.Fragment, { children: [
3409
+ i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
3410
+ /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
3411
+ CommandItem,
3412
+ {
3413
+ value: [item.label, ...item.keywords ?? []].join(" "),
3414
+ onSelect: () => runItem(item),
3415
+ children: [
3416
+ item.icon && /* @__PURE__ */ jsx(item.icon, {}),
3417
+ /* @__PURE__ */ jsx("span", { children: item.label }),
3418
+ item.shortcut && /* @__PURE__ */ jsx(CommandShortcut, { children: item.shortcut })
3419
+ ]
3420
+ },
3421
+ item.id
3422
+ )) })
3423
+ ] }, group.heading))
3424
+ ] })
3425
+ ] })
3426
+ ] });
3427
+ }
3428
+ function formatBytes(bytes) {
3429
+ if (bytes < 1024) return `${bytes} B`;
3430
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
3431
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
3432
+ }
3433
+ function makeId() {
3434
+ return Math.random().toString(36).slice(2, 10);
3435
+ }
3436
+ function FileUpload({
3437
+ accept,
3438
+ multiple = false,
3439
+ maxSizeMb,
3440
+ maxFiles,
3441
+ files: filesProp,
3442
+ defaultFiles,
3443
+ onFilesChange,
3444
+ onFilesAdded,
3445
+ disabled,
3446
+ hint,
3447
+ className
3448
+ }) {
3449
+ const [internalFiles, setInternalFiles] = React30.useState(defaultFiles ?? []);
3450
+ const [isDragging, setIsDragging] = React30.useState(false);
3451
+ const [validationError, setValidationError] = React30.useState(null);
3452
+ const inputRef = React30.useRef(null);
3453
+ const files = filesProp ?? internalFiles;
3454
+ const setFiles = React30.useCallback(
3455
+ (next) => {
3456
+ if (!filesProp) setInternalFiles(next);
3457
+ onFilesChange?.(next);
3458
+ },
3459
+ [filesProp, onFilesChange]
3460
+ );
3461
+ const acceptList = React30.useMemo(
3462
+ () => accept?.split(",").map((a) => a.trim().toLowerCase()).filter(Boolean) ?? [],
3463
+ [accept]
3464
+ );
3465
+ const matchesAccept = (file) => {
3466
+ if (!acceptList.length) return true;
3467
+ const name = file.name.toLowerCase();
3468
+ return acceptList.some(
3469
+ (pattern) => pattern.startsWith(".") ? name.endsWith(pattern) : file.type === pattern || file.type.startsWith(pattern.replace("/*", "/"))
3470
+ );
3471
+ };
3472
+ const handleFiles = (fileList) => {
3473
+ if (!fileList || disabled) return;
3474
+ const incoming = Array.from(fileList);
3475
+ const room = maxFiles ? Math.max(0, maxFiles - files.length) : Infinity;
3476
+ if (maxFiles && room <= 0) {
3477
+ setValidationError(`You can only add up to ${maxFiles} file${maxFiles === 1 ? "" : "s"}.`);
3478
+ return;
3479
+ }
3480
+ const accepted = [];
3481
+ let rejected = false;
3482
+ for (const file of incoming.slice(0, room)) {
3483
+ if (!matchesAccept(file)) {
3484
+ rejected = true;
3485
+ continue;
3486
+ }
3487
+ if (maxSizeMb && file.size > maxSizeMb * 1024 * 1024) {
3488
+ rejected = true;
3489
+ continue;
3490
+ }
3491
+ accepted.push(file);
3492
+ }
3493
+ setValidationError(rejected ? `Some files were skipped \u2014 check the file type and size limit.` : null);
3494
+ if (!accepted.length) return;
3495
+ const entries = accepted.map((file) => ({ id: makeId(), file, status: "pending" }));
3496
+ setFiles(multiple ? [...files, ...entries] : entries);
3497
+ onFilesAdded?.(accepted);
3498
+ };
3499
+ const removeFile = (id) => {
3500
+ setFiles(files.filter((f) => f.id !== id));
3501
+ };
3502
+ return /* @__PURE__ */ jsxs("div", { className: cn$1("space-y-3", className), children: [
3503
+ /* @__PURE__ */ jsxs(
3504
+ "div",
3505
+ {
3506
+ role: "button",
3507
+ tabIndex: disabled ? -1 : 0,
3508
+ onClick: () => !disabled && inputRef.current?.click(),
3509
+ onKeyDown: (e) => {
3510
+ if (e.key === "Enter" || e.key === " ") inputRef.current?.click();
3511
+ },
3512
+ onDragOver: (e) => {
3513
+ e.preventDefault();
3514
+ if (!disabled) setIsDragging(true);
3515
+ },
3516
+ onDragLeave: () => setIsDragging(false),
3517
+ onDrop: (e) => {
3518
+ e.preventDefault();
3519
+ setIsDragging(false);
3520
+ handleFiles(e.dataTransfer.files);
3521
+ },
3522
+ className: cn$1(
3523
+ "flex cursor-pointer flex-col items-center justify-center gap-2 rounded-xl border-2 border-dashed px-6 py-10 text-center transition-colors",
3524
+ isDragging ? "border-primary bg-primary/5" : "border-border hover:border-foreground/30",
3525
+ disabled && "pointer-events-none opacity-50"
3526
+ ),
3527
+ children: [
3528
+ /* @__PURE__ */ jsx(UploadCloud, { className: "size-8 text-muted-foreground" }),
3529
+ /* @__PURE__ */ jsxs("p", { className: "text-sm font-medium", children: [
3530
+ /* @__PURE__ */ jsx("span", { className: "text-primary underline underline-offset-4", children: "Click to upload" }),
3531
+ " or drag and drop"
3532
+ ] }),
3533
+ hint && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: hint }),
3534
+ /* @__PURE__ */ jsx(
3535
+ "input",
3536
+ {
3537
+ ref: inputRef,
3538
+ type: "file",
3539
+ accept,
3540
+ multiple,
3541
+ disabled,
3542
+ className: "sr-only",
3543
+ onChange: (e) => {
3544
+ handleFiles(e.target.files);
3545
+ e.target.value = "";
3546
+ }
3547
+ }
3548
+ )
3549
+ ]
3550
+ }
3551
+ ),
3552
+ validationError && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: validationError }),
3553
+ files.length > 0 && /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: files.map(({ id, file, progress, status = "pending", error }) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3 rounded-lg border bg-card/60 px-3 py-2.5", children: [
3554
+ /* @__PURE__ */ jsx(File, { className: "size-5 shrink-0 text-muted-foreground" }),
3555
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3556
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
3557
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: file.name }),
3558
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatBytes(file.size) })
3559
+ ] }),
3560
+ status === "uploading" && /* @__PURE__ */ jsx("div", { className: "mt-1.5 h-1 w-full overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx("div", { className: "h-full rounded-full bg-primary transition-all", style: { width: `${progress ?? 0}%` } }) }),
3561
+ status === "error" && error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-destructive", children: error })
3562
+ ] }),
3563
+ status === "uploading" && /* @__PURE__ */ jsx(Loader2, { className: "size-4 shrink-0 animate-spin text-muted-foreground" }),
3564
+ status === "done" && /* @__PURE__ */ jsx(CheckCircle2, { className: "size-4 shrink-0 text-primary" }),
3565
+ status === "error" && /* @__PURE__ */ jsx(AlertCircle, { className: "size-4 shrink-0 text-destructive" }),
3566
+ /* @__PURE__ */ jsxs(
3567
+ Button,
3568
+ {
3569
+ type: "button",
3570
+ variant: "ghost",
3571
+ size: "icon",
3572
+ className: "size-7 shrink-0",
3573
+ onClick: () => removeFile(id),
3574
+ children: [
3575
+ /* @__PURE__ */ jsx(X, { className: "size-3.5" }),
3576
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
3577
+ "Remove ",
3578
+ file.name
3579
+ ] })
3580
+ ]
3581
+ }
3582
+ )
3583
+ ] }, id)) })
3584
+ ] });
3585
+ }
3586
+ var variantIcon = {
3587
+ success: /* @__PURE__ */ jsx(CheckCircle2, { className: "size-5 text-primary" }),
3588
+ info: /* @__PURE__ */ jsx(Info, { className: "size-5 text-muted-foreground" }),
3589
+ message: /* @__PURE__ */ jsx(Inbox, { className: "size-5 text-muted-foreground" })
3590
+ };
3591
+ function NotificationToast({
3592
+ variant = "info",
3593
+ title,
3594
+ description,
3595
+ avatar,
3596
+ action,
3597
+ secondaryAction,
3598
+ onDismiss
3599
+ }) {
3600
+ return /* @__PURE__ */ jsx("div", { className: "w-full max-w-sm rounded-lg border bg-card p-4 text-card-foreground shadow-lg", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
3601
+ /* @__PURE__ */ jsx("div", { className: "shrink-0 pt-0.5", children: avatar ? (
3602
+ // eslint-disable-next-line @next/next/no-img-element
3603
+ /* @__PURE__ */ jsx("img", { src: avatar, alt: "", className: "size-9 rounded-full border object-cover" })
3604
+ ) : variantIcon[variant] }),
3605
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 pt-0.5", children: [
3606
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: title }),
3607
+ description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description }),
3608
+ (action || secondaryAction) && /* @__PURE__ */ jsxs("div", { className: cn$1("mt-3 flex gap-4", avatar && "gap-3"), children: [
3609
+ action && /* @__PURE__ */ jsx("button", { type: "button", onClick: action.onClick, className: "text-sm font-medium text-primary hover:underline", children: action.label }),
3610
+ secondaryAction && /* @__PURE__ */ jsx("button", { type: "button", onClick: secondaryAction.onClick, className: "text-sm font-medium text-muted-foreground hover:text-foreground", children: secondaryAction.label })
3611
+ ] })
3612
+ ] }),
3613
+ /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: "size-6 shrink-0 -mt-1 -mr-1", onClick: onDismiss, children: [
3614
+ /* @__PURE__ */ jsx(X, { className: "size-4" }),
3615
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Dismiss" })
3616
+ ] })
3617
+ ] }) });
3618
+ }
3619
+ function notify(options) {
3620
+ const { duration, ...toastProps } = options;
3621
+ return toast.custom((id) => /* @__PURE__ */ jsx(NotificationToast, { ...toastProps, onDismiss: () => toast.dismiss(id) }), { duration });
3622
+ }
2932
3623
  function AppScreenRoot({ children, className }) {
2933
3624
  return /* @__PURE__ */ jsx("div", { className: cn("flex h-full flex-col", className), children });
2934
3625
  }
@@ -3324,7 +4015,7 @@ function PageHeader({
3324
4015
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 pb-6", className), children: [
3325
4016
  breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "mb-2 flex items-center gap-1 text-sm text-muted-foreground", children: breadcrumbs.map((crumb, i) => {
3326
4017
  const isLast = i === breadcrumbs.length - 1;
3327
- return /* @__PURE__ */ jsxs(React29.Fragment, { children: [
4018
+ return /* @__PURE__ */ jsxs(React30.Fragment, { children: [
3328
4019
  i > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 shrink-0" }),
3329
4020
  isLast || !crumb.href ? /* @__PURE__ */ jsx("span", { className: cn(isLast && "text-foreground font-medium"), children: crumb.label }) : renderLink({
3330
4021
  href: crumb.href,
@@ -3360,7 +4051,7 @@ function PageHeader({
3360
4051
  ] });
3361
4052
  }
3362
4053
  function Suspensed({ children, fallback }) {
3363
- return /* @__PURE__ */ jsx(React29.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
4054
+ return /* @__PURE__ */ jsx(React30.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
3364
4055
  }
3365
4056
  function getInitialTheme() {
3366
4057
  if (typeof document === "undefined") return "dark";
@@ -3368,7 +4059,7 @@ function getInitialTheme() {
3368
4059
  return stored ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
3369
4060
  }
3370
4061
  function ThemeSwitchMinimal() {
3371
- const [theme, setThemeState] = React29.useState(getInitialTheme);
4062
+ const [theme, setThemeState] = React30.useState(getInitialTheme);
3372
4063
  const toggle = () => {
3373
4064
  const next = theme === "dark" ? "light" : "dark";
3374
4065
  setThemeState(next);
@@ -3494,9 +4185,9 @@ function getInitialColorName() {
3494
4185
  return localStorage.getItem(STORAGE_KEY) ?? "emerald";
3495
4186
  }
3496
4187
  function BrandColorSwitchMinimal() {
3497
- const [active, setActive] = React29.useState(getInitialColorName);
3498
- const [open, setOpen] = React29.useState(false);
3499
- React29.useEffect(() => {
4188
+ const [active, setActive] = React30.useState(getInitialColorName);
4189
+ const [open, setOpen] = React30.useState(false);
4190
+ React30.useEffect(() => {
3500
4191
  const saved = localStorage.getItem(STORAGE_KEY) ?? "emerald";
3501
4192
  const color = BRAND_COLORS.find((c) => c.name === saved);
3502
4193
  if (color) applyColor(color);
@@ -3557,7 +4248,7 @@ function BrandColorSwitchMinimal() {
3557
4248
  ] });
3558
4249
  }
3559
4250
  function ThemeColorUpdater({ colorTheme }) {
3560
- React29.useEffect(() => {
4251
+ React30.useEffect(() => {
3561
4252
  if (colorTheme) {
3562
4253
  document.documentElement.setAttribute("data-theme", colorTheme);
3563
4254
  } else {
@@ -3567,7 +4258,7 @@ function ThemeColorUpdater({ colorTheme }) {
3567
4258
  return null;
3568
4259
  }
3569
4260
  function VersionBanner({ version, message, className, onDismiss }) {
3570
- const [dismissed, setDismissed] = React29.useState(false);
4261
+ const [dismissed, setDismissed] = React30.useState(false);
3571
4262
  if (dismissed) return null;
3572
4263
  return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-4 bg-primary/10 px-4 py-2 text-sm", className), children: [
3573
4264
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
@@ -3606,23 +4297,6 @@ function DevBanner({ environment = "development", className }) {
3606
4297
  }
3607
4298
  );
3608
4299
  }
3609
- function RegisterHotkeys({ hotkeys }) {
3610
- React29.useEffect(() => {
3611
- const handleKeyDown = (event) => {
3612
- for (const hotkey of hotkeys) {
3613
- const matches = event.key.toLowerCase() === hotkey.key.toLowerCase() && !!event.ctrlKey === !!hotkey.ctrl && !!event.metaKey === !!hotkey.meta && !!event.shiftKey === !!hotkey.shift && !!event.altKey === !!hotkey.alt;
3614
- if (matches) {
3615
- event.preventDefault();
3616
- hotkey.handler();
3617
- break;
3618
- }
3619
- }
3620
- };
3621
- document.addEventListener("keydown", handleKeyDown);
3622
- return () => document.removeEventListener("keydown", handleKeyDown);
3623
- }, [hotkeys]);
3624
- return null;
3625
- }
3626
4300
  function RootErrorFallback({ error, reset }) {
3627
4301
  return /* @__PURE__ */ jsxs("div", { className: "flex min-h-svh flex-col items-center justify-center gap-6 p-8 text-center", children: [
3628
4302
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3", children: [
@@ -3841,36 +4515,29 @@ function useMounted() {
3841
4515
  return mounted;
3842
4516
  }
3843
4517
  function useConfirm() {
3844
- const [isOpen, setIsOpen] = React29.useState(false);
3845
- const [options, setOptions] = React29.useState({});
3846
- const resolveRef = React29.useRef(null);
3847
- const confirm = React29.useCallback((opts = {}) => {
4518
+ const [isOpen, setIsOpen] = React30.useState(false);
4519
+ const [options, setOptions] = React30.useState({});
4520
+ const resolveRef = React30.useRef(null);
4521
+ const confirm = React30.useCallback((opts = {}) => {
3848
4522
  setOptions(opts);
3849
4523
  setIsOpen(true);
3850
4524
  return new Promise((resolve) => {
3851
4525
  resolveRef.current = resolve;
3852
4526
  });
3853
4527
  }, []);
3854
- const handleConfirm = React29.useCallback(() => {
4528
+ const handleConfirm = React30.useCallback(() => {
3855
4529
  resolveRef.current?.(true);
3856
4530
  setIsOpen(false);
3857
4531
  }, []);
3858
- const handleCancel = React29.useCallback(() => {
4532
+ const handleCancel = React30.useCallback(() => {
3859
4533
  resolveRef.current?.(false);
3860
4534
  setIsOpen(false);
3861
4535
  }, []);
3862
4536
  return { confirm, isOpen, options, handleConfirm, handleCancel };
3863
4537
  }
3864
- function useControlledOpen(defaultOpen = false) {
3865
- const [isOpen, setIsOpen] = React29.useState(defaultOpen);
3866
- const open = React29.useCallback(() => setIsOpen(true), []);
3867
- const close = React29.useCallback(() => setIsOpen(false), []);
3868
- const toggle = React29.useCallback(() => setIsOpen((prev) => !prev), []);
3869
- return { isOpen, open, close, toggle, setIsOpen };
3870
- }
3871
4538
  function useScrolledPast(threshold) {
3872
- const [scrolledPast, setScrolledPast] = React29.useState(false);
3873
- React29.useEffect(() => {
4539
+ const [scrolledPast, setScrolledPast] = React30.useState(false);
4540
+ React30.useEffect(() => {
3874
4541
  const handleScroll = () => {
3875
4542
  setScrolledPast(window.scrollY > threshold);
3876
4543
  };
@@ -3881,30 +4548,30 @@ function useScrolledPast(threshold) {
3881
4548
  return scrolledPast;
3882
4549
  }
3883
4550
  function useCopyToClipboard() {
3884
- const [copied, setCopied] = React29.useState(false);
3885
- const timeoutRef = React29.useRef(void 0);
3886
- const copy = React29.useCallback((text) => {
4551
+ const [copied, setCopied] = React30.useState(false);
4552
+ const timeoutRef = React30.useRef(void 0);
4553
+ const copy = React30.useCallback((text) => {
3887
4554
  navigator.clipboard.writeText(text).then(() => {
3888
4555
  setCopied(true);
3889
4556
  clearTimeout(timeoutRef.current);
3890
4557
  timeoutRef.current = setTimeout(() => setCopied(false), 2e3);
3891
4558
  });
3892
4559
  }, []);
3893
- React29.useEffect(() => () => clearTimeout(timeoutRef.current), []);
4560
+ React30.useEffect(() => () => clearTimeout(timeoutRef.current), []);
3894
4561
  return [copied, copy];
3895
4562
  }
3896
4563
  function useDebounce(value, delay) {
3897
- const [debounced, setDebounced] = React29.useState(value);
3898
- React29.useEffect(() => {
4564
+ const [debounced, setDebounced] = React30.useState(value);
4565
+ React30.useEffect(() => {
3899
4566
  const timer = setTimeout(() => setDebounced(value), delay);
3900
4567
  return () => clearTimeout(timer);
3901
4568
  }, [value, delay]);
3902
4569
  return debounced;
3903
4570
  }
3904
4571
  function useIntersectionObserver(options) {
3905
- const ref = React29.useRef(null);
3906
- const [isIntersecting, setIsIntersecting] = React29.useState(false);
3907
- React29.useEffect(() => {
4572
+ const ref = React30.useRef(null);
4573
+ const [isIntersecting, setIsIntersecting] = React30.useState(false);
4574
+ React30.useEffect(() => {
3908
4575
  const el = ref.current;
3909
4576
  if (!el) return;
3910
4577
  const observer = new IntersectionObserver(([entry]) => {
@@ -3916,7 +4583,7 @@ function useIntersectionObserver(options) {
3916
4583
  return [ref, isIntersecting];
3917
4584
  }
3918
4585
  function useLocalStorage(key, initialValue) {
3919
- const [storedValue, setStoredValue] = React29.useState(() => {
4586
+ const [storedValue, setStoredValue] = React30.useState(() => {
3920
4587
  if (typeof window === "undefined") return initialValue;
3921
4588
  try {
3922
4589
  const item = window.localStorage.getItem(key);
@@ -3925,7 +4592,7 @@ function useLocalStorage(key, initialValue) {
3925
4592
  return initialValue;
3926
4593
  }
3927
4594
  });
3928
- const setValue = React29.useCallback(
4595
+ const setValue = React30.useCallback(
3929
4596
  (value) => {
3930
4597
  try {
3931
4598
  setStoredValue(value);
@@ -3940,11 +4607,11 @@ function useLocalStorage(key, initialValue) {
3940
4607
  return [storedValue, setValue];
3941
4608
  }
3942
4609
  function useMediaQuery(query) {
3943
- const [matches, setMatches] = React29.useState(() => {
4610
+ const [matches, setMatches] = React30.useState(() => {
3944
4611
  if (typeof window === "undefined") return false;
3945
4612
  return window.matchMedia(query).matches;
3946
4613
  });
3947
- React29.useEffect(() => {
4614
+ React30.useEffect(() => {
3948
4615
  if (typeof window === "undefined") return;
3949
4616
  const mql = window.matchMedia(query);
3950
4617
  setMatches(mql.matches);
@@ -3955,18 +4622,18 @@ function useMediaQuery(query) {
3955
4622
  return matches;
3956
4623
  }
3957
4624
  function usePagination(total, pageSize) {
3958
- const [page, setPage] = React29.useState(1);
4625
+ const [page, setPage] = React30.useState(1);
3959
4626
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
3960
4627
  const offset = (page - 1) * pageSize;
3961
- const goTo = React29.useCallback(
4628
+ const goTo = React30.useCallback(
3962
4629
  (p) => setPage(Math.min(Math.max(1, p), totalPages)),
3963
4630
  [totalPages]
3964
4631
  );
3965
- const next = React29.useCallback(() => goTo(page + 1), [page, goTo]);
3966
- const prev = React29.useCallback(() => goTo(page - 1), [page, goTo]);
4632
+ const next = React30.useCallback(() => goTo(page + 1), [page, goTo]);
4633
+ const prev = React30.useCallback(() => goTo(page - 1), [page, goTo]);
3967
4634
  return { page, pageSize, totalPages, offset, hasPrev: page > 1, hasNext: page < totalPages, goTo, next, prev };
3968
4635
  }
3969
4636
 
3970
- export { AnimatedPill, AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, BrandColorSwitchMinimal, Button, Card, CarouselSection, ChangelogCard, ChangelogList, Checkbox, ComparisonSection, ConfirmDialog, ContactSection, ContextMenu, CountUp, CountdownTimer, CtaCardSection, CtaSection, DevBanner, Dock, EmptyState, ErrorPage, FadeIn, FaqSection, FeatureCard, FeatureMarqueeSection, FeaturesSection, FlowBracket, FlowConnector, Footer, FullPageSpinner, GlassCard, Grid, GridItem, GroupedFeaturesSection, HeroSection, ImageCard, Input, LogoStrip, ModeSwitchMinimal, Navbar, NewsletterSection, OlwibaUIProvider, Overlay, PageHeader, PageTransition, PhoneFrame, PostCard, PostList, PricingCard, PricingSection, PublicPageFrame, QualificationSection, RegisterHotkeys, RootErrorFallback, Section, SectionTitle, Spotlight, Stack, StaggerChildren, StatCard, StatsSection, StepsSection, Suspensed, Switch, TeamSection, TechStackSection, TestimonialCard, TestimonialsSection, Textarea, ThemeColorUpdater, ThemeSwitchMinimal, Underlay, UpgradePrompt, VersionBanner, marketingSectionSpacing, useConfirm, useControlledOpen, useCopyToClipboard, useDebounce, useIntersectionObserver, useLocalStorage, useMediaQuery, useMounted, useOlwibaUI, usePagination, useScrolledPast, useUIMode };
4637
+ export { AnimatedPill, AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, BillingPanel, BrandColorSwitchMinimal, Button, Card, CarouselSection, ChangelogCard, ChangelogList, Checkbox, CommandMenu, ComparisonSection, ConfirmDialog, ContactSection, ContextMenu, CountUp, CountdownTimer, CtaCardSection, CtaSection, DataTable, DevBanner, Dock, EmptyState, ErrorPage, FadeIn, FaqSection, FeatureCard, FeatureMarqueeSection, FeaturesSection, FileUpload, FlowBracket, FlowConnector, Footer, FullPageSpinner, GlassCard, Grid, GridItem, GroupedFeaturesSection, HeroSection, ImageCard, Input, LogoStrip, ModeSwitchMinimal, Navbar, NewsletterSection, NotificationToast, OlwibaUIProvider, OnboardingWizard, Overlay, PageHeader, PageTransition, PhoneFrame, PostCard, PostList, PricingCard, PricingSection, PublicPageFrame, QualificationSection, RegisterHotkeys, RootErrorFallback, Section, SectionTitle, SettingsSection, Spotlight, Stack, StaggerChildren, StatCard, StatsSection, StepsSection, Suspensed, Switch, TeamMembersPanel, TeamSection, TechStackSection, TestimonialCard, TestimonialsSection, Textarea, ThemeColorUpdater, ThemeSwitchMinimal, Underlay, UpgradePrompt, VersionBanner, marketingSectionSpacing, notify, useConfirm, useControlledOpen, useCopyToClipboard, useDebounce, useIntersectionObserver, useLocalStorage, useMediaQuery, useMounted, useOlwibaUI, usePagination, useScrolledPast, useUIMode };
3971
4638
  //# sourceMappingURL=index.js.map
3972
4639
  //# sourceMappingURL=index.js.map