@olwiba/ui 0.1.14 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,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 React31 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';
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, Popover, PopoverTrigger, PopoverContent, Spinner, 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
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, Loader2, ArrowLeft, ArrowUpRight, X, ArrowRight, Sparkles, Minus, Quote, Star, Twitter, Github, Linkedin, Mail, MessageSquare, ChevronDown, Send, Menu, UploadCloud, File, CheckCircle2, AlertCircle, Bell, Inbox, Activity, TrendingUp, TrendingDown, Sun, Moon, Palette, AlertTriangle, Rocket, 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 = React31.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] = React31.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 React31.useContext(OlwibaUIContext);
25
27
  }
26
28
  function useUIMode() {
27
- return React29.useContext(OlwibaUIContext).mode;
29
+ return React31.useContext(OlwibaUIContext).mode;
28
30
  }
29
- var Button = React29.forwardRef(
31
+ var Button = React31.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 = React31.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 = React31.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 = React31.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 = React31.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 = React31.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] = React31.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,36 @@ function DefaultForm({
550
572
  }
551
573
  )
552
574
  ] }),
553
- error && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: error }),
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
+ ] }),
589
+ error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: error }),
590
+ success && /* @__PURE__ */ jsx("p", { role: "status", 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__ */ jsxs(Button, { type: "submit", className: "w-full", disabled: loading || isVerify && code.length < codeLength, children: [
593
+ loading && /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
594
+ loading ? "Please wait\u2026" : copy.submit
595
+ ] }),
596
+ onSso && (mode === "signin" || mode === "signup") && /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", className: "w-full", onClick: onSso, disabled: loading, children: "Use SSO" })
557
597
  ] })
558
598
  ] }),
559
- /* @__PURE__ */ jsx("p", { className: "text-center text-xs text-muted-foreground", children: isSignUp ? /* @__PURE__ */ jsxs(Fragment, { children: [
599
+ isVerify && onResend && /* @__PURE__ */ jsxs("p", { className: "text-center text-xs text-muted-foreground", children: [
600
+ "Didn\u2019t get a code?",
601
+ " ",
602
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: onResend, className: "text-foreground underline underline-offset-4", children: "Resend" })
603
+ ] }),
604
+ (mode === "signin" || mode === "signup") && /* @__PURE__ */ jsx("p", { className: "text-center text-xs text-muted-foreground", children: isSignUp ? /* @__PURE__ */ jsxs(Fragment, { children: [
560
605
  "Already have an account?",
561
606
  " ",
562
607
  renderLink({ href: signInHref, className: "text-foreground underline underline-offset-4", children: "Sign in" })
@@ -565,6 +610,7 @@ function DefaultForm({
565
610
  " ",
566
611
  renderLink({ href: signUpHref, className: "text-foreground underline underline-offset-4", children: "Create an account" })
567
612
  ] }) }),
613
+ (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
614
  footer && /* @__PURE__ */ jsx("div", { children: footer })
569
615
  ] })
570
616
  ] });
