{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "command-styled",
  "title": "Command essentials",
  "description": "Styled command: the essentials set. 5 decorative command variations (SimpleCommand, GroupedCommand, IconCommand, GlassCommand, KbdCommand) on the ai2 token system, powered by framer-motion, reduced-motion aware and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "motion@^12.42.2",
    "lucide-react@^1.23.0"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/command-styled.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\nimport {\n  Calculator,\n  Calendar,\n  CreditCard,\n  FileText,\n  Search,\n  Settings,\n  Smile,\n  User,\n} from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Command family: 5 decorative SELF-CONTAINED command palettes. NO cmdk, NO radix,\n   NO portal. Each export is a complete palette: a search <input> (role=\"combobox\")\n   + a filtered list (role=\"listbox\" / role=\"option\"). Filtering is a\n   case-insensitive `includes` on the item label as the user types. The arrow keys\n   (Up/Down) move the highlighted index, Enter selects, Escape clears. With no\n   match there is an \"empty\" state. The panel is rendered INLINE (open) - always\n   visible on a documentation page. Color comes ONLY from tokens, via alpha\n   color-mix. Reduced motion through useReducedMotion. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst paletteWidth: Record<StyledSize, string> = {\n  sm: \"w-64\",\n  md: \"w-80\",\n  lg: \"w-96\",\n  xl: \"w-[28rem]\",\n}\n\nexport interface CommandItem {\n  label: React.ReactNode\n  group?: string\n  icon?: React.ReactNode\n  onSelect?: () => void\n}\n\ninterface CommandProps {\n  className?: string\n  size?: StyledSize\n  placeholder?: string\n  items?: CommandItem[]\n}\n\n/* The label can be a ReactNode; flatten it to plain text for filtering. */\nfunction nodeText(node: React.ReactNode): string {\n  if (node === null || node === undefined || typeof node === \"boolean\") return \"\"\n  if (typeof node === \"string\" || typeof node === \"number\") return String(node)\n  if (Array.isArray(node)) return node.map(nodeText).join(\"\")\n  if (React.isValidElement(node)) {\n    return nodeText((node.props as { children?: React.ReactNode }).children)\n  }\n  return \"\"\n}\n\n/* Default list: some entries carry a group and an icon; renders without props. */\nconst defaultItems: CommandItem[] = [\n  { label: \"Calendar\", group: \"Suggestions\", icon: <Calendar /> },\n  { label: \"Search Emoji\", group: \"Suggestions\", icon: <Smile /> },\n  { label: \"Calculator\", group: \"Suggestions\", icon: <Calculator /> },\n  { label: \"Profile\", group: \"Settings\", icon: <User /> },\n  { label: \"Billing\", group: \"Settings\", icon: <CreditCard /> },\n  { label: \"Settings\", group: \"Settings\", icon: <Settings /> },\n  { label: \"New Document\", group: \"Actions\", icon: <FileText /> },\n]\n\n/* Deterministic shortcut tokens (index-based, not random). */\nconst shortcutTokens = [\"K\", \"E\", \"C\", \"P\", \"B\", \"S\", \"N\", \"D\"]\n\n/* Ortak durum: sorgu, filtrelenmis liste, vurgulanan index ve klavye idaresi. */\nfunction useCommandState(source: CommandItem[]) {\n  const [query, setQuery] = React.useState(\"\")\n  const [active, setActive] = React.useState(0)\n\n  const filtered = React.useMemo(() => {\n    const q = query.trim().toLowerCase()\n    if (q.length === 0) return source\n    return source.filter((item) => nodeText(item.label).toLowerCase().includes(q))\n  }, [query, source])\n\n  React.useEffect(() => {\n    setActive((prev) => (prev >= filtered.length ? 0 : prev))\n  }, [filtered.length])\n\n  const select = React.useCallback(\n    (index: number) => {\n      const item = filtered[index]\n      if (item) item.onSelect?.()\n    },\n    [filtered]\n  )\n\n  const onKeyDown = React.useCallback(\n    (e: React.KeyboardEvent<HTMLInputElement>) => {\n      if (e.key === \"ArrowDown\") {\n        e.preventDefault()\n        setActive((prev) => (filtered.length === 0 ? 0 : (prev + 1) % filtered.length))\n      } else if (e.key === \"ArrowUp\") {\n        e.preventDefault()\n        setActive((prev) =>\n          filtered.length === 0 ? 0 : (prev - 1 + filtered.length) % filtered.length\n        )\n      } else if (e.key === \"Enter\") {\n        e.preventDefault()\n        select(active)\n      } else if (e.key === \"Escape\") {\n        e.preventDefault()\n        setQuery(\"\")\n        setActive(0)\n      }\n    },\n    [filtered.length, active, select]\n  )\n\n  return { query, setQuery, active, setActive, filtered, select, onKeyDown }\n}\n\nconst rootBase =\n  \"flex flex-col overflow-hidden rounded-xl border border-border bg-popover text-popover-foreground shadow-lg outline-none\"\n\nconst inputRowBase = \"flex items-center gap-2 border-b border-border px-3\"\n\nconst inputBase =\n  \"h-11 w-full bg-transparent py-3 text-sm text-popover-foreground outline-none placeholder:text-muted-foreground\"\n\nconst listBase = \"max-h-72 overflow-y-auto p-1.5\"\n\nconst optionBase =\n  \"flex cursor-pointer select-none items-center gap-2 rounded-lg px-2.5 py-2 text-sm outline-none transition-colors [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground [&_i]:text-base [&_i]:leading-none [&_i]:text-muted-foreground\"\n\nconst emptyBase = \"py-6 text-center text-sm text-muted-foreground\"\n\n/* Fade transition of the highlighted row; no animation under reduced motion. */\nfunction OptionMotion({\n  active,\n  reduce,\n  children,\n  ...rest\n}: {\n  active: boolean\n  reduce: boolean\n  children: React.ReactNode\n} & React.ComponentProps<typeof motion.div>) {\n  return (\n    <motion.div\n      initial={false}\n      animate={{ opacity: 1 }}\n      transition={reduce ? { duration: 0 } : { duration: 0.15, ease: \"easeOut\" }}\n      {...rest}\n    >\n      {children}\n    </motion.div>\n  )\n}\n\n/* SimpleCommand: temiz arama + duz liste. */\nexport function SimpleCommand({\n  className,\n  size = \"md\",\n  placeholder = \"Type a command...\",\n  items = defaultItems,\n}: CommandProps) {\n  const { query, setQuery, active, setActive, filtered, select, onKeyDown } =\n    useCommandState(items)\n  const reduce = useReducedMotion() ?? false\n\n  return (\n    <div\n      data-slot=\"styled-command\"\n      role=\"listbox\"\n      aria-label=\"Command palette\"\n      className={cn(rootBase, paletteWidth[size], className)}\n    >\n      <div className={inputRowBase}>\n        <Search className=\"size-4 shrink-0 text-muted-foreground\" />\n        <input\n          role=\"combobox\"\n          aria-expanded=\"true\"\n          aria-controls=\"styled-command-simple-list\"\n          aria-autocomplete=\"list\"\n          value={query}\n          placeholder={placeholder}\n          onChange={(e) => setQuery(e.target.value)}\n          onKeyDown={onKeyDown}\n          className={inputBase}\n        />\n      </div>\n      <div id=\"styled-command-simple-list\" className={listBase}>\n        {filtered.length === 0 ? (\n          <p className={emptyBase}>No results found.</p>\n        ) : (\n          filtered.map((item, i) => (\n            <OptionMotion\n              key={`${nodeText(item.label)}-${i}`}\n              active={i === active}\n              reduce={reduce}\n              role=\"option\"\n              aria-selected={i === active}\n              onMouseEnter={() => setActive(i)}\n              onClick={() => select(i)}\n              className={cn(optionBase, i === active && \"bg-accent text-accent-foreground\")}\n            >\n              {item.label}\n            </OptionMotion>\n          ))\n        )}\n      </div>\n    </div>\n  )\n}\n\n/* GroupedCommand: item'lar token grup basliklari altinda kumelenir. */\nexport function GroupedCommand({\n  className,\n  size = \"md\",\n  placeholder = \"Type a command...\",\n  items = defaultItems,\n}: CommandProps) {\n  const { query, setQuery, active, setActive, filtered, select, onKeyDown } =\n    useCommandState(items)\n  const reduce = useReducedMotion() ?? false\n\n  /* Group the flat filtered list while preserving the global index. */\n  const groups = React.useMemo(() => {\n    const map = new Map<string, { item: CommandItem; index: number }[]>()\n    filtered.forEach((item, index) => {\n      const key = item.group ?? \"Other\"\n      const bucket = map.get(key)\n      if (bucket) bucket.push({ item, index })\n      else map.set(key, [{ item, index }])\n    })\n    return Array.from(map.entries())\n  }, [filtered])\n\n  return (\n    <div\n      data-slot=\"styled-command\"\n      role=\"listbox\"\n      aria-label=\"Command palette\"\n      className={cn(rootBase, paletteWidth[size], className)}\n    >\n      <div className={inputRowBase}>\n        <Search className=\"size-4 shrink-0 text-muted-foreground\" />\n        <input\n          role=\"combobox\"\n          aria-expanded=\"true\"\n          aria-controls=\"styled-command-grouped-list\"\n          aria-autocomplete=\"list\"\n          value={query}\n          placeholder={placeholder}\n          onChange={(e) => setQuery(e.target.value)}\n          onKeyDown={onKeyDown}\n          className={inputBase}\n        />\n      </div>\n      <div id=\"styled-command-grouped-list\" className={listBase}>\n        {filtered.length === 0 ? (\n          <p className={emptyBase}>No results found.</p>\n        ) : (\n          groups.map(([group, entries]) => (\n            <div key={group} className=\"pb-1 last:pb-0\">\n              <p className=\"px-2.5 py-1.5 text-xs font-medium text-muted-foreground\">{group}</p>\n              {entries.map(({ item, index }) => (\n                <OptionMotion\n                  key={`${nodeText(item.label)}-${index}`}\n                  active={index === active}\n                  reduce={reduce}\n                  role=\"option\"\n                  aria-selected={index === active}\n                  onMouseEnter={() => setActive(index)}\n                  onClick={() => select(index)}\n                  className={cn(\n                    optionBase,\n                    index === active && \"bg-accent text-accent-foreground\"\n                  )}\n                >\n                  {item.icon}\n                  {item.label}\n                </OptionMotion>\n              ))}\n            </div>\n          ))\n        )}\n      </div>\n    </div>\n  )\n}\n\n/* IconCommand: every item carries a leading token icon plus a right-aligned hint. */\nexport function IconCommand({\n  className,\n  size = \"md\",\n  placeholder = \"Type a command...\",\n  items = defaultItems,\n}: CommandProps) {\n  const { query, setQuery, active, setActive, filtered, select, onKeyDown } =\n    useCommandState(items)\n  const reduce = useReducedMotion() ?? false\n\n  return (\n    <div\n      data-slot=\"styled-command\"\n      role=\"listbox\"\n      aria-label=\"Command palette\"\n      className={cn(rootBase, paletteWidth[size], className)}\n    >\n      <div className={inputRowBase}>\n        <Search className=\"size-4 shrink-0 text-muted-foreground\" />\n        <input\n          role=\"combobox\"\n          aria-expanded=\"true\"\n          aria-controls=\"styled-command-icon-list\"\n          aria-autocomplete=\"list\"\n          value={query}\n          placeholder={placeholder}\n          onChange={(e) => setQuery(e.target.value)}\n          onKeyDown={onKeyDown}\n          className={inputBase}\n        />\n      </div>\n      <div id=\"styled-command-icon-list\" className={listBase}>\n        {filtered.length === 0 ? (\n          <p className={emptyBase}>No results found.</p>\n        ) : (\n          filtered.map((item, i) => (\n            <OptionMotion\n              key={`${nodeText(item.label)}-${i}`}\n              active={i === active}\n              reduce={reduce}\n              role=\"option\"\n              aria-selected={i === active}\n              onMouseEnter={() => setActive(i)}\n              onClick={() => select(i)}\n              className={cn(optionBase, i === active && \"bg-accent text-accent-foreground\")}\n            >\n              {item.icon ?? <Search />}\n              <span className=\"flex-1 truncate\">{item.label}</span>\n              <span className=\"shrink-0 text-xs text-muted-foreground\">\n                {item.group ?? \"Jump to\"}\n              </span>\n            </OptionMotion>\n          ))\n        )}\n      </div>\n    </div>\n  )\n}\n\n/* GlassCommand: buzlu cam palet + backdrop-blur. */\nexport function GlassCommand({\n  className,\n  size = \"md\",\n  placeholder = \"Type a command...\",\n  items = defaultItems,\n}: CommandProps) {\n  const { query, setQuery, active, setActive, filtered, select, onKeyDown } =\n    useCommandState(items)\n  const reduce = useReducedMotion() ?? false\n\n  return (\n    <div\n      data-slot=\"styled-command\"\n      role=\"listbox\"\n      aria-label=\"Command palette\"\n      className={cn(\n        \"flex flex-col overflow-hidden rounded-xl border border-border text-popover-foreground shadow-lg outline-none\",\n        \"bg-[color-mix(in_oklab,var(--color-popover)_75%,transparent)] supports-[backdrop-filter]:bg-[color-mix(in_oklab,var(--color-popover)_55%,transparent)] supports-[backdrop-filter]:backdrop-blur-xl\",\n        \"shadow-[inset_0_1px_0_color-mix(in_oklab,var(--color-foreground)_12%,transparent)]\",\n        paletteWidth[size],\n        className\n      )}\n    >\n      <div className={inputRowBase}>\n        <Search className=\"size-4 shrink-0 text-muted-foreground\" />\n        <input\n          role=\"combobox\"\n          aria-expanded=\"true\"\n          aria-controls=\"styled-command-glass-list\"\n          aria-autocomplete=\"list\"\n          value={query}\n          placeholder={placeholder}\n          onChange={(e) => setQuery(e.target.value)}\n          onKeyDown={onKeyDown}\n          className={inputBase}\n        />\n      </div>\n      <div id=\"styled-command-glass-list\" className={listBase}>\n        {filtered.length === 0 ? (\n          <p className={emptyBase}>No results found.</p>\n        ) : (\n          filtered.map((item, i) => (\n            <OptionMotion\n              key={`${nodeText(item.label)}-${i}`}\n              active={i === active}\n              reduce={reduce}\n              role=\"option\"\n              aria-selected={i === active}\n              onMouseEnter={() => setActive(i)}\n              onClick={() => select(i)}\n              className={cn(\n                optionBase,\n                i === active &&\n                  \"bg-[color-mix(in_oklab,var(--color-accent)_70%,transparent)] text-accent-foreground\"\n              )}\n            >\n              {item.icon}\n              {item.label}\n            </OptionMotion>\n          ))\n        )}\n      </div>\n    </div>\n  )\n}\n\n/* KbdCommand: every item shows a right-aligned token keyboard-shortcut chip. */\nexport function KbdCommand({\n  className,\n  size = \"md\",\n  placeholder = \"Type a command...\",\n  items = defaultItems,\n}: CommandProps) {\n  const { query, setQuery, active, setActive, filtered, select, onKeyDown } =\n    useCommandState(items)\n  const reduce = useReducedMotion() ?? false\n\n  return (\n    <div\n      data-slot=\"styled-command\"\n      role=\"listbox\"\n      aria-label=\"Command palette\"\n      className={cn(rootBase, paletteWidth[size], className)}\n    >\n      <div className={inputRowBase}>\n        <Search className=\"size-4 shrink-0 text-muted-foreground\" />\n        <input\n          role=\"combobox\"\n          aria-expanded=\"true\"\n          aria-controls=\"styled-command-kbd-list\"\n          aria-autocomplete=\"list\"\n          value={query}\n          placeholder={placeholder}\n          onChange={(e) => setQuery(e.target.value)}\n          onKeyDown={onKeyDown}\n          className={inputBase}\n        />\n      </div>\n      <div id=\"styled-command-kbd-list\" className={listBase}>\n        {filtered.length === 0 ? (\n          <p className={emptyBase}>No results found.</p>\n        ) : (\n          filtered.map((item, i) => (\n            <OptionMotion\n              key={`${nodeText(item.label)}-${i}`}\n              active={i === active}\n              reduce={reduce}\n              role=\"option\"\n              aria-selected={i === active}\n              onMouseEnter={() => setActive(i)}\n              onClick={() => select(i)}\n              className={cn(optionBase, i === active && \"bg-accent text-accent-foreground\")}\n            >\n              {item.icon}\n              <span className=\"flex-1 truncate\">{item.label}</span>\n              <kbd className=\"pointer-events-none inline-flex h-5 shrink-0 select-none items-center gap-1 rounded border border-border bg-[color-mix(in_oklab,var(--color-foreground)_6%,transparent)] px-1.5 font-mono text-[10px] font-medium text-muted-foreground\">\n                <span className=\"text-xs\">{\"⌘\"}</span>\n                {shortcutTokens[i % shortcutTokens.length]}\n              </kbd>\n            </OptionMotion>\n          ))\n        )}\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/command-styled.tsx"
    }
  ],
  "categories": [
    "styled",
    "command"
  ],
  "type": "registry:component"
}