{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "command-menu",
  "type": "registry:ui",
  "title": "Command Menu",
  "description": "A macOS Spotlight-style command menu with animated search, keyboard navigation, and customizable groups. Features backdrop blur, smooth animations, and full keyboard support.",
  "dependencies": ["cmdk", "framer-motion", "lucide-react"],
  "devDependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/ui/command-menu.tsx",
      "type": "registry:ui",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as ReactDOM from \"react-dom\"\nimport { Command } from \"cmdk\"\nimport { Search, ArrowRight, X } from \"lucide-react\"\nimport { motion, AnimatePresence } from \"framer-motion\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface CommandMenuItem {\n  id: string\n  title: string\n  group?: string\n  icon?: React.ReactNode\n  onSelect?: () => void\n}\n\nexport interface CommandMenuGroup {\n  title: string\n  items: CommandMenuItem[]\n}\n\nexport interface CommandMenuProps {\n  groups: CommandMenuGroup[]\n  placeholder?: string\n  emptyMessage?: string\n  brandName?: string\n  triggerClassName?: string\n  triggerLabel?: string\n  shortcutKey?: string\n  open?: boolean\n  onOpenChange?: (open: boolean) => void\n}\n\nfunction CommandMenu({\n  groups,\n  placeholder = \"Search...\",\n  emptyMessage = \"No results found\",\n  brandName = \"Command Menu\",\n  triggerClassName,\n  triggerLabel = \"Search...\",\n  shortcutKey = \"K\",\n  open: controlledOpen,\n  onOpenChange,\n}: CommandMenuProps) {\n  const [internalOpen, setInternalOpen] = React.useState(false)\n  const [query, setQuery] = React.useState(\"\")\n  const inputRef = React.useRef<HTMLInputElement>(null)\n\n  const isControlled = controlledOpen !== undefined\n  const open = isControlled ? controlledOpen : internalOpen\n  const setOpen = isControlled ? (onOpenChange ?? (() => {})) : setInternalOpen\n\n  React.useEffect(() => {\n    const down = (e: KeyboardEvent) => {\n      if (e.key.toLowerCase() === shortcutKey.toLowerCase() && (e.metaKey || e.ctrlKey)) {\n        e.preventDefault()\n        setOpen(!open)\n      }\n      if (e.key === \"Escape\") {\n        setOpen(false)\n      }\n    }\n\n    document.addEventListener(\"keydown\", down)\n    return () => document.removeEventListener(\"keydown\", down)\n  }, [open, setOpen, shortcutKey])\n\n  React.useEffect(() => {\n    if (open) {\n      setTimeout(() => {\n        inputRef.current?.focus()\n      }, 0)\n    } else {\n      setQuery(\"\")\n    }\n  }, [open])\n\n  const handleSelect = React.useCallback((item: CommandMenuItem) => {\n    setOpen(false)\n    item.onSelect?.()\n  }, [setOpen])\n\n  return (\n    <>\n      <button\n        onClick={() => setOpen(true)}\n        className={cn(\n          \"group inline-flex items-center gap-2 whitespace-nowrap transition-all duration-200\",\n          \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50\",\n          \"disabled:pointer-events-none disabled:opacity-50\",\n          \"border border-input/50 hover:border-input hover:bg-accent/50\",\n          \"px-3 py-2 relative h-9 w-full justify-start rounded-lg bg-muted/30\",\n          \"text-sm font-normal text-muted-foreground sm:pr-12 md:w-40 lg:w-56\",\n          triggerClassName\n        )}\n      >\n        <Search className=\"h-4 w-4 opacity-50 group-hover:opacity-70 transition-opacity\" />\n        <span className=\"hidden lg:inline-flex\">{triggerLabel}</span>\n        <span className=\"inline-flex lg:hidden\">Search</span>\n        <kbd className=\"pointer-events-none absolute right-1.5 top-1.5 hidden h-6 select-none items-center gap-0.5 rounded-md border bg-background/80 px-1.5 font-mono text-[10px] font-medium text-muted-foreground/70 sm:flex\">\n          <span className=\"text-xs\">⌘</span>{shortcutKey}\n        </kbd>\n      </button>\n\n      {typeof document !== \"undefined\" && ReactDOM.createPortal(\n        <AnimatePresence>\n          {open && (\n            <>\n              <motion.div\n                initial={{ opacity: 0 }}\n                animate={{ opacity: 1 }}\n                exit={{ opacity: 0 }}\n                transition={{ duration: 0.15 }}\n                className=\"fixed inset-0 z-40 bg-black/50 backdrop-blur-sm\"\n                onClick={() => setOpen(false)}\n              />\n              <motion.div\n                initial={{ opacity: 0, scale: 0.96 }}\n                animate={{ opacity: 1, scale: 1 }}\n                exit={{ opacity: 0, scale: 0.96 }}\n                transition={{ \n                  duration: 0.2,\n                  ease: [0.32, 0.72, 0, 1]\n                }}\n                className=\"fixed left-1/2 top-1/2 z-50 w-full max-w-[680px] -translate-x-1/2 -translate-y-1/2 p-4\"\n              >\n                <Command\n                  label=\"Command Menu\"\n                  className=\"overflow-hidden rounded-2xl border border-border/50 bg-popover/95 backdrop-blur-xl shadow-2xl shadow-black/20\"\n                  shouldFilter={true}\n                >\n                  <div className=\"flex items-center gap-3 border-b border-border/50 px-4 py-3\">\n                    <div className=\"flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-primary/20 to-primary/5\">\n                      <Search className=\"h-4 w-4 text-primary\" />\n                    </div>\n                    <Command.Input\n                      ref={inputRef}\n                      value={query}\n                      onValueChange={setQuery}\n                      placeholder={placeholder}\n                      className=\"flex-1 bg-transparent text-base font-normal outline-none placeholder:text-muted-foreground/60\"\n                      autoFocus\n                    />\n                    {query && (\n                      <motion.button\n                        initial={{ opacity: 0, scale: 0.8 }}\n                        animate={{ opacity: 1, scale: 1 }}\n                        onClick={() => setQuery(\"\")}\n                        className=\"rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent hover:text-accent-foreground transition-colors\"\n                      >\n                        Clear\n                      </motion.button>\n                    )}\n                    <kbd className=\"hidden sm:inline-flex h-6 items-center gap-1 rounded-md border bg-muted/50 px-2 font-mono text-[10px] font-medium text-muted-foreground\">\n                      ESC\n                    </kbd>\n                  </div>\n\n                  <Command.List className=\"max-h-[400px] overflow-y-auto overscroll-contain p-2\">\n                    <Command.Empty className=\"flex flex-col items-center justify-center py-14 text-center\">\n                      <div className=\"mb-3 flex h-12 w-12 items-center justify-center rounded-full bg-muted/50\">\n                        <Search className=\"h-5 w-5 text-muted-foreground/50\" />\n                      </div>\n                      <p className=\"text-sm text-muted-foreground\">{emptyMessage}</p>\n                      <p className=\"text-xs text-muted-foreground/60\">Try searching for something else</p>\n                    </Command.Empty>\n\n                    {groups.map((group) => (\n                      <Command.Group \n                        key={group.title} \n                        heading={group.title}\n                        className=\"[&_[cmdk-group-heading]]:px-3 [&_[cmdk-group-heading]]:py-2 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-wider [&_[cmdk-group-heading]]:text-muted-foreground/60\"\n                      >\n                        {group.items.map((item) => (\n                          <Command.Item\n                            key={item.id}\n                            value={`${group.title} ${item.title}`}\n                            onSelect={() => handleSelect(item)}\n                            className=\"group/item relative flex cursor-pointer select-none items-center gap-3 rounded-xl px-3 py-2.5 text-sm outline-none transition-colors hover:bg-accent/70 hover:text-accent-foreground aria-[selected='true']:bg-accent aria-[selected='true']:text-accent-foreground data-[disabled='true']:pointer-events-none data-[disabled='true']:opacity-50\"\n                          >\n                            <div className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-muted/50 text-muted-foreground group-aria-[selected='true']/item:bg-primary/10 group-aria-[selected='true']/item:text-primary transition-colors\">\n                              {item.icon}\n                            </div>\n                            <div className=\"flex flex-1 flex-col gap-0.5\">\n                              <span className=\"font-medium\">{item.title}</span>\n                              <span className=\"text-xs text-muted-foreground/60\">{group.title}</span>\n                            </div>\n                            <ArrowRight className=\"h-4 w-4 opacity-0 transition-all group-aria-selected/item:opacity-100 group-aria-selected/item:translate-x-0 -translate-x-2 text-muted-foreground\" />\n                          </Command.Item>\n                        ))}\n                      </Command.Group>\n                    ))}\n                  </Command.List>\n\n                  <div className=\"flex items-center justify-between border-t border-border/50 bg-muted/30 px-4 py-2.5\">\n                    <div className=\"flex items-center gap-4 text-xs text-muted-foreground/60\">\n                      <span className=\"flex items-center gap-1.5\">\n                        <kbd className=\"rounded border bg-background/80 px-1.5 py-0.5 font-mono text-[10px]\">↑↓</kbd>\n                        Navigate\n                      </span>\n                      <span className=\"flex items-center gap-1.5\">\n                        <kbd className=\"rounded border bg-background/80 px-1.5 py-0.5 font-mono text-[10px]\">↵</kbd>\n                        Select\n                      </span>\n                    </div>\n                    <span className=\"text-xs text-muted-foreground/40\">{brandName}</span>\n                  </div>\n                </Command>\n              </motion.div>\n            </>\n          )}\n        </AnimatePresence>,\n        document.body\n      )}\n    </>\n  )\n}\n\nCommandMenu.displayName = \"CommandMenu\"\n\nexport { CommandMenu }\n"
    }
  ],
  "tailwind": {},
  "cssVars": {},
  "meta": {}
}
