abhishek-portfolio-template 1.0.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 (101) hide show
  1. package/README.md +59 -0
  2. package/bin/cli.js +54 -0
  3. package/package.json +27 -0
  4. package/template/components.json +22 -0
  5. package/template/next.config.ts +79 -0
  6. package/template/package.json +43 -0
  7. package/template/postcss.config.js +6 -0
  8. package/template/public/BoliviaSignature-ZpWnz.ttf +0 -0
  9. package/template/public/Gemini_Generated_Image_xc97toxc97toxc97.png +0 -0
  10. package/template/public/Hendrigo.otf +0 -0
  11. package/template/public/audiomass-output.mp3 +0 -0
  12. package/template/public/file.svg +1 -0
  13. package/template/public/globe.svg +1 -0
  14. package/template/public/googlec77e59474f5a09cb.html +1 -0
  15. package/template/public/icon-192x192.png +0 -0
  16. package/template/public/icon-512x512.png +0 -0
  17. package/template/public/next.svg +1 -0
  18. package/template/public/paper sound .mpeg +0 -0
  19. package/template/public/removebg.png +0 -0
  20. package/template/public/resume.pdf +0 -0
  21. package/template/public/sw.js +1 -0
  22. package/template/public/swe-worker-5c72df51bb1f6ee0.js +1 -0
  23. package/template/public/vercel.svg +1 -0
  24. package/template/public/window.svg +1 -0
  25. package/template/public/workbox-f1770938.js +1 -0
  26. package/template/src/app/about/page.tsx +91 -0
  27. package/template/src/app/actions/optimize-text.ts +54 -0
  28. package/template/src/app/gaming/page.tsx +308 -0
  29. package/template/src/app/github/[username]/page.tsx +97 -0
  30. package/template/src/app/globals.css +321 -0
  31. package/template/src/app/layout.tsx +39 -0
  32. package/template/src/app/manifest.ts +25 -0
  33. package/template/src/app/not-found.tsx +16 -0
  34. package/template/src/app/page.tsx +28 -0
  35. package/template/src/app/robots.ts +12 -0
  36. package/template/src/app/sitemap.ts +38 -0
  37. package/template/src/app/template.tsx +5 -0
  38. package/template/src/app/works/[slug]/page.tsx +50 -0
  39. package/template/src/app/works/client.tsx +44 -0
  40. package/template/src/app/works/page.tsx +24 -0
  41. package/template/src/components/about/about-client.tsx +259 -0
  42. package/template/src/components/home/bento-gallery.tsx +52 -0
  43. package/template/src/components/home/contact-section.tsx +34 -0
  44. package/template/src/components/home/craft-card.tsx +18 -0
  45. package/template/src/components/home/featured-projects.tsx +186 -0
  46. package/template/src/components/home/focus-card.tsx +171 -0
  47. package/template/src/components/home/identity-card.tsx +45 -0
  48. package/template/src/components/home/philosophy-card.tsx +104 -0
  49. package/template/src/components/home/skills-in-motion.tsx +109 -0
  50. package/template/src/components/home/tech-stack-marquee.tsx +56 -0
  51. package/template/src/components/ui/3d-folder.tsx +569 -0
  52. package/template/src/components/ui/avatar.tsx +50 -0
  53. package/template/src/components/ui/badge.tsx +36 -0
  54. package/template/src/components/ui/basic-avatar.tsx +12 -0
  55. package/template/src/components/ui/button.tsx +117 -0
  56. package/template/src/components/ui/clipboard-secret.tsx +39 -0
  57. package/template/src/components/ui/command-menu.tsx +519 -0
  58. package/template/src/components/ui/command-palette.tsx +152 -0
  59. package/template/src/components/ui/consciousness-mode.tsx +200 -0
  60. package/template/src/components/ui/copy-code-button.tsx +135 -0
  61. package/template/src/components/ui/display-cards.tsx +70 -0
  62. package/template/src/components/ui/dotted-map.tsx +128 -0
  63. package/template/src/components/ui/dropdown-menu.tsx +200 -0
  64. package/template/src/components/ui/emoji-rating.tsx +123 -0
  65. package/template/src/components/ui/exit-message.tsx +50 -0
  66. package/template/src/components/ui/image-zoom-overlay.tsx +178 -0
  67. package/template/src/components/ui/input-otp.tsx +71 -0
  68. package/template/src/components/ui/kbd.tsx +87 -0
  69. package/template/src/components/ui/location-tag.tsx +232 -0
  70. package/template/src/components/ui/minimal-testimonial.tsx +97 -0
  71. package/template/src/components/ui/mobile-menu.tsx +191 -0
  72. package/template/src/components/ui/navbar.tsx +148 -0
  73. package/template/src/components/ui/page-transition.tsx +24 -0
  74. package/template/src/components/ui/pixeleted-404-not-found.tsx +110 -0
  75. package/template/src/components/ui/preloader-wrapper.tsx +102 -0
  76. package/template/src/components/ui/preloader.tsx +104 -0
  77. package/template/src/components/ui/project-contributors.tsx +57 -0
  78. package/template/src/components/ui/scroll-area.tsx +117 -0
  79. package/template/src/components/ui/signature.tsx +173 -0
  80. package/template/src/components/ui/smooth-scroll.tsx +31 -0
  81. package/template/src/components/ui/social-icons.tsx +103 -0
  82. package/template/src/components/ui/social-stories.tsx +394 -0
  83. package/template/src/components/ui/sound-constants.ts +1 -0
  84. package/template/src/components/ui/text-explode.tsx +188 -0
  85. package/template/src/components/ui/toast.tsx +80 -0
  86. package/template/src/components/ui/tooltip.tsx +30 -0
  87. package/template/src/components/ui/user-location.tsx +151 -0
  88. package/template/src/components/ui/vertical-image-stack.tsx +345 -0
  89. package/template/src/components/works/changelog-overlay.tsx +212 -0
  90. package/template/src/components/works/currently-working-card.tsx +130 -0
  91. package/template/src/components/works/project-details-view.tsx +464 -0
  92. package/template/src/components/works/project-grid.tsx +81 -0
  93. package/template/src/fonts/BoliviaSignature-ZpWnz.ttf +0 -0
  94. package/template/src/fonts/Hendrigo.otf +0 -0
  95. package/template/src/lib/data.ts +61 -0
  96. package/template/src/lib/fonts.ts +14 -0
  97. package/template/src/lib/github.ts +15 -0
  98. package/template/src/lib/supabase.ts +11 -0
  99. package/template/src/lib/utils.ts +6 -0
  100. package/template/tailwind.config.ts +31 -0
  101. package/template/tsconfig.json +34 -0
