{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "command",
  "title": "Command",
  "description": "A command palette with its own fuzzy search: groups, shortcuts, recent items, an empty state, full combobox and listbox semantics and a Mod+K helper.",
  "dependencies": [
    "lucide-react",
    "motion",
    "radix-ui"
  ],
  "registryDependencies": [
    "utils",
    "https://miseui.junsu4621.workers.dev/r/motion.json"
  ],
  "files": [
    {
      "path": "registry/miseui/ui/command.tsx",
      "content": "import type * as React from \"react\"\nimport { useRef, useState } from \"react\"\nimport { Dialog as DialogPrimitive } from \"radix-ui\"\nimport { Search } from \"lucide-react\"\nimport { CommandList } from \"@/registry/miseui/ui/command-list\"\nimport { pushRecent, type CommandItem } from \"@/registry/miseui/ui/command-score\"\nimport { useCommand, useCommandShortcut } from \"@/registry/miseui/ui/command-state\"\nimport {\n  INPUT_ROW_CLASS,\n  INPUT_TEXT_CLASS,\n  LIST_HEIGHT_CLASS,\n  MATERIALIZE_CLASS,\n  MODAL_POSITION_CLASS,\n  OVERLAY_CLASS,\n  PLAIN_MOTION_CLASS,\n  SURFACE_CLASS,\n  type CommandEffect,\n  type CommandVariant,\n} from \"@/registry/miseui/ui/command-classes\"\nimport { cn } from \"@/registry/miseui/lib/utils\"\n\nconst EMPTY_RECENT: readonly string[] = []\n\ntype CommandProps = Omit<React.ComponentProps<\"div\">, \"onSelect\" | \"defaultValue\" | \"value\"> & {\n  items: readonly CommandItem[]\n  /** `dialog` a centred palette, `inline` part of the page, `compact` dense rows for small spaces, `spotlight` one large input with results below. */\n  variant?: CommandVariant\n  /** `materialize` brings the palette into focus and lets its height spring as results change. */\n  effect?: CommandEffect\n  /** Values to lead the list while nothing is typed, newest first. Keep it with `pushRecent`. */\n  recent?: readonly string[]\n  recentLabel?: string\n  placeholder?: string\n  emptyText?: React.ReactNode\n  /** Name for assistive tech, and the dialog title. */\n  label?: string\n  onSelect?: (value: string, item: CommandItem) => void\n  /** Open state of `dialog` and `spotlight`. Pair with `useCommandShortcut` for Mod+K. */\n  open?: boolean\n  defaultOpen?: boolean\n  onOpenChange?: (open: boolean) => void\n}\n\ntype PaletteProps = Pick<CommandProps, \"items\" | \"recent\" | \"recentLabel\" | \"placeholder\" | \"emptyText\" | \"label\" | \"onSelect\"> & {\n  variant: CommandVariant\n  springHeight: boolean\n  inputRef?: React.Ref<HTMLInputElement>\n  onDone?: () => void\n}\n\ntype CommandState = ReturnType<typeof useCommand>\n\ninterface CommandInputRowProps {\n  state: Pick<CommandState, \"query\" | \"setQuery\" | \"listId\" | \"activeOptionId\" | \"onKeyDown\">\n  variant: CommandVariant\n  label: string\n  placeholder: string\n  expanded: boolean\n  divided: boolean\n  inputRef?: React.Ref<HTMLInputElement>\n}\n\nfunction CommandInputRow({ state, variant, label, placeholder, expanded, divided, inputRef }: CommandInputRowProps) {\n  return (\n    <div data-slot=\"command-input-row\" className={cn(\"flex shrink-0 items-center\", INPUT_ROW_CLASS[variant], divided && \"border-b\")}>\n      <Search aria-hidden=\"true\" className={cn(\"shrink-0 text-muted-foreground\", variant === \"spotlight\" ? \"size-5\" : \"size-4\")} />\n      <input\n        ref={inputRef}\n        role=\"combobox\"\n        aria-label={label}\n        aria-expanded={expanded}\n        aria-controls={state.listId}\n        aria-autocomplete=\"list\"\n        aria-activedescendant={state.activeOptionId}\n        autoComplete=\"off\"\n        autoCorrect=\"off\"\n        spellCheck={false}\n        placeholder={placeholder}\n        value={state.query}\n        onChange={(event) => state.setQuery(event.target.value)}\n        onKeyDown={state.onKeyDown}\n        className={cn(\"h-full w-full min-w-0 bg-transparent outline-none placeholder:text-muted-foreground\", INPUT_TEXT_CLASS[variant])}\n      />\n    </div>\n  )\n}\n\nfunction CommandFooter() {\n  return (\n    <div data-slot=\"command-footer\" className=\"flex items-center gap-4 border-t px-4 py-2 text-[13px] text-muted-foreground pointer-coarse:hidden\">\n      <span>↑↓ to move</span>\n      <span>Enter to run</span>\n      <span className=\"ml-auto\">Esc to close</span>\n    </div>\n  )\n}\n\nfunction Palette({ items, recent = EMPTY_RECENT, recentLabel = \"Recent\", placeholder = \"Type a command or search\", emptyText = \"No results.\", label = \"Command menu\", onSelect, variant, springHeight, inputRef, onDone }: PaletteProps) {\n  const modal = variant === \"dialog\" || variant === \"spotlight\"\n  const state = useCommand({ items, recent, recentLabel, clearOnEscape: !modal, recentOnly: variant === \"spotlight\", onSelect, onDone })\n  const { query, visible } = state\n  const hasQuery = query.trim().length > 0\n  // A spotlight is only the input until it has something to say.\n  const showList = visible.length > 0 || hasQuery || variant !== \"spotlight\"\n  return (\n    <>\n      <CommandInputRow {...{ state, variant, label, placeholder, inputRef }} expanded={visible.length > 0} divided={showList} />\n      {showList ? (\n        <CommandList\n          listId={state.listId}\n          label={label}\n          sections={state.sections}\n          visible={visible}\n          active={state.active}\n          dense={variant === \"compact\"}\n          maxHeightClass={LIST_HEIGHT_CLASS[variant]}\n          springHeight={springHeight}\n          emptyText={emptyText}\n          showEmpty={hasQuery && visible.length === 0}\n          onActive={state.setActiveValue}\n          onPick={state.select}\n        />\n      ) : null}\n      <span role=\"status\" data-slot=\"command-status\" className=\"sr-only\">\n        {hasQuery ? `${visible.length} ${visible.length === 1 ? \"result\" : \"results\"}` : \"\"}\n      </span>\n      {variant === \"dialog\" ? <CommandFooter /> : null}\n    </>\n  )\n}\n\ninterface ModalCommandProps extends Pick<CommandProps, \"open\" | \"defaultOpen\" | \"onOpenChange\" | \"effect\" | \"className\"> {\n  palette: Omit<PaletteProps, \"inputRef\" | \"onDone\">\n  variant: CommandVariant\n  label: string\n  rest: React.ComponentProps<\"div\">\n}\n\nfunction ModalCommand({ open: openProp, defaultOpen = false, onOpenChange, palette, variant, effect, label, className, rest }: ModalCommandProps) {\n  const [uncontrolled, setUncontrolled] = useState(defaultOpen)\n  const inputRef = useRef<HTMLInputElement>(null)\n  // There is no Radix trigger here: the palette opens from a shortcut or from any button, so it remembers for itself.\n  const returnTo = useRef<HTMLElement | null>(null)\n  const open = openProp ?? uncontrolled\n  const setOpen = (next: boolean) => {\n    setUncontrolled(next)\n    onOpenChange?.(next)\n  }\n  return (\n    <DialogPrimitive.Root open={open} onOpenChange={setOpen}>\n      <DialogPrimitive.Portal>\n        <DialogPrimitive.Overlay data-slot=\"command-overlay\" className={OVERLAY_CLASS} />\n        <DialogPrimitive.Content\n          data-slot=\"command\"\n          data-variant={variant}\n          data-effect={effect}\n          aria-describedby={undefined}\n          onOpenAutoFocus={(event) => {\n            returnTo.current = document.activeElement as HTMLElement | null\n            event.preventDefault()\n            inputRef.current?.focus()\n          }}\n          onCloseAutoFocus={(event) => {\n            event.preventDefault()\n            returnTo.current?.focus()\n          }}\n          className={cn(\n            \"fixed left-1/2 z-50 flex w-full -translate-x-1/2 flex-col overflow-hidden\",\n            SURFACE_CLASS[variant],\n            MODAL_POSITION_CLASS[variant],\n            effect === \"materialize\" ? MATERIALIZE_CLASS : PLAIN_MOTION_CLASS,\n            className,\n          )}\n          {...rest}\n        >\n          <DialogPrimitive.Title className=\"sr-only\">{label}</DialogPrimitive.Title>\n          <Palette {...palette} inputRef={inputRef} onDone={() => setOpen(false)} />\n        </DialogPrimitive.Content>\n      </DialogPrimitive.Portal>\n    </DialogPrimitive.Root>\n  )\n}\n\nfunction Command({ items, variant = \"dialog\", effect, recent, recentLabel, placeholder, emptyText, label = \"Command menu\", onSelect, open, defaultOpen, onOpenChange, className, ...rest }: CommandProps) {\n  const palette = { items, variant, recent, recentLabel, placeholder, emptyText, label, onSelect, springHeight: effect === \"materialize\" }\n  if (variant === \"dialog\" || variant === \"spotlight\") {\n    return <ModalCommand {...{ open, defaultOpen, onOpenChange, palette, variant, effect, label, className, rest }} />\n  }\n  return (\n    <div data-slot=\"command\" data-variant={variant} data-effect={effect} className={cn(\"flex min-w-0 flex-col overflow-hidden\", SURFACE_CLASS[variant], className)} {...rest}>\n      <Palette {...palette} />\n    </div>\n  )\n}\n\nexport { Command, pushRecent, useCommandShortcut, type CommandProps, type CommandItem, type CommandVariant, type CommandEffect }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/command-classes.ts",
      "content": "type CommandVariant = \"dialog\" | \"inline\" | \"compact\" | \"spotlight\"\ntype CommandEffect = \"materialize\"\n\nconst SURFACE_CLASS: Record<CommandVariant, string> = {\n  dialog: \"rounded-xl border bg-popover text-popover-foreground shadow-lg\",\n  inline: \"rounded-lg border bg-card text-card-foreground\",\n  compact: \"rounded-md border bg-popover text-popover-foreground\",\n  spotlight: \"rounded-2xl border bg-popover text-popover-foreground shadow-2xl\",\n}\nconst MODAL_POSITION_CLASS: Partial<Record<CommandVariant, string>> = {\n  dialog: \"top-[12%] max-w-[min(32rem,calc(100%-2rem))]\",\n  spotlight: \"top-[18%] max-w-[min(36rem,calc(100%-2rem))]\",\n}\nconst INPUT_ROW_CLASS: Record<CommandVariant, string> = {\n  dialog: \"h-12 gap-2 px-4\",\n  inline: \"h-10 gap-2 px-3\",\n  compact: \"h-8 gap-2 px-2 pointer-coarse:h-10\",\n  spotlight: \"h-16 gap-3 px-5\",\n}\nconst INPUT_TEXT_CLASS: Record<CommandVariant, string> = {\n  dialog: \"text-base md:text-sm\",\n  inline: \"text-base md:text-sm\",\n  // Under 16 px iOS zooms the page on focus, so the small size waits for a pointer that cannot trigger it.\n  compact: \"text-base md:text-[13px]\",\n  spotlight: \"text-xl tracking-tight\",\n}\nconst LIST_HEIGHT_CLASS: Record<CommandVariant, string> = {\n  dialog: \"max-h-80\",\n  inline: \"max-h-64\",\n  compact: \"max-h-56\",\n  spotlight: \"max-h-96\",\n}\n\n/** The enter and exit values stay unqualified so the media overrides below win on source order. */\nconst MATERIALIZE_CLASS = [\n  \"duration-(--duration-base) ease-(--ease-mise-out) will-change-[transform,opacity,filter]\",\n  \"data-[state=open]:animate-in data-[state=closed]:animate-out\",\n  \"fade-in-0 zoom-in-95 blur-in-sm fade-out-0 zoom-out-95 blur-out-sm\",\n  \"motion-reduce:zoom-in-100 motion-reduce:zoom-out-100 motion-reduce:blur-in-0 motion-reduce:blur-out-0\",\n  \"[@media(prefers-reduced-transparency:reduce)]:blur-in-0 [@media(prefers-reduced-transparency:reduce)]:blur-out-0\",\n]\nconst PLAIN_MOTION_CLASS = [\n  \"duration-(--duration-fast) ease-(--ease-mise)\",\n  \"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0\",\n]\n\nconst OVERLAY_CLASS =\n  \"fixed inset-0 z-50 bg-background/60 backdrop-blur-sm duration-(--duration-base) ease-(--ease-mise) data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0\"\n\nexport {\n  INPUT_ROW_CLASS,\n  INPUT_TEXT_CLASS,\n  LIST_HEIGHT_CLASS,\n  MATERIALIZE_CLASS,\n  MODAL_POSITION_CLASS,\n  OVERLAY_CLASS,\n  PLAIN_MOTION_CLASS,\n  SURFACE_CLASS,\n  type CommandEffect,\n  type CommandVariant,\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/command-list.tsx",
      "content": "import type * as React from \"react\"\nimport { useId } from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\nimport { SPRING_DEFAULT } from \"@/registry/miseui/lib/motion\"\nimport type { CommandEntry, CommandItem, CommandSection } from \"@/registry/miseui/ui/command-score\"\nimport { optionId, useMeasuredHeight } from \"@/registry/miseui/ui/command-state\"\nimport { cn } from \"@/registry/miseui/lib/utils\"\n\nconst INSTANT = { duration: 0 } as const\n\n/** Consecutive positions become one slice, so a match never splits a surrogate pair it covers whole. */\nfunction matchSlices(label: string, indices: readonly number[]) {\n  const matched = new Set(indices)\n  const slices: Array<{ text: string; hit: boolean }> = []\n  for (let index = 0; index < label.length; index += 1) {\n    const hit = matched.has(index)\n    const last = slices[slices.length - 1]\n    if (last && last.hit === hit) last.text += label[index]\n    else slices.push({ text: label[index], hit })\n  }\n  return slices\n}\n\n/** Draws the matched letters with weight rather than colour: the eye finds them, and the row stays calm. */\nfunction MatchedLabel({ label, indices }: { label: string; indices: readonly number[] }) {\n  if (indices.length === 0) return label\n  return matchSlices(label, indices).map(({ text, hit }, index) =>\n    hit ? (\n      <mark key={index} className=\"bg-transparent font-semibold text-foreground\">\n        {text}\n      </mark>\n    ) : (\n      <span key={index}>{text}</span>\n    ),\n  )\n}\n\nfunction Shortcut({ keys }: { keys: readonly string[] }) {\n  return (\n    // No keyboard on touch, so the hint would only take room from the label.\n    <span aria-hidden=\"true\" data-slot=\"command-shortcut\" className=\"ml-auto flex shrink-0 items-center gap-1 pl-2 pointer-coarse:hidden\">\n      {keys.map((key) => (\n        <kbd key={key} className=\"inline-flex h-5 min-w-5 items-center justify-center rounded-sm border bg-background px-1 font-sans text-xs text-muted-foreground\">\n          {key}\n        </kbd>\n      ))}\n    </span>\n  )\n}\n\ninterface CommandRowProps {\n  entry: CommandEntry\n  id: string\n  active: boolean\n  dense: boolean\n  onActive: (value: string) => void\n  onPick: (item: CommandItem) => void\n}\n\nfunction CommandRow({ entry, id, active, dense, onActive, onPick }: CommandRowProps) {\n  const { item, indices } = entry\n  return (\n    <div\n      id={id}\n      role=\"option\"\n      aria-selected={active}\n      aria-disabled={item.disabled || undefined}\n      data-slot=\"command-item\"\n      data-active={active ? \"true\" : undefined}\n      // Mouse move, not enter: when the list scrolls under a resting pointer, the active row must not jump.\n      onMouseMove={() => {\n        if (!item.disabled && !active) onActive(item.value)\n      }}\n      onClick={() => onPick(item)}\n      className={cn(\n        \"flex cursor-default items-center gap-2 rounded-md px-2 select-none pointer-coarse:min-h-10\",\n        dense ? \"min-h-8 text-[13px]\" : \"min-h-10 text-sm\",\n        active && \"bg-accent text-accent-foreground\",\n        item.disabled && \"pointer-events-none opacity-50\",\n        \"[&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground\",\n      )}\n    >\n      {item.icon && !dense ? <span aria-hidden=\"true\" className=\"flex shrink-0\">{item.icon}</span> : null}\n      <span className=\"min-w-0 flex-1 py-1\">\n        <span className=\"block truncate\">\n          <MatchedLabel label={item.label} indices={indices} />\n        </span>\n        {item.description && !dense ? (\n          <span data-slot=\"command-description\" className=\"block truncate text-[13px] leading-5 text-muted-foreground\">\n            {item.description}\n          </span>\n        ) : null}\n      </span>\n      {item.shortcut ? <Shortcut keys={item.shortcut} /> : null}\n    </div>\n  )\n}\n\nfunction SectionBlock({ section, dense, children }: { section: CommandSection; dense: boolean; children: React.ReactNode }) {\n  const headingId = useId()\n  const showHeading = section.heading && !dense\n  return (\n    <div role=\"group\" aria-labelledby={showHeading ? headingId : undefined} aria-label={showHeading ? undefined : section.heading} data-slot=\"command-group\">\n      {showHeading ? (\n        <div id={headingId} role=\"presentation\" data-slot=\"command-heading\" className=\"px-2 pt-2 pb-1 text-[13px] text-muted-foreground\">\n          {section.heading}\n        </div>\n      ) : null}\n      {children}\n    </div>\n  )\n}\n\ninterface CommandListProps {\n  listId: string\n  label: string\n  sections: readonly CommandSection[]\n  visible: readonly CommandItem[]\n  active: CommandItem | undefined\n  dense: boolean\n  maxHeightClass: string\n  springHeight: boolean\n  emptyText: React.ReactNode\n  showEmpty: boolean\n  onActive: (value: string) => void\n  onPick: (item: CommandItem) => void\n}\n\nfunction CommandList(props: CommandListProps) {\n  const { listId, label, sections, visible, active, dense, maxHeightClass, springHeight, emptyText, showEmpty, onActive, onPick } = props\n  const reduceMotion = useReducedMotion() ?? false\n  const { height, ref } = useMeasuredHeight()\n  const body = (\n    <div ref={springHeight ? ref : undefined} data-slot=\"command-scroller\" className={cn(\"overflow-y-auto p-1\", maxHeightClass)}>\n      <div id={listId} role=\"listbox\" aria-label={label}>\n        {sections.map((section) => (\n          <SectionBlock key={section.key} section={section} dense={dense}>\n            {section.entries.map((entry) => {\n              const index = visible.indexOf(entry.item)\n              return (\n                <CommandRow\n                  key={entry.item.value}\n                  entry={entry}\n                  id={optionId(listId, index)}\n                  active={entry.item === active}\n                  dense={dense}\n                  onActive={onActive}\n                  onPick={onPick}\n                />\n              )\n            })}\n          </SectionBlock>\n        ))}\n      </div>\n      {showEmpty ? (\n        <div data-slot=\"command-empty\" className=\"px-2 py-6 text-center text-sm text-muted-foreground\">\n          {emptyText}\n        </div>\n      ) : null}\n    </div>\n  )\n  if (!springHeight) return body\n  return (\n    <motion.div\n      data-slot=\"command-height\"\n      className=\"overflow-hidden\"\n      initial={false}\n      animate={{ height: height ?? \"auto\" }}\n      transition={reduceMotion ? INSTANT : SPRING_DEFAULT}\n    >\n      {body}\n    </motion.div>\n  )\n}\n\nexport { CommandList }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/command-score.ts",
      "content": "import type * as React from \"react\"\n\ninterface CommandMatch {\n  score: number\n  /** Positions in the text that matched, for drawing them. */\n  indices: readonly number[]\n}\n\ninterface CommandItem {\n  value: string\n  label: string\n  description?: string\n  /** Section heading. Items without one sit in an untitled section. */\n  group?: string\n  /** Extra words that should find this item, such as synonyms. */\n  keywords?: readonly string[]\n  /** Keys to show on the right, one entry per key. */\n  shortcut?: readonly string[]\n  icon?: React.ReactNode\n  disabled?: boolean\n  onSelect?: () => void\n}\n\ninterface CommandEntry {\n  item: CommandItem\n  indices: readonly number[]\n}\n\ninterface CommandSection {\n  key: string\n  heading?: string\n  entries: readonly CommandEntry[]\n}\n\nconst MATCH_POINTS = 1\nconst CONSECUTIVE_BONUS = 6\nconst WORD_START_BONUS = 8\nconst FIRST_CHARACTER_BONUS = 4\nconst GAP_PENALTY = 1\n/** Keywords are hidden from the reader, so a visible match of the same quality must win. */\nconst KEYWORD_FACTOR = 0.75\n/** Longer strings add cost and no information: nobody searches for the eightieth letter of a label. */\nconst MAX_TEXT_LENGTH = 200\nconst WORD_BOUNDARY = /[\\s\\-_./:\\\\]/\n\nfunction isWordStart(text: string, index: number) {\n  if (index === 0) return true\n  const previous = text[index - 1]\n  if (WORD_BOUNDARY.test(previous)) return true\n  return previous === previous.toLowerCase() && text[index] !== text[index].toLowerCase()\n}\n\nfunction characterBonus(text: string, index: number) {\n  const start = isWordStart(text, index) ? WORD_START_BONUS : 0\n  return MATCH_POINTS + start + (index === 0 ? FIRST_CHARACTER_BONUS : 0)\n}\n\n/** Walks back through the table of best predecessors to recover which letters the winning match used. */\nfunction backtrack(from: Int16Array[], rows: number, column: number) {\n  const indices: number[] = []\n  let at = column\n  for (let row = rows - 1; row >= 0; row -= 1) {\n    indices.unshift(at)\n    at = from[row][at]\n  }\n  return indices\n}\n\n/**\n * Best alignment of the query as a subsequence of the text, or null when it is not one. Dynamic programming rather\n * than a greedy scan, because the first occurrence of a letter is often not the one that starts a word: \"gp\" should\n * land on the capitals of \"Go to Projects\". Runs in O(query length x text length).\n */\nfunction fuzzyScore(query: string, text: string): CommandMatch | null {\n  const clipped = text.slice(0, MAX_TEXT_LENGTH)\n  const lowered = clipped.toLowerCase()\n  // A few characters change length when lowered, which would shift every index, so those match as written.\n  const haystack = lowered.length === clipped.length ? lowered : clipped\n  const needle = query.toLowerCase().replace(/\\s+/g, \"\")\n  if (needle.length === 0) return { score: 0, indices: [] }\n  if (needle.length > haystack.length) return null\n  const columns = haystack.length\n  const from = Array.from({ length: needle.length }, () => new Int16Array(columns).fill(-1))\n  let previous = new Float64Array(columns).fill(Number.NEGATIVE_INFINITY)\n  for (let row = 0; row < needle.length; row += 1) {\n    const current = new Float64Array(columns).fill(Number.NEGATIVE_INFINITY)\n    let carry = Number.NEGATIVE_INFINITY\n    let carryFrom = -1\n    for (let column = 0; column < columns; column += 1) {\n      if (column > 0 && previous[column - 1] - GAP_PENALTY > carry - GAP_PENALTY) {\n        carry = previous[column - 1]\n        carryFrom = column - 1\n      }\n      carry -= GAP_PENALTY\n      if (haystack[column] !== needle[row]) continue\n      const bonus = characterBonus(clipped, column)\n      if (row === 0) {\n        current[column] = bonus - column * GAP_PENALTY * 0.5\n        continue\n      }\n      const adjacent = column > 0 ? previous[column - 1] + CONSECUTIVE_BONUS : Number.NEGATIVE_INFINITY\n      const best = Math.max(adjacent, carry)\n      if (best === Number.NEGATIVE_INFINITY) continue\n      current[column] = best + bonus\n      from[row][column] = adjacent >= carry ? column - 1 : carryFrom\n    }\n    previous = current\n  }\n  let score = Number.NEGATIVE_INFINITY\n  let end = -1\n  previous.forEach((value, column) => {\n    if (value > score) {\n      score = value\n      end = column\n    }\n  })\n  return end === -1 ? null : { score, indices: backtrack(from, needle.length, end) }\n}\n\nfunction scoreItem(item: CommandItem, query: string): CommandMatch | null {\n  const label = fuzzyScore(query, item.label)\n  const keywordScores = (item.keywords ?? []).flatMap((keyword) => {\n    const match = fuzzyScore(query, keyword)\n    return match ? [match.score * KEYWORD_FACTOR] : []\n  })\n  const keyword = keywordScores.length > 0 ? Math.max(...keywordScores) : Number.NEGATIVE_INFINITY\n  if (label && label.score >= keyword) return label\n  return keyword === Number.NEGATIVE_INFINITY ? null : { score: keyword, indices: [] }\n}\n\nfunction groupKey(item: CommandItem) {\n  return item.group ?? \"\"\n}\n\nfunction browseSections(items: readonly CommandItem[], recent: readonly string[], recentLabel: string): CommandSection[] {\n  const recentItems = recent.flatMap((value) => items.filter((item) => item.value === value && !item.disabled))\n  const recentValues = new Set(recentItems.map((item) => item.value))\n  const rest = items.filter((item) => !recentValues.has(item.value))\n  const groups = [...new Set(rest.map(groupKey))]\n  const sections = groups.map((group) => ({\n    key: `group:${group}`,\n    heading: group || undefined,\n    entries: rest.filter((item) => groupKey(item) === group).map((item) => ({ item, indices: [] })),\n  }))\n  if (recentItems.length === 0) return sections\n  return [{ key: \"recent\", heading: recentLabel, entries: recentItems.map((item) => ({ item, indices: [] })) }, ...sections]\n}\n\nfunction searchSections(items: readonly CommandItem[], query: string): CommandSection[] {\n  const matched = items.flatMap((item) => {\n    const match = scoreItem(item, query)\n    return match ? [{ item, ...match }] : []\n  })\n  const ranked = [...matched].sort((a, b) => b.score - a.score)\n  // Sections follow their best result, so the top hit is always the first row.\n  const groups = [...new Set(ranked.map(({ item }) => groupKey(item)))]\n  return groups.map((group) => ({\n    key: `group:${group}`,\n    heading: group || undefined,\n    entries: ranked.filter(({ item }) => groupKey(item) === group).map(({ item, indices }) => ({ item, indices })),\n  }))\n}\n\n/** Recent items lead only while browsing: once someone types, the query alone decides. */\nfunction buildSections(items: readonly CommandItem[], query: string, recent: readonly string[], recentLabel: string, recentOnly = false) {\n  if (query.trim()) return searchSections(items, query)\n  const sections = browseSections(items, recent, recentLabel)\n  return recentOnly ? sections.filter((section) => section.key === \"recent\") : sections\n}\n\n/** Newest first, no duplicates, capped: the list to keep after someone runs a command. */\nfunction pushRecent(recent: readonly string[], value: string, limit = 5) {\n  return [value, ...recent.filter((entry) => entry !== value)].slice(0, limit)\n}\n\nexport { buildSections, fuzzyScore, pushRecent, scoreItem, type CommandEntry, type CommandItem, type CommandMatch, type CommandSection }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/command-state.ts",
      "content": "import type * as React from \"react\"\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\"\nimport { buildSections, type CommandItem } from \"@/registry/miseui/ui/command-score\"\n\ninterface UseCommandOptions {\n  items: readonly CommandItem[]\n  recent: readonly string[]\n  recentLabel: string\n  /** Escape clears the query where no dialog is there to close. */\n  clearOnEscape: boolean\n  /** While nothing is typed, offer recent items alone. */\n  recentOnly: boolean\n  onSelect?: (value: string, item: CommandItem) => void\n  /** Runs after a selection, so a dialog can close. */\n  onDone?: () => void\n}\n\nfunction optionId(listId: string, index: number) {\n  return `${listId}-option-${index}`\n}\n\ninterface ListKeyActions {\n  move: (delta: number) => void\n  run: () => void\n  /** Only there when Escape has a query to clear and no dialog to close. */\n  clear?: () => void\n}\n\nfunction listKeys({ move, run, clear }: ListKeyActions) {\n  return (event: React.KeyboardEvent<HTMLInputElement>) => {\n    // During IME composition these keys belong to the syllable being typed, not to the list.\n    if (event.nativeEvent.isComposing) return\n    const { key, ctrlKey } = event\n    const handled = key === \"ArrowDown\" || key === \"ArrowUp\" || key === \"Enter\" || (ctrlKey && (key === \"n\" || key === \"p\"))\n    if (handled) event.preventDefault()\n    if (key === \"ArrowDown\" || (ctrlKey && key === \"n\")) move(1)\n    else if (key === \"ArrowUp\" || (ctrlKey && key === \"p\")) move(-1)\n    else if (key === \"Enter\") run()\n    else if (key === \"Escape\" && clear) {\n      event.stopPropagation()\n      clear()\n    }\n  }\n}\n\nfunction useCommand({ items, recent, recentLabel, clearOnEscape, recentOnly, onSelect, onDone }: UseCommandOptions) {\n  const [query, setQueryState] = useState(\"\")\n  const [activeValue, setActiveValue] = useState<string | null>(null)\n  const listId = useId()\n  const sections = useMemo(() => buildSections(items, query, recent, recentLabel, recentOnly), [items, query, recent, recentLabel, recentOnly])\n  const visible = useMemo(() => sections.flatMap((section) => section.entries.map((entry) => entry.item)), [sections])\n  const enabled = useMemo(() => visible.filter((item) => !item.disabled), [visible])\n  const active = enabled.find((item) => item.value === activeValue) ?? enabled[0]\n  const activeIndex = active ? visible.indexOf(active) : -1\n  const activeOptionId = activeIndex >= 0 ? optionId(listId, activeIndex) : undefined\n\n  useEffect(() => {\n    if (activeOptionId) document.getElementById(activeOptionId)?.scrollIntoView({ block: \"nearest\" })\n  }, [activeOptionId])\n\n  const setQuery = (next: string) => {\n    setQueryState(next)\n    setActiveValue(null)\n  }\n\n  const select = (item: CommandItem) => {\n    if (item.disabled) return\n    item.onSelect?.()\n    onSelect?.(item.value, item)\n    onDone?.()\n  }\n\n  const move = (delta: number) => {\n    if (enabled.length === 0) return\n    const at = active ? enabled.indexOf(active) : -1\n    setActiveValue(enabled[(at + delta + enabled.length) % enabled.length].value)\n  }\n\n  const onKeyDown = listKeys({\n    move,\n    run: () => {\n      if (active) select(active)\n    },\n    clear: clearOnEscape && query ? () => setQuery(\"\") : undefined,\n  })\n\n  return { query, setQuery, listId, sections, visible, active, activeOptionId, setActiveValue, select, onKeyDown }\n}\n\ninterface ShortcutOptions {\n  /** The letter pressed with Cmd or Ctrl. */\n  key?: string\n  enabled?: boolean\n}\n\n/** Mod+K for the palette. The handler may change every render without re-subscribing. */\nfunction useCommandShortcut(handler: () => void, { key = \"k\", enabled = true }: ShortcutOptions = {}) {\n  const latest = useRef(handler)\n  useEffect(() => {\n    latest.current = handler\n  })\n  useEffect(() => {\n    if (!enabled) return undefined\n    const onKeyDown = (event: KeyboardEvent) => {\n      if (event.repeat || event.isComposing) return\n      if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === key.toLowerCase()) {\n        // Browsers use Ctrl+K for the address bar; the page asked for it, so it wins.\n        event.preventDefault()\n        latest.current()\n      }\n    }\n    window.addEventListener(\"keydown\", onKeyDown)\n    return () => window.removeEventListener(\"keydown\", onKeyDown)\n  }, [key, enabled])\n}\n\n/** The content's height in px, tracked through every change, so a parent can spring to it instead of snapping. */\nfunction useMeasuredHeight() {\n  const [height, setHeight] = useState<number | undefined>(undefined)\n  const observer = useRef<ResizeObserver | null>(null)\n  const ref = useCallback((node: HTMLElement | null) => {\n    observer.current?.disconnect()\n    observer.current = null\n    if (!node || typeof ResizeObserver === \"undefined\") return\n    observer.current = new ResizeObserver(() => setHeight(node.offsetHeight))\n    observer.current.observe(node)\n  }, [])\n  return { height, ref }\n}\n\nexport { optionId, useCommand, useCommandShortcut, useMeasuredHeight }\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "overlays"
  ],
  "type": "registry:ui"
}