{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "command-menu",
  "title": "Command Menu",
  "description": "Popup menu for running commands.",
  "dependencies": [
    "@radix-ui/react-visually-hidden",
    "@radix-ui/react-dialog"
  ],
  "registryDependencies": [
    "kbd",
    "scroll-area"
  ],
  "files": [
    {
      "path": "registry/new-york/ui/command-menu.tsx",
      "content": "\"use client\";\n\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport * as VisuallyHidden from \"@radix-ui/react-visually-hidden\";\nimport { Search, X } from \"lucide-react\";\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { Kbd } from \"@/registry/new-york/ui/kbd\";\nimport { ScrollArea } from \"@/registry/new-york/ui/scroll-area\";\n\nconst getModifierKey = () => {\n  if (typeof navigator === \"undefined\") return { key: \"Ctrl\", symbol: \"Ctrl\" };\n  const isMac =\n    navigator.platform.toUpperCase().includes(\"MAC\") ||\n    navigator.userAgent.toUpperCase().includes(\"MAC\");\n  return isMac ? { key: \"cmd\", symbol: \"⌘\" } : { key: \"ctrl\", symbol: \"Ctrl\" };\n};\n\ninterface CommandMenuContextType {\n  value: string;\n  setValue: (value: string) => void;\n  selectedIndex: number;\n  setSelectedIndex: (index: number) => void;\n  scrollType?: \"auto\" | \"always\" | \"scroll\" | \"hover\";\n  scrollHideDelay?: number;\n}\n\nconst CommandMenuContext = React.createContext<\n  CommandMenuContextType | undefined\n>(undefined);\n\nconst CommandMenuProvider: React.FC<{\n  children: React.ReactNode;\n  value: string;\n  setValue: (value: string) => void;\n  selectedIndex: number;\n  setSelectedIndex: (index: number) => void;\n  scrollType?: \"auto\" | \"always\" | \"scroll\" | \"hover\";\n  scrollHideDelay?: number;\n}> = ({\n  children,\n  value,\n  setValue,\n  selectedIndex,\n  setSelectedIndex,\n  scrollType,\n  scrollHideDelay,\n}) => (\n  <CommandMenuContext.Provider\n    value={{\n      value,\n      setValue,\n      selectedIndex,\n      setSelectedIndex,\n      scrollType,\n      scrollHideDelay,\n    }}\n  >\n    {children}\n  </CommandMenuContext.Provider>\n);\n\nconst useCommandMenu = () => {\n  const context = React.useContext(CommandMenuContext);\n  if (!context)\n    throw new Error(\"useCommandMenu must be used within CommandMenuProvider\");\n  return context;\n};\n\nconst CommandMenu = DialogPrimitive.Root;\nconst CommandMenuTrigger = DialogPrimitive.Trigger;\nconst CommandMenuPortal = DialogPrimitive.Portal;\nconst CommandMenuClose = DialogPrimitive.Close;\n\nconst CommandMenuTitle = React.forwardRef<\n  React.ComponentRef<typeof DialogPrimitive.Title>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Title\n    className={cn(\n      \"font-semibold text-foreground text-lg leading-none tracking-tight\",\n      className\n    )}\n    ref={ref}\n    {...props}\n  />\n));\nCommandMenuTitle.displayName = \"CommandMenuTitle\";\n\nconst CommandMenuDescription = React.forwardRef<\n  React.ComponentRef<typeof DialogPrimitive.Description>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Description\n    className={cn(\"text-muted-foreground text-sm\", className)}\n    ref={ref}\n    {...props}\n  />\n));\nCommandMenuDescription.displayName = \"CommandMenuDescription\";\n\nconst CommandMenuOverlay = React.forwardRef<\n  React.ComponentRef<typeof DialogPrimitive.Overlay>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Overlay\n    className={cn(\n      \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50 backdrop-blur-sm data-[state=closed]:animate-out data-[state=open]:animate-in\",\n      className\n    )}\n    ref={ref}\n    {...props}\n  />\n));\nCommandMenuOverlay.displayName = \"CommandMenuOverlay\";\n\nconst CommandMenuContent = React.forwardRef<\n  React.ComponentRef<typeof DialogPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {\n    showShortcut?: boolean;\n    scrollType?: \"auto\" | \"always\" | \"scroll\" | \"hover\";\n    scrollHideDelay?: number;\n  }\n>(\n  (\n    {\n      className,\n      children,\n      showShortcut = true,\n      scrollType = \"hover\",\n      scrollHideDelay = 600,\n      ...props\n    },\n    ref\n  ) => {\n    const [value, setValue] = React.useState(\"\");\n    const [selectedIndex, setSelectedIndex] = React.useState(0);\n\n    React.useEffect(() => {\n      const handleKeyDown = (e: KeyboardEvent) => {\n        if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\" || e.key === \"Enter\") {\n          e.preventDefault();\n        }\n      };\n      document.addEventListener(\"keydown\", handleKeyDown);\n      return () => document.removeEventListener(\"keydown\", handleKeyDown);\n    }, []);\n\n    return (\n      <CommandMenuPortal>\n        <CommandMenuOverlay />\n        <DialogPrimitive.Content asChild ref={ref} {...props}>\n          <div\n            className={cn(\n              \"fixed top-[30%] left-1/2 z-50 w-[95%] max-w-2xl -translate-x-1/2 -translate-y-1/2\",\n              \"rounded-xl border border-border bg-background\",\n              \"overflow-hidden\",\n              \"motion-safe:duration-200 motion-reduce:animate-none\",\n              className\n            )}\n          >\n            <CommandMenuProvider\n              scrollHideDelay={scrollHideDelay}\n              scrollType={scrollType}\n              selectedIndex={selectedIndex}\n              setSelectedIndex={setSelectedIndex}\n              setValue={setValue}\n              value={value}\n            >\n              <VisuallyHidden.Root>\n                <CommandMenuTitle>Command Menu</CommandMenuTitle>\n              </VisuallyHidden.Root>\n              {children}\n              <CommandMenuClose className=\"absolute top-3 right-3 rounded-lg p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\">\n                <X size={14} />\n                <span className=\"sr-only\">Close</span>\n              </CommandMenuClose>\n              {showShortcut && (\n                <div className=\"absolute top-3 right-12 flex h-6 items-center justify-center gap-1\">\n                  <Kbd>{getModifierKey().symbol}</Kbd>\n                  <Kbd>K</Kbd>\n                </div>\n              )}\n            </CommandMenuProvider>\n          </div>\n        </DialogPrimitive.Content>\n      </CommandMenuPortal>\n    );\n  }\n);\nCommandMenuContent.displayName = \"CommandMenuContent\";\n\nconst CommandMenuInput = React.forwardRef<\n  HTMLInputElement,\n  React.InputHTMLAttributes<HTMLInputElement> & { placeholder?: string }\n>(\n  (\n    { className, placeholder = \"Type a command or search...\", ...props },\n    ref\n  ) => {\n    const { value, setValue } = useCommandMenu();\n    return (\n      <div className=\"flex items-center gap-2 border-border border-b px-3 py-0\">\n        <Search className=\"size-4 shrink-0 text-muted-foreground\" />\n        <input\n          className={cn(\n            \"h-12 w-full rounded-none border-0 bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n            className\n          )}\n          onChange={(e) => setValue(e.target.value)}\n          placeholder={placeholder}\n          ref={ref}\n          value={value}\n          {...props}\n        />\n      </div>\n    );\n  }\n);\nCommandMenuInput.displayName = \"CommandMenuInput\";\n\nconst CommandMenuList = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement> & { maxHeight?: string }\n>(({ className, children, maxHeight = \"300px\", ...props }, ref) => {\n  const {\n    selectedIndex,\n    setSelectedIndex,\n    scrollType = \"hover\",\n    scrollHideDelay = 600,\n  } = useCommandMenu();\n\n  React.useEffect(() => {\n    const handleKeyDown = (e: KeyboardEvent) => {\n      const items = document.querySelectorAll(\"[data-command-item]\");\n      const maxIndex = items.length - 1;\n      if (e.key === \"ArrowDown\") {\n        e.preventDefault();\n        const newIndex = Math.min(selectedIndex + 1, maxIndex);\n        setSelectedIndex(newIndex);\n        (items[newIndex] as HTMLElement | undefined)?.scrollIntoView({\n          block: \"nearest\",\n          behavior: \"smooth\",\n        });\n      } else if (e.key === \"ArrowUp\") {\n        e.preventDefault();\n        const newIndex = Math.max(selectedIndex - 1, 0);\n        setSelectedIndex(newIndex);\n        (items[newIndex] as HTMLElement | undefined)?.scrollIntoView({\n          block: \"nearest\",\n          behavior: \"smooth\",\n        });\n      }\n    };\n    document.addEventListener(\"keydown\", handleKeyDown);\n    return () => document.removeEventListener(\"keydown\", handleKeyDown);\n  }, [selectedIndex, setSelectedIndex]);\n\n  return (\n    <div className=\"p-1\" ref={ref} {...props}>\n      <ScrollArea\n        className={cn(\"w-full\", className)}\n        scrollHideDelay={scrollHideDelay}\n        style={{ height: maxHeight }}\n        type={scrollType}\n      >\n        <div className=\"flex flex-col gap-1 p-1\">{children}</div>\n      </ScrollArea>\n    </div>\n  );\n});\nCommandMenuList.displayName = \"CommandMenuList\";\n\nconst CommandMenuGroup = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement> & { heading?: string }\n>(({ className, children, heading, ...props }, ref) => (\n  <div className={cn(\"\", className)} ref={ref} {...props}>\n    {heading && (\n      <div className=\"px-2 py-1.5 font-medium text-muted-foreground text-xs uppercase tracking-wider\">\n        {heading}\n      </div>\n    )}\n    {children}\n  </div>\n));\nCommandMenuGroup.displayName = \"CommandMenuGroup\";\n\nconst CommandMenuItem = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement> & {\n    onSelect?: () => void;\n    disabled?: boolean;\n    shortcut?: string;\n    icon?: React.ReactNode;\n    index?: number;\n  }\n>(\n  (\n    {\n      className,\n      children,\n      onSelect,\n      disabled = false,\n      shortcut,\n      icon,\n      index = 0,\n      ...props\n    },\n    ref\n  ) => {\n    const { selectedIndex, setSelectedIndex } = useCommandMenu();\n    const isSelected = selectedIndex === index;\n\n    const handleSelect = React.useCallback(() => {\n      if (!disabled && onSelect) onSelect();\n    }, [disabled, onSelect]);\n\n    React.useEffect(() => {\n      const handleKeyDown = (e: KeyboardEvent) => {\n        if (e.key === \"Enter\" && isSelected) {\n          e.preventDefault();\n          handleSelect();\n        }\n      };\n      document.addEventListener(\"keydown\", handleKeyDown);\n      return () => document.removeEventListener(\"keydown\", handleKeyDown);\n    }, [isSelected, handleSelect]);\n\n    return (\n      <div\n        className={cn(\n          \"relative flex cursor-default select-none items-center gap-2 rounded-md px-2 py-2 text-sm outline-none transition-colors\",\n          \"hover:bg-accent hover:text-accent-foreground\",\n          isSelected && \"bg-accent text-accent-foreground\",\n          disabled && \"pointer-events-none opacity-50\",\n          className\n        )}\n        data-command-item\n        onClick={handleSelect}\n        onMouseEnter={() => setSelectedIndex(index)}\n        ref={ref}\n        {...props}\n      >\n        {icon && (\n          <div className=\"flex size-4 items-center justify-center\">{icon}</div>\n        )}\n        <div className=\"flex-1\">{children}</div>\n        {shortcut && (\n          <div className=\"ml-auto flex items-center gap-1\">\n            {shortcut.split(\"+\").map((key, i) => (\n              <React.Fragment key={`${key}-${i}`}>\n                {i > 0 && (\n                  <span className=\"text-muted-foreground text-xs\">+</span>\n                )}\n                <Kbd>\n                  {key === \"cmd\" || key === \"⌘\"\n                    ? getModifierKey().symbol\n                    : key === \"shift\"\n                      ? \"⇧\"\n                      : key === \"alt\"\n                        ? \"⌥\"\n                        : key === \"ctrl\"\n                          ? getModifierKey().key === \"cmd\"\n                            ? \"⌃\"\n                            : \"Ctrl\"\n                          : key}\n                </Kbd>\n              </React.Fragment>\n            ))}\n          </div>\n        )}\n      </div>\n    );\n  }\n);\nCommandMenuItem.displayName = \"CommandMenuItem\";\n\nconst CommandMenuSeparator = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div\n    className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\n    ref={ref}\n    {...props}\n  />\n));\nCommandMenuSeparator.displayName = \"CommandMenuSeparator\";\n\nconst CommandMenuEmpty = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, children = \"No results found.\", ...props }, ref) => (\n  <div\n    className={cn(\"py-6 text-center text-muted-foreground text-sm\", className)}\n    ref={ref}\n    {...props}\n  >\n    {children}\n  </div>\n));\nCommandMenuEmpty.displayName = \"CommandMenuEmpty\";\n\nexport const useCommandMenuShortcut = (callback: () => void) => {\n  React.useEffect(() => {\n    const handleKeyDown = (e: KeyboardEvent) => {\n      if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n        e.preventDefault();\n        callback();\n      }\n    };\n    document.addEventListener(\"keydown\", handleKeyDown);\n    return () => document.removeEventListener(\"keydown\", handleKeyDown);\n  }, [callback]);\n};\n\nexport {\n  CommandMenu,\n  CommandMenuTrigger,\n  CommandMenuContent,\n  CommandMenuTitle,\n  CommandMenuDescription,\n  CommandMenuInput,\n  CommandMenuList,\n  CommandMenuEmpty,\n  CommandMenuGroup,\n  CommandMenuItem,\n  CommandMenuSeparator,\n  CommandMenuClose,\n  CommandMenuProvider,\n  useCommandMenu,\n};\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "component"
  ],
  "type": "registry:ui"
}