@@ -584,6 +630,435 @@ function AuthSection({
584
630
  const form = children ?? /* @__PURE__ */ jsx(DefaultForm, { ...restFormProps });
585
631
  return /* @__PURE__ */ jsx(CenteredAuth, { brand, className, children: form });
586
632
  }
633
+ function SettingsSection({ title, description, children, danger = false, className }) {
634
+ 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: [
635
+ /* @__PURE__ */ jsxs("div", { children: [
636
+ /* @__PURE__ */ jsx("h2", { className: cn$1("text-base font-semibold", danger && "text-destructive"), children: title }),
637
+ description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description })
638
+ ] }),
639
+ /* @__PURE__ */ jsx("div", { className: "sm:col-span-2", children })
640
+ ] });
641
+ }
642
+ var selectColumn = {
643
+ id: "__select",
644
+ header: ({ table }) => /* @__PURE__ */ jsx(
645
+ Checkbox$1,
646
+ {
647
+ checked: table.getIsAllPageRowsSelected() || table.getIsSomePageRowsSelected() && "indeterminate",
648
+ onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value),
649
+ "aria-label": "Select all"
650
+ }
651
+ ),
652
+ cell: ({ row }) => /* @__PURE__ */ jsx(
653
+ Checkbox$1,
654
+ {
655
+ checked: row.getIsSelected(),
656
+ onCheckedChange: (value) => row.toggleSelected(!!value),
657
+ onClick: (e) => e.stopPropagation(),
658
+ "aria-label": "Select row"
659
+ }
660
+ ),
661
+ enableSorting: false
662
+ };
663
+ function DataTable({
664
+ columns,
665
+ data,
666
+ searchKey,
667
+ searchPlaceholder = "Search\u2026",
668
+ selectable = false,
669
+ onSelectionChange,
670
+ pageSize = 10,
671
+ toolbar,
672
+ onRowClick,
673
+ emptyMessage = "No results.",
674
+ className
675
+ }) {
676
+ const [sorting, setSorting] = React31.useState([]);
677
+ const [rowSelection, setRowSelection] = React31.useState({});
678
+ const [globalFilter, setGlobalFilter] = React31.useState("");
679
+ const allColumns = React31.useMemo(
680
+ () => selectable ? [selectColumn, ...columns] : columns,
681
+ [selectable, columns]
682
+ );
683
+ const table = useReactTable({
684
+ data,
685
+ columns: allColumns,
686
+ state: { sorting, rowSelection, globalFilter },
687
+ onSortingChange: setSorting,
688
+ onRowSelectionChange: setRowSelection,
689
+ onGlobalFilterChange: setGlobalFilter,
690
+ globalFilterFn: (row, _columnId, filterValue) => {
691
+ if (!searchKey) return true;
692
+ const value = row.getValue(searchKey);
693
+ return String(value ?? "").toLowerCase().includes(String(filterValue).toLowerCase());
694
+ },
695
+ getCoreRowModel: getCoreRowModel(),
696
+ getSortedRowModel: getSortedRowModel(),
697
+ ...pageSize > 0 ? { getPaginationRowModel: getPaginationRowModel() } : {},
698
+ initialState: pageSize > 0 ? { pagination: { pageSize } } : void 0,
699
+ enableRowSelection: selectable
700
+ });
701
+ React31.useEffect(() => {
702
+ if (!onSelectionChange) return;
703
+ onSelectionChange(table.getSelectedRowModel().rows.map((r) => r.original));
704
+ }, [rowSelection]);
705
+ const rows = table.getRowModel().rows;
706
+ return /* @__PURE__ */ jsxs("div", { className: cn$1("space-y-4", className), children: [
707
+ (searchKey || toolbar) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
708
+ searchKey ? /* @__PURE__ */ jsxs("div", { className: "relative max-w-sm flex-1", children: [
709
+ /* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" }),
710
+ /* @__PURE__ */ jsx(
711
+ Input,
712
+ {
713
+ value: globalFilter,
714
+ onChange: (e) => setGlobalFilter(e.target.value),
715
+ placeholder: searchPlaceholder,
716
+ className: "pl-8"
717
+ }
718
+ )
719
+ ] }) : /* @__PURE__ */ jsx("div", {}),
720
+ toolbar
721
+ ] }),
722
+ /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-lg border", children: /* @__PURE__ */ jsxs(Table, { children: [
723
+ /* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { children: headerGroup.headers.map((header) => {
724
+ const canSort = header.column.getCanSort();
725
+ const sortDir = header.column.getIsSorted();
726
+ return /* @__PURE__ */ jsx(
727
+ TableHead,
728
+ {
729
+ "aria-sort": sortDir === "asc" ? "ascending" : sortDir === "desc" ? "descending" : canSort ? "none" : void 0,
730
+ children: header.isPlaceholder ? null : canSort ? /* @__PURE__ */ jsxs(
731
+ "button",
732
+ {
733
+ type: "button",
734
+ className: "flex items-center gap-1.5 font-medium hover:text-foreground",
735
+ onClick: header.column.getToggleSortingHandler(),
736
+ children: [
737
+ flexRender(header.column.columnDef.header, header.getContext()),
738
+ 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" })
739
+ ]
740
+ }
741
+ ) : flexRender(header.column.columnDef.header, header.getContext())
742
+ },
743
+ header.id
744
+ );
745
+ }) }, headerGroup.id)) }),
746
+ /* @__PURE__ */ jsx(TableBody, { children: rows.length ? rows.map((row) => /* @__PURE__ */ jsx(
747
+ TableRow,
748
+ {
749
+ "data-state": row.getIsSelected() ? "selected" : void 0,
750
+ onClick: () => onRowClick?.(row.original),
751
+ tabIndex: onRowClick ? 0 : void 0,
752
+ onKeyDown: onRowClick ? (e) => {
753
+ if (e.key === "Enter" && e.target === e.currentTarget) onRowClick(row.original);
754
+ } : void 0,
755
+ className: cn$1(onRowClick && "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"),
756
+ children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
757
+ },
758
+ row.id
759
+ )) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: allColumns.length, className: "h-24 text-center text-muted-foreground", children: emptyMessage }) }) })
760
+ ] }) }),
761
+ (selectable || pageSize > 0 && table.getPageCount() > 1) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
762
+ /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
763
+ selectable && `${table.getFilteredSelectedRowModel().rows.length} of ${table.getFilteredRowModel().rows.length} selected`,
764
+ selectable && pageSize > 0 && table.getPageCount() > 1 && " \xB7 ",
765
+ pageSize > 0 && table.getPageCount() > 1 && `Page ${table.getState().pagination.pageIndex + 1} of ${table.getPageCount()}`
766
+ ] }),
767
+ pageSize > 0 && table.getPageCount() > 1 && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
768
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => table.previousPage(), disabled: !table.getCanPreviousPage(), children: [
769
+ /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" }),
770
+ " Previous"
771
+ ] }),
772
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => table.nextPage(), disabled: !table.getCanNextPage(), children: [
773
+ "Next ",
774
+ /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
775
+ ] })
776
+ ] })
777
+ ] })
778
+ ] });
779
+ }
780
+ function InviteDialog({ roles, onInvite, onClose }) {
781
+ const [email, setEmail] = React31.useState("");
782
+ const [role, setRole] = React31.useState(roles[roles.length - 1] ?? roles[0]);
783
+ return /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(
784
+ "form",
785
+ {
786
+ onSubmit: (e) => {
787
+ e.preventDefault();
788
+ onInvite?.(email, role);
789
+ onClose();
790
+ },
791
+ children: [
792
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
793
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Invite a team member" }),
794
+ /* @__PURE__ */ jsx(DialogDescription, { children: "They\u2019ll get an email invite to join this workspace." })
795
+ ] }),
796
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-4", children: [
797
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
798
+ /* @__PURE__ */ jsx(Label, { htmlFor: "invite-email", children: "Email address" }),
799
+ /* @__PURE__ */ jsx(Input, { id: "invite-email", type: "email", required: true, placeholder: "name@company.com", value: email, onChange: (e) => setEmail(e.target.value) })
800
+ ] }),
801
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
802
+ /* @__PURE__ */ jsx(Label, { children: "Role" }),
803
+ /* @__PURE__ */ jsxs(Select, { value: role, onValueChange: setRole, children: [
804
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
805
+ /* @__PURE__ */ jsx(SelectContent, { children: roles.map((r) => /* @__PURE__ */ jsx(SelectItem, { value: r, children: r }, r)) })
806
+ ] })
807
+ ] })
808
+ ] }),
809
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
810
+ /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", children: "Cancel" }) }),
811
+ /* @__PURE__ */ jsx(Button, { type: "submit", children: "Send invite" })
812
+ ] })
813
+ ]
814
+ }
815
+ ) });
816
+ }
817
+ function TeamMembersPanel({
818
+ members,
819
+ roles = ["Owner", "Admin", "Member"],
820
+ onInvite,
821
+ onRoleChange,
822
+ onRemove,
823
+ title = "Team members",
824
+ description = "Manage who has access to this workspace."
825
+ }) {
826
+ const [inviteOpen, setInviteOpen] = React31.useState(false);
827
+ const columns = React31.useMemo(() => [
828
+ {
829
+ accessorKey: "name",
830
+ header: "Name",
831
+ cell: ({ row }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
832
+ /* @__PURE__ */ jsxs(Avatar, { className: "size-8", children: [
833
+ /* @__PURE__ */ jsx(AvatarImage, { src: row.original.avatar, alt: "" }),
834
+ /* @__PURE__ */ jsx(AvatarFallback, { children: row.original.name.slice(0, 2).toUpperCase() })
835
+ ] }),
836
+ /* @__PURE__ */ jsxs("div", { children: [
837
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium leading-none", children: row.original.name }),
838
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: row.original.email })
839
+ ] })
840
+ ] })
841
+ },
842
+ {
843
+ accessorKey: "role",
844
+ header: "Role",
845
+ cell: ({ row }) => onRoleChange ? /* @__PURE__ */ jsxs(Select, { value: row.original.role, onValueChange: (value) => onRoleChange(row.original.id, value), children: [
846
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 w-32", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
847
+ /* @__PURE__ */ jsx(SelectContent, { children: roles.map((r) => /* @__PURE__ */ jsx(SelectItem, { value: r, children: r }, r)) })
848
+ ] }) : /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: row.original.role })
849
+ },
850
+ ...onRemove ? [{
851
+ id: "actions",
852
+ header: "",
853
+ cell: ({ row }) => /* @__PURE__ */ jsxs(DropdownMenu, { children: [
854
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: "size-8", children: [
855
+ /* @__PURE__ */ jsx(MoreHorizontal, { className: "size-4" }),
856
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Row actions" })
857
+ ] }) }),
858
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: /* @__PURE__ */ jsx(DropdownMenuItem, { className: "text-destructive", onClick: () => onRemove(row.original.id), children: "Remove member" }) })
859
+ ] }),
860
+ enableSorting: false
861
+ }] : []
862
+ ], [roles, onRoleChange, onRemove]);
863
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
864
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [
865
+ /* @__PURE__ */ jsxs("div", { children: [
866
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold", children: title }),
867
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description })
868
+ ] }),
869
+ onInvite && /* @__PURE__ */ jsxs(Dialog, { open: inviteOpen, onOpenChange: setInviteOpen, children: [
870
+ /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { children: [
871
+ /* @__PURE__ */ jsx(UserPlus, { className: "size-4" }),
872
+ " Invite member"
873
+ ] }) }),
874
+ /* @__PURE__ */ jsx(InviteDialog, { roles, onInvite, onClose: () => setInviteOpen(false) }, String(inviteOpen))
875
+ ] })
876
+ ] }),
877
+ /* @__PURE__ */ jsx(DataTable, { columns, data: members, searchKey: "name", searchPlaceholder: "Search members\u2026", pageSize: 0 })
878
+ ] });
879
+ }
880
+ var invoiceStatusVariant = {
881
+ paid: "secondary",
882
+ open: "outline",
883
+ void: "destructive"
884
+ };
885
+ function BillingPanel({
886
+ planName,
887
+ planPrice,
888
+ renewalDate,
889
+ usage,
890
+ paymentMethod,
891
+ invoices,
892
+ onManagePlan,
893
+ onUpdatePaymentMethod,
894
+ className
895
+ }) {
896
+ const invoiceColumns = React31.useMemo(() => [
897
+ { accessorKey: "date", header: "Date", enableSorting: false },
898
+ { accessorKey: "amount", header: "Amount", enableSorting: false },
899
+ {
900
+ accessorKey: "status",
901
+ header: "Status",
902
+ enableSorting: false,
903
+ cell: ({ row }) => /* @__PURE__ */ jsx(Badge$1, { variant: invoiceStatusVariant[row.original.status], className: "capitalize", children: row.original.status })
904
+ },
905
+ {
906
+ id: "download",
907
+ header: "",
908
+ 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: [
909
+ /* @__PURE__ */ jsx(Download, { className: "size-4" }),
910
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Download invoice" })
911
+ ] }) }) : null,
912
+ enableSorting: false
913
+ }
914
+ ], []);
915
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsxs("div", { className: "divide-y divide-border", children: [
916
+ /* @__PURE__ */ jsxs(SettingsSection, { title: "Current plan", description: "Your subscription and usage against plan limits.", children: [
917
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 rounded-lg border bg-card/60 p-4", children: [
918
+ /* @__PURE__ */ jsxs("div", { children: [
919
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
920
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold", children: planName }),
921
+ planPrice && /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: planPrice })
922
+ ] }),
923
+ renewalDate && /* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm text-muted-foreground", children: [
924
+ "Renews ",
925
+ renewalDate
926
+ ] })
927
+ ] }),
928
+ onManagePlan && /* @__PURE__ */ jsx(Button, { onClick: onManagePlan, children: "Manage plan" })
929
+ ] }),
930
+ usage && usage.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-4", children: usage.map((metric) => {
931
+ const overLimit = metric.limit > 0 && metric.used > metric.limit;
932
+ const percent = metric.limit > 0 ? Math.min(100, metric.used / metric.limit * 100) : 0;
933
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
934
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", children: [
935
+ /* @__PURE__ */ jsx("span", { children: metric.label }),
936
+ /* @__PURE__ */ jsxs("span", { className: overLimit ? "font-medium text-destructive" : "text-muted-foreground", children: [
937
+ metric.used,
938
+ metric.unit,
939
+ " / ",
940
+ metric.limit,
941
+ metric.unit
942
+ ] })
943
+ ] }),
944
+ /* @__PURE__ */ jsx(Progress, { value: percent, className: overLimit ? "[&>div]:bg-destructive" : void 0 })
945
+ ] }, metric.label);
946
+ }) })
947
+ ] }),
948
+ 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: [
949
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
950
+ /* @__PURE__ */ jsx(CreditCard, { className: "size-5 text-muted-foreground" }),
951
+ /* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
952
+ /* @__PURE__ */ jsx("span", { className: "font-medium capitalize", children: paymentMethod.brand }),
953
+ " ending in ",
954
+ paymentMethod.last4,
955
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
956
+ " \xB7 expires ",
957
+ paymentMethod.expiry
958
+ ] })
959
+ ] })
960
+ ] }),
961
+ onUpdatePaymentMethod && /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onUpdatePaymentMethod, children: "Update" })
962
+ ] }) }),
963
+ 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 }) })
964
+ ] }) });
965
+ }
966
+ function OnboardingWizard({
967
+ steps,
968
+ onComplete,
969
+ onStepChange,
970
+ step: stepProp,
971
+ onStepIndexChange,
972
+ completeLabel = "Finish",
973
+ className
974
+ }) {
975
+ const [internalStep, setInternalStep] = React31.useState(0);
976
+ const [error, setError] = React31.useState(null);
977
+ const [pending, setPending] = React31.useState(false);
978
+ const index = stepProp ?? internalStep;
979
+ const current = steps[index];
980
+ const isLast = index === steps.length - 1;
981
+ const goTo = (next) => {
982
+ setError(null);
983
+ if (stepProp === void 0) setInternalStep(next);
984
+ onStepIndexChange?.(next);
985
+ onStepChange?.(next);
986
+ };
987
+ const handleNext = async () => {
988
+ if (current.onNext) {
989
+ setPending(true);
990
+ let result;
991
+ try {
992
+ result = await current.onNext();
993
+ } catch (err) {
994
+ setError(err instanceof Error ? err.message : "Something went wrong. Please try again.");
995
+ return;
996
+ } finally {
997
+ setPending(false);
998
+ }
999
+ if (result === false) {
1000
+ setError("Please complete this step before continuing.");
1001
+ return;
1002
+ }
1003
+ if (typeof result === "string") {
1004
+ setError(result);
1005
+ return;
1006
+ }
1007
+ }
1008
+ if (isLast) {
1009
+ onComplete?.();
1010
+ } else {
1011
+ goTo(index + 1);
1012
+ }
1013
+ };
1014
+ const handleBack = () => {
1015
+ if (index > 0) goTo(index - 1);
1016
+ };
1017
+ return /* @__PURE__ */ jsxs("div", { className: cn$1("w-full max-w-lg space-y-8", className), children: [
1018
+ /* @__PURE__ */ jsx("ol", { className: "flex items-center", "aria-label": "Progress", children: steps.map((step, i) => /* @__PURE__ */ jsxs(
1019
+ "li",
1020
+ {
1021
+ "aria-current": i === index ? "step" : void 0,
1022
+ className: cn$1("flex items-center", i !== steps.length - 1 && "flex-1"),
1023
+ children: [
1024
+ /* @__PURE__ */ jsxs(
1025
+ "div",
1026
+ {
1027
+ className: cn$1(
1028
+ "flex size-8 shrink-0 items-center justify-center rounded-full border text-xs font-medium",
1029
+ i < index ? "border-primary bg-primary text-primary-foreground" : i === index ? "border-primary text-primary" : "border-border text-muted-foreground"
1030
+ ),
1031
+ children: [
1032
+ i < index ? /* @__PURE__ */ jsx(Check, { className: "size-4" }) : i + 1,
1033
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
1034
+ step.title,
1035
+ i < index ? " (completed)" : ""
1036
+ ] })
1037
+ ]
1038
+ }
1039
+ ),
1040
+ i !== steps.length - 1 && /* @__PURE__ */ jsx("div", { className: cn$1("mx-2 h-px flex-1", i < index ? "bg-primary" : "bg-border") })
1041
+ ]
1042
+ },
1043
+ step.id
1044
+ )) }),
1045
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1046
+ /* @__PURE__ */ jsxs("div", { children: [
1047
+ /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold tracking-tight", children: current.title }),
1048
+ current.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: current.description })
1049
+ ] }),
1050
+ /* @__PURE__ */ jsx("div", { children: current.content }),
1051
+ error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: error })
1052
+ ] }),
1053
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1054
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: handleBack, disabled: index === 0 || pending, children: "Back" }),
1055
+ /* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleNext, disabled: pending, children: [
1056
+ pending && /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
1057
+ pending ? "Please wait\u2026" : isLast ? completeLabel : "Continue"
1058
+ ] })
1059
+ ] })
1060
+ ] });
1061
+ }
587
1062
  function EmptyState({ icon: Icon, title, description, action, className, ...props }) {
588
1063
  return /* @__PURE__ */ jsxs(
589
1064
  "div",
@@ -833,9 +1308,9 @@ function FadeIn({
833
1308
  style,
834
1309
  ...props
835
1310
  }) {
836
- const ref = React29.useRef(null);
837
- const [visible, setVisible] = React29.useState(false);
838
- React29.useEffect(() => {
1311
+ const ref = React31.useRef(null);
1312
+ const [visible, setVisible] = React31.useState(false);
1313
+ React31.useEffect(() => {
839
1314
  const el = ref.current;
840
1315
  if (!el) return;
841
1316
  const observer = new IntersectionObserver(
@@ -942,27 +1417,28 @@ function HeroSection({
942
1417
  socialProofText,
943
1418
  renderLink = defaultRenderLink4
944
1419
  }) {
1420
+ const mode = useUIVariant();
945
1421
  return /* @__PURE__ */ jsx("section", { className: "overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: marketingSectionSpacing.hero, children: [
946
1422
  /* @__PURE__ */ jsxs("div", { className: "mx-auto grid max-w-6xl items-center gap-10 lg:grid-cols-2 lg:gap-16", children: [
947
1423
  /* @__PURE__ */ jsx(FadeIn, { direction: "left", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
948
- badge && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: badge }) }),
1424
+ badge && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge }) }),
949
1425
  /* @__PURE__ */ jsx("h1", { className: "text-balance text-4xl font-semibold tracking-tight sm:text-5xl lg:text-6xl", children: heading }),
950
1426
  /* @__PURE__ */ jsx("p", { className: "max-w-lg text-pretty text-lg text-muted-foreground", children: description }),
951
1427
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
952
1428
  renderLink({
953
1429
  href: primaryCta.href,
954
- children: /* @__PURE__ */ jsxs(Button$1, { size: "lg", children: [
1430
+ children: /* @__PURE__ */ jsxs(Button, { size: "lg", children: [
955
1431
  primaryCta.label,
956
1432
  /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
957
1433
  ] })
958
1434
  }),
959
1435
  secondaryCta && renderLink({
960
1436
  href: secondaryCta.href,
961
- children: /* @__PURE__ */ jsx(Button$1, { size: "lg", variant: "outline", children: secondaryCta.label })
1437
+ children: /* @__PURE__ */ jsx(Button, { size: "lg", variant: "outline", children: secondaryCta.label })
962
1438
  })
963
1439
  ] })
964
1440
  ] }) }),
965
- media !== "none" && /* @__PURE__ */ jsx(FadeIn, { direction: "right", delay: 200, children: media === "phone" ? /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(PhoneFrame, { size: phoneSize, children: heroImage }) }) : /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-2xl", children: heroImage }) })
1441
+ media !== "none" && /* @__PURE__ */ jsx(FadeIn, { direction: "right", delay: 200, children: media === "phone" ? /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(PhoneFrame, { size: phoneSize, children: heroImage }) }) : /* @__PURE__ */ jsx("div", { className: cn$1("overflow-hidden", mode === "smooth" ? "rounded-3xl" : "rounded-2xl"), children: heroImage }) })
966
1442
  ] }),
