{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "command-palette",
  "title": "Command Palette",
  "author": "Poyraz Avsever",
  "description": "Poyraz Soft Glass command-palette component with standardized floating and overlay behavior.",
  "dependencies": [
    "@radix-ui/react-dialog@^1.1.15",
    "@radix-ui/react-visually-hidden@^1.2.4",
    "lucide-react@^0.574.0"
  ],
  "registryDependencies": [
    "@poyraz/poyraz-utils",
    "@poyraz/poyraz-theme",
    "@poyraz/poyraz-recipes"
  ],
  "files": [
    {
      "path": "registry/poyraz/ui/phase6/molecules/command-palette.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { VisuallyHidden } from \"@radix-ui/react-visually-hidden\";\nimport { Search, X } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\nimport {\n  floatingItemVariants,\n  overlaySurfaceVariants,\n  overlayVariants,\n  type FloatingItemProps,\n  type OverlayProps,\n  type OverlaySurfaceProps,\n} from \"@/components/ui/recipes\";\n\n/* ================================================================== */\n/*  COMMAND PALETTE — Cmd+K global search / command overlay            */\n/* ================================================================== */\n\n/* ── Context ──────────────────────────────────────────────────────── */\n\ninterface CommandPaletteContextValue {\n  search: string;\n  setSearch: React.Dispatch<React.SetStateAction<string>>;\n  selectedItemId: string | null;\n  setSelectedItemId: React.Dispatch<React.SetStateAction<string | null>>;\n}\n\nconst CommandPaletteCtx = React.createContext<CommandPaletteContextValue>({\n  search: \"\",\n  setSearch: () => {},\n  selectedItemId: null,\n  setSelectedItemId: () => {},\n});\n\n/* ── Root ─────────────────────────────────────────────────────────── */\n\ninterface CommandPaletteProps extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root> {\n  children: React.ReactNode;\n}\n\nfunction CommandPalette({ children, ...props }: CommandPaletteProps) {\n  const [search, setSearch] = React.useState(\"\");\n  const [selectedItemId, setSelectedItemId] = React.useState<string | null>(null);\n\n  // Reset search when closed\n  const handleOpenChange = (open: boolean) => {\n    if (!open) {\n      setSearch(\"\");\n      setSelectedItemId(null);\n    }\n    props.onOpenChange?.(open);\n  };\n\n  return (\n    <CommandPaletteCtx.Provider value={{ search, setSearch, selectedItemId, setSelectedItemId }}>\n      <DialogPrimitive.Root {...props} onOpenChange={handleOpenChange}>\n        {children}\n      </DialogPrimitive.Root>\n    </CommandPaletteCtx.Provider>\n  );\n}\nCommandPalette.displayName = \"CommandPalette\";\n\n/* ── Trigger ──────────────────────────────────────────────────────── */\n\nconst CommandPaletteTrigger = DialogPrimitive.Trigger;\n\n/* ── Content ──────────────────────────────────────────────────────── */\n\nconst CommandPaletteContent = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> &\n    OverlaySurfaceProps & {\n      overlayTone?: NonNullable<OverlayProps[\"tone\"]>;\n      overlayClassName?: string;\n      mobile?: \"floating\" | \"fullscreen\";\n    }\n>(\n  (\n    {\n      className,\n      children,\n      surface,\n      radius,\n      overlayTone,\n      overlayClassName,\n      mobile = \"floating\",\n      ...props\n    },\n    ref,\n  ) => (\n    <DialogPrimitive.Portal>\n      <DialogPrimitive.Overlay\n        className={cn(overlayVariants({ tone: overlayTone }), overlayClassName)}\n      />\n      <DialogPrimitive.Content\n        ref={ref}\n        className={cn(\n          overlaySurfaceVariants({ surface, radius }),\n          \"fixed left-1/2 top-[clamp(5rem,14vh,8rem)] z-50 w-[calc(100%-2rem)] max-w-2xl [--poyraz-command-translate-x:-50%]\",\n          \"overflow-hidden shadow-[0_28px_90px_-28px_rgb(0_0_0/0.5)] ring-1 ring-foreground/5\",\n          \"data-[state=open]:animate-poyraz-command-in data-[state=closed]:animate-poyraz-command-out\",\n          mobile === \"fullscreen\" &&\n            \"max-sm:inset-0 max-sm:h-dvh max-sm:w-full max-sm:max-w-none max-sm:rounded-none max-sm:[--poyraz-command-translate-x:0%]\",\n          className,\n        )}\n        {...props}\n      >\n        <VisuallyHidden>\n          <DialogPrimitive.Title>Command Palette</DialogPrimitive.Title>\n        </VisuallyHidden>\n        {children}\n      </DialogPrimitive.Content>\n    </DialogPrimitive.Portal>\n  ),\n);\nCommandPaletteContent.displayName = \"CommandPaletteContent\";\n\n/* ── Input ────────────────────────────────────────────────────────── */\n\ninterface CommandPaletteInputProps extends Omit<\n  React.InputHTMLAttributes<HTMLInputElement>,\n  \"onChange\"\n> {\n  onValueChange?: (value: string) => void;\n}\n\nconst CommandPaletteInput = React.forwardRef<HTMLInputElement, CommandPaletteInputProps>(\n  ({ className, onValueChange, ...props }, ref) => {\n    const { search, setSearch } = React.useContext(CommandPaletteCtx);\n\n    return (\n      <div\n        className={cn(\n          \"flex items-center gap-3 border-b border-border bg-surface-subtle/55 px-5\",\n          \"transition-colors duration-[var(--poyraz-motion-duration-fast)] ease-[var(--poyraz-motion-ease-out)]\",\n        )}\n      >\n        <Search className=\"size-4.5 shrink-0 text-muted-foreground transition-transform duration-[var(--poyraz-motion-duration-fast)] ease-[var(--poyraz-motion-ease-out)]\" />\n        <input\n          ref={ref}\n          value={search}\n          onChange={(e) => {\n            setSearch(e.target.value);\n            onValueChange?.(e.target.value);\n          }}\n          className={cn(\n            \"flex h-14 w-full bg-transparent py-3\",\n            \"text-sm text-foreground placeholder:text-placeholder\",\n            \"outline-none\",\n            \"disabled:opacity-40 disabled:cursor-not-allowed\",\n            className,\n          )}\n          {...props}\n        />\n        <DialogPrimitive.Close className=\"flex size-8 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-[color,background-color,transform] duration-[var(--poyraz-motion-duration-fast)] ease-[var(--poyraz-motion-ease-out)] hover:scale-105 hover:bg-accent hover:text-foreground active:scale-95\">\n          <X className=\"h-4 w-4\" />\n          <span className=\"sr-only\">Close</span>\n        </DialogPrimitive.Close>\n      </div>\n    );\n  },\n);\nCommandPaletteInput.displayName = \"CommandPaletteInput\";\n\n/* ── List ─────────────────────────────────────────────────────────── */\n\nconst CommandPaletteList = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, ...props }, ref) => (\n    <div\n      ref={ref}\n      className={cn(\n        \"max-h-[min(56vh,26rem)] overflow-y-auto p-2.5 animate-poyraz-fade-in\",\n        className,\n      )}\n      role=\"listbox\"\n      {...props}\n    />\n  ),\n);\nCommandPaletteList.displayName = \"CommandPaletteList\";\n\n/* ── Group ────────────────────────────────────────────────────────── */\n\ninterface CommandPaletteGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n  heading?: string;\n}\n\nconst CommandPaletteGroup = React.forwardRef<HTMLDivElement, CommandPaletteGroupProps>(\n  ({ className, heading, children, ...props }, ref) => (\n    <div ref={ref} className={cn(\"py-1 animate-poyraz-fade-in\", className)} role=\"group\" {...props}>\n      {heading && (\n        <div className=\"px-2 py-1.5 text-[11px] font-bold uppercase tracking-widest text-placeholder\">\n          {heading}\n        </div>\n      )}\n      {children}\n    </div>\n  ),\n);\nCommandPaletteGroup.displayName = \"CommandPaletteGroup\";\n\n/* ── Item ─────────────────────────────────────────────────────────── */\n\ninterface CommandPaletteItemProps extends React.HTMLAttributes<HTMLDivElement> {\n  /** Keyboard shortcut hint (e.g. \"⌘K\") */\n  shortcut?: string;\n  disabled?: boolean;\n  /** Icon element */\n  icon?: React.ReactNode;\n  description?: React.ReactNode;\n  media?: React.ReactNode;\n  size?: NonNullable<FloatingItemProps[\"size\"]>;\n  radius?: NonNullable<FloatingItemProps[\"radius\"]>;\n}\n\nconst CommandPaletteItem = React.forwardRef<HTMLDivElement, CommandPaletteItemProps>(\n  (\n    {\n      className,\n      children,\n      shortcut,\n      disabled,\n      icon,\n      description,\n      media,\n      size,\n      radius,\n      onFocus,\n      onKeyDown,\n      ...props\n    },\n    ref,\n  ) => {\n    const itemId = React.useId();\n    const { selectedItemId, setSelectedItemId } = React.useContext(CommandPaletteCtx);\n    const controlledSelection = props[\"aria-selected\"];\n    const isSelected = controlledSelection ?? selectedItemId === itemId;\n\n    const moveFocus = (event: React.KeyboardEvent<HTMLDivElement>) => {\n      if (![\"ArrowDown\", \"ArrowUp\", \"Home\", \"End\"].includes(event.key)) return;\n\n      const list = event.currentTarget.closest('[role=\"listbox\"]');\n      const options = Array.from(\n        list?.querySelectorAll<HTMLElement>('[role=\"option\"]:not([aria-disabled=\"true\"])') ?? [],\n      );\n      if (options.length === 0) return;\n\n      event.preventDefault();\n      const currentIndex = options.indexOf(event.currentTarget);\n      const nextIndex =\n        event.key === \"Home\"\n          ? 0\n          : event.key === \"End\"\n            ? options.length - 1\n            : event.key === \"ArrowDown\"\n              ? (currentIndex + 1) % options.length\n              : (currentIndex - 1 + options.length) % options.length;\n      options[nextIndex]?.focus();\n    };\n\n    return (\n      <div\n        {...props}\n        ref={ref}\n        id={props.id ?? itemId}\n        role=\"option\"\n        aria-disabled={disabled || undefined}\n        aria-selected={isSelected}\n        className={cn(\n          floatingItemVariants({ size, radius }),\n          \"cursor-pointer border border-transparent hover:border-border hover:bg-accent\",\n          isSelected && \"border-border bg-accent\",\n          disabled && \"pointer-events-none opacity-40\",\n          className,\n        )}\n        tabIndex={disabled ? -1 : 0}\n        onFocus={(event) => {\n          if (controlledSelection === undefined) setSelectedItemId(itemId);\n          onFocus?.(event);\n        }}\n        onKeyDown={(event) => {\n          moveFocus(event);\n          onKeyDown?.(event);\n        }}\n      >\n        {media && (\n          <span className=\"flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-md bg-surface-subtle [&_img]:size-full [&_img]:object-cover\">\n            {media}\n          </span>\n        )}\n        {icon && (\n          <span className=\"text-placeholder shrink-0 transition-[color,transform] duration-[var(--poyraz-motion-duration-fast)] ease-[var(--poyraz-motion-ease-out)]\">\n            {icon}\n          </span>\n        )}\n        <span className=\"min-w-0 flex-1\">\n          <span className=\"block truncate font-medium\">{children}</span>\n          {description && (\n            <span className=\"mt-0.5 block truncate text-xs text-muted-foreground\">\n              {description}\n            </span>\n          )}\n        </span>\n        {shortcut && (\n          <kbd className=\"ml-auto text-[11px] font-mono tracking-wider text-placeholder border border-border px-1.5 py-0.5 transition-[color,background-color,border-color] duration-[var(--poyraz-motion-duration-fast)] ease-[var(--poyraz-motion-ease-out)]\">\n            {shortcut}\n          </kbd>\n        )}\n      </div>\n    );\n  },\n);\nCommandPaletteItem.displayName = \"CommandPaletteItem\";\n\n/* ── Empty ────────────────────────────────────────────────────────── */\n\nconst CommandPaletteEmpty = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, ...props }, ref) => (\n    <div\n      ref={ref}\n      className={cn(\"py-8 text-center text-sm text-placeholder animate-poyraz-fade-in\", className)}\n      {...props}\n    />\n  ),\n);\nCommandPaletteEmpty.displayName = \"CommandPaletteEmpty\";\n\n/* ── Separator ────────────────────────────────────────────────────── */\n\nconst CommandPaletteSeparator = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div ref={ref} className={cn(\"h-px bg-accent my-1 -mx-2\", className)} {...props} />\n));\nCommandPaletteSeparator.displayName = \"CommandPaletteSeparator\";\n\n/* ── Footer ───────────────────────────────────────────────────────── */\n\nconst CommandPaletteFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, ...props }, ref) => (\n    <div\n      ref={ref}\n      className={cn(\n        \"flex items-center gap-4 border-t border-border bg-surface-subtle/45 px-5 py-2.5\",\n        \"text-[11px] text-placeholder\",\n        className,\n      )}\n      {...props}\n    />\n  ),\n);\nCommandPaletteFooter.displayName = \"CommandPaletteFooter\";\n\n/* ── Hook: useCommandPalette ──────────────────────────────────────── */\n\nfunction useCommandPalette() {\n  return React.useContext(CommandPaletteCtx);\n}\n\n/* ================================================================== */\n/*  EXPORTS                                                            */\n/* ================================================================== */\n\nexport {\n  CommandPalette,\n  CommandPaletteTrigger,\n  CommandPaletteContent,\n  CommandPaletteInput,\n  CommandPaletteList,\n  CommandPaletteGroup,\n  CommandPaletteItem,\n  CommandPaletteEmpty,\n  CommandPaletteSeparator,\n  CommandPaletteFooter,\n  useCommandPalette,\n};\n",
      "type": "registry:ui",
      "target": "@ui/molecules/command-palette.tsx"
    }
  ],
  "meta": {
    "category": "phase-6-interactive",
    "phase": "beta"
  },
  "type": "registry:ui"
}