@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,68 @@
1
+ import * as React from "react";
2
+ import { OTPInput, OTPInputContext } from "input-otp";
3
+ import { MinusIcon } from "lucide-react";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ function InputOTP({
8
+ className,
9
+ containerClassName,
10
+ ...props
11
+ }: React.ComponentProps<typeof OTPInput> & {
12
+ containerClassName?: string;
13
+ }) {
14
+ return (
15
+ <OTPInput
16
+ data-slot="input-otp"
17
+ containerClassName={cn("flex items-center gap-2 has-disabled:opacity-50", containerClassName)}
18
+ className={cn("disabled:cursor-not-allowed", className)}
19
+ {...props}
20
+ />
21
+ );
22
+ }
23
+
24
+ function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
25
+ return (
26
+ <div data-slot="input-otp-group" className={cn("flex items-center", className)} {...props} />
27
+ );
28
+ }
29
+
30
+ function InputOTPSlot({
31
+ index,
32
+ className,
33
+ ...props
34
+ }: React.ComponentProps<"div"> & {
35
+ index: number;
36
+ }) {
37
+ const inputOTPContext = React.useContext(OTPInputContext);
38
+ const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
39
+
40
+ return (
41
+ <div
42
+ data-slot="input-otp-slot"
43
+ data-active={isActive}
44
+ className={cn(
45
+ "data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
46
+ className,
47
+ )}
48
+ {...props}
49
+ >
50
+ {char}
51
+ {hasFakeCaret && (
52
+ <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
53
+ <div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" />
54
+ </div>
55
+ )}
56
+ </div>
57
+ );
58
+ }
59
+
60
+ function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
61
+ return (
62
+ <div data-slot="input-otp-separator" role="separator" {...props}>
63
+ <MinusIcon />
64
+ </div>
65
+ );
66
+ }
67
+
68
+ export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "@/lib/utils";
4
+
5
+ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6
+ return (
7
+ <input
8
+ type={type}
9
+ data-slot="input"
10
+ className={cn(
11
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
12
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
13
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
14
+ className,
15
+ )}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+
21
+ export { Input };
@@ -0,0 +1,172 @@
1
+ import * as React from "react";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+
5
+ import { cn } from "@/lib/utils";
6
+ import { Separator } from "@/components/ui/separator";
7
+
8
+ function ItemGroup({ className, ...props }: React.ComponentProps<"div">) {
9
+ return (
10
+ <div
11
+ role="list"
12
+ data-slot="item-group"
13
+ className={cn("group/item-group flex flex-col", className)}
14
+ {...props}
15
+ />
16
+ );
17
+ }
18
+
19
+ function ItemSeparator({ className, ...props }: React.ComponentProps<typeof Separator>) {
20
+ return (
21
+ <Separator
22
+ data-slot="item-separator"
23
+ orientation="horizontal"
24
+ className={cn("my-0", className)}
25
+ {...props}
26
+ />
27
+ );
28
+ }
29
+
30
+ const itemVariants = cva(
31
+ "group/item flex items-center border border-transparent text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
32
+ {
33
+ variants: {
34
+ variant: {
35
+ default: "bg-transparent",
36
+ outline: "border-border",
37
+ muted: "bg-muted/50",
38
+ },
39
+ size: {
40
+ default: "p-4 gap-4 ",
41
+ sm: "py-3 px-4 gap-2.5",
42
+ },
43
+ },
44
+ defaultVariants: {
45
+ variant: "default",
46
+ size: "default",
47
+ },
48
+ },
49
+ );
50
+
51
+ function Item({
52
+ className,
53
+ variant = "default",
54
+ size = "default",
55
+ asChild = false,
56
+ ...props
57
+ }: React.ComponentProps<"div"> & VariantProps<typeof itemVariants> & { asChild?: boolean }) {
58
+ const Comp = asChild ? Slot : "div";
59
+ return (
60
+ <Comp
61
+ data-slot="item"
62
+ data-variant={variant}
63
+ data-size={size}
64
+ className={cn(itemVariants({ variant, size, className }))}
65
+ {...props}
66
+ />
67
+ );
68
+ }
69
+
70
+ const itemMediaVariants = cva(
71
+ "flex shrink-0 items-center justify-center gap-2 group-has-[[data-slot=item-description]]/item:self-start [&_svg]:pointer-events-none group-has-[[data-slot=item-description]]/item:translate-y-0.5",
72
+ {
73
+ variants: {
74
+ variant: {
75
+ default: "bg-transparent",
76
+ icon: "size-8 border rounded-sm bg-muted [&_svg:not([class*='size-'])]:size-4",
77
+ image: "size-10 rounded-sm overflow-hidden [&_img]:size-full [&_img]:object-cover",
78
+ },
79
+ },
80
+ defaultVariants: {
81
+ variant: "default",
82
+ },
83
+ },
84
+ );
85
+
86
+ function ItemMedia({
87
+ className,
88
+ variant = "default",
89
+ ...props
90
+ }: React.ComponentProps<"div"> & VariantProps<typeof itemMediaVariants>) {
91
+ return (
92
+ <div
93
+ data-slot="item-media"
94
+ data-variant={variant}
95
+ className={cn(itemMediaVariants({ variant, className }))}
96
+ {...props}
97
+ />
98
+ );
99
+ }
100
+
101
+ function ItemContent({ className, ...props }: React.ComponentProps<"div">) {
102
+ return (
103
+ <div
104
+ data-slot="item-content"
105
+ className={cn("flex flex-1 flex-col gap-1 [&+[data-slot=item-content]]:flex-none", className)}
106
+ {...props}
107
+ />
108
+ );
109
+ }
110
+
111
+ function ItemTitle({ className, ...props }: React.ComponentProps<"div">) {
112
+ return (
113
+ <div
114
+ data-slot="item-title"
115
+ className={cn("flex w-fit items-center gap-2 text-sm leading-snug font-medium", className)}
116
+ {...props}
117
+ />
118
+ );
119
+ }
120
+
121
+ function ItemDescription({ className, ...props }: React.ComponentProps<"p">) {
122
+ return (
123
+ <p
124
+ data-slot="item-description"
125
+ className={cn(
126
+ "text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance",
127
+ "[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
128
+ className,
129
+ )}
130
+ {...props}
131
+ />
132
+ );
133
+ }
134
+
135
+ function ItemActions({ className, ...props }: React.ComponentProps<"div">) {
136
+ return (
137
+ <div data-slot="item-actions" className={cn("flex items-center gap-2", className)} {...props} />
138
+ );
139
+ }
140
+
141
+ function ItemHeader({ className, ...props }: React.ComponentProps<"div">) {
142
+ return (
143
+ <div
144
+ data-slot="item-header"
145
+ className={cn("flex basis-full items-center justify-between gap-2", className)}
146
+ {...props}
147
+ />
148
+ );
149
+ }
150
+
151
+ function ItemFooter({ className, ...props }: React.ComponentProps<"div">) {
152
+ return (
153
+ <div
154
+ data-slot="item-footer"
155
+ className={cn("flex basis-full items-center justify-between gap-2", className)}
156
+ {...props}
157
+ />
158
+ );
159
+ }
160
+
161
+ export {
162
+ Item,
163
+ ItemMedia,
164
+ ItemContent,
165
+ ItemActions,
166
+ ItemGroup,
167
+ ItemSeparator,
168
+ ItemTitle,
169
+ ItemDescription,
170
+ ItemHeader,
171
+ ItemFooter,
172
+ };
@@ -0,0 +1,28 @@
1
+ import { cn } from "@/lib/utils";
2
+
3
+ function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
4
+ return (
5
+ <kbd
6
+ data-slot="kbd"
7
+ className={cn(
8
+ "bg-muted text-muted-foreground pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm px-1 font-sans text-xs font-medium select-none",
9
+ "[&_svg:not([class*='size-'])]:size-3",
10
+ "[[data-slot=tooltip-content]_&]:bg-background/20 [[data-slot=tooltip-content]_&]:text-background dark:[[data-slot=tooltip-content]_&]:bg-background/10",
11
+ className,
12
+ )}
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
19
+ return (
20
+ <kbd
21
+ data-slot="kbd-group"
22
+ className={cn("inline-flex items-center gap-1", className)}
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ export { Kbd, KbdGroup };
@@ -0,0 +1,21 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as LabelPrimitive from "@radix-ui/react-label";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ function Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {
9
+ return (
10
+ <LabelPrimitive.Root
11
+ data-slot="label"
12
+ className={cn(
13
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
14
+ className,
15
+ )}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+
21
+ export { Label };
@@ -0,0 +1,250 @@
1
+ import * as React from "react";
2
+ import * as MenubarPrimitive from "@radix-ui/react-menubar";
3
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ function Menubar({ className, ...props }: React.ComponentProps<typeof MenubarPrimitive.Root>) {
8
+ return (
9
+ <MenubarPrimitive.Root
10
+ data-slot="menubar"
11
+ className={cn(
12
+ "bg-background flex h-9 items-center gap-1 rounded-md border p-1 shadow-xs",
13
+ className,
14
+ )}
15
+ {...props}
16
+ />
17
+ );
18
+ }
19
+
20
+ function MenubarMenu({ ...props }: React.ComponentProps<typeof MenubarPrimitive.Menu>) {
21
+ return <MenubarPrimitive.Menu data-slot="menubar-menu" {...props} />;
22
+ }
23
+
24
+ function MenubarGroup({ ...props }: React.ComponentProps<typeof MenubarPrimitive.Group>) {
25
+ return <MenubarPrimitive.Group data-slot="menubar-group" {...props} />;
26
+ }
27
+
28
+ function MenubarPortal({ ...props }: React.ComponentProps<typeof MenubarPrimitive.Portal>) {
29
+ return <MenubarPrimitive.Portal data-slot="menubar-portal" {...props} />;
30
+ }
31
+
32
+ function MenubarRadioGroup({ ...props }: React.ComponentProps<typeof MenubarPrimitive.RadioGroup>) {
33
+ return <MenubarPrimitive.RadioGroup data-slot="menubar-radio-group" {...props} />;
34
+ }
35
+
36
+ function MenubarTrigger({
37
+ className,
38
+ ...props
39
+ }: React.ComponentProps<typeof MenubarPrimitive.Trigger>) {
40
+ return (
41
+ <MenubarPrimitive.Trigger
42
+ data-slot="menubar-trigger"
43
+ className={cn(
44
+ "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex items-center rounded-sm px-2 py-1 text-sm font-medium outline-hidden select-none",
45
+ className,
46
+ )}
47
+ {...props}
48
+ />
49
+ );
50
+ }
51
+
52
+ function MenubarContent({
53
+ className,
54
+ align = "start",
55
+ alignOffset = -4,
56
+ sideOffset = 8,
57
+ ...props
58
+ }: React.ComponentProps<typeof MenubarPrimitive.Content>) {
59
+ return (
60
+ <MenubarPortal>
61
+ <MenubarPrimitive.Content
62
+ data-slot="menubar-content"
63
+ align={align}
64
+ alignOffset={alignOffset}
65
+ sideOffset={sideOffset}
66
+ className={cn(
67
+ "bg-popover text-popover-foreground data-[state=open]:animate-in 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 z-50 min-w-[12rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-md",
68
+ className,
69
+ )}
70
+ {...props}
71
+ />
72
+ </MenubarPortal>
73
+ );
74
+ }
75
+
76
+ function MenubarItem({
77
+ className,
78
+ inset,
79
+ variant = "default",
80
+ ...props
81
+ }: React.ComponentProps<typeof MenubarPrimitive.Item> & {
82
+ inset?: boolean;
83
+ variant?: "default" | "destructive";
84
+ }) {
85
+ return (
86
+ <MenubarPrimitive.Item
87
+ data-slot="menubar-item"
88
+ data-inset={inset}
89
+ data-variant={variant}
90
+ className={cn(
91
+ "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_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]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
92
+ className,
93
+ )}
94
+ {...props}
95
+ />
96
+ );
97
+ }
98
+
99
+ function MenubarCheckboxItem({
100
+ className,
101
+ children,
102
+ checked,
103
+ ...props
104
+ }: React.ComponentProps<typeof MenubarPrimitive.CheckboxItem>) {
105
+ return (
106
+ <MenubarPrimitive.CheckboxItem
107
+ data-slot="menubar-checkbox-item"
108
+ className={cn(
109
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
110
+ className,
111
+ )}
112
+ checked={checked}
113
+ {...props}
114
+ >
115
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
116
+ <MenubarPrimitive.ItemIndicator>
117
+ <CheckIcon className="size-4" />
118
+ </MenubarPrimitive.ItemIndicator>
119
+ </span>
120
+ {children}
121
+ </MenubarPrimitive.CheckboxItem>
122
+ );
123
+ }
124
+
125
+ function MenubarRadioItem({
126
+ className,
127
+ children,
128
+ ...props
129
+ }: React.ComponentProps<typeof MenubarPrimitive.RadioItem>) {
130
+ return (
131
+ <MenubarPrimitive.RadioItem
132
+ data-slot="menubar-radio-item"
133
+ className={cn(
134
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
135
+ className,
136
+ )}
137
+ {...props}
138
+ >
139
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
140
+ <MenubarPrimitive.ItemIndicator>
141
+ <CircleIcon className="size-2 fill-current" />
142
+ </MenubarPrimitive.ItemIndicator>
143
+ </span>
144
+ {children}
145
+ </MenubarPrimitive.RadioItem>
146
+ );
147
+ }
148
+
149
+ function MenubarLabel({
150
+ className,
151
+ inset,
152
+ ...props
153
+ }: React.ComponentProps<typeof MenubarPrimitive.Label> & {
154
+ inset?: boolean;
155
+ }) {
156
+ return (
157
+ <MenubarPrimitive.Label
158
+ data-slot="menubar-label"
159
+ data-inset={inset}
160
+ className={cn("px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className)}
161
+ {...props}
162
+ />
163
+ );
164
+ }
165
+
166
+ function MenubarSeparator({
167
+ className,
168
+ ...props
169
+ }: React.ComponentProps<typeof MenubarPrimitive.Separator>) {
170
+ return (
171
+ <MenubarPrimitive.Separator
172
+ data-slot="menubar-separator"
173
+ className={cn("bg-border -mx-1 my-1 h-px", className)}
174
+ {...props}
175
+ />
176
+ );
177
+ }
178
+
179
+ function MenubarShortcut({ className, ...props }: React.ComponentProps<"span">) {
180
+ return (
181
+ <span
182
+ data-slot="menubar-shortcut"
183
+ className={cn("text-muted-foreground ml-auto text-xs tracking-widest", className)}
184
+ {...props}
185
+ />
186
+ );
187
+ }
188
+
189
+ function MenubarSub({ ...props }: React.ComponentProps<typeof MenubarPrimitive.Sub>) {
190
+ return <MenubarPrimitive.Sub data-slot="menubar-sub" {...props} />;
191
+ }
192
+
193
+ function MenubarSubTrigger({
194
+ className,
195
+ inset,
196
+ children,
197
+ ...props
198
+ }: React.ComponentProps<typeof MenubarPrimitive.SubTrigger> & {
199
+ inset?: boolean;
200
+ }) {
201
+ return (
202
+ <MenubarPrimitive.SubTrigger
203
+ data-slot="menubar-sub-trigger"
204
+ data-inset={inset}
205
+ className={cn(
206
+ "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[inset]:pl-8",
207
+ className,
208
+ )}
209
+ {...props}
210
+ >
211
+ {children}
212
+ <ChevronRightIcon className="ml-auto h-4 w-4" />
213
+ </MenubarPrimitive.SubTrigger>
214
+ );
215
+ }
216
+
217
+ function MenubarSubContent({
218
+ className,
219
+ ...props
220
+ }: React.ComponentProps<typeof MenubarPrimitive.SubContent>) {
221
+ return (
222
+ <MenubarPrimitive.SubContent
223
+ data-slot="menubar-sub-content"
224
+ className={cn(
225
+ "bg-popover text-popover-foreground 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 z-50 min-w-[8rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
226
+ className,
227
+ )}
228
+ {...props}
229
+ />
230
+ );
231
+ }
232
+
233
+ export {
234
+ Menubar,
235
+ MenubarPortal,
236
+ MenubarMenu,
237
+ MenubarTrigger,
238
+ MenubarContent,
239
+ MenubarGroup,
240
+ MenubarSeparator,
241
+ MenubarLabel,
242
+ MenubarItem,
243
+ MenubarShortcut,
244
+ MenubarCheckboxItem,
245
+ MenubarRadioGroup,
246
+ MenubarRadioItem,
247
+ MenubarSub,
248
+ MenubarSubTrigger,
249
+ MenubarSubContent,
250
+ };