@agentforge-ai/cli 0.4.0 → 0.4.1

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 (79) hide show
  1. package/dist/default/.env.example +11 -0
  2. package/dist/default/dashboard/app/components/DashboardLayout.tsx +245 -0
  3. package/dist/default/dashboard/app/components/ui/badge.tsx +26 -0
  4. package/dist/default/dashboard/app/components/ui/button.tsx +41 -0
  5. package/dist/default/dashboard/app/components/ui/card.tsx +44 -0
  6. package/dist/default/dashboard/app/components/ui/dialog.tsx +66 -0
  7. package/dist/default/dashboard/app/components/ui/input.tsx +21 -0
  8. package/dist/default/dashboard/app/components/ui/label.tsx +18 -0
  9. package/dist/default/dashboard/app/components/ui/select.tsx +75 -0
  10. package/dist/default/dashboard/app/components/ui/sheet.tsx +73 -0
  11. package/dist/default/dashboard/app/components/ui/switch.tsx +34 -0
  12. package/dist/default/dashboard/app/components/ui/table.tsx +60 -0
  13. package/dist/default/dashboard/app/components/ui/tabs.tsx +50 -0
  14. package/dist/default/dashboard/app/components/ui/tooltip.tsx +23 -0
  15. package/dist/default/dashboard/app/lib/utils.ts +6 -0
  16. package/dist/default/dashboard/app/main.tsx +35 -0
  17. package/dist/default/dashboard/app/routeTree.gen.ts +352 -0
  18. package/dist/default/dashboard/app/routes/__root.tsx +10 -0
  19. package/dist/default/dashboard/app/routes/agents.tsx +255 -0
  20. package/dist/default/dashboard/app/routes/chat.tsx +427 -0
  21. package/dist/default/dashboard/app/routes/connections.tsx +413 -0
  22. package/dist/default/dashboard/app/routes/cron.tsx +322 -0
  23. package/dist/default/dashboard/app/routes/files.tsx +203 -0
  24. package/dist/default/dashboard/app/routes/index.tsx +141 -0
  25. package/dist/default/dashboard/app/routes/projects.tsx +254 -0
  26. package/dist/default/dashboard/app/routes/sessions.tsx +272 -0
  27. package/dist/default/dashboard/app/routes/settings.tsx +583 -0
  28. package/dist/default/dashboard/app/routes/skills.tsx +252 -0
  29. package/dist/default/dashboard/app/routes/usage.tsx +181 -0
  30. package/dist/default/dashboard/app/styles/globals.css +93 -0
  31. package/dist/default/dashboard/index.html +13 -0
  32. package/dist/default/dashboard/package.json +36 -0
  33. package/dist/default/dashboard/postcss.config.js +6 -0
  34. package/dist/default/dashboard/tailwind.config.js +50 -0
  35. package/dist/default/dashboard/tsconfig.json +24 -0
  36. package/dist/default/dashboard/vite.config.ts +16 -0
  37. package/dist/default/package.json +5 -2
  38. package/dist/default/src/agent.ts +42 -2
  39. package/dist/index.js +135 -22
  40. package/dist/index.js.map +1 -1
  41. package/package.json +1 -1
  42. package/templates/default/.env.example +11 -0
  43. package/templates/default/dashboard/app/components/DashboardLayout.tsx +245 -0
  44. package/templates/default/dashboard/app/components/ui/badge.tsx +26 -0
  45. package/templates/default/dashboard/app/components/ui/button.tsx +41 -0
  46. package/templates/default/dashboard/app/components/ui/card.tsx +44 -0
  47. package/templates/default/dashboard/app/components/ui/dialog.tsx +66 -0
  48. package/templates/default/dashboard/app/components/ui/input.tsx +21 -0
  49. package/templates/default/dashboard/app/components/ui/label.tsx +18 -0
  50. package/templates/default/dashboard/app/components/ui/select.tsx +75 -0
  51. package/templates/default/dashboard/app/components/ui/sheet.tsx +73 -0
  52. package/templates/default/dashboard/app/components/ui/switch.tsx +34 -0
  53. package/templates/default/dashboard/app/components/ui/table.tsx +60 -0
  54. package/templates/default/dashboard/app/components/ui/tabs.tsx +50 -0
  55. package/templates/default/dashboard/app/components/ui/tooltip.tsx +23 -0
  56. package/templates/default/dashboard/app/lib/utils.ts +6 -0
  57. package/templates/default/dashboard/app/main.tsx +35 -0
  58. package/templates/default/dashboard/app/routeTree.gen.ts +352 -0
  59. package/templates/default/dashboard/app/routes/__root.tsx +10 -0
  60. package/templates/default/dashboard/app/routes/agents.tsx +255 -0
  61. package/templates/default/dashboard/app/routes/chat.tsx +427 -0
  62. package/templates/default/dashboard/app/routes/connections.tsx +413 -0
  63. package/templates/default/dashboard/app/routes/cron.tsx +322 -0
  64. package/templates/default/dashboard/app/routes/files.tsx +203 -0
  65. package/templates/default/dashboard/app/routes/index.tsx +141 -0
  66. package/templates/default/dashboard/app/routes/projects.tsx +254 -0
  67. package/templates/default/dashboard/app/routes/sessions.tsx +272 -0
  68. package/templates/default/dashboard/app/routes/settings.tsx +583 -0
  69. package/templates/default/dashboard/app/routes/skills.tsx +252 -0
  70. package/templates/default/dashboard/app/routes/usage.tsx +181 -0
  71. package/templates/default/dashboard/app/styles/globals.css +93 -0
  72. package/templates/default/dashboard/index.html +13 -0
  73. package/templates/default/dashboard/package.json +36 -0
  74. package/templates/default/dashboard/postcss.config.js +6 -0
  75. package/templates/default/dashboard/tailwind.config.js +50 -0
  76. package/templates/default/dashboard/tsconfig.json +24 -0
  77. package/templates/default/dashboard/vite.config.ts +16 -0
  78. package/templates/default/package.json +5 -2
  79. package/templates/default/src/agent.ts +42 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge-ai/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "CLI tool for creating, running, and managing AgentForge projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -32,6 +32,17 @@ AGENTFORGE_DEFAULT_MODEL=gpt-4o-mini
