{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "command",
  "title": "Command",
  "description": "A searchable list of actions, inline or as a ⌘K palette, with pages, shortcuts and highlighted matches.",
  "dependencies": [
    "@base-ui/react@^1.8.0",
    "@tabler/icons-react",
    "cmdk@^1.1.1",
    "cn"
  ],
  "registryDependencies": [
    "https://hextaui.com/r/theme.json",
    "https://hextaui.com/r/button.json",
    "https://hextaui.com/r/hotkey.json",
    "https://hextaui.com/r/motion.json",
    "https://hextaui.com/r/spinner.json"
  ],
  "files": [
    {
      "path": "components/ui/command.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\"\nimport { IconChevronLeft, IconSearch, IconX } from \"@tabler/icons-react\"\nimport { Command as CommandPrimitive, useCommandState } from \"cmdk\"\nimport { cn } from \"cn\"\n\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { formatHotkey, matchesHotkey, useIsApple } from \"@/lib/hotkey\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { easeOut, prefersReducedMotion } from \"@/lib/motion\"\n\ntype CommandPageEntry = { id: string; title: string }\n\ntype CommandContextValue = {\n  search: string\n  setSearch: (search: string) => void\n  pages: CommandPageEntry[]\n  direction: 1 | -1\n  pushPage: (id: string, title?: string) => void\n  popPage: (count?: number) => void\n  registerShortcut: (hotkey: string, run: () => void) => () => void\n  takeKeyboardSelect: () => { fromKeyboard: boolean; newTab: boolean }\n}\n\nconst CommandContext = React.createContext<CommandContextValue | null>(null)\nconst CommandPageContext = React.createContext<string | null>(null)\n\nfunction useCommandContext(part: string) {\n  const context = React.useContext(CommandContext)\n  if (!context) {\n    throw new Error(`<${part}> must be used within <Command>.`)\n  }\n  return context\n}\n\nfunction useCommandPages() {\n  const { pages, pushPage, popPage } = useCommandContext(\"useCommandPages\")\n  return {\n    pages,\n    page: pages.at(-1)?.id ?? null,\n    push: pushPage,\n    pop: popPage,\n    reset: () => popPage(pages.length),\n  }\n}\n\nfunction usePageVisible() {\n  const context = React.useContext(CommandContext)\n  const page = React.useContext(CommandPageContext)\n  const current = context?.pages.at(-1)?.id ?? null\n  return page === current\n}\n\nfunction subscribeNothing() {\n  return () => {}\n}\n\nfunction isEditable(target: EventTarget | null) {\n  return (\n    target instanceof HTMLElement &&\n    (target.isContentEditable ||\n      target.closest(\"input, textarea, select, [contenteditable='true']\") !==\n        null)\n  )\n}\n\nconst internalEvents = new WeakSet<Event>()\n\nconst highlightName = \"command-match\"\nconst highlightRanges = new Map<string, Range[]>()\nlet highlightSheet: CSSStyleSheet | null = null\n\nfunction adoptHighlightStyle() {\n  if (\n    highlightSheet ||\n    typeof CSSStyleSheet === \"undefined\" ||\n    !(\"adoptedStyleSheets\" in document)\n  ) {\n    return\n  }\n  highlightSheet = new CSSStyleSheet()\n  highlightSheet.replaceSync(\n    `::highlight(${highlightName}) { color: var(--foreground); }`\n  )\n  document.adoptedStyleSheets = [...document.adoptedStyleSheets, highlightSheet]\n}\n\nfunction publishHighlights() {\n  if (typeof CSS === \"undefined\" || !(\"highlights\" in CSS)) {\n    return\n  }\n  adoptHighlightStyle()\n  const ranges = [...highlightRanges.values()].flat()\n  if (ranges.length === 0) {\n    CSS.highlights.delete(highlightName)\n  } else {\n    CSS.highlights.set(highlightName, new Highlight(...ranges))\n  }\n}\n\nfunction matchIndices(text: string, query: string) {\n  const haystack = text.toLowerCase()\n  const needle = query.toLowerCase()\n  const start = haystack.indexOf(needle)\n  if (start !== -1) {\n    return Array.from({ length: needle.length }, (_, index) => start + index)\n  }\n  const indices: number[] = []\n  let from = 0\n  for (const char of needle) {\n    if (char === \" \") {\n      continue\n    }\n    const found = haystack.indexOf(char, from)\n    if (found === -1) {\n      return []\n    }\n    indices.push(found)\n    from = found + 1\n  }\n  return indices\n}\n\nfunction highlightItem(item: HTMLElement, query: string) {\n  const walker = document.createTreeWalker(item, NodeFilter.SHOW_TEXT, {\n    acceptNode: (node) =>\n      node.parentElement?.closest(\n        \"[data-slot=command-shortcut], svg, [aria-hidden=true]\"\n      )\n        ? NodeFilter.FILTER_REJECT\n        : NodeFilter.FILTER_ACCEPT,\n  })\n  const nodes: { node: Text; start: number }[] = []\n  let text = \"\"\n  for (let node = walker.nextNode(); node; node = walker.nextNode()) {\n    nodes.push({ node: node as Text, start: text.length })\n    text += node.textContent ?? \"\"\n  }\n  const ranges: Range[] = []\n  for (const index of matchIndices(text, query.trim())) {\n    const owner = nodes.findLast((entry) => entry.start <= index)\n    if (!owner) {\n      continue\n    }\n    const range = document.createRange()\n    range.setStart(owner.node, index - owner.start)\n    range.setEnd(owner.node, index - owner.start + 1)\n    ranges.push(range)\n  }\n  return ranges\n}\n\nfunction CommandHighlighter() {\n  const id = React.useId()\n  const search = useCommandState((state) => state.search)\n  const count = useCommandState((state) => state.filtered.count)\n  const anchorRef = React.useRef<HTMLSpanElement>(null)\n\n  React.useLayoutEffect(() => {\n    const root = anchorRef.current?.closest<HTMLElement>(\"[cmdk-root]\")\n    if (!root) {\n      return\n    }\n    const query = search.trim()\n    root.toggleAttribute(\"data-highlighting\", query !== \"\")\n    highlightRanges.set(\n      id,\n      query\n        ? Array.from(root.querySelectorAll<HTMLElement>(\"[cmdk-item]\")).flatMap(\n            (item) => highlightItem(item, query)\n          )\n        : []\n    )\n    publishHighlights()\n  }, [count, id, search])\n\n  React.useEffect(\n    () => () => {\n      highlightRanges.delete(id)\n      publishHighlights()\n    },\n    [id]\n  )\n\n  return <span ref={anchorRef} hidden />\n}\n\nfunction defaultFormatResults(count: number) {\n  if (count === 0) {\n    return \"No results\"\n  }\n  return count === 1 ? \"1 result\" : `${count} results`\n}\n\nfunction CommandAnnouncer({\n  format,\n  pageTitle,\n}: {\n  format: (count: number) => string\n  pageTitle: string\n}) {\n  const search = useCommandState((state) => state.search)\n  const count = useCommandState((state) => state.filtered.count)\n  const [message, setMessage] = React.useState(\"\")\n  const [previousPage, setPreviousPage] = React.useState(pageTitle)\n  const [pageMessage, setPageMessage] = React.useState(\"\")\n\n  if (pageTitle !== previousPage) {\n    setPreviousPage(pageTitle)\n    setPageMessage(pageTitle)\n  }\n\n  React.useEffect(() => {\n    const timer = setTimeout(\n      () => setMessage(search ? format(count) : \"\"),\n      search ? 500 : 0\n    )\n    return () => clearTimeout(timer)\n  }, [count, format, search])\n\n  return (\n    <span\n      data-slot=\"command-announcer\"\n      role=\"status\"\n      aria-live=\"polite\"\n      aria-atomic\n      className=\"sr-only\"\n    >\n      {message || pageMessage}\n    </span>\n  )\n}\n\ntype CommandProps = React.ComponentProps<typeof CommandPrimitive> & {\n  formatResults?: (count: number) => string\n  highlight?: boolean\n  rootTitle?: string\n}\n\nfunction Command({\n  className,\n  children,\n  label = \"Command menu\",\n  formatResults = defaultFormatResults,\n  highlight = false,\n  rootTitle = \"All commands\",\n  onKeyDown,\n  ...props\n}: CommandProps) {\n  const [search, setSearch] = React.useState(\"\")\n  const [pages, setPages] = React.useState<CommandPageEntry[]>([])\n  const [direction, setDirection] = React.useState<1 | -1>(1)\n  const shortcutsRef = React.useRef(\n    new Map<symbol, { hotkey: string; run: () => void }>()\n  )\n  const enterRef = React.useRef({ at: -Infinity, newTab: false })\n\n  const pushPage = React.useCallback((id: string, title?: string) => {\n    setDirection(1)\n    setPages((current) => [...current, { id, title: title ?? id }])\n    setSearch(\"\")\n  }, [])\n\n  const popPage = React.useCallback((count = 1) => {\n    setDirection(-1)\n    setPages((current) => current.slice(0, Math.max(0, current.length - count)))\n    setSearch(\"\")\n  }, [])\n\n  const registerShortcut = React.useCallback(\n    (hotkey: string, run: () => void) => {\n      const key = Symbol(hotkey)\n      shortcutsRef.current.set(key, { hotkey, run })\n      return () => {\n        shortcutsRef.current.delete(key)\n      }\n    },\n    []\n  )\n\n  const takeKeyboardSelect = React.useCallback(() => {\n    const entry = enterRef.current\n    const fromKeyboard = performance.now() - entry.at < 100\n    enterRef.current = { at: -Infinity, newTab: false }\n    return { fromKeyboard, newTab: fromKeyboard && entry.newTab }\n  }, [])\n\n  const context = React.useMemo(\n    () => ({\n      search,\n      setSearch,\n      pages,\n      direction,\n      pushPage,\n      popPage,\n      registerShortcut,\n      takeKeyboardSelect,\n    }),\n    [\n      direction,\n      pages,\n      popPage,\n      pushPage,\n      registerShortcut,\n      search,\n      takeKeyboardSelect,\n    ]\n  )\n\n  return (\n    <CommandContext.Provider value={context}>\n      <CommandPrimitive\n        data-slot=\"command\"\n        label={label}\n        onKeyDown={(event) => {\n          if (internalEvents.has(event.nativeEvent)) {\n            return\n          }\n          onKeyDown?.(event)\n          if (event.defaultPrevented || event.nativeEvent.isComposing) {\n            return\n          }\n          if (event.key === \"Enter\") {\n            enterRef.current = {\n              at: performance.now(),\n              newTab: event.metaKey || event.ctrlKey,\n            }\n            return\n          }\n          if (event.repeat) {\n            return\n          }\n          for (const { hotkey, run } of shortcutsRef.current.values()) {\n            if (matchesHotkey(event.nativeEvent, hotkey)) {\n              event.preventDefault()\n              run()\n              return\n            }\n          }\n        }}\n        className={cn(\n          \"flex size-full min-w-0 flex-col overflow-hidden rounded-(--command-radius) bg-popover text-popover-foreground ring-(length:--hairline) ring-foreground/10 outline-none [--command-inset:--spacing(1.5)] [--command-radius:var(--radius-xl)] focus-visible:outline-hidden in-data-[slot=command-dialog]:rounded-none in-data-[slot=command-dialog]:ring-0 forced-colors:border\",\n          className\n        )}\n        {...props}\n      >\n        {children}\n        {highlight ? <CommandHighlighter /> : null}\n        <CommandAnnouncer\n          format={formatResults}\n          pageTitle={pages.at(-1)?.title ?? (pages.length ? \"\" : rootTitle)}\n        />\n      </CommandPrimitive>\n    </CommandContext.Provider>\n  )\n}\n\nfunction CommandPage({\n  id,\n  children,\n}: {\n  id: string\n  children?: React.ReactNode\n}) {\n  return (\n    <CommandPageContext.Provider value={id}>\n      {children}\n    </CommandPageContext.Provider>\n  )\n}\n\ntype CommandInputProps = Omit<\n  React.ComponentProps<typeof CommandPrimitive.Input>,\n  \"value\" | \"onValueChange\"\n> & {\n  value?: string\n  onValueChange?: (search: string) => void\n  clearLabel?: string\n  backLabel?: (title: string) => string\n}\n\nfunction CommandInput({\n  className,\n  value,\n  onValueChange,\n  onKeyDown,\n  clearLabel = \"Clear search\",\n  backLabel = (title) => `Back from ${title}`,\n  ref,\n  ...props\n}: CommandInputProps) {\n  const context = useCommandContext(\"CommandInput\")\n  const search = value ?? context.search\n  const inputRef = React.useRef<HTMLInputElement | null>(null)\n  const page = context.pages.at(-1)\n\n  const setInput = React.useCallback(\n    (node: HTMLInputElement | null) => {\n      inputRef.current = node\n      if (typeof ref === \"function\") {\n        ref(node)\n      } else if (ref) {\n        ref.current = node\n      }\n    },\n    [ref]\n  )\n\n  const change = (next: string) => {\n    if (value === undefined) {\n      context.setSearch(next)\n    }\n    onValueChange?.(next)\n  }\n\n  return (\n    <div\n      data-slot=\"command-input-wrapper\"\n      className=\"flex shrink-0 items-center gap-2.5 border-b ps-3.5 pe-2\"\n    >\n      {page ? (\n        <button\n          type=\"button\"\n          data-slot=\"command-page-chip\"\n          aria-label={backLabel(page.title)}\n          onClick={() => {\n            context.popPage()\n            inputRef.current?.focus()\n          }}\n          className=\"-ms-1.5 inline-flex h-6 max-w-40 min-w-0 shrink-0 items-center gap-0.5 rounded-[calc(var(--radius-sm)*0.8)] bg-muted ps-0.5 pe-2 text-xs font-medium text-foreground transition-[background-color,scale] duration-150 ease-out-quint outline-none hover:bg-foreground/10 focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:outline-hidden motion-safe:animate-in motion-safe:animation-duration-150 motion-safe:fade-in-0 motion-safe:zoom-in-95 motion-safe:active:scale-95 pointer-coarse:after:absolute pointer-coarse:after:-inset-2.5\"\n        >\n          <IconChevronLeft\n            aria-hidden\n            className=\"size-3.5 shrink-0 rtl:-scale-x-100\"\n          />\n          <span className=\"truncate\">{page.title}</span>\n        </button>\n      ) : (\n        <IconSearch\n          aria-hidden\n          className=\"pointer-events-none size-4 shrink-0 text-muted-foreground\"\n        />\n      )}\n      <CommandPrimitive.Input\n        ref={setInput}\n        data-slot=\"command-input\"\n        value={search}\n        onValueChange={change}\n        onKeyDown={(event) => {\n          onKeyDown?.(event)\n          if (event.defaultPrevented || event.nativeEvent.isComposing) {\n            return\n          }\n          if (event.key === \"Escape\") {\n            if (search !== \"\") {\n              event.preventDefault()\n              change(\"\")\n            } else if (context.pages.length > 0) {\n              event.preventDefault()\n              context.popPage()\n            }\n            return\n          }\n          if (\n            event.key === \"Backspace\" &&\n            search === \"\" &&\n            context.pages.length > 0 &&\n            !event.repeat\n          ) {\n            event.preventDefault()\n            context.popPage()\n          }\n        }}\n        className={cn(\n          \"h-11 w-full min-w-0 bg-transparent text-sm outline-none placeholder:text-muted-foreground focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:h-12 pointer-coarse:text-[max(16px,1rem)] [&::-webkit-search-cancel-button]:hidden\",\n          className\n        )}\n        {...props}\n      />\n      <Button\n        type=\"button\"\n        variant=\"ghost\"\n        size=\"icon-xs\"\n        tabIndex={-1}\n        aria-label={clearLabel}\n        aria-hidden={search === \"\" || undefined}\n        data-slot=\"command-clear\"\n        onClick={() => {\n          change(\"\")\n          inputRef.current?.focus()\n        }}\n        className={cn(\n          \"shrink-0 rounded-full text-muted-foreground transition-[opacity,scale,color,background-color] duration-150 hover:text-foreground\",\n          search === \"\"\n            ? \"pointer-events-none opacity-0 motion-safe:scale-75\"\n            : \"opacity-100\"\n        )}\n      >\n        <IconX />\n      </Button>\n    </div>\n  )\n}\n\nfunction scrollIntoList(element: HTMLElement) {\n  const list = element.closest<HTMLElement>(\"[cmdk-list]\")\n  if (!list) {\n    return\n  }\n  const style = getComputedStyle(list)\n  const top = parseFloat(style.scrollPaddingTop) || 0\n  const bottom = parseFloat(style.scrollPaddingBottom) || 0\n  const listRect = list.getBoundingClientRect()\n  const rect = element.getBoundingClientRect()\n  if (rect.top < listRect.top + top) {\n    list.scrollTop -= listRect.top + top - rect.top\n  } else if (rect.bottom > listRect.bottom - bottom) {\n    list.scrollTop += rect.bottom - (listRect.bottom - bottom)\n  }\n}\n\nfunction useScrollWithinList(\n  ref: React.RefObject<HTMLElement | null>,\n  selector?: string\n) {\n  React.useLayoutEffect(() => {\n    const root = ref.current\n    const target = selector ? root?.querySelector<HTMLElement>(selector) : root\n    if (!target) {\n      return\n    }\n    target.scrollIntoView = () => scrollIntoList(target)\n    return () => {\n      delete (target as Partial<HTMLElement>).scrollIntoView\n    }\n  })\n}\n\nfunction CommandList({\n  className,\n  ...props\n}: React.ComponentProps<typeof CommandPrimitive.List>) {\n  const context = useCommandContext(\"CommandList\")\n  const listRef = React.useRef<HTMLDivElement>(null)\n  const depth = context.pages.length\n  const [previousDepth, setPreviousDepth] = React.useState(depth)\n  const [pageTurn, setPageTurn] = React.useState(0)\n\n  if (depth !== previousDepth) {\n    setPreviousDepth(depth)\n    setPageTurn((turn) => turn + 1)\n  }\n\n  React.useLayoutEffect(() => {\n    const list = listRef.current\n    if (!pageTurn || !list) {\n      return\n    }\n    const home = new KeyboardEvent(\"keydown\", {\n      key: \"Home\",\n      bubbles: true,\n      cancelable: true,\n    })\n    internalEvents.add(home)\n    list.closest(\"[cmdk-root]\")?.dispatchEvent(home)\n    list.scrollTop = 0\n\n    const sizer = list.querySelector<HTMLElement>(\":scope > [cmdk-list-sizer]\")\n    if (!sizer || typeof sizer.animate !== \"function\") {\n      return\n    }\n    const rtl = getComputedStyle(sizer).direction === \"rtl\"\n    const offset = 16 * context.direction * (rtl ? -1 : 1)\n    const animation = sizer.animate(\n      prefersReducedMotion()\n        ? [{ opacity: 0 }, { opacity: 1 }]\n        : [\n            { translate: `${offset}px 0`, opacity: 0 },\n            { translate: \"0 0\", opacity: 1 },\n          ],\n      { duration: 180, easing: easeOut }\n    )\n    return () => animation.cancel()\n  }, [context.direction, pageTurn])\n\n  React.useLayoutEffect(() => {\n    const list = listRef.current\n    if (!list || typeof ResizeObserver !== \"function\") {\n      return\n    }\n    let frame = 0\n    const settle = () => {\n      cancelAnimationFrame(frame)\n      frame = requestAnimationFrame(() => {\n        frame = requestAnimationFrame(() => {\n          list.setAttribute(\"data-settled\", \"\")\n        })\n      })\n    }\n    const observer = new ResizeObserver(() => {\n      if (list.getClientRects().length === 0) {\n        cancelAnimationFrame(frame)\n        list.removeAttribute(\"data-settled\")\n      } else if (!list.hasAttribute(\"data-settled\")) {\n        settle()\n      }\n    })\n    observer.observe(list)\n    return () => {\n      cancelAnimationFrame(frame)\n      observer.disconnect()\n      list.removeAttribute(\"data-settled\")\n    }\n  }, [])\n\n  return (\n    <CommandPrimitive.List\n      ref={listRef}\n      data-slot=\"command-list\"\n      className={cn(\n        \"max-h-[min(24rem,45dvh)] scroll-py-(--command-inset) [scrollbar-gutter:stable] overflow-x-hidden overflow-y-auto overscroll-none duration-150 ease-out-quint outline-none focus-visible:outline-hidden data-settled:h-(--cmdk-list-height) motion-safe:data-settled:transition-[height] motion-reduce:transition-none [&>[cmdk-list-sizer]]:p-(--command-inset)\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\ntype CommandEmptyProps = Omit<\n  React.ComponentProps<typeof CommandPrimitive.Empty>,\n  \"children\"\n> & {\n  children?: React.ReactNode | ((search: string) => React.ReactNode)\n}\n\nfunction CommandEmpty({ className, children, ...props }: CommandEmptyProps) {\n  const search = useCommandState((state) => state.search)\n  const hydrating = React.useSyncExternalStore(\n    subscribeNothing,\n    () => false,\n    () => true\n  )\n\n  if (hydrating) {\n    return null\n  }\n\n  return (\n    <CommandPrimitive.Empty\n      data-slot=\"command-empty\"\n      className={cn(\n        \"px-3 py-8 text-center text-sm text-balance text-muted-foreground in-[[cmdk-list]:has([cmdk-loading])]:hidden\",\n        className\n      )}\n      {...props}\n    >\n      {typeof children === \"function\" ? children(search) : children}\n    </CommandPrimitive.Empty>\n  )\n}\n\nfunction useCommandLoading(\n  loading: boolean,\n  {\n    delay = 150,\n    minDuration = 300,\n  }: { delay?: number; minDuration?: number } = {}\n) {\n  const [visible, setVisible] = React.useState(false)\n  const shownAtRef = React.useRef(0)\n\n  React.useEffect(() => {\n    if (loading && !visible) {\n      const timer = setTimeout(() => {\n        shownAtRef.current = performance.now()\n        setVisible(true)\n      }, delay)\n      return () => clearTimeout(timer)\n    }\n    if (!loading && visible) {\n      const remaining = Math.max(\n        0,\n        minDuration - (performance.now() - shownAtRef.current)\n      )\n      const timer = setTimeout(() => setVisible(false), remaining)\n      return () => clearTimeout(timer)\n    }\n  }, [delay, loading, minDuration, visible])\n\n  return visible\n}\n\ntype CommandLoadingProps = React.ComponentProps<\n  typeof CommandPrimitive.Loading\n> & {\n  loading?: boolean\n  delay?: number\n  minDuration?: number\n}\n\nfunction CommandLoading({\n  className,\n  children,\n  label,\n  loading = true,\n  delay,\n  minDuration,\n  ...props\n}: CommandLoadingProps) {\n  const visible = useCommandLoading(loading, { delay, minDuration })\n\n  if (!loading && !visible) {\n    return null\n  }\n\n  return (\n    <CommandPrimitive.Loading\n      data-slot=\"command-loading\"\n      data-pending={visible ? undefined : \"\"}\n      label={label ?? (typeof children === \"string\" ? children : undefined)}\n      className={cn(\n        \"flex min-h-9 items-center justify-center px-3 py-4 text-sm text-muted-foreground data-pending:sr-only motion-safe:animate-in motion-safe:animation-duration-150 motion-safe:fade-in-0 [&>div]:flex [&>div]:items-center [&>div]:gap-2\",\n        className\n      )}\n      {...props}\n    >\n      <Spinner aria-hidden />\n      {children}\n    </CommandPrimitive.Loading>\n  )\n}\n\nfunction CommandGroup({\n  className,\n  ...props\n}: React.ComponentProps<typeof CommandPrimitive.Group>) {\n  const groupRef = React.useRef<HTMLDivElement>(null)\n  useScrollWithinList(groupRef, \":scope > [cmdk-group-heading]\")\n\n  if (!usePageVisible()) {\n    return null\n  }\n\n  return (\n    <CommandPrimitive.Group\n      ref={groupRef}\n      data-slot=\"command-group\"\n      className={cn(\n        \"overflow-hidden text-foreground not-first:mt-1 [&>[cmdk-group-heading]]:px-2.5 [&>[cmdk-group-heading]]:pt-2 [&>[cmdk-group-heading]]:pb-1.5 [&>[cmdk-group-heading]]:text-xs [&>[cmdk-group-heading]]:font-medium [&>[cmdk-group-heading]]:text-muted-foreground [&>[cmdk-group-heading]]:select-none\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction CommandSeparator({\n  className,\n  ...props\n}: React.ComponentProps<typeof CommandPrimitive.Separator>) {\n  if (!usePageVisible()) {\n    return null\n  }\n\n  return (\n    <CommandPrimitive.Separator\n      data-slot=\"command-separator\"\n      aria-hidden\n      className={cn(\n        \"-mx-(--command-inset) my-(--command-inset) h-(length:--hairline) bg-border\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction textOf(node: React.ReactNode): string {\n  if (typeof node === \"string\" || typeof node === \"number\") {\n    return String(node)\n  }\n  if (Array.isArray(node)) {\n    return node.map(textOf).join(\"\")\n  }\n  if (React.isValidElement<{ children?: React.ReactNode }>(node)) {\n    return node.type === CommandShortcut ? \"\" : textOf(node.props.children)\n  }\n  return \"\"\n}\n\nconst confirmBlink = 60\nconst confirmDelay = 120\n\ntype CommandItemProps = Omit<\n  React.ComponentProps<typeof CommandPrimitive.Item>,\n  \"asChild\"\n> & {\n  shortcut?: string\n  page?: string\n  pageTitle?: string\n  href?: string\n  render?: React.ReactElement<{ href?: string }>\n  confirm?: boolean\n}\n\nfunction CommandItem({\n  className,\n  children,\n  shortcut,\n  page,\n  pageTitle,\n  href,\n  render,\n  confirm = true,\n  onSelect,\n  disabled,\n  value,\n  ...props\n}: CommandItemProps) {\n  const context = useCommandContext(\"CommandItem\")\n  const visible = usePageVisible()\n  const apple = useIsApple()\n  const itemRef = React.useRef<HTMLDivElement>(null)\n  useScrollWithinList(itemRef)\n  const [confirming, setConfirming] = React.useState(false)\n  const busyRef = React.useRef(false)\n  const timersRef = React.useRef<ReturnType<typeof setTimeout>[]>([])\n  const linkHref = href ?? render?.props.href\n\n  React.useEffect(() => {\n    const timers = timersRef\n    return () => timers.current.forEach(clearTimeout)\n  }, [])\n\n  const act = (\n    value: string,\n    keyboard: { fromKeyboard: boolean; newTab: boolean }\n  ) => {\n    busyRef.current = false\n    if (page) {\n      context.pushPage(page, pageTitle ?? value)\n    } else if (linkHref && keyboard.fromKeyboard) {\n      if (keyboard.newTab) {\n        window.open(linkHref, \"_blank\", \"noopener\")\n      } else {\n        itemRef.current?.click()\n      }\n    }\n    onSelect?.(value)\n  }\n\n  const select = (value: string) => {\n    if (busyRef.current) {\n      return\n    }\n    const keyboard = context.takeKeyboardSelect()\n    if (!confirm || prefersReducedMotion()) {\n      act(value, keyboard)\n      return\n    }\n    busyRef.current = true\n    setConfirming(true)\n    timersRef.current.push(\n      setTimeout(() => setConfirming(false), confirmBlink),\n      setTimeout(() => act(value, keyboard), confirmDelay)\n    )\n  }\n\n  const selectRef = React.useRef(select)\n  React.useLayoutEffect(() => {\n    selectRef.current = select\n  })\n\n  React.useEffect(() => {\n    if (!shortcut || disabled) {\n      return\n    }\n    return context.registerShortcut(shortcut, () => {\n      const value =\n        itemRef.current?.getAttribute(\"data-value\") ??\n        itemRef.current?.textContent ??\n        \"\"\n      selectRef.current(value)\n    })\n  }, [context, disabled, shortcut])\n\n  if (!visible) {\n    return null\n  }\n\n  const content = (\n    <>\n      {children}\n      {page ? (\n        <IconChevronLeft\n          aria-hidden\n          className=\"ms-auto size-4 shrink-0 rotate-180 text-muted-foreground rtl:rotate-0\"\n        />\n      ) : null}\n      {shortcut ? (\n        <CommandShortcut aria-hidden>\n          {formatHotkey(shortcut, apple)}\n        </CommandShortcut>\n      ) : null}\n    </>\n  )\n\n  const link = render ?? (href ? <a href={href} /> : null)\n\n  return (\n    <CommandPrimitive.Item\n      ref={itemRef}\n      data-slot=\"command-item\"\n      data-confirming={confirming ? \"\" : undefined}\n      data-page={page ? \"\" : undefined}\n      aria-keyshortcuts={\n        shortcut\n          ? shortcut\n              .split(\"+\")\n              .map((part) =>\n                part === \"mod\" ? (apple ? \"Meta\" : \"Control\") : part\n              )\n              .join(\"+\")\n          : undefined\n      }\n      value={value ?? (textOf(children).trim() || undefined)}\n      disabled={disabled}\n      onSelect={select}\n      asChild={Boolean(link)}\n      className={cn(\n        \"relative flex min-h-9 min-w-0 cursor-default items-center gap-2.5 rounded-[max(calc(var(--radius-sm)*0.5),calc(var(--command-radius)-var(--command-inset)))] px-2.5 py-1.5 text-sm text-foreground outline-none select-none focus-visible:outline-hidden in-data-highlighting:text-muted-foreground data-[confirming]:bg-transparent data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-muted forced-colors:data-[selected=true]:outline-2 forced-colors:data-[selected=true]:-outline-offset-2 forced-colors:data-[selected=true]:outline-solid pointer-coarse:min-h-11 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[selected=true]:[&_svg:not([class*='text-'])]:text-foreground [&:is(a)]:cursor-pointer\",\n        className\n      )}\n      {...props}\n    >\n      {link ? React.cloneElement(link, undefined, content) : content}\n    </CommandPrimitive.Item>\n  )\n}\n\ntype CommandShortcutProps = React.ComponentProps<\"kbd\"> & {\n  hotkey?: string\n}\n\nfunction CommandShortcut({\n  className,\n  dir = \"ltr\",\n  hotkey,\n  children,\n  ...props\n}: CommandShortcutProps) {\n  const apple = useIsApple()\n\n  return (\n    <kbd\n      data-slot=\"command-shortcut\"\n      dir={dir}\n      className={cn(\n        \"ms-auto shrink-0 font-sans text-xs tracking-widest text-muted-foreground [unicode-bidi:isolate]\",\n        className\n      )}\n      {...props}\n    >\n      {children ?? (hotkey ? formatHotkey(hotkey, apple) : null)}\n    </kbd>\n  )\n}\n\nfunction CommandKey({ children }: { children: React.ReactNode }) {\n  return (\n    <kbd className=\"inline-flex h-5 min-w-5 items-center justify-center rounded-[calc(var(--radius-sm)*0.7)] bg-muted px-1 font-sans text-[0.6875rem] font-medium text-muted-foreground\">\n      {children}\n    </kbd>\n  )\n}\n\nfunction CommandFooter({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  const context = React.useContext(CommandContext)\n  const nested = (context?.pages.length ?? 0) > 0\n\n  return (\n    <div\n      data-slot=\"command-footer\"\n      className={cn(\n        \"flex shrink-0 items-center gap-4 border-t px-3.5 py-2 text-xs text-muted-foreground select-none pointer-coarse:hidden\",\n        className\n      )}\n      {...props}\n    >\n      {children ?? (\n        <>\n          <span className=\"inline-flex items-center gap-1.5\">\n            <CommandKey>↑</CommandKey>\n            <CommandKey>↓</CommandKey>\n            Navigate\n          </span>\n          <span className=\"inline-flex items-center gap-1.5\">\n            <CommandKey>↵</CommandKey>\n            Open\n          </span>\n          {nested ? (\n            <span className=\"inline-flex items-center gap-1.5\">\n              <CommandKey>⌫</CommandKey>\n              Back\n            </span>\n          ) : null}\n          <span className=\"ms-auto inline-flex items-center gap-1.5\">\n            <CommandKey>esc</CommandKey>\n            {nested ? \"Back\" : \"Close\"}\n          </span>\n        </>\n      )}\n    </div>\n  )\n}\n\ntype CommandDialogProps = DialogPrimitive.Root.Props & {\n  title?: string\n  description?: string\n  className?: string\n  showCloseButton?: boolean\n  preserveSearch?: boolean\n  children?: React.ReactNode\n}\n\nfunction CommandDialog({\n  title = \"Command menu\",\n  description = \"Search for a command to run.\",\n  className,\n  showCloseButton = false,\n  preserveSearch = false,\n  children,\n  open,\n  defaultOpen = false,\n  onOpenChange,\n  ...props\n}: CommandDialogProps) {\n  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n  const isOpen = open ?? uncontrolledOpen\n  const popupRef = React.useRef<HTMLDivElement>(null)\n\n  React.useEffect(() => {\n    if (!isOpen || !preserveSearch) {\n      return\n    }\n    const frame = requestAnimationFrame(() => {\n      popupRef.current\n        ?.querySelector<HTMLInputElement>(\"[data-slot=command-input]\")\n        ?.select()\n    })\n    return () => cancelAnimationFrame(frame)\n  }, [isOpen, preserveSearch])\n\n  return (\n    <DialogPrimitive.Root\n      open={isOpen}\n      onOpenChange={(next, details) => {\n        if (\n          !next &&\n          details.reason === \"escape-key\" &&\n          details.event.defaultPrevented\n        ) {\n          details.cancel()\n          return\n        }\n        onOpenChange?.(next, details)\n        if (!details.isCanceled && open === undefined) {\n          setUncontrolledOpen(next)\n        }\n      }}\n      {...props}\n    >\n      <DialogPrimitive.Portal keepMounted={preserveSearch}>\n        <DialogPrimitive.Backdrop\n          data-slot=\"command-dialog-overlay\"\n          className=\"fixed inset-0 z-50 min-h-dvh bg-black/40 supports-backdrop-filter:backdrop-blur-xs dark:bg-black/60\"\n        />\n        <DialogPrimitive.Popup\n          ref={popupRef}\n          data-slot=\"command-dialog\"\n          className={cn(\n            \"fixed inset-x-4 top-[max(env(safe-area-inset-top),1rem)] z-50 mx-auto flex max-w-lg flex-col overflow-hidden rounded-(--command-radius) bg-popover text-popover-foreground shadow-2xl ring-(length:--hairline) ring-foreground/10 outline-none [--command-radius:var(--radius-xl)] focus-visible:outline-hidden sm:top-[12dvh] forced-colors:border\",\n            className\n          )}\n        >\n          <DialogPrimitive.Title className=\"sr-only\">\n            {title}\n          </DialogPrimitive.Title>\n          <DialogPrimitive.Description className=\"sr-only\">\n            {description}\n          </DialogPrimitive.Description>\n          {children}\n          {showCloseButton ? (\n            <DialogPrimitive.Close\n              data-slot=\"command-dialog-close\"\n              render={\n                <Button\n                  variant=\"ghost\"\n                  size=\"icon-sm\"\n                  className=\"absolute end-2 top-2\"\n                />\n              }\n            >\n              <IconX />\n              <span className=\"sr-only\">Close</span>\n            </DialogPrimitive.Close>\n          ) : null}\n        </DialogPrimitive.Popup>\n      </DialogPrimitive.Portal>\n    </DialogPrimitive.Root>\n  )\n}\n\nfunction useCommandHotkey(\n  hotkey: string,\n  callback: (event: KeyboardEvent) => void,\n  { enabled = true }: { enabled?: boolean } = {}\n) {\n  const callbackRef = React.useRef(callback)\n\n  React.useLayoutEffect(() => {\n    callbackRef.current = callback\n  })\n\n  React.useEffect(() => {\n    if (!enabled) {\n      return\n    }\n    const bare = !/(^|\\+)(mod|meta|ctrl|alt)\\+/i.test(hotkey)\n\n    const onKeyDown = (event: KeyboardEvent) => {\n      if (\n        event.defaultPrevented ||\n        event.repeat ||\n        event.isComposing ||\n        !matchesHotkey(event, hotkey) ||\n        (bare && isEditable(event.target))\n      ) {\n        return\n      }\n      event.preventDefault()\n      callbackRef.current(event)\n    }\n\n    document.addEventListener(\"keydown\", onKeyDown)\n    return () => document.removeEventListener(\"keydown\", onKeyDown)\n  }, [enabled, hotkey])\n}\n\nfunction useHotkeyLabel(hotkey: string) {\n  return formatHotkey(hotkey, useIsApple())\n}\n\nexport {\n  Command,\n  CommandDialog,\n  CommandInput,\n  CommandList,\n  CommandEmpty,\n  CommandLoading,\n  CommandGroup,\n  CommandItem,\n  CommandShortcut,\n  CommandSeparator,\n  CommandPage,\n  CommandFooter,\n  useCommandHotkey,\n  useCommandLoading,\n  useCommandPages,\n  useCommandState,\n  useHotkeyLabel,\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "overlays"
  ],
  "type": "registry:ui"
}