{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "command-search",
  "type": "registry:component",
  "title": "Command Search",
  "description": "A premium command palette with sectioned results, keyboard navigation, and smooth animations.",
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "files": [
    {
      "path": "components/watermelon/command-search.tsx",
      "type": "registry:component",
      "content": "'use client';\n\nimport {\n  useState,\n  useMemo,\n  useEffect,\n  useRef,\n  type KeyboardEvent,\n  type FC,\n} from 'react';\nimport type { ReactNode } from 'react';\nimport { motion, AnimatePresence } from 'motion/react';\nimport {\n  Search,\n  User,\n  Bell,\n  HelpCircle,\n  MessageSquare,\n  ArrowRight,\n} from 'lucide-react';\n\nexport interface CommandItem {\n  id: string;\n  title: string;\n  section: 'Suggestions' | 'Settings' | 'Help';\n  icon: ReactNode;\n  shortcut?: string;\n  action: () => void;\n}\n\n/*  DEFAULT DATA */\nconst DEFAULT_ITEMS: CommandItem[] = [\n  {\n    id: '1',\n    title: 'Calendar',\n    section: 'Suggestions',\n    icon: <ArrowRight size={16} />,\n    action: () => console.log('Calendar'),\n  },\n  {\n    id: '2',\n    title: 'Search Emoji',\n    section: 'Suggestions',\n    icon: <ArrowRight size={16} />,\n    action: () => console.log('Emoji'),\n  },\n  {\n    id: '3',\n    title: 'Calculator',\n    section: 'Suggestions',\n    icon: <ArrowRight size={16} />,\n    action: () => console.log('Calculator'),\n  },\n\n  {\n    id: '4',\n    title: 'Profile',\n    section: 'Settings',\n    icon: <User size={16} />,\n    shortcut: '⌘ P',\n    action: () => console.log('Profile'),\n  },\n  {\n    id: '5',\n    title: 'Notifications',\n    section: 'Settings',\n    icon: <Bell size={16} />,\n    shortcut: '⌘ N',\n    action: () => console.log('Notifications'),\n  },\n\n  {\n    id: '6',\n    title: 'FAQ',\n    section: 'Help',\n    icon: <HelpCircle size={16} />,\n    action: () => console.log('FAQ'),\n  },\n  {\n    id: '7',\n    title: 'Messages',\n    section: 'Help',\n    icon: <MessageSquare size={16} />,\n    action: () => console.log('Messages'),\n  },\n];\n\ninterface Props {\n  items?: CommandItem[];\n}\n\nexport const CommandSearch: FC<Props> = ({ items = DEFAULT_ITEMS }) => {\n  const [isOpen, setIsOpen] = useState(false);\n  const [query, setQuery] = useState('');\n  const [activeIndex, setActiveIndex] = useState(0);\n  const inputRef = useRef<HTMLInputElement>(null);\n\n  useEffect(() => {\n    if (isOpen) {\n      const timeout = setTimeout(() => {\n        inputRef.current?.focus();\n      }, 100);\n      return () => clearTimeout(timeout);\n    }\n  }, [isOpen]);\n\n  useEffect(() => {\n    const handleKeyDown = (e: any) => {\n      if (\n        e.key.toLowerCase() === 'f' &&\n        !isOpen &&\n        document.activeElement?.tagName !== 'INPUT' &&\n        document.activeElement?.tagName !== 'TEXTAREA'\n      ) {\n        e.preventDefault();\n        setIsOpen(true);\n      }\n      if (e.key === 'Escape' && isOpen) {\n        e.preventDefault();\n        e.stopPropagation();\n        setIsOpen(false);\n      }\n    };\n    // Use capture to catch the event before other listeners\n    window.addEventListener('keydown', handleKeyDown, true);\n    return () => window.removeEventListener('keydown', handleKeyDown, true);\n  }, [isOpen]);\n\n  const filteredItems = useMemo(() => {\n    return items.filter((item) =>\n      item.title.toLowerCase().includes(query.toLowerCase()),\n    );\n  }, [query, items]);\n\n  useEffect(() => {\n    requestAnimationFrame(() => setActiveIndex(0));\n  }, [query]);\n\n  const sections = useMemo(() => {\n    const groups: { [key: string]: CommandItem[] } = {};\n    filteredItems.forEach((item) => {\n      if (!groups[item.section]) groups[item.section] = [];\n      groups[item.section].push(item);\n    });\n\n    return Object.entries(groups).map(([name, items]) => ({\n      name,\n      items,\n    }));\n  }, [filteredItems]);\n\n  const handleKeyDown = (e: KeyboardEvent) => {\n    if (e.key === 'ArrowDown') {\n      e.preventDefault();\n      setActiveIndex((prev) => (prev + 1) % filteredItems.length);\n    } else if (e.key === 'ArrowUp') {\n      e.preventDefault();\n      setActiveIndex(\n        (prev) => (prev - 1 + filteredItems.length) % filteredItems.length,\n      );\n    } else if (e.key === 'Enter') {\n      const selectedItem = filteredItems[activeIndex];\n      if (selectedItem) {\n        selectedItem.action();\n        setIsOpen(false);\n      }\n    }\n  };\n\n  const sharedTransition = {\n    type: 'tween' as const,\n    ease: 'easeOut' as const,\n    duration: 0.15,\n  };\n\n  return (\n    <>\n      <AnimatePresence mode=\"popLayout\">\n        {isOpen && (\n            <motion.div\n              initial={{ opacity: 0 }}\n              animate={{ opacity: 1 }}\n              exit={{ opacity: 0 }}\n              className=\"fixed inset-0 z-40 bg-zinc-950/10 backdrop-blur-[2px] dark:bg-black/40\"\n              onClick={() => setIsOpen(false)}\n            />\n        )}\n      </AnimatePresence>\n\n      <div className=\"relative z-50 h-10 w-full max-w-[280px] md:w-64\">\n        <AnimatePresence mode=\"popLayout\">\n          {!isOpen ? (\n            <motion.button\n              key=\"trigger\"\n              layoutId=\"command-pallete\"\n              onClick={() => setIsOpen(true)}\n              className=\"group absolute top-0 left-0 flex h-10 w-full items-center gap-3 overflow-hidden rounded-lg border border-zinc-200 bg-white px-4 py-2 text-zinc-500 shadow-sm hover:text-zinc-900 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-400 dark:shadow-none dark:hover:text-white\"\n              transition={sharedTransition}\n            >\n              <motion.div layoutId=\"search-icon\" transition={sharedTransition}>\n                <Search size={16} className=\"opacity-40\" />\n              </motion.div>\n              <motion.span\n                layoutId=\"search-text\"\n                transition={sharedTransition}\n                className=\"pr-8 text-sm font-medium\"\n              >\n                Find...\n              </motion.span>\n              <motion.kbd\n                layoutId=\"search-shortcut\"\n                transition={sharedTransition}\n                className=\"absolute right-2 rounded border border-zinc-200 bg-zinc-50 px-2 py-0.5 text-[14px] font-bold text-zinc-400 group-hover:text-zinc-600 dark:border-zinc-800 dark:bg-zinc-900 dark:text-zinc-500 dark:group-hover:text-zinc-300\"\n              >\n                F\n              </motion.kbd>\n            </motion.button>\n          ) : (\n            <motion.div\n              layoutId=\"command-pallete\"\n              transition={sharedTransition}\n              className=\"absolute -top-2 -left-2 z-50 flex h-80 w-xs flex-col overflow-hidden rounded-2xl border-[1.4px] border-zinc-200 bg-white shadow-[0_32px_64px_-15px_rgba(0,0,0,0.1)] md:w-[400px] dark:border-zinc-800 dark:bg-zinc-950 dark:shadow-black\"\n              onClick={(e) => e.stopPropagation()}\n            >\n              {/* Search Header */}\n              <div className=\"flex items-center border-b-[1.4px] border-zinc-100 px-4 py-3.5 dark:border-zinc-800/50\">\n                <motion.div\n                  layoutId=\"search-icon\"\n                  transition={sharedTransition}\n                >\n                  <Search\n                    size={18}\n                    className=\"mr-3 text-zinc-400 dark:text-zinc-500\"\n                    strokeWidth={2.5}\n                  />\n                </motion.div>\n                <div className=\"relative flex flex-1 items-center\">\n                  <input\n                    ref={inputRef}\n                    type=\"text\"\n                    className=\"w-full bg-transparent text-base font-medium text-zinc-900 outline-none md:text-[15px] dark:text-white\"\n                    value={query}\n                    onChange={(e) => setQuery(e.target.value)}\n                    onKeyDown={handleKeyDown}\n                  />\n                  {!query && (\n                    <motion.span\n                      layoutId=\"search-text\"\n                      transition={sharedTransition}\n                      className=\"pointer-events-none absolute left-0 text-[15px] font-medium text-zinc-400 dark:text-zinc-600\"\n                    >\n                      Find...\n                    </motion.span>\n                  )}\n                </div>\n                <div className=\"ml-2 flex items-center gap-1.5\">\n                  <motion.span\n                    layoutId=\"search-shortcut\"\n                    transition={sharedTransition}\n                    className=\"rounded-[2px] border border-zinc-200 bg-zinc-50 p-0.5 px-1 text-[11px] font-bold text-zinc-400 dark:border-zinc-800 dark:bg-zinc-900/50 dark:text-zinc-500\"\n                  >\n                    Esc\n                  </motion.span>\n                </div>\n              </div>\n\n              {/* Results Body */}\n              <div className=\"custom-scrollbar flex-1 overflow-y-auto p-1.5 md:max-h-[380px]\">\n                {filteredItems.length === 0 ? (\n                  <div className=\"py-12 text-center text-sm text-zinc-500\">\n                    No results found for \"{query}\"\n                  </div>\n                ) : (\n                  <div className=\"space-y-4 py-1\">\n                    {sections.map((section) => (\n                      <div key={section.name} className=\"space-y-1\">\n                        <h3 className=\"px-3 py-1 text-[11px] font-semibold tracking-wider text-zinc-400 uppercase dark:text-zinc-500\">\n                          {section.name}\n                        </h3>\n                        <div className=\"space-y-0.5\">\n                          {section.items.map((item) => {\n                            const globalIndex = filteredItems.findIndex(\n                              (fi) => fi.id === item.id,\n                            );\n                            const isActive = globalIndex === activeIndex;\n\n                            return (\n                              <button\n                                key={item.id}\n                                className={`group flex w-full items-center justify-between rounded-md px-3 py-2.5 text-left ${isActive ? 'bg-zinc-100 text-zinc-900 dark:bg-zinc-800 dark:text-white' : 'text-zinc-500 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200'} `}\n                                onMouseEnter={() => setActiveIndex(globalIndex)}\n                                onClick={() => {\n                                  item.action();\n                                  setIsOpen(false);\n                                }}\n                              >\n                                <div className=\"flex items-center gap-3\">\n                                  <span\n                                    className={`${isActive ? 'text-zinc-900 dark:text-white' : 'text-zinc-400 group-hover:text-zinc-600 dark:text-zinc-500 dark:group-hover:text-zinc-300'}`}\n                                  >\n                                    {item.icon}\n                                  </span>\n                                  <span className=\"text-[14px] leading-none font-medium\">\n                                    {item.title}\n                                  </span>\n                                </div>\n\n                                {item.shortcut && (\n                                  <kbd\n                                    className={`rounded border px-1.5 py-0.5 text-[10px] font-bold ${isActive ? 'border-zinc-300 bg-white text-zinc-500 dark:border-zinc-600 dark:bg-zinc-700/50 dark:text-zinc-300' : 'border-transparent bg-transparent text-zinc-400 group-hover:text-zinc-500 dark:text-zinc-600'} `}\n                                  >\n                                    {item.shortcut}\n                                  </kbd>\n                                )}\n                              </button>\n                            );\n                          })}\n                        </div>\n                      </div>\n                    ))}\n                  </div>\n                )}\n              </div>\n            </motion.div>\n          )}\n        </AnimatePresence>\n      </div>\n    </>\n  );\n};\n"
    }
  ]
}
