{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "command-palette",
  "title": "Command Palette",
  "description": "A fully customizable, smooth-animated Command Palette (Cmd+K) component",
  "dependencies": [
    "framer-motion",
    "lucide-react",
    "next-themes"
  ],
  "files": [
    {
      "path": "app/registry/command-palette/command-palette.tsx",
      "content": "/**\n * Spectrum UI — CommandPalette\n * \n * Dependencies: framer-motion, lucide-react, next-themes, @/lib/utils\n * \n * @example\n * <CommandPalette isOpen={isOpen} onClose={() => setIsOpen(false)} />\n */\n\n\"use client\"\n\nimport React, { useState, useEffect, useRef, useCallback, useMemo } from \"react\"\nimport { useRouter } from \"next/navigation\"\nimport { useTheme } from \"next-themes\"\nimport { motion, AnimatePresence, useReducedMotion } from \"framer-motion\"\nimport { Search, Home, Code, HelpCircle, Laptop, Sun, Moon, Copy, Github, CornerDownLeft } from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface CommandItem {\n  id: string\n  title: string\n  description: string\n  category: \"Navigation\" | \"Theme\" | \"Repository\"\n  shortcut?: string[]\n  icon: React.ReactNode\n  action: () => void\n}\n\nexport interface CommandPaletteProps {\n  isOpen: boolean\n  onClose: () => void\n  className?: string\n}\n\n// ─── Animation Springs ────────────────────────────────────────────────────────\n\nconst SPRING_FLUID = {\n  type: \"spring\",\n  stiffness: 300,\n  damping: 30,\n} as const\n\nconst SPRING_ENTRANCE = {\n  type: \"spring\",\n  stiffness: 260,\n  damping: 20,\n} as const\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport function CommandPalette({ isOpen, onClose, className }: CommandPaletteProps) {\n  const router = useRouter()\n  const { theme, setTheme } = useTheme()\n  const shouldReduceMotion = useReducedMotion()\n  const [query, setQuery] = useState(\"\")\n  const [activeIndex, setActiveIndex] = useState(0)\n  const [isCopied, setIsCopied] = useState(false)\n  const inputRef = useRef<HTMLInputElement>(null)\n  const itemsContainerRef = useRef<HTMLDivElement>(null)\n\n  // Auto-focus input when opened\n  useEffect(() => {\n    if (isOpen) {\n      setTimeout(() => {\n        inputRef.current?.focus()\n      }, 50)\n      setQuery(\"\")\n      setActiveIndex(0)\n    }\n  }, [isOpen])\n\n  // Copy CLI command action\n  const handleCopyCommand = useCallback(() => {\n    navigator.clipboard.writeText(\"npx -y @spectrumui/mcp\").then(() => {\n      setIsCopied(true)\n      setTimeout(() => setIsCopied(false), 2000)\n    })\n  }, [])\n\n  // Defined Command List\n  const commandsList = useMemo<CommandItem[]>(() => {\n    return [\n      {\n        id: \"nav-home\",\n        title: \"Go to Homepage\",\n        description: \"Navigate back to the landing page\",\n        category: \"Navigation\",\n        shortcut: [\"G\", \"H\"],\n        icon: <Home className=\"h-4 w-4\" />,\n        action: () => {\n          router.push(\"/\")\n          onClose()\n        },\n      },\n      {\n        id: \"nav-components\",\n        title: \"Explore Components\",\n        description: \"Browse React & Next.js copy-paste components\",\n        category: \"Navigation\",\n        shortcut: [\"G\", \"C\"],\n        icon: <Code className=\"h-4 w-4\" />,\n        action: () => {\n          router.push(\"/docs\")\n          onClose()\n        },\n      },\n      {\n        id: \"nav-faqs\",\n        title: \"Frequently Asked Questions\",\n        description: \"Answers to common integration questions\",\n        category: \"Navigation\",\n        shortcut: [\"G\", \"F\"],\n        icon: <HelpCircle className=\"h-4 w-4\" />,\n        action: () => {\n          router.push(\"/faqs\")\n          onClose()\n        },\n      },\n      {\n        id: \"theme-light\",\n        title: \"Set Theme to Light\",\n        description: \"Switch to light mode interface appearance\",\n        category: \"Theme\",\n        shortcut: [\"T\", \"L\"],\n        icon: <Sun className=\"h-4 w-4\" />,\n        action: () => {\n          setTheme(\"light\")\n          onClose()\n        },\n      },\n      {\n        id: \"theme-dark\",\n        title: \"Set Theme to Dark\",\n        description: \"Switch to dark mode interface appearance\",\n        category: \"Theme\",\n        shortcut: [\"T\", \"D\"],\n        icon: <Moon className=\"h-4 w-4\" />,\n        action: () => {\n          setTheme(\"dark\")\n          onClose()\n        },\n      },\n      {\n        id: \"theme-system\",\n        title: \"Set Theme to System\",\n        description: \"Match the system device theme\",\n        category: \"Theme\",\n        shortcut: [\"T\", \"S\"],\n        icon: <Laptop className=\"h-4 w-4\" />,\n        action: () => {\n          setTheme(\"system\")\n          onClose()\n        },\n      },\n      {\n        id: \"cmd-copy\",\n        title: isCopied ? \"CLI Command Copied!\" : \"Copy CLI Install Command\",\n        description: \"Copy npx spectrum-ui installer to clipboard\",\n        category: \"Repository\",\n        shortcut: [\"C\", \"C\"],\n        icon: <Copy className=\"h-4 w-4\" />,\n        action: () => {\n          handleCopyCommand()\n        },\n      },\n      {\n        id: \"cmd-github\",\n        title: \"View GitHub Repository\",\n        description: \"Open the open-source spectrum-ui repo on GitHub\",\n        category: \"Repository\",\n        shortcut: [\"G\", \"R\"],\n        icon: <Github className=\"h-4 w-4\" />,\n        action: () => {\n          window.open(\"https://github.com/arihantcodes/spectrum-ui\", \"_blank\")\n          onClose()\n        },\n      },\n    ]\n  }, [router, setTheme, onClose, isCopied, handleCopyCommand])\n\n  // Filter commands by search query\n  const filteredCommands = useMemo(() => {\n    return commandsList.filter((item) => {\n      const matchText = (item.title + \" \" + item.description + \" \" + item.category).toLowerCase()\n      return matchText.includes(query.toLowerCase())\n    })\n  }, [commandsList, query])\n\n  // Reset active index when query changes\n  useEffect(() => {\n    setActiveIndex(0)\n  }, [query])\n\n  // Keyboard navigation logic\n  useEffect(() => {\n    if (!isOpen) return\n\n    const handleKeyDown = (e: KeyboardEvent) => {\n      if (e.key === \"Escape\") {\n        e.preventDefault()\n        onClose()\n      } else if (e.key === \"ArrowDown\") {\n        e.preventDefault()\n        setActiveIndex((prev) => (filteredCommands.length > 0 ? (prev + 1) % filteredCommands.length : 0))\n      } else if (e.key === \"ArrowUp\") {\n        e.preventDefault()\n        setActiveIndex((prev) => (filteredCommands.length > 0 ? (prev - 1 + filteredCommands.length) % filteredCommands.length : 0))\n      } else if (e.key === \"Enter\") {\n        e.preventDefault()\n        const target = filteredCommands[activeIndex]\n        if (target) {\n          target.action()\n        }\n      }\n    }\n\n    window.addEventListener(\"keydown\", handleKeyDown)\n    return () => window.removeEventListener(\"keydown\", handleKeyDown)\n  }, [isOpen, onClose, filteredCommands, activeIndex])\n\n  // Scroll active item into view\n  useEffect(() => {\n    const activeEl = itemsContainerRef.current?.querySelector(`[data-index=\"${activeIndex}\"]`)\n    if (activeEl) {\n      activeEl.scrollIntoView({ block: \"nearest\" })\n    }\n  }, [activeIndex])\n\n  // Grouped filtered commands\n  const categories = useMemo(() => {\n    const groups: { [key: string]: typeof filteredCommands } = {}\n    filteredCommands.forEach((cmd) => {\n      if (!groups[cmd.category]) {\n        groups[cmd.category] = []\n      }\n      groups[cmd.category].push(cmd)\n    })\n    return groups\n  }, [filteredCommands])\n\n  // Get index mapping in the grouped list to keep index alignment consistent\n  const getFlatIndex = (cmdId: string) => {\n    return filteredCommands.findIndex((c) => c.id === cmdId)\n  }\n\n  return (\n    <AnimatePresence>\n      {isOpen && (\n        <div className=\"fixed inset-0 z-50 flex items-start justify-center pt-[15vh] px-4\">\n          \n          {/* Backdrop Blur Overlay */}\n          <motion.div\n            initial={{ opacity: 0 }}\n            animate={{ opacity: 1 }}\n            exit={{ opacity: 0 }}\n            transition={{ duration: shouldReduceMotion ? 0 : 0.2 }}\n            onClick={onClose}\n            className=\"fixed inset-0 bg-neutral-950/20 dark:bg-black/40 backdrop-blur-xs\"\n          />\n\n          {/* Palette Dialog Box */}\n          <motion.div\n            initial={{ opacity: 0, y: -15, scale: 0.98 }}\n            animate={{ opacity: 1, y: 0, scale: 1 }}\n            exit={{ opacity: 0, y: -10, scale: 0.98 }}\n            transition={shouldReduceMotion ? { duration: 0 } : SPRING_ENTRANCE}\n            className={cn(\n              \"relative w-full max-w-lg overflow-hidden rounded-2xl border shadow-2xl z-10 flex flex-col text-left\",\n              \"bg-white/80 dark:bg-[#0C0C0C]/75 border-neutral-200/60 dark:border-neutral-800/60 backdrop-blur-xl\",\n              className\n            )}\n          >\n            {/* Search Input Bar */}\n            <div className=\"flex items-center gap-3 px-4 py-3.5 border-b border-neutral-200/40 dark:border-neutral-850/40\">\n              <Search className=\"h-4 w-4 text-neutral-400 dark:text-neutral-500 shrink-0\" />\n              <input\n                ref={inputRef}\n                type=\"text\"\n                placeholder=\"Type a command or search...\"\n                value={query}\n                onChange={(e) => setQuery(e.target.value)}\n                className=\"flex-1 bg-transparent border-0 outline-hidden text-sm text-neutral-800 dark:text-neutral-200 placeholder:text-neutral-400 dark:placeholder:text-neutral-600 focus:ring-0 focus:outline-hidden\"\n              />\n              <kbd className=\"hidden sm:inline-flex h-5 select-none items-center gap-1 rounded border px-1.5 font-mono text-[9px] font-medium opacity-60 text-neutral-500 bg-neutral-100/50 dark:bg-neutral-900 border-neutral-200 dark:border-neutral-800\">\n                ESC\n              </kbd>\n            </div>\n\n            {/* Commands List Container */}\n            <div\n              ref={itemsContainerRef}\n              className=\"max-h-[340px] overflow-y-auto overflow-x-hidden p-2\"\n            >\n              {filteredCommands.length === 0 ? (\n                /* Empty state */\n                <div className=\"py-12 text-center text-sm text-neutral-400 dark:text-neutral-600 font-mono\">\n                  No commands found matching &quot;{query}&quot;\n                </div>\n              ) : (\n                /* Categorized items mapping */\n                Object.entries(categories).map(([category, items]) => (\n                  <div key={category} className=\"mb-2 last:mb-0\">\n                    <h4 className=\"px-3 py-1.5 text-[9px] font-mono font-medium tracking-widest text-neutral-400 dark:text-neutral-600 uppercase\">\n                      {category}\n                    </h4>\n                    <div className=\"space-y-0.5 mt-1\">\n                      {items.map((item) => {\n                        const flatIdx = getFlatIndex(item.id)\n                        const isActive = flatIdx === activeIndex\n\n                        return (\n                          <div\n                            key={item.id}\n                            data-index={flatIdx}\n                            onClick={() => {\n                              item.action()\n                            }}\n                            onMouseEnter={() => setActiveIndex(flatIdx)}\n                            className={cn(\n                              \"relative flex items-center justify-between px-3 py-2.5 rounded-xl cursor-pointer select-none transition-colors group z-10\",\n                              isActive ? \"text-neutral-900 dark:text-white\" : \"text-neutral-500 dark:text-neutral-400 hover:text-neutral-800 dark:hover:text-neutral-200\"\n                            )}\n                          >\n                            {/* Layout animation active indicator card */}\n                            {isActive && (\n                              <motion.div\n                                layoutId=\"active-item-pill\"\n                                className=\"absolute inset-0 bg-neutral-100 dark:bg-neutral-900 rounded-xl -z-10\"\n                                transition={shouldReduceMotion ? { duration: 0 } : SPRING_FLUID}\n                              />\n                            )}\n\n                            {/* Info Left */}\n                            <div className=\"flex items-center gap-3 min-w-0\">\n                              <div className={cn(\n                                \"shrink-0 transition-colors\",\n                                isActive ? \"text-neutral-800 dark:text-neutral-200\" : \"text-neutral-400 dark:text-neutral-600\"\n                              )}>\n                                {item.icon}\n                              </div>\n                              <div className=\"min-w-0\">\n                                <span className=\"block text-sm font-medium leading-none\">\n                                  {item.title}\n                                </span>\n                                <span className=\"block text-[11px] text-neutral-400 dark:text-neutral-500 mt-1 leading-none truncate max-w-xs\">\n                                  {item.description}\n                                </span>\n                              </div>\n                            </div>\n\n                            {/* Shortcut Badges Right */}\n                            {item.shortcut && (\n                              <div className=\"flex items-center gap-1 shrink-0 ml-4\">\n                                {item.shortcut.map((key, kIdx) => (\n                                  <kbd\n                                    key={kIdx}\n                                    className=\"inline-flex h-5 w-5 select-none items-center justify-center rounded border font-mono text-[9px] font-medium bg-neutral-50 dark:bg-neutral-950 border-neutral-200/50 dark:border-neutral-800/50 text-neutral-400 dark:text-neutral-600\"\n                                  >\n                                    {key}\n                                  </kbd>\n                                ))}\n                                {isActive && (\n                                  <CornerDownLeft className=\"h-3 w-3 text-neutral-400 dark:text-neutral-600 ml-1.5 opacity-60 animate-pulse\" />\n                                )}\n                              </div>\n                            )}\n\n                          </div>\n                        )\n                      })}\n                    </div>\n                  </div>\n                ))\n              )}\n            </div>\n\n            {/* Bottom Status bar */}\n            <div className=\"flex items-center justify-between px-4 py-2 bg-neutral-50/50 dark:bg-neutral-950/20 border-t border-neutral-200/40 dark:border-neutral-850/40 text-[10px] font-mono text-neutral-400 dark:text-neutral-600\">\n              <div className=\"flex items-center gap-1.5\">\n                <span>Use arrows</span>\n                <kbd className=\"rounded border border-neutral-200 dark:border-neutral-800 bg-neutral-100/50 dark:bg-neutral-900 px-1 font-mono text-[9px]\">↑↓</kbd>\n                <span>and</span>\n                <kbd className=\"rounded border border-neutral-200 dark:border-neutral-800 bg-neutral-100/50 dark:bg-neutral-900 px-1 font-mono text-[9px]\">Enter</kbd>\n              </div>\n              <div>Spectrum Palette</div>\n            </div>\n\n          </motion.div>\n        </div>\n      )}\n    </AnimatePresence>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/spectrumui/command-palette.tsx"
    }
  ],
  "type": "registry:component"
}
