{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "command-compact",
  "title": "Compact command",
  "description": "Styled command: the Compact set. 5 decorative command variations (DenseCommand, MinimalCommand, NarrowCommand, InlineCommand, BareCommand) 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-compact.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { 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/* Compact command family: 5 SELF-CONTAINED command palettes, all of them DENSE /\n   low-chrome layouts. NO cmdk, NO radix, NO portal. The difference is in row\n   height, typography and how much chrome there is: dense (tight rows), minimal\n   (no chrome, a thin separator), narrow (a narrow column), inline (a field that\n   sits inside a form), bare (no chrome at all). All of them are complete palettes:\n   typing filters, the arrow keys move the highlight, Enter selects, Escape clears.\n   Color comes ONLY from tokens, via alpha color-mix. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst wideWidth: Record<StyledSize, string> = {\n  sm: \"w-64\",\n  md: \"w-80\",\n  lg: \"w-96\",\n  xl: \"w-[28rem]\",\n}\n\nconst narrowWidth: Record<StyledSize, string> = {\n  sm: \"w-48\",\n  md: \"w-56\",\n  lg: \"w-64\",\n  xl: \"w-72\",\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/* Varsayilan liste: prop'suz render eder. */\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/* Shared state plus combobox/listbox ids. useId keeps several instances on\n   the same page from colliding. */\nfunction usePalette(source: CommandItem[]) {\n  const uid = React.useId()\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  const listId = `${uid}-list`\n  const optionId = React.useCallback((i: number) => `${uid}-option-${i}`, [uid])\n\n  return { query, setQuery, active, setActive, filtered, select, onKeyDown, listId, optionId }\n}\n\n/* One density picture: shell, search row, list and option measurements. */\ninterface Density {\n  root: string\n  widths: Record<StyledSize, string>\n  inputRow: string\n  input: string\n  list: string\n  option: string\n  activeOption: string\n  empty: string\n  showIcons: boolean\n  searchIcon: boolean\n}\n\nconst iconCompat =\n  \"[&_svg]:shrink-0 [&_svg]:text-muted-foreground [&_i]:leading-none [&_i]:text-muted-foreground\"\n\nconst focusRing = \"focus-visible:ring-[3px] focus-visible:ring-ring/50\"\n\n/* Shared shell: every density uses this, only Density changes. */\nfunction CompactPalette({ props, d }: { props: CommandProps; d: Density }) {\n  const {\n    className,\n    size = \"md\",\n    placeholder = \"Search...\",\n    items = defaultItems,\n  } = props\n  const p = usePalette(items)\n  const reduce = useReducedMotion() ?? false\n\n  return (\n    <div data-slot=\"styled-command\" className={cn(d.root, d.widths[size], className)}>\n      <div className={d.inputRow}>\n        {d.searchIcon ? (\n          <Search className=\"size-3.5 shrink-0 text-muted-foreground\" />\n        ) : null}\n        <input\n          role=\"combobox\"\n          aria-expanded={true}\n          aria-controls={p.listId}\n          aria-autocomplete=\"list\"\n          aria-activedescendant={p.filtered.length > 0 ? p.optionId(p.active) : undefined}\n          value={p.query}\n          placeholder={placeholder}\n          onChange={(e) => p.setQuery(e.target.value)}\n          onKeyDown={p.onKeyDown}\n          className={cn(d.input, focusRing)}\n        />\n      </div>\n      <div id={p.listId} role=\"listbox\" aria-label=\"Command palette\" className={d.list}>\n        {p.filtered.length === 0 ? (\n          <p className={d.empty}>No results</p>\n        ) : (\n          p.filtered.map((item, i) => (\n            <motion.div\n              key={nodeText(item.label) || String(i)}\n              id={p.optionId(i)}\n              role=\"option\"\n              aria-selected={i === p.active}\n              tabIndex={-1}\n              initial={{ opacity: 0 }}\n              animate={{ opacity: 1 }}\n              transition={reduce ? { duration: 0 } : { duration: 0.12, ease: \"easeOut\" }}\n              onMouseEnter={() => p.setActive(i)}\n              onClick={() => p.select(i)}\n              className={cn(d.option, iconCompat, focusRing, i === p.active && d.activeOption)}\n            >\n              {d.showIcons ? item.icon : null}\n              <span className=\"flex-1 truncate\">{item.label}</span>\n            </motion.div>\n          ))\n        )}\n      </div>\n    </div>\n  )\n}\n\n/* DenseCommand: the full shell, but with the rows and the search field tightened. */\nexport function DenseCommand(props: CommandProps) {\n  return (\n    <CompactPalette\n      props={props}\n      d={{\n        root: \"flex flex-col overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-md outline-none\",\n        widths: wideWidth,\n        inputRow: \"flex items-center gap-1.5 border-b border-border px-2\",\n        input:\n          \"h-8 w-full rounded bg-transparent py-1 text-xs text-popover-foreground outline-none placeholder:text-muted-foreground\",\n        list: \"max-h-56 overflow-y-auto p-1\",\n        option:\n          \"flex cursor-pointer select-none items-center gap-1.5 rounded px-1.5 py-1 text-xs outline-none transition-colors [&_svg]:size-3.5 [&_i]:text-xs\",\n        activeOption: \"bg-accent text-accent-foreground\",\n        empty: \"py-3 text-center text-xs text-muted-foreground\",\n        showIcons: true,\n        searchIcon: true,\n      }}\n    />\n  )\n}\n\n/* MinimalCommand: no shell, just a thin separator and a plain list. */\nexport function MinimalCommand(props: CommandProps) {\n  return (\n    <CompactPalette\n      props={props}\n      d={{\n        root: \"flex flex-col overflow-hidden bg-transparent text-foreground outline-none\",\n        widths: wideWidth,\n        inputRow: \"flex items-center gap-2 border-b border-border px-1\",\n        input:\n          \"h-9 w-full rounded bg-transparent py-2 text-sm text-foreground outline-none placeholder:text-muted-foreground\",\n        list: \"max-h-64 overflow-y-auto py-1\",\n        option:\n          \"flex cursor-pointer select-none items-center gap-2 rounded-md px-1.5 py-1.5 text-sm outline-none transition-colors [&_svg]:size-4 [&_i]:text-base\",\n        activeOption: \"bg-accent text-accent-foreground\",\n        empty: \"py-4 text-center text-sm text-muted-foreground\",\n        showIcons: false,\n        searchIcon: false,\n      }}\n    />\n  )\n}\n\n/* NarrowCommand: a narrow column; at the width of a side panel or a menu. */\nexport function NarrowCommand(props: CommandProps) {\n  return (\n    <CompactPalette\n      props={props}\n      d={{\n        root: \"flex flex-col overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-md outline-none\",\n        widths: narrowWidth,\n        inputRow: \"flex items-center gap-1.5 border-b border-border px-2\",\n        input:\n          \"h-9 w-full rounded bg-transparent py-2 text-xs text-popover-foreground outline-none placeholder:text-muted-foreground\",\n        list: \"max-h-52 overflow-y-auto p-1\",\n        option:\n          \"flex cursor-pointer select-none items-center gap-1.5 rounded px-1.5 py-1.5 text-xs outline-none transition-colors [&_svg]:size-3.5 [&_i]:text-xs\",\n        activeOption: \"bg-accent text-accent-foreground\",\n        empty: \"py-3 text-center text-xs text-muted-foreground\",\n        showIcons: true,\n        searchIcon: true,\n      }}\n    />\n  )\n}\n\n/* InlineCommand: form alani gibi gorunen arama kutusu, liste hemen altinda. */\nexport function InlineCommand(props: CommandProps) {\n  return (\n    <CompactPalette\n      props={props}\n      d={{\n        root: \"flex flex-col gap-1 bg-transparent text-foreground outline-none\",\n        widths: wideWidth,\n        inputRow:\n          \"flex items-center gap-2 rounded-md border border-field-border bg-background px-2.5 shadow-xs\",\n        input:\n          \"h-9 w-full rounded bg-transparent py-2 text-sm text-foreground outline-none placeholder:text-muted-foreground\",\n        list: \"max-h-56 overflow-y-auto rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-sm\",\n        option:\n          \"flex cursor-pointer select-none items-center gap-2 rounded px-2 py-1.5 text-sm outline-none transition-colors [&_svg]:size-4 [&_i]:text-base\",\n        activeOption: \"bg-accent text-accent-foreground\",\n        empty: \"py-3 text-center text-sm text-muted-foreground\",\n        showIcons: true,\n        searchIcon: true,\n      }}\n    />\n  )\n}\n\n/* BareCommand: no chrome at all; the selected row is marked only by text tone. */\nexport function BareCommand(props: CommandProps) {\n  return (\n    <CompactPalette\n      props={props}\n      d={{\n        root: \"flex flex-col bg-transparent text-foreground outline-none\",\n        widths: wideWidth,\n        inputRow: \"flex items-center gap-2 px-0\",\n        input:\n          \"h-8 w-full rounded bg-transparent py-1 text-sm font-medium text-foreground outline-none placeholder:text-muted-foreground\",\n        list: \"max-h-64 overflow-y-auto py-1\",\n        option:\n          \"flex cursor-pointer select-none items-center gap-2 rounded px-0 py-1 text-sm text-muted-foreground outline-none transition-colors [&_svg]:size-4 [&_i]:text-base\",\n        activeOption: \"text-foreground underline underline-offset-4\",\n        empty: \"py-3 text-sm text-muted-foreground\",\n        showIcons: false,\n        searchIcon: false,\n      }}\n    />\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/command-compact.tsx"
    }
  ],
  "categories": [
    "styled",
    "command"
  ],
  "type": "registry:component"
}