@porkytheblack/garage-dashboard 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/.env.example +3 -0
  2. package/DESIGN.md +90 -0
  3. package/README.md +62 -0
  4. package/app/(app)/credentials/add-profile-modal.tsx +116 -0
  5. package/app/(app)/credentials/add-provider-modal.tsx +172 -0
  6. package/app/(app)/credentials/edit-provider-modal.tsx +107 -0
  7. package/app/(app)/credentials/edit-reasoning-modal.tsx +96 -0
  8. package/app/(app)/credentials/form.tsx +132 -0
  9. package/app/(app)/credentials/model-combobox.tsx +151 -0
  10. package/app/(app)/credentials/page.tsx +126 -0
  11. package/app/(app)/keys/page.tsx +164 -0
  12. package/app/(app)/layout.tsx +53 -0
  13. package/app/(app)/namespaces/list.tsx +47 -0
  14. package/app/(app)/namespaces/mint-dialog.tsx +64 -0
  15. package/app/(app)/namespaces/page.tsx +137 -0
  16. package/app/(app)/page.tsx +86 -0
  17. package/app/(app)/provisioning/page.tsx +84 -0
  18. package/app/(app)/sessions/[id]/page.tsx +184 -0
  19. package/app/(app)/sessions/page.tsx +88 -0
  20. package/app/(app)/storage/page.tsx +164 -0
  21. package/app/(app)/storage/toggle.tsx +43 -0
  22. package/app/(app)/workspaces/list.tsx +34 -0
  23. package/app/(app)/workspaces/page.tsx +111 -0
  24. package/app/globals.css +297 -0
  25. package/app/layout.tsx +32 -0
  26. package/app/login/page.tsx +62 -0
  27. package/components/app-sidebar.tsx +120 -0
  28. package/components/app-topbar.tsx +76 -0
  29. package/components/brand.tsx +47 -0
  30. package/components/chat/composer.tsx +199 -0
  31. package/components/chat/conversation.tsx +86 -0
  32. package/components/chat/error-card.tsx +89 -0
  33. package/components/chat/markdown.tsx +12 -0
  34. package/components/chat/message.tsx +99 -0
  35. package/components/chat/permission-prompt.tsx +45 -0
  36. package/components/chat/slash-menu.tsx +54 -0
  37. package/components/chat/task-list.tsx +56 -0
  38. package/components/chat/tool-call.tsx +91 -0
  39. package/components/fleet/lanes.tsx +107 -0
  40. package/components/fleet/launch.tsx +99 -0
  41. package/components/fleet/onboarding-launch.tsx +46 -0
  42. package/components/fleet/onboarding-model.tsx +109 -0
  43. package/components/fleet/onboarding-provider.tsx +137 -0
  44. package/components/fleet/onboarding-shared.tsx +66 -0
  45. package/components/fleet/onboarding.tsx +75 -0
  46. package/components/primitives.tsx +65 -0
  47. package/components/provisioning/provision-dialog.tsx +130 -0
  48. package/components/session/agent-roster.tsx +121 -0
  49. package/components/session/files-panel.tsx +170 -0
  50. package/components/session/files-remote.tsx +63 -0
  51. package/components/session/inspector.tsx +148 -0
  52. package/components/session/model-switcher.tsx +59 -0
  53. package/components/session/new-session-dialog.tsx +139 -0
  54. package/components/session/reasoning-knob.tsx +100 -0
  55. package/components/session/session-switcher.tsx +62 -0
  56. package/components/shared.tsx +171 -0
  57. package/components/theme-toggle.tsx +26 -0
  58. package/components/ui/avatar.tsx +39 -0
  59. package/components/ui/badge.tsx +30 -0
  60. package/components/ui/button.tsx +45 -0
  61. package/components/ui/card.tsx +44 -0
  62. package/components/ui/command.tsx +90 -0
  63. package/components/ui/dialog.tsx +88 -0
  64. package/components/ui/dropdown-menu.tsx +77 -0
  65. package/components/ui/input.tsx +22 -0
  66. package/components/ui/label.tsx +22 -0
  67. package/components/ui/popover.tsx +33 -0
  68. package/components/ui/scroll-area.tsx +41 -0
  69. package/components/ui/select.tsx +100 -0
  70. package/components/ui/separator.tsx +25 -0
  71. package/components/ui/skeleton.tsx +7 -0
  72. package/components/ui/sonner.tsx +28 -0
  73. package/components/ui/table.tsx +51 -0
  74. package/components/ui/tabs.tsx +50 -0
  75. package/components/ui/textarea.tsx +20 -0
  76. package/components/ui/tooltip.tsx +30 -0
  77. package/components.json +21 -0
  78. package/lib/api.ts +85 -0
  79. package/lib/auth.tsx +80 -0
  80. package/lib/format.ts +34 -0
  81. package/lib/hooks.ts +65 -0
  82. package/lib/launch.ts +25 -0
  83. package/lib/template.ts +34 -0
  84. package/lib/theme.ts +71 -0
  85. package/lib/types.ts +262 -0
  86. package/lib/useSession.ts +193 -0
  87. package/lib/utils.ts +7 -0
  88. package/lib/verify-provider.ts +31 -0
  89. package/next.config.mjs +16 -0
  90. package/package.json +49 -0
  91. package/postcss.config.mjs +6 -0
  92. package/tailwind.config.ts +94 -0
  93. package/tsconfig.json +21 -0