@@ -0,0 +1,117 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Slot } from "@radix-ui/react-slot";
5
+ import { cva, type VariantProps } from "class-variance-authority";
6
+ import { cn } from "@/lib/utils";
7
+
8
+ const buttonVariants = cva(
9
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
10
+ {
11
+ variants: {
12
+ variant: {
13
+ default:
14
+ "bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-ring shadow-sm",
15
+ destructive:
16
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive shadow-sm",
17
+ outline:
18
+ "border border-border text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring shadow-sm",
19
+ secondary:
20
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80 focus-visible:ring-ring",
21
+ ghost:
22
+ "text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring",
23
+ link: "text-secondary-foreground underline-offset-4 hover:underline focus-visible:ring-ring",
24
+ },
25
+ size: {
26
+ default: "h-9 px-4 py-2",
27
+ sm: "h-8 px-3 text-xs",
28
+ lg: "h-10 px-8",
29
+ xl: "h-12 px-10 text-base",
30
+ icon: "h-9 w-9",
31
+ },
32
+ },
33
+ defaultVariants: {
34
+ variant: "default",
35
+ size: "default",
36
+ },
37
+ }
38
+ );
39
+
40
+ export type CustomButtonProps = Omit<
41
+ ButtonProps,
42
+ keyof React.ComponentProps<"button">
43
+ >;
44
+
45
+ export interface ButtonProps
46
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
47
+ VariantProps<typeof buttonVariants> {
48
+ asChild?: boolean;
49
+ loading?: boolean;
50
+ leftIcon?: React.ReactNode;
51
+ rightIcon?: React.ReactNode;
52
+ }
53
+
54
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
55
+ (
56
+ {
57
+ className,
58
+ variant,
59
+ size,
60
+ asChild = false,
61
+ loading = false,
62
+ leftIcon,
63
+ rightIcon,
64
+ children,
65
+ disabled,
66
+ ...props
67
+ },
68
+ ref
69
+ ) => {
70
+ const Comp = asChild ? Slot : "button";
71
+
72
+ const content = (
73
+ <>
74
+ {loading && (
75
+ <svg
76
+ className="animate-spin h-4 w-4"
77
+ xmlns="http://www.w3.org/2000/svg"
78
+ fill="none"
79
+ viewBox="0 0 24 24"
80
+ >
81
+ <circle
82
+ className="opacity-25"
83
+ cx="12"
84
+ cy="12"
85
+ r="10"
86
+ stroke="currentColor"
87
+ strokeWidth="4"
88
+ />
89
+ <path
90
+ className="opacity-75"
91
+ fill="currentColor"
92
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
93
+ />
94
+ </svg>
95
+ )}
96
+ {leftIcon && !loading && leftIcon}
97
+ {children}
98
+ {rightIcon && !loading && rightIcon}
99
+ </>
100
+ );
101
+
102
+ return (
103
+ <Comp
104
+ className={cn(buttonVariants({ variant, size, className }))}
105
+ ref={ref}
106
+ disabled={disabled || loading}
107
+ {...props}
108
+ >
109
+ {asChild ? children : content}
110
+ </Comp>
111
+ );
112
+ }
113
+ );
114
+
115
+ Button.displayName = "Button";
116
+
117
+ export { Button, buttonVariants };
@@ -0,0 +1,39 @@
1
+ "use client";
2
+
3
+ import { useEffect } from "react";
4
+
5
+ export const ClipboardSecret = () => {
6
+ useEffect(() => {
7
+ const handleCopy = (e: ClipboardEvent) => {
8
+ const selection = window.getSelection();
9
+ if (!selection) return;
10
+
11
+ // Get text and normalize (remove line breaks, double spaces)
12
+ const selectedText = selection.toString().replace(/\s+/g, ' ').trim().toLowerCase();
13
+
14
+ console.log("📋 Clipboard Detect:", selectedText);
15
+
16
+ // TRIGGERS
17
+ // Note: identity-card.tsx has "Product Engineer."
18
+ const triggers = ["product engineer", "abhishek"];
19
+
20
+ const isMatch = triggers.some(trigger => selectedText.includes(trigger));
21
+
22
+ if (isMatch) {
23
+ e.preventDefault();
24
+ if (e.clipboardData) {
25
+ const secret = "If you found this, we should talk.";
26
+ e.clipboardData.setData("text/plain", secret);
27
+ // Also try the navigator API as backup (though preventDefault usually handles it)
28
+ // navigator.clipboard.writeText(secret).catch(() => {});
29
+ console.log("🔒 Secret Injected");
30
+ }
31
+ }
32
+ };
33
+
34
+ document.addEventListener("copy", handleCopy);
35
+ return () => document.removeEventListener("copy", handleCopy);
36
+ }, []);
37
+
38
+ return null; // Component is logic only, no visuals
39
+ };
@@ -0,0 +1,519 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
5
+ import * as VisuallyHidden from "@radix-ui/react-visually-hidden";
6
+ import { motion } from "motion/react";
7
+ import { Search, X } from "lucide-react";
8
+ import { cn } from "@/lib/utils";
9
+ import { Kbd } from "@/components/ui/kbd";
10
+ import { ScrollArea } from "@/components/ui/scroll-area";
11
+
12
+ // Utility function to detect OS and return appropriate modifier key
13
+ const getModifierKey = () => {
14
+ if (typeof navigator === "undefined") return { key: "Ctrl", symbol: "Ctrl" };
15
+
16
+ const isMac =
17
+ navigator.platform.toUpperCase().indexOf("MAC") >= 0 ||
18
+ navigator.userAgent.toUpperCase().indexOf("MAC") >= 0;
19
+
20
+ return isMac ? { key: "cmd", symbol: "⌘" } : { key: "ctrl", symbol: "Ctrl" };
21
+ };
22
+
23
+ // Context for sharing state between components
24
+ interface CommandMenuContextType {
25
+ value: string;
26
+ setValue: (value: string) => void;
27
+ selectedIndex: number;
28
+ setSelectedIndex: (index: number) => void;
29
+ scrollType?: "auto" | "always" | "scroll" | "hover";
30
+ scrollHideDelay?: number;
31
+ }
32
+
33
+ const CommandMenuContext = React.createContext<
34
+ CommandMenuContextType | undefined
35
+ >(undefined);
36
+
37
+ const CommandMenuProvider: React.FC<{
38
+ children: React.ReactNode;
39
+ value: string;
40
+ setValue: (value: string) => void;
41
+ selectedIndex: number;
42
+ setSelectedIndex: (index: number) => void;
43
+ scrollType?: "auto" | "always" | "scroll" | "hover";
44
+ scrollHideDelay?: number;
45
+ }> = ({
46
+ children,
47
+ value,
48
+ setValue,
49
+ selectedIndex,
50
+ setSelectedIndex,
51
+ scrollType,
52
+ scrollHideDelay,
53
+ }) => (
54
+ <CommandMenuContext.Provider
55
+ value={{
56
+ value,
57
+ setValue,
58
+ selectedIndex,
59
+ setSelectedIndex,
60
+ scrollType,
61
+ scrollHideDelay,
62
+ }}
63
+ >
64
+ {children}
65
+ </CommandMenuContext.Provider>
66
+ );
67
+
68
+ const useCommandMenu = () => {
69
+ const context = React.useContext(CommandMenuContext);
70
+ if (!context) {
71
+ throw new Error("useCommandMenu must be used within CommandMenuProvider");
72
+ }
73
+ return context;
74
+ };
75
+
76
+ // Core CommandMenu component using Dialog
77
+ const CommandMenu = DialogPrimitive.Root;
78
+ const CommandMenuTrigger = DialogPrimitive.Trigger;
79
+ const CommandMenuPortal = DialogPrimitive.Portal;
80
+ const CommandMenuClose = DialogPrimitive.Close;
81
+
82
+ // Title components for accessibility
83
+ const CommandMenuTitle = React.forwardRef<
84
+ React.ElementRef<typeof DialogPrimitive.Title>,
85
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
86
+ >(({ className, ...props }, ref) => (
87
+ <DialogPrimitive.Title
88
+ ref={ref}
89
+ className={cn(
90
+ "text-lg font-semibold leading-none tracking-tight text-foreground",
91
+ className
92
+ )}
93
+ {...props}
94
+ />
95
+ ));
96
+ CommandMenuTitle.displayName = "CommandMenuTitle";
97
+
98
+ const CommandMenuDescription = React.forwardRef<
99
+ React.ElementRef<typeof DialogPrimitive.Description>,
100
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
101
+ >(({ className, ...props }, ref) => (
102
+ <DialogPrimitive.Description
103
+ ref={ref}
104
+ className={cn("text-sm text-muted-foreground", className)}
105
+ {...props}
106
+ />
107
+ ));
108
+ CommandMenuDescription.displayName = "CommandMenuDescription";
109
+
110
+ // Overlay with backdrop blur
111
+ const CommandMenuOverlay = React.forwardRef<
112
+ React.ElementRef<typeof DialogPrimitive.Overlay>,
113
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
114
+ >(({ className, ...props }, ref) => (
115
+ <DialogPrimitive.Overlay
116
+ ref={ref}
117
+ className={cn(
118
+ "fixed inset-0 z-50 bg-black/50 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",
119
+ className
120
+ )}
121
+ {...props}
122
+ />
123
+ ));
124
+ CommandMenuOverlay.displayName = "CommandMenuOverlay";
125
+
126
+ // Main content container with keyboard navigation
127
+ const CommandMenuContent = React.forwardRef<
128
+ React.ElementRef<typeof DialogPrimitive.Content>,
129
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
130
+ showShortcut?: boolean;
131
+ scrollType?: "auto" | "always" | "scroll" | "hover";
132
+ scrollHideDelay?: number;
133
+ }
134
+ >(
135
+ (
136
+ {
137
+ className,
138
+ children,
139
+ showShortcut = true,
140
+ scrollType = "hover",
141
+ scrollHideDelay = 600,
142
+ ...props
143
+ },
144
+ ref
145
+ ) => {
146
+ const [value, setValue] = React.useState("");
147
+ const [selectedIndex, setSelectedIndex] = React.useState(0);
148
+
149
+ // Keyboard navigation
150
+ React.useEffect(() => {
151
+ const handleKeyDown = (e: KeyboardEvent) => {
152
+ if (e.key === "ArrowDown") {
153
+ e.preventDefault();
154
+ // Logic will be handled by CommandMenuList
155
+ } else if (e.key === "ArrowUp") {
156
+ e.preventDefault();
157
+ // Logic will be handled by CommandMenuList
158
+ } else if (e.key === "Enter") {
159
+ e.preventDefault();
160
+ // Logic will be handled by CommandMenuItem
161
+ }
162
+ };
163
+
164
+ document.addEventListener("keydown", handleKeyDown);
165
+ return () => document.removeEventListener("keydown", handleKeyDown);
166
+ }, []);
167
+
168
+ return (
169
+ <CommandMenuPortal>
170
+ <CommandMenuOverlay />
171
+ <DialogPrimitive.Content asChild ref={ref} {...props}>
172
+ <motion.div
173
+ initial={{ opacity: 0, scale: 0.95, y: -20 }}
174
+ animate={{ opacity: 1, scale: 1, y: 0 }}
175
+ exit={{ opacity: 0, scale: 0.95, y: -20 }}
176
+ transition={{ duration: 0.2, ease: "easeOut" }}
177
+ className={cn(
178
+ "fixed left-[50%] top-[50%] z-50 w-[95%] max-w-2xl translate-x-[-50%] translate-y-[-50%]",
179
+ // GLASS EFFECT UPDATE: Heavy blur, translucent black, subtle border
180
+ "bg-[#09090b]/80 backdrop-blur-3xl border border-white/5 rounded-2xl shadow-2xl ring-1 ring-white/5",
181
+ "overflow-hidden max-h-[85vh] flex flex-col",
182
+ className
183
+ )}
184
+ >
185
+ {" "}
186
+ <CommandMenuProvider
187
+ value={value}
188
+ setValue={setValue}
189
+ selectedIndex={selectedIndex}
190
+ setSelectedIndex={setSelectedIndex}
191
+ scrollType={scrollType}
192
+ scrollHideDelay={scrollHideDelay}
193
+ >
194
+ <VisuallyHidden.Root>
195
+ <CommandMenuTitle>Command Menu</CommandMenuTitle>
196
+ </VisuallyHidden.Root>
197
+
198
+ {children}
199
+
200
+ <CommandMenuClose className="absolute right-3 top-3 rounded-lg p-1.5 text-zinc-400 hover:text-white hover:bg-white/10 focus-visible:outline-none transition-colors">
201
+ <X size={14} />
202
+ <span className="sr-only">Close</span>
203
+ </CommandMenuClose>
204
+
205
+ {showShortcut && (
206
+ <div className="absolute right-12 top-3 flex items-center justify-center gap-1 h-6.5">
207
+ <Kbd size="xs" className="bg-white/5 border-white/10 text-zinc-400">{getModifierKey().symbol}</Kbd>
208
+ <Kbd size="xs" className="bg-white/5 border-white/10 text-zinc-400">K</Kbd>
209
+ </div>
210
+ )}
211
+ </CommandMenuProvider>
212
+ </motion.div>
213
+ </DialogPrimitive.Content>
214
+ </CommandMenuPortal>
215
+ );
216
+ }
217
+ );
218
+ CommandMenuContent.displayName = "CommandMenuContent";
219
+
220
+ // Input component for search
221
+ const CommandMenuInput = React.forwardRef<
222
+ HTMLInputElement,
223
+ React.InputHTMLAttributes<HTMLInputElement> & {
224
+ placeholder?: string;
225
+ }
226
+ >(
227
+ (
228
+ { className, placeholder = "Type a command or search...", ...props },
229
+ ref
230
+ ) => {
231
+ const { value, setValue } = useCommandMenu();
232
+
233
+ return (
234
+ <div className="flex items-center border-b border-white/5 px-4 py-2 shrink-0">
235
+ <Search className="mr-3 h-4 w-4 shrink-0 text-zinc-500" />
236
+ <input
237
+ ref={ref}
238
+ value={value}
239
+ onChange={(e) => setValue(e.target.value)}
240
+ className={cn(
241
+ "flex h-10 w-full rounded-none border-0 bg-transparent py-3 text-sm outline-none placeholder:text-zinc-600 text-zinc-200 disabled:cursor-not-allowed disabled:opacity-50 font-medium",
242
+ className
243
+ )}
244
+ placeholder={placeholder}
245
+ {...props}
246
+ />
247
+ </div>
248
+ );
249
+ }
250
+ );
251
+ CommandMenuInput.displayName = "CommandMenuInput";
252
+
253
+ // List container for command items with scroll area
254
+ const CommandMenuList = React.forwardRef<
255
+ HTMLDivElement,
256
+ React.HTMLAttributes<HTMLDivElement> & {
257
+ maxHeight?: string;
258
+ }
259
+ >(({ className, children, maxHeight, ...props }, ref) => {
260
+ const {
261
+ selectedIndex,
262
+ setSelectedIndex,
263
+ scrollType = "hover",
264
+ scrollHideDelay = 600,
265
+ } = useCommandMenu();
266
+
267
+ // Handle keyboard navigation
268
+ React.useEffect(() => {
269
+ const handleKeyDown = (e: KeyboardEvent) => {
270
+ const items = document.querySelectorAll("[data-command-item]");
271
+ const maxIndex = items.length - 1;
272
+
273
+ if (e.key === "ArrowDown") {
274
+ e.preventDefault();
275
+ const newIndex = Math.min(selectedIndex + 1, maxIndex);
276
+ setSelectedIndex(newIndex);
277
+
278
+ // Scroll selected item into view
279
+ const selectedItem = items[newIndex] as HTMLElement;
280
+ if (selectedItem) {
281
+ selectedItem.scrollIntoView({
282
+ block: "nearest",
283
+ behavior: "smooth",
284
+ });
285
+ }
286
+ } else if (e.key === "ArrowUp") {
287
+ e.preventDefault();
288
+ const newIndex = Math.max(selectedIndex - 1, 0);
289
+ setSelectedIndex(newIndex);
290
+
291
+ // Scroll selected item into view
292
+ const selectedItem = items[newIndex] as HTMLElement;
293
+ if (selectedItem) {
294
+ selectedItem.scrollIntoView({
295
+ block: "nearest",
296
+ behavior: "smooth",
297
+ });
298
+ }
299
+ }
300
+ };
301
+
302
+ document.addEventListener("keydown", handleKeyDown);
303
+ return () => document.removeEventListener("keydown", handleKeyDown);
304
+ }, [selectedIndex, setSelectedIndex]);
305
+
306
+ return (
307
+ <div ref={ref} className={cn("p-2 overflow-hidden", className)} {...props}>
308
+ <ScrollArea
309
+ className="w-full [&_[data-radix-scroll-area-viewport]]:overscroll-contain [&_[data-radix-scroll-area-scrollbar]]:opacity-0 [&_[data-radix-scroll-area-scrollbar]]:w-0 [&_[data-radix-scroll-area-scrollbar]]:bg-transparent"
310
+ style={{ height: maxHeight || "auto" }}
311
+ type="always"
312
+ >
313
+ <div className="space-y-1 p-1">{children}</div>
314
+ </ScrollArea>
315
+ </div>
316
+ );
317
+ });
318
+ CommandMenuList.displayName = "CommandMenuList";
319
+
320
+ // Command group with optional heading
321
+ const CommandMenuGroup = React.forwardRef<
322
+ HTMLDivElement,
323
+ React.HTMLAttributes<HTMLDivElement> & {
324
+ heading?: string;
325
+ }
326
+ >(({ className, children, heading, ...props }, ref) => (
327
+ <div ref={ref} className={cn("", className)} {...props}>
328
+ {heading && (
329
+ <div className="px-3 py-2 text-[10px] font-semibold text-zinc-500 uppercase tracking-widest leading-none">
330
+ {heading}
331
+ </div>
332
+ )}
333
+ {children}
334
+ </div>
335
+ ));
336
+ CommandMenuGroup.displayName = "CommandMenuGroup";
337
+
338
+ // Individual command item
339
+ const CommandMenuItem = React.forwardRef<
340
+ HTMLDivElement,
341
+ React.HTMLAttributes<HTMLDivElement> & {
342
+ onSelect?: () => void;
343
+ disabled?: boolean;
344
+ shortcut?: string;
345
+ icon?: React.ReactNode;
346
+ index?: number;
347
+ keywords?: string[];
348
+ label?: string;
349
+ }
350
+ >(
351
+ (
352
+ {
353
+ className,
354
+ children,
355
+ onSelect,
356
+ disabled = false,
357
+ shortcut,
358
+ icon,
359
+ index = 0,
360
+ keywords,
361
+ label,
362
+ ...props
363
+ },
364
+ ref
365
+ ) => {
366
+ const { selectedIndex, setSelectedIndex, value: searchValue } = useCommandMenu();
367
+ const isSelected = selectedIndex === index;
368
+
369
+ // Filter logic
370
+ const matches = !searchValue || (label || (typeof children === 'string' ? children : '')).toLowerCase().includes(searchValue.toLowerCase()) || keywords?.some(k => k.toLowerCase().includes(searchValue.toLowerCase()));
371
+
372
+ // Handle click and enter key
373
+ const handleSelect = React.useCallback(() => {
374
+ if (!disabled && onSelect) {
375
+ onSelect();
376
+ }
377
+ }, [disabled, onSelect]);
378
+
379
+ React.useEffect(() => {
380
+ const handleKeyDown = (e: KeyboardEvent) => {
381
+ if (e.key === "Enter" && isSelected) {
382
+ e.preventDefault();
383
+ handleSelect();
384
+ }
385
+ };
386
+
387
+ document.addEventListener("keydown", handleKeyDown);
388
+ return () => document.removeEventListener("keydown", handleKeyDown);
389
+ }, [isSelected, handleSelect]);
390
+
391
+ if (!matches) return null;
392
+
393
+ return (
394
+ <motion.div
395
+ layout
396
+ initial={{ opacity: 0, scale: 0.98 }}
397
+ animate={{ opacity: 1, scale: 1 }}
398
+ exit={{ opacity: 0, scale: 0.98 }}
399
+ transition={{ duration: 0.2 }}
400
+ ref={ref}
401
+ data-command-item
402
+ className={cn(
403
+ "relative flex cursor-pointer select-none items-center rounded-lg px-3 py-2.5 text-sm outline-none transition-all duration-200 gap-3",
404
+ // Default state
405
+ "text-zinc-400",
406
+ // Selected / Hover state -- SUBTLE & SLEEK
407
+ isSelected
408
+ ? "bg-white/[0.08] text-zinc-100 shadow-[0_0_0_1px_rgba(255,255,255,0.05)]"
409
+ : "hover:bg-white/[0.04] hover:text-zinc-300",
410
+
411
+ disabled && "pointer-events-none opacity-50",
412
+ className
413
+ )}
414
+ onClick={handleSelect}
415
+ onMouseEnter={() => setSelectedIndex(index)}
416
+ {...(props as any)}
417
+ >
418
+ {icon && (
419
+ <div className={cn("h-4 w-4 flex items-center justify-center transition-colors", isSelected ? "text-zinc-100" : "text-zinc-500")}>
420
+ {/* Clone icon to enforce size if needed, but styling parent is usually enough */}
421
+ {icon}
422
+ </div>
423
+ )}
424
+
425
+ <div className="flex-1 truncate font-medium">{children}</div>
426
+
427
+ {shortcut && (
428
+ <div className="ml-auto flex items-center gap-1">
429
+ {shortcut.split("+").map((key, i) => (
430
+ <React.Fragment key={key}>
431
+ {i > 0 && (
432
+ <span className="text-zinc-600 text-xs">
433
+ +
434
+ </span>
435
+ )}
436
+ <Kbd size="xs" className="bg-transparent border-white/10 text-zinc-500 group-hover:text-zinc-400">
437
+ {key === "cmd" || key === "⌘"
438
+ ? getModifierKey().symbol
439
+ : key === "shift"
440
+ ? "⇧"
441
+ : key === "alt"
442
+ ? "⌥"
443
+ : key === "ctrl"
444
+ ? getModifierKey().key === "cmd"
445
+ ? "⌃"
446
+ : "Ctrl"
447
+ : key}
448
+ </Kbd>
449
+ </React.Fragment>
450
+ ))}
451
+ </div>
452
+ )}
453
+ </motion.div>
454
+ );
455
+ }
456
+ );
457
+ CommandMenuItem.displayName = "CommandMenuItem";
458
+
459
+ // Separator between groups
460
+ const CommandMenuSeparator = React.forwardRef<
461
+ HTMLDivElement,
462
+ React.HTMLAttributes<HTMLDivElement>
463
+ >(({ className, ...props }, ref) => (
464
+ <div
465
+ ref={ref}
466
+ className={cn("-mx-1 my-2 h-px bg-white/5", className)}
467
+ {...props}
468
+ />
469
+ ));
470
+ CommandMenuSeparator.displayName = "CommandMenuSeparator";
471
+
472
+ // Empty state
473
+ const CommandMenuEmpty = React.forwardRef<
474
+ HTMLDivElement,
475
+ React.HTMLAttributes<HTMLDivElement>
476
+ >(({ className, children = "No results found.", ...props }, ref) => (
477
+ <div
478
+ ref={ref}
479
+ className={cn(
480
+ "py-6 text-center text-sm text-muted-foreground",
481
+ className
482
+ )}
483
+ {...props}
484
+ >
485
+ {children}
486
+ </div>
487
+ ));
488
+ CommandMenuEmpty.displayName = "CommandMenuEmpty";
489
+
490
+ // Hook for global keyboard shortcut
491
+ export const useCommandMenuShortcut = (callback: () => void) => {
492
+ React.useEffect(() => {
493
+ const handleKeyDown = (e: KeyboardEvent) => {
494
+ if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
495
+ e.preventDefault();
496
+ callback();
497
+ }
498
+ };
499
+
500
+ document.addEventListener("keydown", handleKeyDown);
501
+ return () => document.removeEventListener("keydown", handleKeyDown);
502
+ }, [callback]);
503
+ };
504
+
505
+ export {
506
+ CommandMenu,
507
+ CommandMenuTrigger,
508
+ CommandMenuContent,
509
+ CommandMenuTitle,
510
+ CommandMenuDescription,
511
+ CommandMenuInput,
512
+ CommandMenuList,
513
+ CommandMenuEmpty,
514
+ CommandMenuGroup,
515
+ CommandMenuItem,
516
+ CommandMenuSeparator,
517
+ CommandMenuClose,
518
+ useCommandMenu,
519
+ };