@getjack/jack 0.1.19 → 0.1.22

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 (105) hide show
  1. package/package.json +5 -2
  2. package/src/commands/down.ts +11 -1
  3. package/src/commands/init.ts +19 -6
  4. package/src/commands/new.ts +56 -4
  5. package/src/commands/publish.ts +1 -1
  6. package/src/lib/agents.ts +3 -1
  7. package/src/lib/auth/ensure-auth.test.ts +3 -3
  8. package/src/lib/control-plane.ts +15 -1
  9. package/src/lib/deploy-upload.ts +26 -1
  10. package/src/lib/hooks.ts +232 -1
  11. package/src/lib/managed-deploy.ts +13 -6
  12. package/src/lib/managed-down.ts +66 -45
  13. package/src/lib/progress.ts +76 -5
  14. package/src/lib/project-list.ts +6 -1
  15. package/src/lib/project-operations.ts +21 -31
  16. package/src/lib/project-resolver.ts +1 -1
  17. package/src/lib/zip-packager.ts +36 -7
  18. package/src/templates/index.ts +1 -1
  19. package/src/templates/types.ts +16 -0
  20. package/templates/CLAUDE.md +172 -5
  21. package/templates/miniapp/.jack.json +1 -3
  22. package/templates/saas/.jack.json +154 -0
  23. package/templates/saas/AGENTS.md +333 -0
  24. package/templates/saas/bun.lock +925 -0
  25. package/templates/saas/components.json +21 -0
  26. package/templates/saas/index.html +12 -0
  27. package/templates/saas/package.json +75 -0
  28. package/templates/saas/public/icon.png +0 -0
  29. package/templates/saas/public/og.png +0 -0
  30. package/templates/saas/schema.sql +73 -0
  31. package/templates/saas/src/auth.ts +77 -0
  32. package/templates/saas/src/client/App.tsx +63 -0
  33. package/templates/saas/src/client/components/ProtectedRoute.tsx +29 -0
  34. package/templates/saas/src/client/components/ThemeToggle.tsx +32 -0
  35. package/templates/saas/src/client/components/ui/accordion.tsx +62 -0
  36. package/templates/saas/src/client/components/ui/alert-dialog.tsx +133 -0
  37. package/templates/saas/src/client/components/ui/alert.tsx +60 -0
  38. package/templates/saas/src/client/components/ui/aspect-ratio.tsx +9 -0
  39. package/templates/saas/src/client/components/ui/avatar.tsx +39 -0
  40. package/templates/saas/src/client/components/ui/badge.tsx +39 -0
  41. package/templates/saas/src/client/components/ui/breadcrumb.tsx +102 -0
  42. package/templates/saas/src/client/components/ui/button-group.tsx +78 -0
  43. package/templates/saas/src/client/components/ui/button.tsx +60 -0
  44. package/templates/saas/src/client/components/ui/card.tsx +75 -0
  45. package/templates/saas/src/client/components/ui/carousel.tsx +228 -0
  46. package/templates/saas/src/client/components/ui/chart.tsx +326 -0
  47. package/templates/saas/src/client/components/ui/checkbox.tsx +29 -0
  48. package/templates/saas/src/client/components/ui/collapsible.tsx +19 -0
  49. package/templates/saas/src/client/components/ui/command.tsx +159 -0
  50. package/templates/saas/src/client/components/ui/context-menu.tsx +224 -0
  51. package/templates/saas/src/client/components/ui/dialog.tsx +127 -0
  52. package/templates/saas/src/client/components/ui/drawer.tsx +124 -0
  53. package/templates/saas/src/client/components/ui/dropdown-menu.tsx +226 -0
  54. package/templates/saas/src/client/components/ui/empty.tsx +94 -0
  55. package/templates/saas/src/client/components/ui/field.tsx +232 -0
  56. package/templates/saas/src/client/components/ui/form.tsx +152 -0
  57. package/templates/saas/src/client/components/ui/hover-card.tsx +38 -0
  58. package/templates/saas/src/client/components/ui/input-group.tsx +158 -0
  59. package/templates/saas/src/client/components/ui/input-otp.tsx +68 -0
  60. package/templates/saas/src/client/components/ui/input.tsx +21 -0
  61. package/templates/saas/src/client/components/ui/item.tsx +172 -0
  62. package/templates/saas/src/client/components/ui/kbd.tsx +28 -0
  63. package/templates/saas/src/client/components/ui/label.tsx +21 -0
  64. package/templates/saas/src/client/components/ui/menubar.tsx +250 -0
  65. package/templates/saas/src/client/components/ui/navigation-menu.tsx +161 -0
  66. package/templates/saas/src/client/components/ui/pagination.tsx +106 -0
  67. package/templates/saas/src/client/components/ui/popover.tsx +42 -0
  68. package/templates/saas/src/client/components/ui/progress.tsx +26 -0
  69. package/templates/saas/src/client/components/ui/radio-group.tsx +45 -0
  70. package/templates/saas/src/client/components/ui/resizable.tsx +46 -0
  71. package/templates/saas/src/client/components/ui/scroll-area.tsx +56 -0
  72. package/templates/saas/src/client/components/ui/select.tsx +173 -0
  73. package/templates/saas/src/client/components/ui/separator.tsx +28 -0
  74. package/templates/saas/src/client/components/ui/sheet.tsx +128 -0
  75. package/templates/saas/src/client/components/ui/sidebar.tsx +694 -0
  76. package/templates/saas/src/client/components/ui/skeleton.tsx +13 -0
  77. package/templates/saas/src/client/components/ui/slider.tsx +58 -0
  78. package/templates/saas/src/client/components/ui/sonner.tsx +38 -0
  79. package/templates/saas/src/client/components/ui/spinner.tsx +16 -0
  80. package/templates/saas/src/client/components/ui/switch.tsx +28 -0
  81. package/templates/saas/src/client/components/ui/table.tsx +90 -0
  82. package/templates/saas/src/client/components/ui/tabs.tsx +54 -0
  83. package/templates/saas/src/client/components/ui/textarea.tsx +18 -0
  84. package/templates/saas/src/client/components/ui/toggle-group.tsx +80 -0
  85. package/templates/saas/src/client/components/ui/toggle.tsx +44 -0
  86. package/templates/saas/src/client/components/ui/tooltip.tsx +57 -0
  87. package/templates/saas/src/client/hooks/use-mobile.ts +19 -0
  88. package/templates/saas/src/client/hooks/useAuth.ts +14 -0
  89. package/templates/saas/src/client/hooks/useSubscription.ts +86 -0
  90. package/templates/saas/src/client/index.css +165 -0
  91. package/templates/saas/src/client/lib/auth-client.ts +7 -0
  92. package/templates/saas/src/client/lib/plans.ts +82 -0
  93. package/templates/saas/src/client/lib/utils.ts +6 -0
  94. package/templates/saas/src/client/main.tsx +15 -0
  95. package/templates/saas/src/client/pages/DashboardPage.tsx +394 -0
  96. package/templates/saas/src/client/pages/ForgotPasswordPage.tsx +153 -0
  97. package/templates/saas/src/client/pages/HomePage.tsx +285 -0
  98. package/templates/saas/src/client/pages/LoginPage.tsx +169 -0
  99. package/templates/saas/src/client/pages/PricingPage.tsx +467 -0
  100. package/templates/saas/src/client/pages/ResetPasswordPage.tsx +200 -0
  101. package/templates/saas/src/client/pages/SignupPage.tsx +192 -0
  102. package/templates/saas/src/index.ts +208 -0
  103. package/templates/saas/tsconfig.json +18 -0
  104. package/templates/saas/vite.config.ts +14 -0
  105. package/templates/saas/wrangler.jsonc +20 -0