967
1443
  (avatarUrls?.length || socialProofText) && /* @__PURE__ */ jsx(FadeIn, { direction: "up", delay: 400, children: /* @__PURE__ */ jsxs("div", { className: "mx-auto mt-12 flex max-w-6xl items-center gap-4", children: [
968
1444
  avatarUrls && avatarUrls.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex -space-x-3", children: avatarUrls.map((url, i) => /* @__PURE__ */ jsxs(Avatar, { className: "size-9 border-2 border-background", children: [
@@ -1021,6 +1497,88 @@ function FeatureCard({ icon: Icon, title, description, href, className, ...props
1021
1497
  }
1022
1498
  return content;
1023
1499
  }
1500
+ function Carousel({
1501
+ children,
1502
+ itemClassName = "w-[280px] sm:w-[320px]",
1503
+ controls = "top-right",
1504
+ ariaLabel,
1505
+ className
1506
+ }) {
1507
+ const mode = useUIVariant();
1508
+ const trackRef = React31.useRef(null);
1509
+ const [canPrev, setCanPrev] = React31.useState(false);
1510
+ const [canNext, setCanNext] = React31.useState(true);
1511
+ const updateScrollState = React31.useCallback(() => {
1512
+ const track = trackRef.current;
1513
+ if (!track) return;
1514
+ setCanPrev(track.scrollLeft > 8);
1515
+ setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
1516
+ }, []);
1517
+ React31.useEffect(() => {
1518
+ const track = trackRef.current;
1519
+ if (!track) return;
1520
+ updateScrollState();
1521
+ track.addEventListener("scroll", updateScrollState, { passive: true });
1522
+ const observer = new ResizeObserver(updateScrollState);
1523
+ observer.observe(track);
1524
+ return () => {
1525
+ track.removeEventListener("scroll", updateScrollState);
1526
+ observer.disconnect();
1527
+ };
1528
+ }, [updateScrollState]);
1529
+ const scrollByItem = (direction) => {
1530
+ const track = trackRef.current;
1531
+ if (!track) return;
1532
+ const item = track.querySelector("[data-carousel-item]");
1533
+ const gap = parseFloat(getComputedStyle(track).columnGap) || 24;
1534
+ const step = item ? item.offsetWidth + gap : track.clientWidth * 0.8;
1535
+ track.scrollBy({ left: direction * step, behavior: "smooth" });
1536
+ };
1537
+ const controlClasses = cn$1(
1538
+ "flex size-9 items-center justify-center border bg-background text-foreground transition-colors",
1539
+ "hover:bg-muted disabled:opacity-40 disabled:hover:bg-background",
1540
+ mode === "smooth" ? "rounded-2xl" : "rounded-xl"
1541
+ );
1542
+ return /* @__PURE__ */ jsxs("div", { className, children: [
1543
+ controls === "top-right" && /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center justify-end gap-2", children: [
1544
+ /* @__PURE__ */ jsx(
1545
+ "button",
1546
+ {
1547
+ type: "button",
1548
+ "aria-label": "Previous",
1549
+ className: controlClasses,
1550
+ onClick: () => scrollByItem(-1),
1551
+ disabled: !canPrev,
1552
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
1553
+ }
1554
+ ),
1555
+ /* @__PURE__ */ jsx(
1556
+ "button",
1557
+ {
1558
+ type: "button",
1559
+ "aria-label": "Next",
1560
+ className: controlClasses,
1561
+ onClick: () => scrollByItem(1),
1562
+ disabled: !canNext,
1563
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
1564
+ }
1565
+ )
1566
+ ] }),
1567
+ /* @__PURE__ */ jsx(
1568
+ "div",
1569
+ {
1570
+ ref: trackRef,
1571
+ role: "region",
1572
+ "aria-label": ariaLabel,
1573
+ className: "flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
1574
+ children: React31.Children.map(
1575
+ children,
1576
+ (child) => child == null ? null : /* @__PURE__ */ jsx("div", { "data-carousel-item": true, className: cn$1("shrink-0 snap-start", itemClassName), children: child })
1577
+ )
1578
+ }
1579
+ )
1580
+ ] });
1581
+ }
1024
1582
  var translateMap2 = {
1025
1583
  up: [0, 20],
1026
1584
  down: [0, -20],
@@ -1038,9 +1596,9 @@ function StaggerChildren({
1038
1596
  className,
1039
1597
  ...props
1040
1598
  }) {
1041
- const ref = React29.useRef(null);
1042
- const [visible, setVisible] = React29.useState(false);
1043
- React29.useEffect(() => {
1599
+ const ref = React31.useRef(null);
1600
+ const [visible, setVisible] = React31.useState(false);
1601
+ React31.useEffect(() => {
1044
1602
  const el = ref.current;
1045
1603
  if (!el) return;
1046
1604
  const observer = new IntersectionObserver(
@@ -1058,7 +1616,7 @@ function StaggerChildren({
1058
1616
  return () => observer.disconnect();
1059
1617
  }, [once]);
1060
1618
  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(
1619
+ return /* @__PURE__ */ jsx("div", { ref, className: cn$1(className), ...props, children: React31.Children.map(children, (child, i) => /* @__PURE__ */ jsx(
1062
1620
  "div",
1063
1621
  {
1064
1622
  className: "h-full",
@@ -1076,7 +1634,8 @@ function FeaturesSection({
1076
1634
  title = "Everything you need to ship fast",
1077
1635
  description = "A complete system of components, blocks, and hooks designed to work together - and get out of your way.",
1078
1636
  badge = "Features",
1079
- features
1637
+ features,
1638
+ layout = "grid"
1080
1639
  }) {
1081
1640
  const mode = useUIVariant();
1082
1641
  const sectionClasses = cn$1(
@@ -1085,18 +1644,19 @@ function FeaturesSection({
1085
1644
  mode === "playful" && "rounded-2xl border-primary/25 border",
1086
1645
  !mode && "rounded-2xl border"
1087
1646
  );
1088
- return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-4xl", children: [
1647
+ const cards = features.map((feature) => /* @__PURE__ */ jsx(
1648
+ FeatureCard,
1649
+ {
1650
+ icon: feature.icon,
1651
+ title: feature.title,
1652
+ description: feature.description,
1653
+ href: feature.href
1654
+ },
1655
+ feature.title
1656
+ ));
1657
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: cn$1("mx-auto", layout === "carousel" ? "max-w-5xl" : "max-w-4xl"), children: [
1089
1658
  /* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
1090
- /* @__PURE__ */ jsx(StaggerChildren, { className: "mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-3", children: features.map((feature) => /* @__PURE__ */ jsx(
1091
- FeatureCard,
1092
- {
1093
- icon: feature.icon,
1094
- title: feature.title,
1095
- description: feature.description,
1096
- href: feature.href
1097
- },
1098
- feature.title
1099
- )) })
1659
+ layout === "carousel" ? /* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsx(Carousel, { className: "mt-10", ariaLabel: title, children: cards }) }) : /* @__PURE__ */ jsx(StaggerChildren, { className: "mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-3", children: cards })
1100
1660
  ] }) }) });
1101
1661
  }
1102
1662
  function GroupedFeaturesSection({
@@ -1226,106 +1786,6 @@ function TechStackSection({
1226
1786
  }) })
1227
1787
  ] }) }) });
1228
1788
  }
1229
- function CarouselSection({
1230
- title = "Explore the platform",
1231
- description,
1232
- badge = "Features",
1233
- features
1234
- }) {
1235
- 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(() => {
1240
- const track = trackRef.current;
1241
- if (!track) return;
1242
- setCanPrev(track.scrollLeft > 8);
1243
- setCanNext(track.scrollLeft < track.scrollWidth - track.clientWidth - 8);
1244
- }, []);
1245
- React29.useEffect(() => {
1246
- const track = trackRef.current;
1247
- if (!track) return;
1248
- updateScrollState();
1249
- track.addEventListener("scroll", updateScrollState, { passive: true });
1250
- const observer = new ResizeObserver(updateScrollState);
1251
- observer.observe(track);
1252
- return () => {
1253
- track.removeEventListener("scroll", updateScrollState);
1254
- observer.disconnect();
1255
- };
1256
- }, [updateScrollState]);
1257
- const scrollByCard = (direction) => {
1258
- const track = trackRef.current;
1259
- if (!track) return;
1260
- const card = track.querySelector("[data-carousel-item]");
1261
- const step = card ? card.offsetWidth + 24 : track.clientWidth * 0.8;
1262
- track.scrollBy({ left: direction * step, behavior: "smooth" });
1263
- };
1264
- const sectionClasses = cn$1(
1265
- "overflow-hidden bg-card",
1266
- mode === "smooth" && "rounded-3xl border",
1267
- mode === "playful" && "rounded-2xl border-primary/25 border",
1268
- !mode && "rounded-2xl border"
1269
- );
1270
- const controlClasses = cn$1(
1271
- "flex size-9 items-center justify-center border bg-background text-foreground transition-colors",
1272
- "hover:bg-muted disabled:opacity-40 disabled:hover:bg-background",
1273
- mode === "smooth" ? "rounded-2xl" : "rounded-xl"
1274
- );
1275
- return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-5xl", children: [
1276
- /* @__PURE__ */ jsx(SectionTitle, { title, description, badge }),
1277
- /* @__PURE__ */ jsxs(FadeIn, { direction: "up", children: [
1278
- /* @__PURE__ */ jsxs("div", { className: "mt-10 flex items-center justify-end gap-2", children: [
1279
- /* @__PURE__ */ jsx(
1280
- "button",
1281
- {
1282
- type: "button",
1283
- "aria-label": "Previous",
1284
- className: controlClasses,
1285
- onClick: () => scrollByCard(-1),
1286
- disabled: !canPrev,
1287
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
1288
- }
1289
- ),
1290
- /* @__PURE__ */ jsx(
1291
- "button",
1292
- {
1293
- type: "button",
1294
- "aria-label": "Next",
1295
- className: controlClasses,
1296
- onClick: () => scrollByCard(1),
1297
- disabled: !canNext,
1298
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
1299
- }
1300
- )
1301
- ] }),
1302
- /* @__PURE__ */ jsx(
1303
- "div",
1304
- {
1305
- ref: trackRef,
1306
- className: "mt-4 flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
1307
- children: features.map((feature) => /* @__PURE__ */ jsx(
1308
- "div",
1309
- {
1310
- "data-carousel-item": true,
1311
- className: "w-[280px] shrink-0 snap-start sm:w-[320px]",
1312
- children: /* @__PURE__ */ jsx(
1313
- FeatureCard,
1314
- {
1315
- icon: feature.icon,
1316
- title: feature.title,
1317
- description: feature.description,
1318
- href: feature.href
1319
- }
1320
- )
1321
- },
1322
- feature.title
1323
- ))
1324
- }
1325
- )
1326
- ] })
1327
- ] }) }) });
1328
- }
1329
1789
  var speedDuration = {
1330
1790
  slow: "280s",
1331
1791
  normal: "180s",
@@ -1406,79 +1866,42 @@ function FeatureMarqueeSection({
1406
1866
  ` })
1407
1867
  ] });
1408
1868
  }
1409
- var defaultRenderLink5 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
1410
- function CtaSection({
1411
- heading,
1412
- description,
1413
- primaryCta,
1414
- secondaryCta,
1415
- footnote,
1416
- renderLink = defaultRenderLink5
1417
- }) {
1418
- const mode = useUIVariant();
1419
- const sectionClasses = cn$1(
1420
- "overflow-hidden bg-card",
1421
- mode === "smooth" && "rounded-3xl border",
1422
- mode === "playful" && "rounded-2xl border-primary/25 border",
1423
- !mode && "rounded-2xl border"
1424
- );
1425
- return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-16 sm:px-10 sm:py-24", children: [
1426
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_center,hsl(var(--primary)/0.15),transparent_70%)]" }),
1427
- /* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-2xl text-center", children: [
1428
- /* @__PURE__ */ jsx("div", { className: cn$1("mb-4 inline-flex h-10 w-10 items-center justify-center bg-primary/10 text-primary", mode === "smooth" ? "rounded-3xl" : "rounded-2xl"), children: /* @__PURE__ */ jsx(Sparkles, { className: "size-5" }) }),
1429
- /* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: heading }),
1430
- /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
1431
- /* @__PURE__ */ jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-3", children: [
1432
- renderLink({
1433
- href: primaryCta.href,
1434
- children: /* @__PURE__ */ jsxs(Button$1, { size: "lg", children: [
1435
- primaryCta.label,
1436
- /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
1437
- ] })
1438
- }),
1439
- secondaryCta && renderLink({
1440
- href: secondaryCta.href,
1441
- children: /* @__PURE__ */ jsx(Button$1, { size: "lg", variant: "outline", children: secondaryCta.label })
1442
- })
1443
- ] }),
1444
- footnote && /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-muted-foreground", children: footnote })
1445
- ] }) })
1446
- ] }) });
1869
+ function useIntersectionObserver(options) {
1870
+ const ref = React31.useRef(null);
1871
+ const [isIntersecting, setIsIntersecting] = React31.useState(false);
1872
+ React31.useEffect(() => {
1873
+ const el = ref.current;
1874
+ if (!el) return;
1875
+ const observer = new IntersectionObserver(([entry]) => {
1876
+ setIsIntersecting(entry.isIntersecting);
1877
+ }, options);
1878
+ observer.observe(el);
1879
+ return () => observer.disconnect();
1880
+ }, []);
1881
+ return [ref, isIntersecting];
1447
1882
  }
1448
- var defaultRenderLink6 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
1449
- function CtaCardSection({
1883
+ var defaultRenderLink5 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
1884
+ function ShowcaseCta({
1450
1885
  heading,
1451
1886
  description,
1452
1887
  primaryCta,
1453
1888
  footnote,
1454
1889
  icon,
1455
- renderLink = defaultRenderLink6
1890
+ renderLink = defaultRenderLink5,
1891
+ sectionClasses
1456
1892
  }) {
1457
- const ref = React29.useRef(null);
1458
- const [visible, setVisible] = React29.useState(false);
1459
- React29.useEffect(() => {
1460
- const el = ref.current;
1461
- if (!el) return;
1462
- const observer = new IntersectionObserver(
1463
- ([entry]) => {
1464
- if (entry.isIntersecting) {
1465
- setVisible(true);
1466
- observer.disconnect();
1467
- }
1468
- },
1469
- { threshold: 0.15 }
1470
- );
1471
- observer.observe(el);
1472
- return () => observer.disconnect();
1473
- }, []);
1893
+ const [ref, intersecting] = useIntersectionObserver({ threshold: 0.15 });
1894
+ const [visible, setVisible] = React31.useState(false);
1895
+ React31.useEffect(() => {
1896
+ if (intersecting) setVisible(true);
1897
+ }, [intersecting]);
1474
1898
  return /* @__PURE__ */ jsxs(
1475
1899
  "section",
1476
1900
  {
1477
1901
  ref,
1478
1902
  className: cn$1(
1479
- "relative overflow-hidden rounded-3xl border border-gray-200 shadow-sm dark:border-border",
1480
- "bg-[#E0DFDB] dark:bg-card",
1481
- "transition-[opacity,transform] duration-700 ease-out",
1903
+ sectionClasses,
1904
+ "relative transition-[opacity,transform] duration-700 ease-out",
1482
1905
  visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-6"
1483
1906
  ),
1484
1907
  children: [
@@ -1487,11 +1910,11 @@ function CtaCardSection({
1487
1910
  {
1488
1911
  className: "pointer-events-none absolute inset-0 flex items-center justify-center",
1489
1912
  style: {
1490
- opacity: visible ? 0.12 : 0,
1913
+ opacity: visible ? 0.1 : 0,
1491
1914
  transform: visible ? "scale(1) rotate(0deg)" : "scale(0.9) rotate(-5deg)",
1492
1915
  transition: "opacity 700ms ease 200ms, transform 700ms ease 200ms"
1493
1916
  },
1494
- children: icon ?? /* @__PURE__ */ jsx(Rocket, { className: "size-72 text-foreground dark:text-foreground", strokeWidth: 0.75 })
1917
+ children: icon ?? /* @__PURE__ */ jsx(Rocket, { className: "size-72 text-foreground", strokeWidth: 0.75 })
1495
1918
  }
1496
1919
  ),
1497
1920
  /* @__PURE__ */ jsxs("div", { className: "relative z-10 px-6 py-20 text-center sm:px-10 sm:py-28", children: [
@@ -1499,9 +1922,9 @@ function CtaCardSection({
1499
1922
  description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
1500
1923
  /* @__PURE__ */ jsx("div", { className: "mt-8", children: renderLink({
1501
1924
  href: primaryCta.href,
1502
- children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 rounded-full bg-foreground px-6 py-3 text-sm font-medium text-background transition-opacity hover:opacity-80", children: [
1925
+ children: /* @__PURE__ */ jsxs(Button, { size: "lg", className: "rounded-full px-6", children: [
1503
1926
  primaryCta.label,
1504
- /* @__PURE__ */ jsx(ArrowRight, { className: "size-4" })
1927
+ /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
1505
1928
  ] })
1506
1929
  }) }),
1507
1930
  footnote && /* @__PURE__ */ jsx("p", { className: "mt-5 text-sm text-muted-foreground", children: footnote })
@@ -1510,6 +1933,49 @@ function CtaCardSection({
1510
1933
  }
1511
1934
  );
1512
1935
  }
1936
+ function CtaSection(props) {
1937
+ const {
1938
+ heading,
1939
+ description,
1940
+ primaryCta,
1941
+ secondaryCta,
1942
+ footnote,
1943
+ variant = "default",
1944
+ renderLink = defaultRenderLink5
1945
+ } = props;
1946
+ const mode = useUIVariant();
1947
+ const sectionClasses = cn$1(
1948
+ "overflow-hidden bg-card",
1949
+ mode === "smooth" && "rounded-3xl border",
1950
+ mode === "playful" && "rounded-2xl border-primary/25 border",
1951
+ !mode && "rounded-2xl border"
1952
+ );
1953
+ if (variant === "showcase") {
1954
+ return /* @__PURE__ */ jsx(ShowcaseCta, { ...props, sectionClasses });
1955
+ }
1956
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-16 sm:px-10 sm:py-24", children: [
1957
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_center,hsl(var(--primary)/0.15),transparent_70%)]" }),
1958
+ /* @__PURE__ */ jsx(FadeIn, { direction: "up", children: /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-2xl text-center", children: [
1959
+ /* @__PURE__ */ jsx("div", { className: cn$1("mb-4 inline-flex h-10 w-10 items-center justify-center bg-primary/10 text-primary", mode === "smooth" ? "rounded-3xl" : "rounded-2xl"), children: /* @__PURE__ */ jsx(Sparkles, { className: "size-5" }) }),
1960
+ /* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: heading }),
1961
+ description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-lg text-pretty text-muted-foreground", children: description }),
1962
+ /* @__PURE__ */ jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-3", children: [
1963
+ renderLink({
1964
+ href: primaryCta.href,
1965
+ children: /* @__PURE__ */ jsxs(Button, { size: "lg", children: [
1966
+ primaryCta.label,
1967
+ /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
1968
+ ] })
1969
+ }),
1970
+ secondaryCta && renderLink({
1971
+ href: secondaryCta.href,
1972
+ children: /* @__PURE__ */ jsx(Button, { size: "lg", variant: "outline", children: secondaryCta.label })
1973
+ })
1974
+ ] }),
1975
+ footnote && /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-muted-foreground", children: footnote })
1976
+ ] }) })
1977
+ ] }) });
1978
+ }
1513
1979
  function PricingCard({
1514
1980
  name,
1515
1981
  price,
@@ -1603,8 +2069,8 @@ function CountdownTimer({
1603
2069
  className,
1604
2070
  compact = false
1605
2071
  }) {
1606
- const [timeLeft, setTimeLeft] = React29.useState(() => getTimeLeft(deadline));
1607
- React29.useEffect(() => {
2072
+ const [timeLeft, setTimeLeft] = React31.useState(() => getTimeLeft(deadline));
2073
+ React31.useEffect(() => {
1608
2074
  setTimeLeft(getTimeLeft(deadline));
1609
2075
  const id = setInterval(() => setTimeLeft(getTimeLeft(deadline)), 1e3);
1610
2076
  return () => clearInterval(id);
@@ -1630,7 +2096,7 @@ function CountdownTimer({
1630
2096
  ];
1631
2097
  return /* @__PURE__ */ jsxs("div", { className: cn$1("flex items-center justify-center gap-2 text-sm", className), children: [
1632
2098
  /* @__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: [
2099
+ /* @__PURE__ */ jsx("span", { className: "flex items-center gap-1 font-mono font-semibold tabular-nums", children: units.map(({ value, unit }, i) => /* @__PURE__ */ jsxs(React31.Fragment, { children: [
1634
2100
  /* @__PURE__ */ jsxs("span", { children: [
1635
2101
  String(value).padStart(2, "0"),
1636
2102
  /* @__PURE__ */ jsx("span", { className: "font-normal text-muted-foreground", children: unit })
@@ -1639,7 +2105,7 @@ function CountdownTimer({
1639
2105
  ] }, unit)) })
1640
2106
  ] });
1641
2107
  }
1642
- var defaultRenderLink7 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2108
+ var defaultRenderLink6 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
1643
2109
  function PricingSection({
1644
2110
  title = "Simple, transparent pricing",
1645
2111
  description = "Start for free. Scale as you grow. No hidden fees.",
@@ -1647,13 +2113,13 @@ function PricingSection({
1647
2113
  plans,
1648
2114
  saveBadge = "Save 34%",
1649
2115
  isAuthenticated,
1650
- renderLink = defaultRenderLink7,
2116
+ renderLink = defaultRenderLink6,
1651
2117
  footnote,
1652
2118
  mode = "subscription",
1653
2119
  foundingDeadline,
1654
2120
  renderPlanFooter
1655
2121
  }) {
1656
- const [annual, setAnnual] = React29.useState(false);
2122
+ const [annual, setAnnual] = React31.useState(false);
1657
2123
  const isOneTime = mode === "one-time";
1658
2124
  const uiMode = useUIVariant();
1659
2125
  const sectionClasses = cn$1(
@@ -1857,9 +2323,16 @@ function TeamSection({
1857
2323
  title = "The people behind Olwiba",
1858
2324
  description = "A small team with deep experience in design systems, open source, and developer tooling."
1859
2325
  }) {
1860
- return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-5xl", children: [
2326
+ const mode = useUIVariant();
2327
+ const sectionClasses = cn$1(
2328
+ "overflow-hidden bg-card",
2329
+ mode === "smooth" && "rounded-3xl border",
2330
+ mode === "playful" && "rounded-2xl border-primary/25 border",
2331
+ !mode && "rounded-2xl border"
2332
+ );
2333
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsx("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-5xl", children: [
1861
2334
  /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
1862
- /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", className: "mb-4", children: badge }),
2335
+ /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "mb-4", children: badge }),
1863
2336
  /* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: title }),
1864
2337
  /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-xl text-pretty text-muted-foreground", children: description })
1865
2338
  ] }),
@@ -1915,10 +2388,10 @@ function CountUp({
1915
2388
  className,
1916
2389
  ...props
1917
2390
  }) {
1918
- const ref = React29.useRef(null);
1919
- const [value, setValue] = React29.useState(from);
1920
- const hasStarted = React29.useRef(false);
1921
- React29.useEffect(() => {
2391
+ const ref = React31.useRef(null);
2392
+ const [value, setValue] = React31.useState(from);
2393
+ const hasStarted = React31.useRef(false);
2394
+ React31.useEffect(() => {
1922
2395
  const el = ref.current;
1923
2396
  if (!el) return;
1924
2397
  const observer = new IntersectionObserver(
@@ -2011,20 +2484,27 @@ function NewsletterSection({
2011
2484
  successDescription = defaults.successDescription,
2012
2485
  onSubscribe
2013
2486
  } = {}) {
2014
- const [email, setEmail] = React29.useState("");
2015
- const [submitted, setSubmitted] = React29.useState(false);
2487
+ const [email, setEmail] = React31.useState("");
2488
+ const [submitted, setSubmitted] = React31.useState(false);
2489
+ const mode = useUIVariant();
2490
+ const sectionClasses = cn$1(
2491
+ "overflow-hidden bg-card",
2492
+ mode === "smooth" && "rounded-3xl border",
2493
+ mode === "playful" && "rounded-2xl border-primary/25 border",
2494
+ !mode && "rounded-2xl border"
2495
+ );
2016
2496
  async function handleSubmit(e) {
2017
2497
  e.preventDefault();
2018
2498
  if (!email) return;
2019
2499
  await onSubscribe?.(email);
2020
2500
  setSubmitted(true);
2021
2501
  }
2022
- return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-14 sm:px-10 sm:py-20", children: [
2502
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "relative px-6 py-14 sm:px-10 sm:py-20", children: [
2023
2503
  /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,hsl(var(--primary)/0.1),transparent_60%)]" }),
2024
2504
  /* @__PURE__ */ jsxs("div", { className: "relative mx-auto max-w-xl text-center", children: [
2025
2505
  /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center justify-center gap-2", children: [
2026
2506
  /* @__PURE__ */ jsx("div", { className: "inline-flex h-8 w-8 items-center justify-center rounded-xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Mail, { className: "size-4" }) }),
2027
- /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: badge })
2507
+ /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge })
2028
2508
  ] }),
2029
2509
  /* @__PURE__ */ jsx("h2", { className: "text-balance text-3xl font-semibold tracking-tight sm:text-4xl", children: title }),
2030
2510
  /* @__PURE__ */ jsx("p", { className: "mx-auto mt-4 max-w-sm text-pretty text-muted-foreground", children: description }),
@@ -2045,7 +2525,7 @@ function NewsletterSection({
2045
2525
  }
2046
2526
  ),
2047
2527
  /* @__PURE__ */ jsx(
2048
- Input$1,
2528
+ Input,
2049
2529
  {
2050
2530
  type: "email",
2051
2531
  placeholder,
@@ -2055,7 +2535,7 @@ function NewsletterSection({
2055
2535
  className: "flex-1"
2056
2536
  }
2057
2537
  ),
2058
- /* @__PURE__ */ jsxs(Button$1, { type: "submit", children: [
2538
+ /* @__PURE__ */ jsxs(Button, { type: "submit", children: [
2059
2539
  submitLabel,
2060
2540
  /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 size-4" })
2061
2541
  ] })
@@ -2096,15 +2576,22 @@ function ContactSection({
2096
2576
  sendAnotherLabel = defaults2.sendAnotherLabel,
2097
2577
  onSubmit
2098
2578
  } = {}) {
2099
- const [submitted, setSubmitted] = React29.useState(false);
2579
+ const [submitted, setSubmitted] = React31.useState(false);
2580
+ const mode = useUIVariant();
2581
+ const sectionClasses = cn$1(
2582
+ "overflow-hidden bg-card",
2583
+ mode === "smooth" && "rounded-3xl border",
2584
+ mode === "playful" && "rounded-2xl border-primary/25 border",
2585
+ !mode && "rounded-2xl border"
2586
+ );
2100
2587
  async function handleSubmit(e) {
2101
2588
  e.preventDefault();
2102
2589
  await onSubmit?.(e);
2103
2590
  setSubmitted(true);
2104
2591
  }
2105
- return /* @__PURE__ */ jsx("section", { className: "overflow-hidden rounded-2xl border bg-card", children: /* @__PURE__ */ jsxs("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: [
2592
+ return /* @__PURE__ */ jsx("section", { className: sectionClasses, children: /* @__PURE__ */ jsxs("div", { className: "px-6 py-14 sm:px-10 sm:py-20", children: [
2106
2593
  /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-2xl text-center", children: [
2107
- badge && /* @__PURE__ */ jsx("div", { className: "mb-4 flex justify-center", children: /* @__PURE__ */ jsx(Badge$1, { variant: "secondary", children: badge }) }),
2594
+ badge && /* @__PURE__ */ jsx("div", { className: "mb-4 flex justify-center", children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: badge }) }),
2108
2595
  /* @__PURE__ */ jsx("h2", { className: "text-balance text-4xl font-semibold tracking-tight sm:text-5xl", children: title }),
2109
2596
  /* @__PURE__ */ jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: description }),
2110
2597
  contactInfo && contactInfo.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-8 flex flex-wrap justify-center gap-6", children: contactInfo.map(({ icon: Icon = Mail, label, value }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
@@ -2116,7 +2603,7 @@ function ContactSection({
2116
2603
  /* @__PURE__ */ jsx("div", { className: "mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(MessageSquare, { className: "size-6" }) }),
2117
2604
  /* @__PURE__ */ jsx("h3", { className: "mt-5 text-lg font-semibold", children: successTitle }),
2118
2605
  /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: successDescription }),
2119
- /* @__PURE__ */ jsx(Button$1, { variant: "outline", size: "sm", className: "mt-6", onClick: () => setSubmitted(false), children: sendAnotherLabel })
2606
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "mt-6", onClick: () => setSubmitted(false), children: sendAnotherLabel })
2120
2607
  ] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "mx-auto mt-16 max-w-xl sm:mt-20", children: [
2121
2608
  /* @__PURE__ */ jsx(
2122
2609
  "input",
@@ -2133,7 +2620,7 @@ function ContactSection({
2133
2620
  /* @__PURE__ */ jsxs("div", { children: [
2134
2621
  /* @__PURE__ */ jsx(Label, { htmlFor: "contact-first", className: "block text-sm/6 font-semibold", children: "First name" }),
2135
2622
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2136
- Input$1,
2623
+ Input,
2137
2624
  {
2138
2625
  id: "contact-first",
2139
2626
  name: "firstName",
@@ -2146,7 +2633,7 @@ function ContactSection({
2146
2633
  /* @__PURE__ */ jsxs("div", { children: [
2147
2634
  /* @__PURE__ */ jsx(Label, { htmlFor: "contact-last", className: "block text-sm/6 font-semibold", children: "Last name" }),
2148
2635
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2149
- Input$1,
2636
+ Input,
2150
2637
  {
2151
2638
  id: "contact-last",
2152
2639
  name: "lastName",
@@ -2163,7 +2650,7 @@ function ContactSection({
2163
2650
  /* @__PURE__ */ jsx("span", { className: "font-normal text-muted-foreground", children: "(optional)" })
2164
2651
  ] }),
2165
2652
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2166
- Input$1,
2653
+ Input,
2167
2654
  {
2168
2655
  id: "contact-company",
2169
2656
  name: "company",
@@ -2175,7 +2662,7 @@ function ContactSection({
2175
2662
  /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
2176
2663
  /* @__PURE__ */ jsx(Label, { htmlFor: "contact-email", className: "block text-sm/6 font-semibold", children: "Email" }),
2177
2664
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2178
- Input$1,
2665
+ Input,
2179
2666
  {
2180
2667
  id: "contact-email",
2181
2668
  name: "email",
@@ -2221,7 +2708,7 @@ function ContactSection({
2221
2708
  /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
2222
2709
  /* @__PURE__ */ jsx(Label, { htmlFor: "contact-message", className: "block text-sm/6 font-semibold", children: "Message" }),
2223
2710
  /* @__PURE__ */ jsx("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx(
2224
- Textarea$1,
2711
+ Textarea,
2225
2712
  {
2226
2713
  id: "contact-message",
2227
2714
  name: "message",
@@ -2269,7 +2756,7 @@ function ContactSection({
2269
2756
  )
2270
2757
  ] })
2271
2758
  ] }),
2272
- /* @__PURE__ */ jsx("div", { className: "mt-10", children: /* @__PURE__ */ jsxs(Button$1, { type: "submit", className: "w-full", children: [
2759
+ /* @__PURE__ */ jsx("div", { className: "mt-10", children: /* @__PURE__ */ jsxs(Button, { type: "submit", className: "w-full", children: [
2273
2760
  "Let's talk",
2274
2761
  /* @__PURE__ */ jsx(Send, { className: "ml-2 size-4" })
2275
2762
  ] }) })
@@ -2379,15 +2866,15 @@ function QualificationSection({
2379
2866
  }) })
2380
2867
  ] }) }) });
2381
2868
  }
2382
- var defaultRenderLink8 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2869
+ var defaultRenderLink7 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2383
2870
  function Navbar({
2384
2871
  brand,
2385
2872
  navLinks,
2386
2873
  cta,
2387
2874
  controls,
2388
- renderLink = defaultRenderLink8
2875
+ renderLink = defaultRenderLink7
2389
2876
  }) {
2390
- const [open, setOpen] = React29.useState(false);
2877
+ const [open, setOpen] = React31.useState(false);
2391
2878
  const brandHref = brand.href ?? "/";
2392
2879
  const hasRightContent = controls?.length || cta?.primary || cta?.secondary;
2393
2880
  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 +2891,7 @@ function Navbar({
2404
2891
  children: link.label
2405
2892
  }) }, link.label)) }),
2406
2893
  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)),
2894
+ controls?.map((control, i) => /* @__PURE__ */ jsx(React31.Fragment, { children: control }, i)),
2408
2895
  (cta?.secondary || cta?.primary) && controls?.length ? /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-border" }) : null,
2409
2896
  cta?.secondary && renderLink({
2410
2897
  href: cta.secondary.href,
@@ -2428,7 +2915,7 @@ function Navbar({
2428
2915
  /* @__PURE__ */ jsx("span", { children: brand.name })
2429
2916
  ] })
2430
2917
  }) }) }),
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,
2918
+ controls?.length ? /* @__PURE__ */ jsx("div", { className: "mb-4 flex w-full items-center justify-between gap-1", children: controls.map((control, i) => /* @__PURE__ */ jsx(React31.Fragment, { children: control }, i)) }) : null,
2432
2919
  /* @__PURE__ */ jsx(Separator, {}),
2433
2920
  /* @__PURE__ */ jsx("nav", { className: "mt-4 flex flex-col gap-1", children: navLinks.map((link) => /* @__PURE__ */ jsx("span", { onClick: () => setOpen(false), children: renderLink({
2434
2921
  href: link.href,
@@ -2449,14 +2936,14 @@ function Navbar({
2449
2936
  ] })
2450
2937
  ] }) });
2451
2938
  }
2452
- var defaultRenderLink9 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2939
+ var defaultRenderLink8 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
2453
2940
  function Footer({
2454
2941
  brand,
2455
2942
  navLinks,
2456
2943
  socialLinks,
2457
2944
  legal,
2458
2945
  status,
2459
- renderLink = defaultRenderLink9
2946
+ renderLink = defaultRenderLink8
2460
2947
  }) {
2461
2948
  const brandHref = brand.href ?? "/";
2462
2949
  const copyrightText = legal ?? `\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} ${brand.name}. All rights reserved.`;
@@ -2577,7 +3064,7 @@ function LogoStrip({
2577
3064
  ] });
2578
3065
  }
2579
3066
  function GridUnderlay({ variant, size = 24, className, ...props }) {
2580
- const id = React29.useId();
3067
+ const id = React31.useId();
2581
3068
  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
3069
  /* @__PURE__ */ jsx("line", { x1: size / 2, y1: 0, x2: size / 2, y2: size, stroke: "currentColor", strokeWidth: 0.5 }),
2583
3070
  /* @__PURE__ */ jsx("line", { x1: 0, y1: size / 2, x2: size, y2: size / 2, stroke: "currentColor", strokeWidth: 0.5 })
@@ -2605,9 +3092,9 @@ function GlowUnderlay({
2605
3092
  style,
2606
3093
  ...props
2607
3094
  }) {
2608
- const [pos, setPos] = React29.useState({ x: "50%", y: "50%" });
2609
- const containerRef = React29.useRef(null);
2610
- React29.useEffect(() => {
3095
+ const [pos, setPos] = React31.useState({ x: "50%", y: "50%" });
3096
+ const containerRef = React31.useRef(null);
3097
+ React31.useEffect(() => {
2611
3098
  if (!followCursor) return;
2612
3099
  const handleMouseMove = (e) => {
2613
3100
  const el = containerRef.current;
@@ -2746,8 +3233,8 @@ function PageTransition({
2746
3233
  style,
2747
3234
  ...props
2748
3235
  }) {
2749
- const [mounted, setMounted] = React29.useState(false);
2750
- React29.useEffect(() => {
3236
+ const [mounted, setMounted] = React31.useState(false);
3237
+ React31.useEffect(() => {
2751
3238
  const id = requestAnimationFrame(() => setMounted(true));
2752
3239
  return () => cancelAnimationFrame(id);
2753
3240
  }, []);
@@ -2774,8 +3261,8 @@ function Spotlight({
2774
3261
  triggerClassName,
2775
3262
  shortcut = "\u2318K"
2776
3263
  }) {
2777
- const [open, setOpen] = React29.useState(false);
2778
- React29.useEffect(() => {
3264
+ const [open, setOpen] = React31.useState(false);
3265
+ React31.useEffect(() => {
2779
3266
  const down = (e) => {
2780
3267
  if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
2781
3268
  e.preventDefault();
@@ -2805,7 +3292,7 @@ function Spotlight({
2805
3292
  /* @__PURE__ */ jsx(CommandInput, { placeholder }),
2806
3293
  /* @__PURE__ */ jsxs(CommandList, { children: [
2807
3294
  /* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
2808
- groups.map((group, i) => /* @__PURE__ */ jsxs(React29.Fragment, { children: [
3295
+ groups.map((group, i) => /* @__PURE__ */ jsxs(React31.Fragment, { children: [
2809
3296
  i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
2810
3297
  /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
2811
3298
  CommandItem,
@@ -2830,8 +3317,8 @@ function Spotlight({
2830
3317
  var BASE = 40;
2831
3318
  var MAGNIFY_RADIUS = 80;
2832
3319
  function Dock({ items, iconSize = BASE, magnification = 1.7, className, ...props }) {
2833
- const [mouseX, setMouseX] = React29.useState(null);
2834
- const containerRef = React29.useRef(null);
3320
+ const [mouseX, setMouseX] = React31.useState(null);
3321
+ const containerRef = React31.useRef(null);
2835
3322
  function getScale(index) {
2836
3323
  if (mouseX === null || !containerRef.current) return 1;
2837
3324
  const container = containerRef.current;
@@ -2929,6 +3416,391 @@ function ConfirmDialog({ isOpen, options, handleConfirm, handleCancel, destructi
2929
3416
  ] })
2930
3417
  ] }) });
2931
3418
  }
3419
+ function RegisterHotkeys({ hotkeys }) {
3420
+ React31.useEffect(() => {
3421
+ const handleKeyDown = (event) => {
3422
+ for (const hotkey of hotkeys) {
3423
+ const matches = event.key.toLowerCase() === hotkey.key.toLowerCase() && !!event.ctrlKey === !!hotkey.ctrl && !!event.metaKey === !!hotkey.meta && !!event.shiftKey === !!hotkey.shift && !!event.altKey === !!hotkey.alt;
3424
+ if (matches) {
3425
+ event.preventDefault();
3426
+ hotkey.handler();
3427
+ break;
3428
+ }
3429
+ }
3430
+ };
3431
+ document.addEventListener("keydown", handleKeyDown);
3432
+ return () => document.removeEventListener("keydown", handleKeyDown);
3433
+ }, [hotkeys]);
3434
+ return null;
3435
+ }
3436
+ function useControlledOpen(defaultOpen = false) {
3437
+ const [isOpen, setIsOpen] = React31.useState(defaultOpen);
3438
+ const open = React31.useCallback(() => setIsOpen(true), []);
3439
+ const close = React31.useCallback(() => setIsOpen(false), []);
3440
+ const toggle = React31.useCallback(() => setIsOpen((prev) => !prev), []);
3441
+ return { isOpen, open, close, toggle, setIsOpen };
3442
+ }
3443
+ function CommandMenu({
3444
+ groups,
3445
+ placeholder = "Type a command or search\u2026",
3446
+ emptyMessage = "No results found.",
3447
+ open: openProp,
3448
+ onOpenChange,
3449
+ hotkey = true
3450
+ }) {
3451
+ const internal = useControlledOpen(false);
3452
+ const isOpen = openProp ?? internal.isOpen;
3453
+ const setOpen = React31.useCallback(
3454
+ (next) => {
3455
+ if (openProp === void 0) internal.setIsOpen(next);
3456
+ onOpenChange?.(next);
3457
+ },
3458
+ [openProp, onOpenChange, internal.setIsOpen]
3459
+ );
3460
+ const runItem = (item) => {
3461
+ setOpen(false);
3462
+ item.onSelect();
3463
+ };
3464
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3465
+ hotkey && /* @__PURE__ */ jsx(
3466
+ RegisterHotkeys,
3467
+ {
3468
+ hotkeys: [
3469
+ { key: "k", meta: true, handler: () => setOpen(!isOpen) },
3470
+ { key: "k", ctrl: true, handler: () => setOpen(!isOpen) }
3471
+ ]
3472
+ }
3473
+ ),
3474
+ /* @__PURE__ */ jsxs(CommandDialog, { open: isOpen, onOpenChange: setOpen, children: [
3475
+ /* @__PURE__ */ jsx(CommandInput, { placeholder }),
3476
+ /* @__PURE__ */ jsxs(CommandList, { children: [
3477
+ /* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage }),
3478
+ groups.map((group, i) => /* @__PURE__ */ jsxs(React31.Fragment, { children: [
3479
+ i > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
3480
+ /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: group.items.map((item) => /* @__PURE__ */ jsxs(
3481
+ CommandItem,
3482
+ {
3483
+ value: [item.label, ...item.keywords ?? []].join(" "),
3484
+ onSelect: () => runItem(item),
3485
+ children: [
3486
+ item.icon && /* @__PURE__ */ jsx(item.icon, {}),
3487
+ /* @__PURE__ */ jsx("span", { children: item.label }),
3488
+ item.shortcut && /* @__PURE__ */ jsx(CommandShortcut, { children: item.shortcut })
3489
+ ]
3490
+ },
3491
+ item.id
3492
+ )) })
3493
+ ] }, group.heading))
3494
+ ] })
3495
+ ] })
3496
+ ] });
3497
+ }
3498
+ function formatBytes(bytes) {
3499
+ if (bytes < 1024) return `${bytes} B`;
3500
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
3501
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
3502
+ }
3503
+ function makeId() {
3504
+ return Math.random().toString(36).slice(2, 10);
3505
+ }
3506
+ function FileUpload({
3507
+ accept,
3508
+ multiple = false,
3509
+ maxSizeMb,
3510
+ maxFiles,
3511
+ files: filesProp,
3512
+ defaultFiles,
3513
+ onFilesChange,
3514
+ onFilesAdded,
3515
+ disabled,
3516
+ hint,
3517
+ className
3518
+ }) {
3519
+ const [internalFiles, setInternalFiles] = React31.useState(defaultFiles ?? []);
3520
+ const [isDragging, setIsDragging] = React31.useState(false);
3521
+ const [validationError, setValidationError] = React31.useState(null);
3522
+ const inputRef = React31.useRef(null);
3523
+ const dragDepth = React31.useRef(0);
3524
+ const files = filesProp ?? internalFiles;
3525
+ const setFiles = React31.useCallback(
3526
+ (next) => {
3527
+ if (!filesProp) setInternalFiles(next);
3528
+ onFilesChange?.(next);
3529
+ },
3530
+ [filesProp, onFilesChange]
3531
+ );
3532
+ const acceptList = React31.useMemo(
3533
+ () => accept?.split(",").map((a) => a.trim().toLowerCase()).filter(Boolean) ?? [],
3534
+ [accept]
3535
+ );
3536
+ const matchesAccept = (file) => {
3537
+ if (!acceptList.length) return true;
3538
+ const name = file.name.toLowerCase();
3539
+ return acceptList.some(
3540
+ (pattern) => pattern.startsWith(".") ? name.endsWith(pattern) : file.type === pattern || file.type.startsWith(pattern.replace("/*", "/"))
3541
+ );
3542
+ };
3543
+ const handleFiles = (fileList) => {
3544
+ if (!fileList || disabled) return;
3545
+ const incoming = Array.from(fileList);
3546
+ const room = maxFiles && multiple ? Math.max(0, maxFiles - files.length) : maxFiles || Infinity;
3547
+ if (maxFiles && room <= 0) {
3548
+ setValidationError(`You can only add up to ${maxFiles} file${maxFiles === 1 ? "" : "s"}.`);
3549
+ return;
3550
+ }
3551
+ const accepted = [];
3552
+ let rejected = false;
3553
+ for (const file of incoming.slice(0, room)) {
3554
+ if (!matchesAccept(file)) {
3555
+ rejected = true;
3556
+ continue;
3557
+ }
3558
+ if (maxSizeMb && file.size > maxSizeMb * 1024 * 1024) {
3559
+ rejected = true;
3560
+ continue;
3561
+ }
3562
+ accepted.push(file);
3563
+ }
3564
+ setValidationError(rejected ? `Some files were skipped \u2014 check the file type and size limit.` : null);
3565
+ if (!accepted.length) return;
3566
+ const entries = accepted.map((file) => ({ id: makeId(), file, status: "pending" }));
3567
+ setFiles(multiple ? [...files, ...entries] : entries);
3568
+ onFilesAdded?.(accepted);
3569
+ };
3570
+ const removeFile = (id) => {
3571
+ setFiles(files.filter((f) => f.id !== id));
3572
+ };
3573
+ return /* @__PURE__ */ jsxs("div", { className: cn$1("space-y-3", className), children: [
3574
+ /* @__PURE__ */ jsxs(
3575
+ "div",
3576
+ {
3577
+ role: "button",
3578
+ tabIndex: disabled ? -1 : 0,
3579
+ onClick: () => !disabled && inputRef.current?.click(),
3580
+ onKeyDown: (e) => {
3581
+ if (disabled) return;
3582
+ if (e.key === "Enter" || e.key === " ") {
3583
+ e.preventDefault();
3584
+ inputRef.current?.click();
3585
+ }
3586
+ },
3587
+ onDragOver: (e) => e.preventDefault(),
3588
+ onDragEnter: (e) => {
3589
+ e.preventDefault();
3590
+ dragDepth.current += 1;
3591
+ if (!disabled) setIsDragging(true);
3592
+ },
3593
+ onDragLeave: () => {
3594
+ dragDepth.current = Math.max(0, dragDepth.current - 1);
3595
+ if (dragDepth.current === 0) setIsDragging(false);
3596
+ },
3597
+ onDrop: (e) => {
3598
+ e.preventDefault();
3599
+ dragDepth.current = 0;
3600
+ setIsDragging(false);
3601
+ handleFiles(e.dataTransfer.files);
3602
+ },
3603
+ className: cn$1(
3604
+ "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",
3605
+ isDragging ? "border-primary bg-primary/5" : "border-border hover:border-foreground/30",
3606
+ disabled && "pointer-events-none opacity-50"
3607
+ ),
3608
+ children: [
3609
+ /* @__PURE__ */ jsx(UploadCloud, { className: "size-8 text-muted-foreground" }),
3610
+ /* @__PURE__ */ jsxs("p", { className: "text-sm font-medium", children: [
3611
+ /* @__PURE__ */ jsx("span", { className: "text-primary underline underline-offset-4", children: "Click to upload" }),
3612
+ " or drag and drop"
3613
+ ] }),
3614
+ hint && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: hint }),
3615
+ /* @__PURE__ */ jsx(
3616
+ "input",
3617
+ {
3618
+ ref: inputRef,
3619
+ type: "file",
3620
+ accept,
3621
+ multiple,
3622
+ disabled,
3623
+ className: "sr-only",
3624
+ onChange: (e) => {
3625
+ handleFiles(e.target.files);
3626
+ e.target.value = "";
3627
+ }
3628
+ }
3629
+ )
3630
+ ]
3631
+ }
3632
+ ),
3633
+ validationError && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm font-medium text-destructive", children: validationError }),
3634
+ 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: [
3635
+ /* @__PURE__ */ jsx(File, { className: "size-5 shrink-0 text-muted-foreground" }),
3636
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3637
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
3638
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: file.name }),
3639
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatBytes(file.size) })
3640
+ ] }),
3641
+ 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}%` } }) }),
3642
+ status === "error" && error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-destructive", children: error })
3643
+ ] }),
3644
+ status === "uploading" && /* @__PURE__ */ jsx(Loader2, { className: "size-4 shrink-0 animate-spin text-muted-foreground" }),
3645
+ status === "done" && /* @__PURE__ */ jsx(CheckCircle2, { className: "size-4 shrink-0 text-primary" }),
3646
+ status === "error" && /* @__PURE__ */ jsx(AlertCircle, { className: "size-4 shrink-0 text-destructive" }),
3647
+ /* @__PURE__ */ jsxs(
3648
+ Button,
3649
+ {
3650
+ type: "button",
3651
+ variant: "ghost",
3652
+ size: "icon",
3653
+ className: "size-7 shrink-0",
3654
+ onClick: () => removeFile(id),
3655
+ children: [
3656
+ /* @__PURE__ */ jsx(X, { className: "size-3.5" }),
3657
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
3658
+ "Remove ",
3659
+ file.name
3660
+ ] })
3661
+ ]
3662
+ }
3663
+ )
3664
+ ] }, id)) })
3665
+ ] });
3666
+ }
3667
+ var variantIcon = {
3668
+ success: /* @__PURE__ */ jsx(CheckCircle2, { className: "size-5 text-primary" }),
3669
+ info: /* @__PURE__ */ jsx(Info, { className: "size-5 text-muted-foreground" }),
3670
+ warning: /* @__PURE__ */ jsx(AlertTriangle, { className: "size-5 text-amber-500 dark:text-amber-400" }),
3671
+ error: /* @__PURE__ */ jsx(AlertCircle, { className: "size-5 text-destructive" }),
3672
+ message: /* @__PURE__ */ jsx(Inbox, { className: "size-5 text-muted-foreground" })
3673
+ };
3674
+ function NotificationToast({
3675
+ variant = "info",
3676
+ title,
3677
+ description,
3678
+ avatar,
3679
+ action,
3680
+ secondaryAction,
3681
+ onDismiss
3682
+ }) {
3683
+ 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: [
3684
+ /* @__PURE__ */ jsx("div", { className: "shrink-0 pt-0.5", children: avatar ? (
3685
+ // eslint-disable-next-line @next/next/no-img-element
3686
+ /* @__PURE__ */ jsx("img", { src: avatar, alt: "", className: "size-9 rounded-full border object-cover" })
3687
+ ) : variantIcon[variant] }),
3688
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 pt-0.5", children: [
3689
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: title }),
3690
+ description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description }),
3691
+ (action || secondaryAction) && /* @__PURE__ */ jsxs("div", { className: cn$1("mt-3 flex gap-4", avatar && "gap-3"), children: [
3692
+ action && /* @__PURE__ */ jsx("button", { type: "button", onClick: action.onClick, className: "text-sm font-medium text-primary hover:underline", children: action.label }),
3693
+ secondaryAction && /* @__PURE__ */ jsx("button", { type: "button", onClick: secondaryAction.onClick, className: "text-sm font-medium text-muted-foreground hover:text-foreground", children: secondaryAction.label })
3694
+ ] })
3695
+ ] }),
3696
+ /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: "size-6 shrink-0 -mt-1 -mr-1", onClick: onDismiss, children: [
3697
+ /* @__PURE__ */ jsx(X, { className: "size-4" }),
3698
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Dismiss" })
3699
+ ] })
3700
+ ] }) });
3701
+ }
3702
+ function notify(options) {
3703
+ const { duration, ...toastProps } = options;
3704
+ return toast.custom((id) => /* @__PURE__ */ jsx(NotificationToast, { ...toastProps, onDismiss: () => toast.dismiss(id) }), { duration });
3705
+ }
3706
+ function NotificationsPopover({
3707
+ notifications,
3708
+ onNotificationClick,
3709
+ onMarkAllRead,
3710
+ title = "Notifications",
3711
+ emptyMessage = "Nothing new \u2014 you\u2019re all caught up.",
3712
+ align = "end",
3713
+ open,
3714
+ onOpenChange,
3715
+ className
3716
+ }) {
3717
+ const unreadCount = notifications.filter((n) => !n.read).length;
3718
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange, children: [
3719
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "icon", className: cn$1("relative size-8", className), children: [
3720
+ /* @__PURE__ */ jsx(Bell, { className: "size-4" }),
3721
+ unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute -right-0.5 -top-0.5 flex min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-semibold leading-4 text-primary-foreground", children: unreadCount > 9 ? "9+" : unreadCount }),
3722
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
3723
+ title,
3724
+ unreadCount > 0 ? ` (${unreadCount} unread)` : ""
3725
+ ] })
3726
+ ] }) }),
3727
+ /* @__PURE__ */ jsxs(PopoverContent, { align, className: "w-80 p-0", children: [
3728
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
3729
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: title }),
3730
+ onMarkAllRead && unreadCount > 0 && /* @__PURE__ */ jsx(
3731
+ "button",
3732
+ {
3733
+ type: "button",
3734
+ onClick: onMarkAllRead,
3735
+ className: "text-xs font-medium text-muted-foreground hover:text-foreground",
3736
+ children: "Mark all read"
3737
+ }
3738
+ )
3739
+ ] }),
3740
+ notifications.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 px-4 py-10 text-center", children: [
3741
+ /* @__PURE__ */ jsx(Inbox, { className: "size-6 text-muted-foreground" }),
3742
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage })
3743
+ ] }) : /* @__PURE__ */ jsx("ul", { className: "max-h-80 overflow-y-auto", children: notifications.map((notification) => /* @__PURE__ */ jsx("li", { className: "border-b last:border-b-0", children: /* @__PURE__ */ jsxs(
3744
+ "button",
3745
+ {
3746
+ type: "button",
3747
+ onClick: () => onNotificationClick?.(notification),
3748
+ className: cn$1(
3749
+ "flex w-full items-start gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/60",
3750
+ !notification.read && "bg-muted/40"
3751
+ ),
3752
+ children: [
3753
+ /* @__PURE__ */ jsx(
3754
+ "span",
3755
+ {
3756
+ "aria-hidden": true,
3757
+ className: cn$1(
3758
+ "mt-1.5 size-2 shrink-0 rounded-full",
3759
+ notification.read ? "bg-transparent" : "bg-primary"
3760
+ )
3761
+ }
3762
+ ),
3763
+ notification.avatar ? /* @__PURE__ */ jsxs(Avatar, { className: "size-8 shrink-0", children: [
3764
+ /* @__PURE__ */ jsx(AvatarImage, { src: notification.avatar, alt: "" }),
3765
+ /* @__PURE__ */ jsx(AvatarFallback, { children: notification.title.slice(0, 2).toUpperCase() })
3766
+ ] }) : notification.icon ? /* @__PURE__ */ jsx("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: notification.icon }) : null,
3767
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
3768
+ /* @__PURE__ */ jsx("span", { className: cn$1("block truncate text-sm", !notification.read && "font-medium"), children: notification.title }),
3769
+ notification.description && /* @__PURE__ */ jsx("span", { className: "mt-0.5 line-clamp-2 block text-xs text-muted-foreground", children: notification.description }),
3770
+ notification.timestamp && /* @__PURE__ */ jsx("span", { className: "mt-1 block text-xs text-muted-foreground/70", children: notification.timestamp })
3771
+ ] })
3772
+ ]
3773
+ }
3774
+ ) }, notification.id)) })
3775
+ ] })
3776
+ ] });
3777
+ }
3778
+ function ActivityFeed({
3779
+ items,
3780
+ emptyMessage = "No activity yet.",
3781
+ className
3782
+ }) {
3783
+ if (items.length === 0) {
3784
+ return /* @__PURE__ */ jsxs("div", { className: cn$1("flex flex-col items-center gap-2 py-10 text-center", className), children: [
3785
+ /* @__PURE__ */ jsx(Activity, { className: "size-6 text-muted-foreground" }),
3786
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage })
3787
+ ] });
3788
+ }
3789
+ return /* @__PURE__ */ jsx("ol", { className, children: items.map((item, i) => /* @__PURE__ */ jsxs("li", { className: "relative flex gap-3 pb-6 last:pb-0", children: [
3790
+ i !== items.length - 1 && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "absolute left-4 top-9 bottom-0 w-px -translate-x-1/2 bg-border" }),
3791
+ /* @__PURE__ */ jsx("span", { className: "relative z-10 flex size-8 shrink-0 items-center justify-center", children: item.avatar ? /* @__PURE__ */ jsxs(Avatar, { className: "size-8", children: [
3792
+ /* @__PURE__ */ jsx(AvatarImage, { src: item.avatar, alt: "" }),
3793
+ /* @__PURE__ */ jsx(AvatarFallback, { className: "text-xs", children: item.initials ?? "?" })
3794
+ ] }) : /* @__PURE__ */ jsx("span", { className: "flex size-8 items-center justify-center rounded-full border bg-card text-muted-foreground [&>svg]:size-4", children: item.icon ?? /* @__PURE__ */ jsx(Activity, {}) }) }),
3795
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 pt-1", children: [
3796
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
3797
+ /* @__PURE__ */ jsx("p", { className: "text-sm", children: item.title }),
3798
+ item.timestamp && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: item.timestamp })
3799
+ ] }),
3800
+ item.description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: item.description })
3801
+ ] })
3802
+ ] }, item.id)) });
3803
+ }
2932
3804
  function AppScreenRoot({ children, className }) {
2933
3805
  return /* @__PURE__ */ jsx("div", { className: cn("flex h-full flex-col", className), children });
2934
3806
  }