@@ -0,0 +1,90 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Command as CommandPrimitive } from "cmdk";
5
+ import { Search } from "lucide-react";
6
+ import { cn } from "@/lib/utils";
7
+
8
+ /** cmdk wrapped in the Garage language: quiet surfaces, hairline dividers,
9
+ * mono-friendly items, and selection states on `surface-2`. */
10
+ const Command = React.forwardRef<
11
+ React.ElementRef<typeof CommandPrimitive>,
12
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive>
13
+ >(({ className, ...props }, ref) => (
14
+ <CommandPrimitive
15
+ ref={ref}
16
+ className={cn("flex h-full w-full flex-col overflow-hidden rounded-lg bg-popover text-popover-foreground", className)}
17
+ {...props}
18
+ />
19
+ ));
20
+ Command.displayName = CommandPrimitive.displayName;
21
+
22
+ const CommandInput = React.forwardRef<
23
+ React.ElementRef<typeof CommandPrimitive.Input>,
24
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
25
+ >(({ className, ...props }, ref) => (
26
+ <div className="flex items-center gap-2 border-b border-border/70 px-3" cmdk-input-wrapper="">
27
+ <Search className="size-3.5 shrink-0 text-faint" />
28
+ <CommandPrimitive.Input
29
+ ref={ref}
30
+ className={cn(
31
+ "flex h-9 w-full bg-transparent py-2 text-[13px] outline-none placeholder:text-muted-foreground/60 disabled:cursor-not-allowed disabled:opacity-50",
32
+ className,
33
+ )}
34
+ {...props}
35
+ />
36
+ </div>
37
+ ));
38
+ CommandInput.displayName = CommandPrimitive.Input.displayName;
39
+
40
+ const CommandList = React.forwardRef<
41
+ React.ElementRef<typeof CommandPrimitive.List>,
42
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
43
+ >(({ className, ...props }, ref) => (
44
+ <CommandPrimitive.List ref={ref} className={cn("max-h-[280px] overflow-y-auto overflow-x-hidden p-1", className)} {...props} />
45
+ ));
46
+ CommandList.displayName = CommandPrimitive.List.displayName;
47
+
48
+ const CommandEmpty = React.forwardRef<
49
+ React.ElementRef<typeof CommandPrimitive.Empty>,
50
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
51
+ >((props, ref) => (
52
+ <CommandPrimitive.Empty ref={ref} className="px-3 py-6 text-center text-[12.5px] text-muted-foreground" {...props} />
53
+ ));
54
+ CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
55
+
56
+ const CommandGroup = React.forwardRef<
57
+ React.ElementRef<typeof CommandPrimitive.Group>,
58
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
59
+ >(({ className, ...props }, ref) => (
60
+ <CommandPrimitive.Group
61
+ ref={ref}
62
+ className={cn(
63
+ "overflow-hidden [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:pb-1 [&_[cmdk-group-heading]]:pt-2",
64
+ "[&_[cmdk-group-heading]]:text-[10.5px] [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:uppercase",
65
+ "[&_[cmdk-group-heading]]:tracking-[0.12em] [&_[cmdk-group-heading]]:text-faint",
66
+ className,
67
+ )}
68
+ {...props}
69
+ />
70
+ ));
71
+ CommandGroup.displayName = CommandPrimitive.Group.displayName;
72
+
73
+ const CommandItem = React.forwardRef<
74
+ React.ElementRef<typeof CommandPrimitive.Item>,
75
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
76
+ >(({ className, ...props }, ref) => (
77
+ <CommandPrimitive.Item
78
+ ref={ref}
79
+ className={cn(
80
+ "relative flex cursor-default select-none items-center gap-2 rounded-md px-2 py-1.5 text-[13px] outline-none",
81
+ "data-[selected=true]:bg-surface-2 data-[selected=true]:text-foreground",
82
+ "data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
83
+ className,
84
+ )}
85
+ {...props}
86
+ />
87
+ ));
88
+ CommandItem.displayName = CommandPrimitive.Item.displayName;
89
+
90
+ export { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem };
@@ -0,0 +1,88 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
5
+ import { X } from "lucide-react";
6
+ import { cn } from "@/lib/utils";
7
+
8
+ const Dialog = DialogPrimitive.Root;
9
+ const DialogTrigger = DialogPrimitive.Trigger;
10
+ const DialogPortal = DialogPrimitive.Portal;
11
+ const DialogClose = DialogPrimitive.Close;
12
+
13
+ const DialogOverlay = React.forwardRef<
14
+ React.ElementRef<typeof DialogPrimitive.Overlay>,
15
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
16
+ >(({ className, ...props }, ref) => (
17
+ <DialogPrimitive.Overlay
18
+ ref={ref}
19
+ className={cn(
20
+ "fixed inset-0 z-50 bg-black/65 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
21
+ className,
22
+ )}
23
+ {...props}
24
+ />
25
+ ));
26
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
27
+
28
+ const DialogContent = React.forwardRef<
29
+ React.ElementRef<typeof DialogPrimitive.Content>,
30
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
31
+ >(({ className, children, ...props }, ref) => (
32
+ <DialogPortal>
33
+ <DialogOverlay />
34
+ <DialogPrimitive.Content
35
+ ref={ref}
36
+ className={cn(
37
+ "fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl border border-border bg-popover p-6 shadow-elevated duration-200",
38
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
39
+ className,
40
+ )}
41
+ {...props}
42
+ >
43
+ {children}
44
+ <DialogPrimitive.Close className="absolute right-4 top-4 rounded-md p-1 text-muted-foreground opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring/50">
45
+ <X className="size-4" />
46
+ <span className="sr-only">Close</span>
47
+ </DialogPrimitive.Close>
48
+ </DialogPrimitive.Content>
49
+ </DialogPortal>
50
+ ));
51
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
52
+
53
+ function DialogHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
54
+ return <div className={cn("flex flex-col gap-1.5 text-left", className)} {...props} />;
55
+ }
56
+
57
+ function DialogFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
58
+ return <div className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)} {...props} />;
59
+ }
60
+
61
+ const DialogTitle = React.forwardRef<
62
+ React.ElementRef<typeof DialogPrimitive.Title>,
63
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
64
+ >(({ className, ...props }, ref) => (
65
+ <DialogPrimitive.Title ref={ref} className={cn("text-base font-semibold tracking-tight", className)} {...props} />
66
+ ));
67
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
68
+
69
+ const DialogDescription = React.forwardRef<
70
+ React.ElementRef<typeof DialogPrimitive.Description>,
71
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
72
+ >(({ className, ...props }, ref) => (
73
+ <DialogPrimitive.Description ref={ref} className={cn("text-[13px] text-muted-foreground", className)} {...props} />
74
+ ));
75
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
76
+
77
+ export {
78
+ Dialog,
79
+ DialogPortal,
80
+ DialogOverlay,
81
+ DialogTrigger,
82
+ DialogClose,
83
+ DialogContent,
84
+ DialogHeader,
85
+ DialogFooter,
86
+ DialogTitle,
87
+ DialogDescription,
88
+ };
@@ -0,0 +1,77 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
5
+ import { Check, ChevronRight, Circle } from "lucide-react";
6
+ import { cn } from "@/lib/utils";
7
+
8
+ const DropdownMenu = DropdownMenuPrimitive.Root;
9
+ const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
10
+ const DropdownMenuGroup = DropdownMenuPrimitive.Group;
11
+ const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
12
+
13
+ const DropdownMenuContent = React.forwardRef<
14
+ React.ElementRef<typeof DropdownMenuPrimitive.Content>,
15
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
16
+ >(({ className, sideOffset = 6, ...props }, ref) => (
17
+ <DropdownMenuPrimitive.Portal>
18
+ <DropdownMenuPrimitive.Content
19
+ ref={ref}
20
+ sideOffset={sideOffset}
21
+ className={cn(
22
+ "z-50 min-w-[11rem] overflow-hidden rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-elevated",
23
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
24
+ className,
25
+ )}
26
+ {...props}
27
+ />
28
+ </DropdownMenuPrimitive.Portal>
29
+ ));
30
+ DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
31
+
32
+ const DropdownMenuItem = React.forwardRef<
33
+ React.ElementRef<typeof DropdownMenuPrimitive.Item>,
34
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { inset?: boolean }
35
+ >(({ className, inset, ...props }, ref) => (
36
+ <DropdownMenuPrimitive.Item
37
+ ref={ref}
38
+ className={cn(
39
+ "relative flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-1.5 text-[13px] outline-none transition-colors focus:bg-secondary focus:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:size-4 [&_svg]:text-muted-foreground",
40
+ inset && "pl-8",
41
+ className,
42
+ )}
43
+ {...props}
44
+ />
45
+ ));
46
+ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
47
+
48
+ const DropdownMenuLabel = React.forwardRef<
49
+ React.ElementRef<typeof DropdownMenuPrimitive.Label>,
50
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label>
51
+ >(({ className, ...props }, ref) => (
52
+ <DropdownMenuPrimitive.Label
53
+ ref={ref}
54
+ className={cn("px-2.5 py-1.5 text-[11px] font-medium uppercase tracking-wider text-muted-foreground", className)}
55
+ {...props}
56
+ />
57
+ ));
58
+ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
59
+
60
+ const DropdownMenuSeparator = React.forwardRef<
61
+ React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
62
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
63
+ >(({ className, ...props }, ref) => (
64
+ <DropdownMenuPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-border", className)} {...props} />
65
+ ));
66
+ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
67
+
68
+ export {
69
+ DropdownMenu,
70
+ DropdownMenuTrigger,
71
+ DropdownMenuContent,
72
+ DropdownMenuItem,
73
+ DropdownMenuLabel,
74
+ DropdownMenuGroup,
75
+ DropdownMenuPortal,
76
+ DropdownMenuSeparator,
77
+ };
@@ -0,0 +1,22 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+
4
+ const Input = React.forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(
5
+ ({ className, type, ...props }, ref) => (
6
+ <input
7
+ type={type}
8
+ ref={ref}
9
+ className={cn(
10
+ "flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm text-foreground shadow-sm transition-colors",
11
+ "placeholder:text-muted-foreground/70 focus-visible:border-ring/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
12
+ "disabled:cursor-not-allowed disabled:opacity-50",
13
+ "file:border-0 file:bg-transparent file:text-sm file:font-medium",
14
+ className,
15
+ )}
16
+ {...props}
17
+ />
18
+ ),
19
+ );
20
+ Input.displayName = "Input";
21
+
22
+ export { Input };
@@ -0,0 +1,22 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as LabelPrimitive from "@radix-ui/react-label";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ const Label = React.forwardRef<
8
+ React.ElementRef<typeof LabelPrimitive.Root>,
9
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
10
+ >(({ className, ...props }, ref) => (
11
+ <LabelPrimitive.Root
12
+ ref={ref}
13
+ className={cn(
14
+ "text-[13px] font-medium text-muted-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
15
+ className,
16
+ )}
17
+ {...props}
18
+ />
19
+ ));
20
+ Label.displayName = LabelPrimitive.Root.displayName;
21
+
22
+ export { Label };
@@ -0,0 +1,33 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ const Popover = PopoverPrimitive.Root;
8
+ const PopoverTrigger = PopoverPrimitive.Trigger;
9
+ const PopoverAnchor = PopoverPrimitive.Anchor;
10
+
11
+ const PopoverContent = React.forwardRef<
12
+ React.ElementRef<typeof PopoverPrimitive.Content>,
13
+ React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
14
+ >(({ className, align = "center", sideOffset = 6, ...props }, ref) => (
15
+ <PopoverPrimitive.Portal>
16
+ <PopoverPrimitive.Content
17
+ ref={ref}
18
+ align={align}
19
+ sideOffset={sideOffset}
20
+ className={cn(
21
+ "z-50 w-72 rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-elevated outline-none",
22
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
23
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
24
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
25
+ className,
26
+ )}
27
+ {...props}
28
+ />
29
+ </PopoverPrimitive.Portal>
30
+ ));
31
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
32
+
33
+ export { Popover, PopoverTrigger, PopoverAnchor, PopoverContent };
@@ -0,0 +1,41 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ const ScrollArea = React.forwardRef<
8
+ React.ElementRef<typeof ScrollAreaPrimitive.Root>,
9
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & { viewportRef?: React.Ref<HTMLDivElement> }
10
+ >(({ className, children, viewportRef, ...props }, ref) => (
11
+ <ScrollAreaPrimitive.Root ref={ref} className={cn("relative overflow-hidden", className)} {...props}>
12
+ <ScrollAreaPrimitive.Viewport ref={viewportRef} className="h-full w-full rounded-[inherit] [&>div]:!block">
13
+ {children}
14
+ </ScrollAreaPrimitive.Viewport>
15
+ <ScrollBar />
16
+ <ScrollAreaPrimitive.Corner />
17
+ </ScrollAreaPrimitive.Root>
18
+ ));
19
+ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
20
+
21
+ const ScrollBar = React.forwardRef<
22
+ React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
23
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
24
+ >(({ className, orientation = "vertical", ...props }, ref) => (
25
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
26
+ ref={ref}
27
+ orientation={orientation}
28
+ className={cn(
29
+ "flex touch-none select-none transition-colors",
30
+ orientation === "vertical" && "h-full w-2 border-l border-l-transparent p-px",
31
+ orientation === "horizontal" && "h-2 flex-col border-t border-t-transparent p-px",
32
+ className,
33
+ )}
34
+ {...props}
35
+ >
36
+ <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
37
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
38
+ ));
39
+ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
40
+
41
+ export { ScrollArea, ScrollBar };
@@ -0,0 +1,100 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as SelectPrimitive from "@radix-ui/react-select";
5
+ import { Check, ChevronDown, ChevronUp } from "lucide-react";
6
+ import { cn } from "@/lib/utils";
7
+
8
+ const Select = SelectPrimitive.Root;
9
+ const SelectGroup = SelectPrimitive.Group;
10
+ const SelectValue = SelectPrimitive.Value;
11
+
12
+ const SelectTrigger = React.forwardRef<
13
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
14
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
15
+ >(({ className, children, ...props }, ref) => (
16
+ <SelectPrimitive.Trigger
17
+ ref={ref}
18
+ className={cn(
19
+ "flex h-9 w-full items-center justify-between gap-2 rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm transition-colors",
20
+ "placeholder:text-muted-foreground focus:outline-none focus-visible:border-ring/50 focus-visible:ring-2 focus-visible:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50",
21
+ "data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground",
22
+ className,
23
+ )}
24
+ {...props}
25
+ >
26
+ {children}
27
+ <SelectPrimitive.Icon asChild>
28
+ <ChevronDown className="opacity-70" />
29
+ </SelectPrimitive.Icon>
30
+ </SelectPrimitive.Trigger>
31
+ ));
32
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
33
+
34
+ const SelectContent = React.forwardRef<
35
+ React.ElementRef<typeof SelectPrimitive.Content>,
36
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
37
+ >(({ className, children, position = "popper", ...props }, ref) => (
38
+ <SelectPrimitive.Portal>
39
+ <SelectPrimitive.Content
40
+ ref={ref}
41
+ className={cn(
42
+ "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-elevated",
43
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
44
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1",
45
+ className,
46
+ )}
47
+ position={position}
48
+ {...props}
49
+ >
50
+ <SelectPrimitive.ScrollUpButton className="flex h-6 items-center justify-center text-muted-foreground">
51
+ <ChevronUp className="size-4" />
52
+ </SelectPrimitive.ScrollUpButton>
53
+ <SelectPrimitive.Viewport
54
+ className={cn("p-1", position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]")}
55
+ >
56
+ {children}
57
+ </SelectPrimitive.Viewport>
58
+ <SelectPrimitive.ScrollDownButton className="flex h-6 items-center justify-center text-muted-foreground">
59
+ <ChevronDown className="size-4" />
60
+ </SelectPrimitive.ScrollDownButton>
61
+ </SelectPrimitive.Content>
62
+ </SelectPrimitive.Portal>
63
+ ));
64
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
65
+
66
+ const SelectItem = React.forwardRef<
67
+ React.ElementRef<typeof SelectPrimitive.Item>,
68
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
69
+ >(({ className, children, ...props }, ref) => (
70
+ <SelectPrimitive.Item
71
+ ref={ref}
72
+ className={cn(
73
+ "relative flex w-full cursor-pointer select-none items-center rounded-md py-1.5 pl-8 pr-2 text-[13px] outline-none focus:bg-secondary focus:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
74
+ className,
75
+ )}
76
+ {...props}
77
+ >
78
+ <span className="absolute left-2 flex size-4 items-center justify-center">
79
+ <SelectPrimitive.ItemIndicator>
80
+ <Check className="size-4 text-brand" />
81
+ </SelectPrimitive.ItemIndicator>
82
+ </span>
83
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
84
+ </SelectPrimitive.Item>
85
+ ));
86
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
87
+
88
+ const SelectLabel = React.forwardRef<
89
+ React.ElementRef<typeof SelectPrimitive.Label>,
90
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
91
+ >(({ className, ...props }, ref) => (
92
+ <SelectPrimitive.Label
93
+ ref={ref}
94
+ className={cn("px-2 py-1.5 text-[11px] font-medium uppercase tracking-wider text-muted-foreground", className)}
95
+ {...props}
96
+ />
97
+ ));
98
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
99
+
100
+ export { Select, SelectGroup, SelectValue, SelectTrigger, SelectContent, SelectItem, SelectLabel };
@@ -0,0 +1,25 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as SeparatorPrimitive from "@radix-ui/react-separator";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ const Separator = React.forwardRef<
8
+ React.ElementRef<typeof SeparatorPrimitive.Root>,
9
+ React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
10
+ >(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
11
+ <SeparatorPrimitive.Root
12
+ ref={ref}
13
+ decorative={decorative}
14
+ orientation={orientation}
15
+ className={cn(
16
+ "shrink-0 bg-border",
17
+ orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
18
+ className,
19
+ )}
20
+ {...props}
21
+ />
22
+ ));
23
+ Separator.displayName = SeparatorPrimitive.Root.displayName;
24
+
25
+ export { Separator };
@@ -0,0 +1,7 @@
1
+ import { cn } from "@/lib/utils";
2
+
3
+ function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
4
+ return <div className={cn("animate-pulse rounded-md bg-secondary/70", className)} {...props} />;
5
+ }
6
+
7
+ export { Skeleton };
@@ -0,0 +1,28 @@
1
+ "use client";
2
+
3
+ import { Toaster as Sonner, type ToasterProps } from "sonner";
4
+ import { useTheme } from "@/lib/theme";
5
+
6
+ /** App-wide toast surface, themed via tokens. Use `toast()` from sonner. */
7
+ export function Toaster(props: ToasterProps) {
8
+ const { resolved } = useTheme();
9
+ return (
10
+ <Sonner
11
+ theme={resolved}
12
+ position="bottom-right"
13
+ toastOptions={{
14
+ classNames: {
15
+ toast:
16
+ "group bg-popover text-popover-foreground border border-border rounded-lg shadow-xl text-[13px]",
17
+ description: "text-muted-foreground",
18
+ actionButton: "bg-primary text-primary-foreground",
19
+ cancelButton: "bg-secondary text-secondary-foreground",
20
+ error: "[&_[data-icon]]:text-destructive",
21
+ success: "[&_[data-icon]]:text-success",
22
+ },
23
+ }}
24
+ style={{ "--normal-border": "hsl(var(--border))" } as React.CSSProperties}
25
+ {...props}
26
+ />
27
+ );
28
+ }
@@ -0,0 +1,51 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+
4
+ const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
5
+ ({ className, ...props }, ref) => (
6
+ <div className="relative w-full overflow-x-auto">
7
+ <table ref={ref} className={cn("w-full caption-bottom text-sm", className)} {...props} />
8
+ </div>
9
+ ),
10
+ );
11
+ Table.displayName = "Table";
12
+
13
+ const TableHeader = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
14
+ ({ className, ...props }, ref) => <thead ref={ref} className={cn("[&_tr]:border-b [&_tr]:border-border", className)} {...props} />,
15
+ );
16
+ TableHeader.displayName = "TableHeader";
17
+
18
+ const TableBody = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
19
+ ({ className, ...props }, ref) => <tbody ref={ref} className={cn("[&_tr:last-child]:border-0", className)} {...props} />,
20
+ );
21
+ TableBody.displayName = "TableBody";
22
+
23
+ const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
24
+ ({ className, ...props }, ref) => (
25
+ <tr ref={ref} className={cn("border-b border-border transition-colors hover:bg-secondary/40 data-[state=selected]:bg-secondary", className)} {...props} />
26
+ ),
27
+ );
28
+ TableRow.displayName = "TableRow";
29
+
30
+ const TableHead = React.forwardRef<HTMLTableCellElement, React.ThHTMLAttributes<HTMLTableCellElement>>(
31
+ ({ className, ...props }, ref) => (
32
+ <th
33
+ ref={ref}
34
+ className={cn(
35
+ "h-10 px-4 text-left align-middle text-[11px] font-medium uppercase tracking-wider text-muted-foreground [&:has([role=checkbox])]:pr-0",
36
+ className,
37
+ )}
38
+ {...props}
39
+ />
40
+ ),
41
+ );
42
+ TableHead.displayName = "TableHead";
43
+
44
+ const TableCell = React.forwardRef<HTMLTableCellElement, React.TdHTMLAttributes<HTMLTableCellElement>>(
45
+ ({ className, ...props }, ref) => (
46
+ <td ref={ref} className={cn("px-4 py-3 align-middle [&:has([role=checkbox])]:pr-0", className)} {...props} />
47
+ ),
48
+ );
49
+ TableCell.displayName = "TableCell";
50
+
51
+ export { Table, TableHeader, TableBody, TableRow, TableHead, TableCell };
@@ -0,0 +1,50 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ const Tabs = TabsPrimitive.Root;
8
+
9
+ const TabsList = React.forwardRef<
10
+ React.ElementRef<typeof TabsPrimitive.List>,
11
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
12
+ >(({ className, ...props }, ref) => (
13
+ <TabsPrimitive.List
14
+ ref={ref}
15
+ className={cn("inline-flex items-center gap-1 border-b border-border", className)}
16
+ {...props}
17
+ />
18
+ ));
19
+ TabsList.displayName = TabsPrimitive.List.displayName;
20
+
21
+ const TabsTrigger = React.forwardRef<
22
+ React.ElementRef<typeof TabsPrimitive.Trigger>,
23
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
24
+ >(({ className, ...props }, ref) => (
25
+ <TabsPrimitive.Trigger
26
+ ref={ref}
27
+ className={cn(
28
+ "relative -mb-px inline-flex items-center gap-1.5 whitespace-nowrap border-b-2 border-transparent px-3 pb-2.5 pt-1.5 text-[13px] font-medium text-muted-foreground transition-colors",
29
+ "hover:text-foreground focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50",
30
+ "data-[state=active]:border-brand data-[state=active]:text-foreground [&_svg]:size-4",
31
+ className,
32
+ )}
33
+ {...props}
34
+ />
35
+ ));
36
+ TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
37
+
38
+ const TabsContent = React.forwardRef<
39
+ React.ElementRef<typeof TabsPrimitive.Content>,
40
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
41
+ >(({ className, ...props }, ref) => (
42
+ <TabsPrimitive.Content
43
+ ref={ref}
44
+ className={cn("focus-visible:outline-none", className)}
45
+ {...props}
46
+ />
47
+ ));
48
+ TabsContent.displayName = TabsPrimitive.Content.displayName;
49
+
50
+ export { Tabs, TabsList, TabsTrigger, TabsContent };
@@ -0,0 +1,20 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+
4
+ const Textarea = React.forwardRef<HTMLTextAreaElement, React.TextareaHTMLAttributes<HTMLTextAreaElement>>(
5
+ ({ className, ...props }, ref) => (
6
+ <textarea
7
+ ref={ref}
8
+ className={cn(
9
+ "flex min-h-[72px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground shadow-sm transition-colors",
10
+ "placeholder:text-muted-foreground/70 focus-visible:border-ring/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
11
+ "disabled:cursor-not-allowed disabled:opacity-50",
12
+ className,
13
+ )}
14
+ {...props}
15
+ />
16
+ ),
17
+ );
18
+ Textarea.displayName = "Textarea";
19
+
20
+ export { Textarea };