@@ -0,0 +1,326 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as RechartsPrimitive from "recharts";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ // Format: { THEME_NAME: CSS_SELECTOR }
9
+ const THEMES = { light: "", dark: ".dark" } as const;
10
+
11
+ export type ChartConfig = {
12
+ [k in string]: {
13
+ label?: React.ReactNode;
14
+ icon?: React.ComponentType;
15
+ } & (
16
+ | { color?: string; theme?: never }
17
+ | { color?: never; theme: Record<keyof typeof THEMES, string> }
18
+ );
19
+ };
20
+
21
+ type ChartContextProps = {
22
+ config: ChartConfig;
23
+ };
24
+
25
+ const ChartContext = React.createContext<ChartContextProps | null>(null);
26
+
27
+ function useChart() {
28
+ const context = React.useContext(ChartContext);
29
+
30
+ if (!context) {
31
+ throw new Error("useChart must be used within a <ChartContainer />");
32
+ }
33
+
34
+ return context;
35
+ }
36
+
37
+ function ChartContainer({
38
+ id,
39
+ className,
40
+ children,
41
+ config,
42
+ ...props
43
+ }: React.ComponentProps<"div"> & {
44
+ config: ChartConfig;
45
+ children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>["children"];
46
+ }) {
47
+ const uniqueId = React.useId();
48
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
49
+
50
+ return (
51
+ <ChartContext.Provider value={{ config }}>
52
+ <div
53
+ data-slot="chart"
54
+ data-chart={chartId}
55
+ className={cn(
56
+ "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
57
+ className,
58
+ )}
59
+ {...props}
60
+ >
61
+ <ChartStyle id={chartId} config={config} />
62
+ <RechartsPrimitive.ResponsiveContainer>{children}</RechartsPrimitive.ResponsiveContainer>
63
+ </div>
64
+ </ChartContext.Provider>
65
+ );
66
+ }
67
+
68
+ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
69
+ const colorConfig = Object.entries(config).filter(([, config]) => config.theme || config.color);
70
+
71
+ if (!colorConfig.length) {
72
+ return null;
73
+ }
74
+
75
+ return (
76
+ <style
77
+ dangerouslySetInnerHTML={{
78
+ __html: Object.entries(THEMES)
79
+ .map(
80
+ ([theme, prefix]) => `
81
+ ${prefix} [data-chart=${id}] {
82
+ ${colorConfig
83
+ .map(([key, itemConfig]) => {
84
+ const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color;
85
+ return color ? ` --color-${key}: ${color};` : null;
86
+ })
87
+ .join("\n")}
88
+ }
89
+ `,
90
+ )
91
+ .join("\n"),
92
+ }}
93
+ />
94
+ );
95
+ };
96
+
97
+ const ChartTooltip = RechartsPrimitive.Tooltip;
98
+
99
+ function ChartTooltipContent({
100
+ active,
101
+ payload,
102
+ className,
103
+ indicator = "dot",
104
+ hideLabel = false,
105
+ hideIndicator = false,
106
+ label,
107
+ labelFormatter,
108
+ labelClassName,
109
+ formatter,
110
+ color,
111
+ nameKey,
112
+ labelKey,
113
+ }: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
114
+ React.ComponentProps<"div"> & {
115
+ hideLabel?: boolean;
116
+ hideIndicator?: boolean;
117
+ indicator?: "line" | "dot" | "dashed";
118
+ nameKey?: string;
119
+ labelKey?: string;
120
+ }) {
121
+ const { config } = useChart();
122
+
123
+ const tooltipLabel = React.useMemo(() => {
124
+ if (hideLabel || !payload?.length) {
125
+ return null;
126
+ }
127
+
128
+ const [item] = payload;
129
+ const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
130
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
131
+ const value =
132
+ !labelKey && typeof label === "string"
133
+ ? config[label as keyof typeof config]?.label || label
134
+ : itemConfig?.label;
135
+
136
+ if (labelFormatter) {
137
+ return (
138
+ <div className={cn("font-medium", labelClassName)}>{labelFormatter(value, payload)}</div>
139
+ );
140
+ }
141
+
142
+ if (!value) {
143
+ return null;
144
+ }
145
+
146
+ return <div className={cn("font-medium", labelClassName)}>{value}</div>;
147
+ }, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);
148
+
149
+ if (!active || !payload?.length) {
150
+ return null;
151
+ }
152
+
153
+ const nestLabel = payload.length === 1 && indicator !== "dot";
154
+
155
+ return (
156
+ <div
157
+ className={cn(
158
+ "border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
159
+ className,
160
+ )}
161
+ >
162
+ {!nestLabel ? tooltipLabel : null}
163
+ <div className="grid gap-1.5">
164
+ {payload
165
+ .filter((item) => item.type !== "none")
166
+ .map((item, index) => {
167
+ const key = `${nameKey || item.name || item.dataKey || "value"}`;
168
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
169
+ const indicatorColor = color || item.payload.fill || item.color;
170
+
171
+ return (
172
+ <div
173
+ key={item.dataKey}
174
+ className={cn(
175
+ "[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
176
+ indicator === "dot" && "items-center",
177
+ )}
178
+ >
179
+ {formatter && item?.value !== undefined && item.name ? (
180
+ formatter(item.value, item.name, item, index, item.payload)
181
+ ) : (
182
+ <>
183
+ {itemConfig?.icon ? (
184
+ <itemConfig.icon />
185
+ ) : (
186
+ !hideIndicator && (
187
+ <div
188
+ className={cn(
189
+ "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
190
+ {
191
+ "h-2.5 w-2.5": indicator === "dot",
192
+ "w-1": indicator === "line",
193
+ "w-0 border-[1.5px] border-dashed bg-transparent":
194
+ indicator === "dashed",
195
+ "my-0.5": nestLabel && indicator === "dashed",
196
+ },
197
+ )}
198
+ style={
199
+ {
200
+ "--color-bg": indicatorColor,
201
+ "--color-border": indicatorColor,
202
+ } as React.CSSProperties
203
+ }
204
+ />
205
+ )
206
+ )}
207
+ <div
208
+ className={cn(
209
+ "flex flex-1 justify-between leading-none",
210
+ nestLabel ? "items-end" : "items-center",
211
+ )}
212
+ >
213
+ <div className="grid gap-1.5">
214
+ {nestLabel ? tooltipLabel : null}
215
+ <span className="text-muted-foreground">
216
+ {itemConfig?.label || item.name}
217
+ </span>
218
+ </div>
219
+ {item.value && (
220
+ <span className="text-foreground font-mono font-medium tabular-nums">
221
+ {item.value.toLocaleString()}
222
+ </span>
223
+ )}
224
+ </div>
225
+ </>
226
+ )}
227
+ </div>
228
+ );
229
+ })}
230
+ </div>
231
+ </div>
232
+ );
233
+ }
234
+
235
+ const ChartLegend = RechartsPrimitive.Legend;
236
+
237
+ function ChartLegendContent({
238
+ className,
239
+ hideIcon = false,
240
+ payload,
241
+ verticalAlign = "bottom",
242
+ nameKey,
243
+ }: React.ComponentProps<"div"> &
244
+ Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
245
+ hideIcon?: boolean;
246
+ nameKey?: string;
247
+ }) {
248
+ const { config } = useChart();
249
+
250
+ if (!payload?.length) {
251
+ return null;
252
+ }
253
+
254
+ return (
255
+ <div
256
+ className={cn(
257
+ "flex items-center justify-center gap-4",
258
+ verticalAlign === "top" ? "pb-3" : "pt-3",
259
+ className,
260
+ )}
261
+ >
262
+ {payload
263
+ .filter((item) => item.type !== "none")
264
+ .map((item) => {
265
+ const key = `${nameKey || item.dataKey || "value"}`;
266
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
267
+
268
+ return (
269
+ <div
270
+ key={item.value}
271
+ className={cn(
272
+ "[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3",
273
+ )}
274
+ >
275
+ {itemConfig?.icon && !hideIcon ? (
276
+ <itemConfig.icon />
277
+ ) : (
278
+ <div
279
+ className="h-2 w-2 shrink-0 rounded-[2px]"
280
+ style={{
281
+ backgroundColor: item.color,
282
+ }}
283
+ />
284
+ )}
285
+ {itemConfig?.label}
286
+ </div>
287
+ );
288
+ })}
289
+ </div>
290
+ );
291
+ }
292
+
293
+ // Helper to extract item config from a payload.
294
+ function getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key: string) {
295
+ if (typeof payload !== "object" || payload === null) {
296
+ return undefined;
297
+ }
298
+
299
+ const payloadPayload =
300
+ "payload" in payload && typeof payload.payload === "object" && payload.payload !== null
301
+ ? payload.payload
302
+ : undefined;
303
+
304
+ let configLabelKey: string = key;
305
+
306
+ if (key in payload && typeof payload[key as keyof typeof payload] === "string") {
307
+ configLabelKey = payload[key as keyof typeof payload] as string;
308
+ } else if (
309
+ payloadPayload &&
310
+ key in payloadPayload &&
311
+ typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
312
+ ) {
313
+ configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string;
314
+ }
315
+
316
+ return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];
317
+ }
318
+
319
+ export {
320
+ ChartContainer,
321
+ ChartTooltip,
322
+ ChartTooltipContent,
323
+ ChartLegend,
324
+ ChartLegendContent,
325
+ ChartStyle,
326
+ };
@@ -0,0 +1,29 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
5
+ import { CheckIcon } from "lucide-react";
6
+
7
+ import { cn } from "@/lib/utils";
8
+
9
+ function Checkbox({ className, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
10
+ return (
11
+ <CheckboxPrimitive.Root
12
+ data-slot="checkbox"
13
+ className={cn(
14
+ "peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
15
+ className,
16
+ )}
17
+ {...props}
18
+ >
19
+ <CheckboxPrimitive.Indicator
20
+ data-slot="checkbox-indicator"
21
+ className="grid place-content-center text-current transition-none"
22
+ >
23
+ <CheckIcon className="size-3.5" />
24
+ </CheckboxPrimitive.Indicator>
25
+ </CheckboxPrimitive.Root>
26
+ );
27
+ }
28
+
29
+ export { Checkbox };
@@ -0,0 +1,19 @@
1
+ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
2
+
3
+ function Collapsible({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
4
+ return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
5
+ }
6
+
7
+ function CollapsibleTrigger({
8
+ ...props
9
+ }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
10
+ return <CollapsiblePrimitive.CollapsibleTrigger data-slot="collapsible-trigger" {...props} />;
11
+ }
12
+
13
+ function CollapsibleContent({
14
+ ...props
15
+ }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
16
+ return <CollapsiblePrimitive.CollapsibleContent data-slot="collapsible-content" {...props} />;
17
+ }
18
+
19
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent };
@@ -0,0 +1,159 @@
1
+ import * as React from "react";
2
+ import { Command as CommandPrimitive } from "cmdk";
3
+ import { SearchIcon } from "lucide-react";
4
+
5
+ import { cn } from "@/lib/utils";
6
+ import {
7
+ Dialog,
8
+ DialogContent,
9
+ DialogDescription,
10
+ DialogHeader,
11
+ DialogTitle,
12
+ } from "@/components/ui/dialog";
13
+
14
+ function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {
15
+ return (
16
+ <CommandPrimitive
17
+ data-slot="command"
18
+ className={cn(
19
+ "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
20
+ className,
21
+ )}
22
+ {...props}
23
+ />
24
+ );
25
+ }
26
+
27
+ function CommandDialog({
28
+ title = "Command Palette",
29
+ description = "Search for a command to run...",
30
+ children,
31
+ className,
32
+ showCloseButton = true,
33
+ ...props
34
+ }: React.ComponentProps<typeof Dialog> & {
35
+ title?: string;
36
+ description?: string;
37
+ className?: string;
38
+ showCloseButton?: boolean;
39
+ }) {
40
+ return (
41
+ <Dialog {...props}>
42
+ <DialogHeader className="sr-only">
43
+ <DialogTitle>{title}</DialogTitle>
44
+ <DialogDescription>{description}</DialogDescription>
45
+ </DialogHeader>
46
+ <DialogContent
47
+ className={cn("overflow-hidden p-0", className)}
48
+ showCloseButton={showCloseButton}
49
+ >
50
+ <Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
51
+ {children}
52
+ </Command>
53
+ </DialogContent>
54
+ </Dialog>
55
+ );
56
+ }
57
+
58
+ function CommandInput({
59
+ className,
60
+ ...props
61
+ }: React.ComponentProps<typeof CommandPrimitive.Input>) {
62
+ return (
63
+ <div data-slot="command-input-wrapper" className="flex h-9 items-center gap-2 border-b px-3">
64
+ <SearchIcon className="size-4 shrink-0 opacity-50" />
65
+ <CommandPrimitive.Input
66
+ data-slot="command-input"
67
+ className={cn(
68
+ "placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
69
+ className,
70
+ )}
71
+ {...props}
72
+ />
73
+ </div>
74
+ );
75
+ }
76
+
77
+ function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
78
+ return (
79
+ <CommandPrimitive.List
80
+ data-slot="command-list"
81
+ className={cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className)}
82
+ {...props}
83
+ />
84
+ );
85
+ }
86
+
87
+ function CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
88
+ return (
89
+ <CommandPrimitive.Empty
90
+ data-slot="command-empty"
91
+ className="py-6 text-center text-sm"
92
+ {...props}
93
+ />
94
+ );
95
+ }
96
+
97
+ function CommandGroup({
98
+ className,
99
+ ...props
100
+ }: React.ComponentProps<typeof CommandPrimitive.Group>) {
101
+ return (
102
+ <CommandPrimitive.Group
103
+ data-slot="command-group"
104
+ className={cn(
105
+ "text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
106
+ className,
107
+ )}
108
+ {...props}
109
+ />
110
+ );
111
+ }
112
+
113
+ function CommandSeparator({
114
+ className,
115
+ ...props
116
+ }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
117
+ return (
118
+ <CommandPrimitive.Separator
119
+ data-slot="command-separator"
120
+ className={cn("bg-border -mx-1 h-px", className)}
121
+ {...props}
122
+ />
123
+ );
124
+ }
125
+
126
+ function CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {
127
+ return (
128
+ <CommandPrimitive.Item
129
+ data-slot="command-item"
130
+ className={cn(
131
+ "data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
132
+ className,
133
+ )}
134
+ {...props}
135
+ />
136
+ );
137
+ }
138
+
139
+ function CommandShortcut({ className, ...props }: React.ComponentProps<"span">) {
140
+ return (
141
+ <span
142
+ data-slot="command-shortcut"
143
+ className={cn("text-muted-foreground ml-auto text-xs tracking-widest", className)}
144
+ {...props}
145
+ />
146
+ );
147
+ }
148
+
149
+ export {
150
+ Command,
151
+ CommandDialog,
152
+ CommandInput,
153
+ CommandList,
154
+ CommandEmpty,
155
+ CommandGroup,
156
+ CommandItem,
157
+ CommandShortcut,
158
+ CommandSeparator,
159
+ };