@@ -3311,20 +4183,20 @@ function FullPageSpinner({ message }) {
3311
4183
  message && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: message })
3312
4184
  ] });
3313
4185
  }
3314
- var defaultRenderLink10 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
4186
+ var defaultRenderLink9 = ({ href, children, className }) => /* @__PURE__ */ jsx("a", { href, className, children });
3315
4187
  function PageHeader({
3316
4188
  title,
3317
4189
  description,
3318
4190
  breadcrumbs,
3319
4191
  backButton,
3320
- renderLink = defaultRenderLink10,
4192
+ renderLink = defaultRenderLink9,
3321
4193
  className,
3322
4194
  children
3323
4195
  }) {
3324
4196
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 pb-6", className), children: [
3325
4197
  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
4198
  const isLast = i === breadcrumbs.length - 1;
3327
- return /* @__PURE__ */ jsxs(React29.Fragment, { children: [
4199
+ return /* @__PURE__ */ jsxs(React31.Fragment, { children: [
3328
4200
  i > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 shrink-0" }),
3329
4201
  isLast || !crumb.href ? /* @__PURE__ */ jsx("span", { className: cn(isLast && "text-foreground font-medium"), children: crumb.label }) : renderLink({
3330
4202
  href: crumb.href,
@@ -3360,7 +4232,7 @@ function PageHeader({
3360
4232
  ] });
3361
4233
  }
3362
4234
  function Suspensed({ children, fallback }) {
3363
- return /* @__PURE__ */ jsx(React29.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
4235
+ return /* @__PURE__ */ jsx(React31.Suspense, { fallback: fallback ?? /* @__PURE__ */ jsx(Spinner, {}), children });
3364
4236
  }
3365
4237
  function getInitialTheme() {
3366
4238
  if (typeof document === "undefined") return "dark";
@@ -3368,7 +4240,7 @@ function getInitialTheme() {
3368
4240
  return stored ?? (document.documentElement.classList.contains("dark") ? "dark" : "light");
3369
4241
  }
3370
4242
  function ThemeSwitchMinimal() {
3371
- const [theme, setThemeState] = React29.useState(getInitialTheme);
4243
+ const [theme, setThemeState] = React31.useState(getInitialTheme);
3372
4244
  const toggle = () => {
3373
4245
  const next = theme === "dark" ? "light" : "dark";
3374
4246
  setThemeState(next);
@@ -3494,9 +4366,9 @@ function getInitialColorName() {
3494
4366
  return localStorage.getItem(STORAGE_KEY) ?? "emerald";
3495
4367
  }
3496
4368
  function BrandColorSwitchMinimal() {
3497
- const [active, setActive] = React29.useState(getInitialColorName);
3498
- const [open, setOpen] = React29.useState(false);
3499
- React29.useEffect(() => {
4369
+ const [active, setActive] = React31.useState(getInitialColorName);
4370
+ const [open, setOpen] = React31.useState(false);
4371
+ React31.useEffect(() => {
3500
4372
  const saved = localStorage.getItem(STORAGE_KEY) ?? "emerald";
3501
4373
  const color = BRAND_COLORS.find((c) => c.name === saved);
3502
4374
  if (color) applyColor(color);
@@ -3557,7 +4429,7 @@ function BrandColorSwitchMinimal() {
3557
4429
  ] });
3558
4430
  }
3559
4431
  function ThemeColorUpdater({ colorTheme }) {
3560
- React29.useEffect(() => {
4432
+ React31.useEffect(() => {
3561
4433
  if (colorTheme) {
3562
4434
  document.documentElement.setAttribute("data-theme", colorTheme);
3563
4435
  } else {
@@ -3567,7 +4439,7 @@ function ThemeColorUpdater({ colorTheme }) {
3567
4439
  return null;
3568
4440
  }
3569
4441
  function VersionBanner({ version, message, className, onDismiss }) {
3570
- const [dismissed, setDismissed] = React29.useState(false);
4442
+ const [dismissed, setDismissed] = React31.useState(false);
3571
4443
  if (dismissed) return null;
3572
4444
  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
4445
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
@@ -3606,23 +4478,6 @@ function DevBanner({ environment = "development", className }) {
3606
4478
  }
3607
4479
  );
3608
4480
  }
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
4481
  function RootErrorFallback({ error, reset }) {
3627
4482
  return /* @__PURE__ */ jsxs("div", { className: "flex min-h-svh flex-col items-center justify-center gap-6 p-8 text-center", children: [
3628
4483
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3", children: [
@@ -3841,36 +4696,29 @@ function useMounted() {
3841
4696
  return mounted;
3842
4697
  }
3843
4698
  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 = {}) => {
4699
+ const [isOpen, setIsOpen] = React31.useState(false);
4700
+ const [options, setOptions] = React31.useState({});
4701
+ const resolveRef = React31.useRef(null);
4702
+ const confirm = React31.useCallback((opts = {}) => {
3848
4703
  setOptions(opts);
3849
4704
  setIsOpen(true);
3850
4705
  return new Promise((resolve) => {
3851
4706
  resolveRef.current = resolve;
3852
4707
  });
3853
4708
  }, []);
3854
- const handleConfirm = React29.useCallback(() => {
4709
+ const handleConfirm = React31.useCallback(() => {
3855
4710
  resolveRef.current?.(true);
3856
4711
  setIsOpen(false);
3857
4712
  }, []);
3858
- const handleCancel = React29.useCallback(() => {
4713
+ const handleCancel = React31.useCallback(() => {
3859
4714
  resolveRef.current?.(false);
3860
4715
  setIsOpen(false);
3861
4716
  }, []);
3862
4717
  return { confirm, isOpen, options, handleConfirm, handleCancel };
3863
4718
  }
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
4719
  function useScrolledPast(threshold) {
3872
- const [scrolledPast, setScrolledPast] = React29.useState(false);
3873
- React29.useEffect(() => {
4720
+ const [scrolledPast, setScrolledPast] = React31.useState(false);
4721
+ React31.useEffect(() => {
3874
4722
  const handleScroll = () => {
3875
4723
  setScrolledPast(window.scrollY > threshold);
3876
4724
  };
@@ -3881,42 +4729,28 @@ function useScrolledPast(threshold) {
3881
4729
  return scrolledPast;
3882
4730
  }
3883
4731
  function useCopyToClipboard() {
3884
- const [copied, setCopied] = React29.useState(false);
3885
- const timeoutRef = React29.useRef(void 0);
3886
- const copy = React29.useCallback((text) => {
4732
+ const [copied, setCopied] = React31.useState(false);
4733
+ const timeoutRef = React31.useRef(void 0);
4734
+ const copy = React31.useCallback((text) => {
3887
4735
  navigator.clipboard.writeText(text).then(() => {
3888
4736
  setCopied(true);
3889
4737
  clearTimeout(timeoutRef.current);
3890
4738
  timeoutRef.current = setTimeout(() => setCopied(false), 2e3);
3891
4739
  });
3892
4740
  }, []);
3893
- React29.useEffect(() => () => clearTimeout(timeoutRef.current), []);
4741
+ React31.useEffect(() => () => clearTimeout(timeoutRef.current), []);
3894
4742
  return [copied, copy];
3895
4743
  }
3896
4744
  function useDebounce(value, delay) {
3897
- const [debounced, setDebounced] = React29.useState(value);
3898
- React29.useEffect(() => {
4745
+ const [debounced, setDebounced] = React31.useState(value);
4746
+ React31.useEffect(() => {
3899
4747
  const timer = setTimeout(() => setDebounced(value), delay);
3900
4748
  return () => clearTimeout(timer);
3901
4749
  }, [value, delay]);
3902
4750
  return debounced;
3903
4751
  }
3904
- function useIntersectionObserver(options) {
3905
- const ref = React29.useRef(null);
3906
- const [isIntersecting, setIsIntersecting] = React29.useState(false);
3907
- React29.useEffect(() => {
3908
- const el = ref.current;
3909
- if (!el) return;
3910
- const observer = new IntersectionObserver(([entry]) => {
3911
- setIsIntersecting(entry.isIntersecting);
3912
- }, options);
3913
- observer.observe(el);
3914
- return () => observer.disconnect();
3915
- }, []);
3916
- return [ref, isIntersecting];
3917
- }
3918
4752
  function useLocalStorage(key, initialValue) {
3919
- const [storedValue, setStoredValue] = React29.useState(() => {
4753
+ const [storedValue, setStoredValue] = React31.useState(() => {
3920
4754
  if (typeof window === "undefined") return initialValue;
3921
4755
  try {
3922
4756
  const item = window.localStorage.getItem(key);
@@ -3925,7 +4759,7 @@ function useLocalStorage(key, initialValue) {
3925
4759
  return initialValue;
3926
4760
  }
3927
4761
  });
3928
- const setValue = React29.useCallback(
4762
+ const setValue = React31.useCallback(
3929
4763
  (value) => {
3930
4764
  try {
3931
4765
  setStoredValue(value);
@@ -3940,11 +4774,11 @@ function useLocalStorage(key, initialValue) {
3940
4774
  return [storedValue, setValue];
3941
4775
  }
3942
4776
  function useMediaQuery(query) {
3943
- const [matches, setMatches] = React29.useState(() => {
4777
+ const [matches, setMatches] = React31.useState(() => {
3944
4778
  if (typeof window === "undefined") return false;
3945
4779
  return window.matchMedia(query).matches;
3946
4780
  });
3947
- React29.useEffect(() => {
4781
+ React31.useEffect(() => {
3948
4782
  if (typeof window === "undefined") return;
3949
4783
  const mql = window.matchMedia(query);
3950
4784
  setMatches(mql.matches);
@@ -3955,18 +4789,18 @@ function useMediaQuery(query) {
3955
4789
  return matches;
3956
4790
  }
3957
4791
  function usePagination(total, pageSize) {
3958
- const [page, setPage] = React29.useState(1);
4792
+ const [page, setPage] = React31.useState(1);
3959
4793
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
3960
4794
  const offset = (page - 1) * pageSize;
3961
- const goTo = React29.useCallback(
4795
+ const goTo = React31.useCallback(
3962
4796
  (p) => setPage(Math.min(Math.max(1, p), totalPages)),
3963
4797
  [totalPages]
3964
4798
  );
3965
- const next = React29.useCallback(() => goTo(page + 1), [page, goTo]);
3966
- const prev = React29.useCallback(() => goTo(page - 1), [page, goTo]);
4799
+ const next = React31.useCallback(() => goTo(page + 1), [page, goTo]);
4800
+ const prev = React31.useCallback(() => goTo(page - 1), [page, goTo]);
3967
4801
  return { page, pageSize, totalPages, offset, hasPrev: page > 1, hasNext: page < totalPages, goTo, next, prev };
3968
4802
  }
3969
4803
 
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 };
4804
+ export { ActivityFeed, AnimatedPill, AppContent, AppGrid, AppGridCell, AppScreen, AppShell, AuthSection, Badge, BillingPanel, BrandColorSwitchMinimal, Button, Card, Carousel, ChangelogCard, ChangelogList, Checkbox, CommandMenu, ComparisonSection, ConfirmDialog, ContactSection, ContextMenu, CountUp, CountdownTimer, 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, NotificationsPopover, 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
4805
  //# sourceMappingURL=index.js.map
3972
4806
  //# sourceMappingURL=index.js.map