32
32
  # E2B API key for secure code execution (https://e2b.dev/)
33
33
  E2B_API_KEY=
34
34
 
35
+ # ─── Workspace ──────────────────────────────────────────────────────────
36
+ # Workspace base path for local development (default: ./workspace)
37
+ AGENTFORGE_WORKSPACE_PATH=./workspace
38
+
39
+ # ─── Cloudflare R2 (Cloud Workspace) ────────────────────────────────────
40
+ # Required for cloud workspace deployment on Cloudflare
41
+ R2_ENDPOINT=
42
+ R2_ACCESS_KEY_ID=
43
+ R2_SECRET_ACCESS_KEY=
44
+ R2_BUCKET=
45
+
35
46
  # ─── Dashboard ──────────────────────────────────────────────────────────
36
47
  # Port for the web dashboard (default: 3000)
37
48
  AGENTFORGE_DASHBOARD_PORT=3000
@@ -0,0 +1,245 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { Link, useRouterState } from "@tanstack/react-router";
3
+ import {
4
+ MessageSquare, LayoutDashboard, Radio, Server, Activity, Clock,
5
+ Bot, Sparkles, Network, Settings, Bug, FileText, Menu, X,
6
+ ChevronLeft, ChevronRight, User, Heart, FolderKanban, Folder,
7
+ } from "lucide-react";
8
+
9
+ const navItems = [
10
+ {
11
+ section: "Chat",
12
+ items: [
13
+ { href: "/chat", label: "Chat", icon: MessageSquare },
14
+ ],
15
+ },
16
+ {
17
+ section: "Control",
18
+ items: [
19
+ { href: "/", label: "Overview", icon: LayoutDashboard },
20
+ { href: "/sessions", label: "Sessions", icon: Activity },
21
+ { href: "/usage", label: "Usage", icon: Clock },
22
+ { href: "/cron", label: "Cron Jobs", icon: Clock },
23
+ ],
24
+ },
25
+ {
26
+ section: "Agent",
27
+ items: [
28
+ { href: "/agents", label: "Agents", icon: Bot },
29
+ { href: "/skills", label: "Skills", icon: Sparkles },
30
+ { href: "/connections", label: "Connections", icon: Network },
31
+ ],
32
+ },
33
+ {
34
+ section: "Workspace",
35
+ items: [
36
+ { href: "/projects", label: "Projects", icon: FolderKanban },
37
+ { href: "/files", label: "Files", icon: Folder },
38
+ ],
39
+ },
40
+ {
41
+ section: "Settings",
42
+ items: [
43
+ { href: "/settings", label: "Config", icon: Settings },
44
+ ],
45
+ },
46
+ ];
47
+
48
+ const HealthStatus = () => {
49
+ const [isOnline, setIsOnline] = useState(true);
50
+
51
+ useEffect(() => {
52
+ const interval = setInterval(() => {
53
+ // In production, poll the Convex backend heartbeat
54
+ }, 30000);
55
+ return () => clearInterval(interval);
56
+ }, []);
57
+
58
+ return (
59
+ <div className="flex items-center gap-2 text-sm text-muted-foreground">
60
+ <span className="relative flex h-2 w-2">
61
+ <span
62
+ className={`animate-ping absolute inline-flex h-full w-full rounded-full ${
63
+ isOnline ? "bg-green-400" : "bg-red-400"
64
+ } opacity-75`}
65
+ ></span>
66
+ <span
67
+ className={`relative inline-flex rounded-full h-2 w-2 ${
68
+ isOnline ? "bg-green-500" : "bg-red-500"
69
+ }`}
70
+ ></span>
71
+ </span>
72
+ {isOnline ? "Online" : "Offline"}
73
+ </div>
74
+ );
75
+ };
76
+
77
+ const Breadcrumb = () => {
78
+ const { location } = useRouterState();
79
+ const pathnames = location.pathname.split("/").filter((x) => x);
80
+
81
+ if (pathnames.length === 0) {
82
+ return <span className="text-sm font-medium">Overview</span>;
83
+ }
84
+
85
+ return (
86
+ <nav className="flex" aria-label="Breadcrumb">
87
+ <ol className="inline-flex items-center space-x-1 md:space-x-2 rtl:space-x-reverse">
88
+ <li className="inline-flex items-center">
89
+ <Link
90
+ to="/"
91
+ className="inline-flex items-center text-sm font-medium text-muted-foreground hover:text-foreground"
92
+ >
93
+ <LayoutDashboard className="w-4 h-4 me-2.5" />
94
+ Home
95
+ </Link>
96
+ </li>
97
+ {pathnames.map((value, index) => {
98
+ const to = `/${pathnames.slice(0, index + 1).join("/")}`;
99
+ const isLast = index === pathnames.length - 1;
100
+ return (
101
+ <li key={to}>
102
+ <div className="flex items-center">
103
+ <ChevronRight className="w-4 h-4 text-muted-foreground" />
104
+ <Link
105
+ to={to}
106
+ className={`ms-1 text-sm font-medium ${
107
+ isLast
108
+ ? "text-foreground"
109
+ : "text-muted-foreground hover:text-foreground"
110
+ } md:ms-2`}
111
+ >
112
+ {value.charAt(0).toUpperCase() + value.slice(1)}
113
+ </Link>
114
+ </div>
115
+ </li>
116
+ );
117
+ })}
118
+ </ol>
119
+ </nav>
120
+ );
121
+ };
122
+
123
+ export function DashboardLayout({ children }: { children: React.ReactNode }) {
124
+ const [isSidebarOpen, setIsSidebarOpen] = useState(true);
125
+ const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
126
+ const { location } = useRouterState();
127
+
128
+ const NavLink = ({ item }: { item: any }) => {
129
+ const isActive = location.pathname === item.href;
130
+ return (
131
+ <Link
132
+ to={item.href}
133
+ className={`flex items-center px-3 py-2 text-sm font-medium rounded-md transition-colors ${
134
+ isActive
135
+ ? "bg-primary text-primary-foreground"
136
+ : "text-muted-foreground hover:bg-muted hover:text-foreground"
137
+ }`}
138
+ onClick={() => setIsMobileMenuOpen(false)}
139
+ >
140
+ <item.icon className="w-5 h-5 mr-3" />
141
+ <span>{item.label}</span>
142
+ </Link>
143
+ );
144
+ };
145
+
146
+ const sidebarContent = (
147
+ <div className="flex flex-col h-full">
148
+ <div className="flex items-center justify-between h-16 px-4 border-b border-border">
149
+ <Link to="/" className="flex items-center gap-2 text-lg font-bold">
150
+ <Bot className="w-7 h-7 text-primary" />
151
+ <span>AgentForge</span>
152
+ </Link>
153
+ </div>
154
+ <nav className="flex-1 px-2 py-4 space-y-4 overflow-y-auto">
155
+ {navItems.map((section) => (
156
+ <div key={section.section}>
157
+ <h3 className="px-3 mb-2 text-xs font-semibold tracking-wider text-muted-foreground/80 uppercase">
158
+ {section.section}
159
+ </h3>
160
+ <div className="space-y-1">
161
+ {section.items.map((item) => (
162
+ <NavLink key={item.href} item={item} />
163
+ ))}
164
+ </div>
165
+ </div>
166
+ ))}
167
+ </nav>
168
+ <div className="p-4 border-t border-border">
169
+ <HealthStatus />
170
+ </div>
171
+ </div>
172
+ );
173
+
174
+ return (
175
+ <div className="flex h-screen bg-background text-foreground">
176
+ {/* Desktop Sidebar */}
177
+ <aside
178
+ className={`hidden md:block bg-card border-r border-border transition-all duration-300 ease-in-out ${
179
+ isSidebarOpen ? "w-64" : "w-0 overflow-hidden"
180
+ }`}
181
+ >
182
+ {sidebarContent}
183
+ </aside>
184
+
185
+ {/* Mobile Sidebar Overlay */}
186
+ {isMobileMenuOpen && (
187
+ <div
188
+ className="fixed inset-0 z-40 flex md:hidden"
189
+ role="dialog"
190
+ aria-modal="true"
191
+ >
192
+ <div
193
+ className="fixed inset-0 bg-black/60"
194
+ aria-hidden="true"
195
+ onClick={() => setIsMobileMenuOpen(false)}
196
+ ></div>
197
+ <div className="relative flex flex-col flex-1 w-full max-w-xs bg-card">
198
+ <div className="absolute top-0 right-0 pt-2 -mr-12">
199
+ <button
200
+ type="button"
201
+ className="flex items-center justify-center w-10 h-10 ml-1 rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
202
+ onClick={() => setIsMobileMenuOpen(false)}
203
+ >
204
+ <span className="sr-only">Close sidebar</span>
205
+ <X className="w-6 h-6 text-white" />
206
+ </button>
207
+ </div>
208
+ {sidebarContent}
209
+ </div>
210
+ </div>
211
+ )}
212
+
213
+ <div className="flex flex-col flex-1 min-w-0">
214
+ <header className="flex items-center justify-between h-16 px-4 bg-card border-b border-border md:px-6">
215
+ <div className="flex items-center gap-4">
216
+ <button
217
+ onClick={() => setIsSidebarOpen(!isSidebarOpen)}
218
+ className="hidden p-2 rounded-md md:block hover:bg-muted"
219
+ >
220
+ {isSidebarOpen ? (
221
+ <ChevronLeft className="w-5 h-5" />
222
+ ) : (
223
+ <ChevronRight className="w-5 h-5" />
224
+ )}
225
+ </button>
226
+ <button
227
+ onClick={() => setIsMobileMenuOpen(true)}
228
+ className="p-2 rounded-md md:hidden hover:bg-muted"
229
+ >
230
+ <Menu className="w-5 h-5" />
231
+ </button>
232
+ <Breadcrumb />
233
+ </div>
234
+ <div className="flex items-center gap-4">
235
+ <HealthStatus />
236
+ <div className="p-2 rounded-full bg-muted">
237
+ <User className="w-5 h-5 text-muted-foreground" />
238
+ </div>
239
+ </div>
240
+ </header>
241
+ <main className="flex-1 p-4 overflow-y-auto md:p-6">{children}</main>
242
+ </div>
243
+ </div>
244
+ );
245
+ }
@@ -0,0 +1,26 @@
1
+ import React from "react";
2
+ import { cn } from "../../lib/utils";
3
+
4
+ export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement> {
5
+ variant?: "default" | "secondary" | "destructive" | "outline";
6
+ }
7
+
8
+ const variants: Record<string, string> = {
9
+ default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
10
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
11
+ destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
12
+ outline: "text-foreground",
13
+ };
14
+
15
+ export function Badge({ className, variant = "default", ...props }: BadgeProps) {
16
+ return (
17
+ <div
18
+ className={cn(
19
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
20
+ variants[variant],
21
+ className
22
+ )}
23
+ {...props}
24
+ />
25
+ );
26
+ }
@@ -0,0 +1,41 @@
1
+ import React from "react";
2
+ import { cn } from "../../lib/utils";
3
+
4
+ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
5
+ variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
6
+ size?: "default" | "sm" | "lg" | "icon";
7
+ }
8
+
9
+ const variants: Record<string, string> = {
10
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
11
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
12
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
13
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
14
+ ghost: "hover:bg-accent hover:text-accent-foreground",
15
+ link: "text-primary underline-offset-4 hover:underline",
16
+ };
17
+
18
+ const sizes: Record<string, string> = {
19
+ default: "h-10 px-4 py-2",
20
+ sm: "h-9 rounded-md px-3",
21
+ lg: "h-11 rounded-md px-8",
22
+ icon: "h-10 w-10",
23
+ };
24
+
25
+ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
26
+ ({ className, variant = "default", size = "default", ...props }, ref) => {
27
+ return (
28
+ <button
29
+ className={cn(
30
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
31
+ variants[variant],
32
+ sizes[size],
33
+ className
34
+ )}
35
+ ref={ref}
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+ );
41
+ Button.displayName = "Button";
@@ -0,0 +1,44 @@
1
+ import React from "react";
2
+ import { cn } from "../../lib/utils";
3
+
4
+ export const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
5
+ ({ className, ...props }, ref) => (
6
+ <div ref={ref} className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)} {...props} />
7
+ )
8
+ );
9
+ Card.displayName = "Card";
10
+
11
+ export const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
12
+ ({ className, ...props }, ref) => (
13
+ <div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
14
+ )
15
+ );
16
+ CardHeader.displayName = "CardHeader";
17
+
18
+ export const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
19
+ ({ className, ...props }, ref) => (
20
+ <h3 ref={ref} className={cn("text-2xl font-semibold leading-none tracking-tight", className)} {...props} />
21
+ )
22
+ );
23
+ CardTitle.displayName = "CardTitle";
24
+
25
+ export const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
26
+ ({ className, ...props }, ref) => (
27
+ <p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
28
+ )
29
+ );
30
+ CardDescription.displayName = "CardDescription";
31
+
32
+ export const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
33
+ ({ className, ...props }, ref) => (
34
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
35
+ )
36
+ );
37
+ CardContent.displayName = "CardContent";
38
+
39
+ export const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
40
+ ({ className, ...props }, ref) => (
41
+ <div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
42
+ )
43
+ );
44
+ CardFooter.displayName = "CardFooter";
@@ -0,0 +1,66 @@
1
+ import React from "react";
2
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
3
+ import { X } from "lucide-react";
4
+ import { cn } from "../../lib/utils";
5
+
6
+ export const Dialog = DialogPrimitive.Root;
7
+ export const DialogTrigger = DialogPrimitive.Trigger;
8
+ export const DialogPortal = DialogPrimitive.Portal;
9
+ export const DialogClose = DialogPrimitive.Close;
10
+
11
+ export const DialogOverlay = React.forwardRef<
12
+ React.ElementRef<typeof DialogPrimitive.Overlay>,
13
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
14
+ >(({ className, ...props }, ref) => (
15
+ <DialogPrimitive.Overlay
16
+ ref={ref}
17
+ className={cn("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", className)}
18
+ {...props}
19
+ />
20
+ ));
21
+ DialogOverlay.displayName = "DialogOverlay";
22
+
23
+ export const DialogContent = React.forwardRef<
24
+ React.ElementRef<typeof DialogPrimitive.Content>,
25
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
26
+ >(({ className, children, ...props }, ref) => (
27
+ <DialogPortal>
28
+ <DialogOverlay />
29
+ <DialogPrimitive.Content
30
+ ref={ref}
31
+ className={cn("fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-card p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", className)}
32
+ {...props}
33
+ >
34
+ {children}
35
+ <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
36
+ <X className="h-4 w-4" />
37
+ <span className="sr-only">Close</span>
38
+ </DialogPrimitive.Close>
39
+ </DialogPrimitive.Content>
40
+ </DialogPortal>
41
+ ));
42
+ DialogContent.displayName = "DialogContent";
43
+
44
+ export const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
45
+ <div className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)} {...props} />
46
+ );
47
+
48
+ export const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
49
+ <div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)} {...props} />
50
+ );
51
+
52
+ export const DialogTitle = React.forwardRef<
53
+ React.ElementRef<typeof DialogPrimitive.Title>,
54
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
55
+ >(({ className, ...props }, ref) => (
56
+ <DialogPrimitive.Title ref={ref} className={cn("text-lg font-semibold leading-none tracking-tight", className)} {...props} />
57
+ ));
58
+ DialogTitle.displayName = "DialogTitle";
59
+
60
+ export const DialogDescription = React.forwardRef<
61
+ React.ElementRef<typeof DialogPrimitive.Description>,
62
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
63
+ >(({ className, ...props }, ref) => (
64
+ <DialogPrimitive.Description ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
65
+ ));
66
+ DialogDescription.displayName = "DialogDescription";
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import { cn } from "../../lib/utils";
3
+
4
+ export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
5
+
6
+ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
7
+ ({ className, type, ...props }, ref) => {
8
+ return (
9
+ <input
10
+ type={type}
11
+ className={cn(
12
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
13
+ className
14
+ )}
15
+ ref={ref}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+ );
21
+ Input.displayName = "Input";
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import { cn } from "../../lib/utils";
3
+
4
+ export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {}
5
+
6
+ export const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
7
+ ({ className, ...props }, ref) => (
8
+ <label
9
+ ref={ref}
10
+ className={cn(
11
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
12
+ className
13
+ )}
14
+ {...props}
15
+ />
16
+ )
17
+ );
18
+ Label.displayName = "Label";
@@ -0,0 +1,75 @@
1
+ import React from "react";
2
+ import * as SelectPrimitive from "@radix-ui/react-select";
3
+ import { Check, ChevronDown, ChevronUp } from "lucide-react";
4
+ import { cn } from "../../lib/utils";
5
+
6
+ export const Select = SelectPrimitive.Root;
7
+ export const SelectGroup = SelectPrimitive.Group;
8
+ export const SelectValue = SelectPrimitive.Value;
9
+
10
+ export const SelectTrigger = React.forwardRef<
11
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
12
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
13
+ >(({ className, children, ...props }, ref) => (
14
+ <SelectPrimitive.Trigger
15
+ ref={ref}
16
+ className={cn(
17
+ "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
18
+ className
19
+ )}
20
+ {...props}
21
+ >
22
+ {children}
23
+ <SelectPrimitive.Icon asChild>
24
+ <ChevronDown className="h-4 w-4 opacity-50" />
25
+ </SelectPrimitive.Icon>
26
+ </SelectPrimitive.Trigger>
27
+ ));
28
+ SelectTrigger.displayName = "SelectTrigger";
29
+
30
+ export const SelectContent = React.forwardRef<
31
+ React.ElementRef<typeof SelectPrimitive.Content>,
32
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
33
+ >(({ className, children, position = "popper", ...props }, ref) => (
34
+ <SelectPrimitive.Portal>
35
+ <SelectPrimitive.Content
36
+ ref={ref}
37
+ className={cn(
38
+ "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
39
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
40
+ className
41
+ )}
42
+ position={position}
43
+ {...props}
44
+ >
45
+ <SelectPrimitive.Viewport
46
+ className={cn("p-1", position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]")}
47
+ >
48
+ {children}
49
+ </SelectPrimitive.Viewport>
50
+ </SelectPrimitive.Content>
51
+ </SelectPrimitive.Portal>
52
+ ));
53
+ SelectContent.displayName = "SelectContent";
54
+
55
+ export const SelectItem = React.forwardRef<
56
+ React.ElementRef<typeof SelectPrimitive.Item>,
57
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
58
+ >(({ className, children, ...props }, ref) => (
59
+ <SelectPrimitive.Item
60
+ ref={ref}
61
+ className={cn(
62
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
63
+ className
64
+ )}
65
+ {...props}
66
+ >
67
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
68
+ <SelectPrimitive.ItemIndicator>
69
+ <Check className="h-4 w-4" />
70
+ </SelectPrimitive.ItemIndicator>
71
+ </span>
72
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
73
+ </SelectPrimitive.Item>
74
+ ));
75
+ SelectItem.displayName = "SelectItem";
@@ -0,0 +1,73 @@
1
+ import React from "react";
2
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
3
+ import { X } from "lucide-react";
4
+ import { cn } from "../../lib/utils";
5
+
6
+ export const Sheet = DialogPrimitive.Root;
7
+ export const SheetTrigger = DialogPrimitive.Trigger;
8
+ export const SheetClose = DialogPrimitive.Close;
9
+ export const SheetPortal = DialogPrimitive.Portal;
10
+
11
+ export const SheetOverlay = React.forwardRef<
12
+ React.ElementRef<typeof DialogPrimitive.Overlay>,
13
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
14
+ >(({ className, ...props }, ref) => (
15
+ <DialogPrimitive.Overlay
16
+ className={cn("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", className)}
17
+ {...props}
18
+ ref={ref}
19
+ />
20
+ ));
21
+ SheetOverlay.displayName = "SheetOverlay";
22
+
23
+ export const SheetContent = React.forwardRef<
24
+ React.ElementRef<typeof DialogPrimitive.Content>,
25
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & { side?: "top" | "bottom" | "left" | "right" }
26
+ >(({ side = "right", className, children, ...props }, ref) => (
27
+ <SheetPortal>
28
+ <SheetOverlay />
29
+ <DialogPrimitive.Content
30
+ ref={ref}
31
+ className={cn(
32
+ "fixed z-50 gap-4 bg-card p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
33
+ side === "right" && "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
34
+ side === "left" && "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
35
+ side === "top" && "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
36
+ side === "bottom" && "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
37
+ className
38
+ )}
39
+ {...props}
40
+ >
41
+ {children}
42
+ <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
43
+ <X className="h-4 w-4" />
44
+ <span className="sr-only">Close</span>
45
+ </DialogPrimitive.Close>
46
+ </DialogPrimitive.Content>
47
+ </SheetPortal>
48
+ ));
49
+ SheetContent.displayName = "SheetContent";
50
+
51
+ export const SheetHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
52
+ <div className={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...props} />
53
+ );
54
+
55
+ export const SheetFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
56
+ <div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)} {...props} />
57
+ );
58
+
59
+ export const SheetTitle = React.forwardRef<
60
+ React.ElementRef<typeof DialogPrimitive.Title>,
61
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
62
+ >(({ className, ...props }, ref) => (
63
+ <DialogPrimitive.Title ref={ref} className={cn("text-lg font-semibold text-foreground", className)} {...props} />
64
+ ));
65
+ SheetTitle.displayName = "SheetTitle";
66
+
67
+ export const SheetDescription = React.forwardRef<
68
+ React.ElementRef<typeof DialogPrimitive.Description>,
69
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
70
+ >(({ className, ...props }, ref) => (
71
+ <DialogPrimitive.Description ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
72
+ ));
73
+ SheetDescription.displayName = "SheetDescription";