{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "command",
  "title": "Command",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/command/Command.vue",
      "content": "<script setup lang=\"ts\">\nimport type { ListboxRootEmits, ListboxRootProps } from 'reka-ui'\nimport type { HTMLAttributes } from 'vue'\nimport { reactiveOmit } from '@vueuse/core'\nimport { ListboxRoot, useFilter, useForwardPropsEmits } from 'reka-ui'\nimport { reactive, ref, watch } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { provideCommandContext } from '.'\n\nconst props = withDefaults(defineProps<ListboxRootProps & { class?: HTMLAttributes['class'] }>(), {\n  modelValue: '',\n})\n\nconst emits = defineEmits<ListboxRootEmits>()\n\nconst delegatedProps = reactiveOmit(props, 'class')\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n\nconst allItems = ref<Map<string, string>>(new Map())\nconst allGroups = ref<Map<string, Set<string>>>(new Map())\n\nconst { contains } = useFilter({ sensitivity: 'base' })\nconst filterState = reactive({\n  search: '',\n  filtered: {\n    /** The count of all visible items. */\n    count: 0,\n    /** Map from visible item id to its search score. */\n    items: new Map() as Map<string, number>,\n    /** Set of groups with at least one visible item. */\n    groups: new Set() as Set<string>,\n  },\n})\n\nfunction filterItems() {\n  if (!filterState.search) {\n    filterState.filtered.count = allItems.value.size\n    // Do nothing, each item will know to show itself because search is empty\n    return\n  }\n\n  // Reset the groups\n  filterState.filtered.groups = new Set()\n  let itemCount = 0\n\n  // Check which items should be included\n  for (const [id, value] of allItems.value) {\n    const score = contains(value, filterState.search)\n    filterState.filtered.items.set(id, score ? 1 : 0)\n    if (score) itemCount++\n  }\n\n  // Check which groups have at least 1 item shown\n  for (const [groupId, group] of allGroups.value) {\n    for (const itemId of group) {\n      if (filterState.filtered.items.get(itemId)! > 0) {\n        filterState.filtered.groups.add(groupId)\n        break\n      }\n    }\n  }\n\n  filterState.filtered.count = itemCount\n}\n\nwatch(\n  () => filterState.search,\n  () => {\n    filterItems()\n  },\n)\n\nprovideCommandContext({\n  allItems,\n  allGroups,\n  filterState,\n})\n</script>\n\n<template>\n  <ListboxRoot\n    data-uipkge\n    data-slot=\"command\"\n    v-bind=\"forwarded\"\n    :class=\"\n      cn('bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md', props.class)\n    \"\n  >\n    <slot />\n  </ListboxRoot>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/command/Command.vue"
    },
    {
      "path": "packages/registry-vue/components/command/CommandDialog.vue",
      "content": "<script setup lang=\"ts\">\nimport type { DialogRootEmits, DialogRootProps } from 'reka-ui'\nimport { useForwardPropsEmits } from 'reka-ui'\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'\nimport Command from './Command.vue'\n\nconst props = withDefaults(\n  defineProps<\n    DialogRootProps & {\n      title?: string\n      description?: string\n    }\n  >(),\n  {\n    title: 'Command Palette',\n    description: 'Search for a command to run...',\n  },\n)\nconst emits = defineEmits<DialogRootEmits>()\n\nconst forwarded = useForwardPropsEmits(props, emits)\n</script>\n\n<template>\n  <Dialog v-slot=\"slotProps\" v-bind=\"forwarded\">\n    <DialogContent class=\"overflow-hidden p-0\">\n      <DialogHeader class=\"sr-only\">\n        <DialogTitle>{{ title }}</DialogTitle>\n        <DialogDescription>{{ description }}</DialogDescription>\n      </DialogHeader>\n      <Command>\n        <slot v-bind=\"slotProps\" />\n      </Command>\n    </DialogContent>\n  </Dialog>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/command/CommandDialog.vue"
    },
    {
      "path": "packages/registry-vue/components/command/CommandEmpty.vue",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from 'reka-ui'\nimport type { HTMLAttributes } from 'vue'\nimport { reactiveOmit } from '@vueuse/core'\nimport { Primitive } from 'reka-ui'\nimport { computed } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { useCommand } from '.'\n\nconst props = defineProps<PrimitiveProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = reactiveOmit(props, 'class')\n\nconst { filterState } = useCommand()\nconst isRender = computed(() => !!filterState.search && filterState.filtered.count === 0)\n</script>\n\n<template>\n  <Primitive\n    v-if=\"isRender\"\n    data-uipkge\n    data-slot=\"command-empty\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('py-6 text-center text-sm', props.class)\"\n  >\n    <slot />\n  </Primitive>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/command/CommandEmpty.vue"
    },
    {
      "path": "packages/registry-vue/components/command/CommandGroup.vue",
      "content": "<script setup lang=\"ts\">\nimport type { ListboxGroupProps } from 'reka-ui'\nimport type { HTMLAttributes } from 'vue'\nimport { reactiveOmit } from '@vueuse/core'\nimport { ListboxGroup, ListboxGroupLabel, useId } from 'reka-ui'\nimport { computed, onMounted, onUnmounted } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { provideCommandGroupContext, useCommand } from '.'\n\nconst props = defineProps<\n  ListboxGroupProps & {\n    class?: HTMLAttributes['class']\n    heading?: string\n  }\n>()\n\nconst delegatedProps = reactiveOmit(props, 'class')\n\nconst { allGroups, filterState } = useCommand()\nconst id = useId()\n\nconst isRender = computed(() => (!filterState.search ? true : filterState.filtered.groups.has(id)))\n\nprovideCommandGroupContext({ id })\nonMounted(() => {\n  if (!allGroups.value.has(id)) allGroups.value.set(id, new Set())\n})\nonUnmounted(() => {\n  allGroups.value.delete(id)\n})\n</script>\n\n<template>\n  <ListboxGroup\n    v-bind=\"delegatedProps\"\n    :id=\"id\"\n    data-uipkge\n    data-slot=\"command-group\"\n    :class=\"cn('text-foreground overflow-hidden p-1', props.class)\"\n    :hidden=\"isRender ? undefined : true\"\n  >\n    <ListboxGroupLabel\n      v-if=\"heading\"\n      data-uipkge\n      data-slot=\"command-group-heading\"\n      class=\"text-muted-foreground px-2 py-1.5 text-xs font-medium\"\n    >\n      {{ heading }}\n    </ListboxGroupLabel>\n    <slot />\n  </ListboxGroup>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/command/CommandGroup.vue"
    },
    {
      "path": "packages/registry-vue/components/command/CommandInput.vue",
      "content": "<script setup lang=\"ts\">\nimport type { ListboxFilterProps } from 'reka-ui'\nimport type { HTMLAttributes } from 'vue'\nimport { reactiveOmit } from '@vueuse/core'\nimport { Search } from 'lucide-vue-next'\nimport { ListboxFilter, useForwardProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\nimport { useCommand } from '.'\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(\n  defineProps<\n    ListboxFilterProps & {\n      class?: HTMLAttributes['class']\n    }\n  >(),\n  // Focus on mount by default (command-palette behavior). Consumers embedding\n  // Command inline on a page can pass :auto-focus=\"false\" so it doesn't steal\n  // focus and scroll the input into view on load.\n  { autoFocus: true },\n)\n\nconst delegatedProps = reactiveOmit(props, 'class')\n\nconst forwardedProps = useForwardProps(delegatedProps)\n\nconst { filterState } = useCommand()\n</script>\n\n<template>\n  <div data-uipkge data-slot=\"command-input-wrapper\" class=\"flex h-9 items-center gap-2 border-b px-3\">\n    <Search class=\"text-muted-foreground size-4 shrink-0\" aria-hidden=\"true\" />\n    <ListboxFilter\n      v-bind=\"{ ...forwardedProps, ...$attrs }\"\n      v-model=\"filterState.search\"\n      data-uipkge\n      data-slot=\"command-input\"\n      :class=\"\n        cn(\n          'placeholder:text-muted-foreground focus-visible:ring-ring/40 flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden focus-visible:ring-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',\n          props.class,\n        )\n      \"\n    />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/command/CommandInput.vue"
    },
    {
      "path": "packages/registry-vue/components/command/CommandItem.vue",
      "content": "<script setup lang=\"ts\">\nimport type { ListboxItemEmits, ListboxItemProps } from 'reka-ui'\nimport type { HTMLAttributes } from 'vue'\nimport { reactiveOmit, useCurrentElement } from '@vueuse/core'\nimport { ListboxItem, useForwardPropsEmits, useId } from 'reka-ui'\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { useCommand, useCommandGroup } from '.'\n\nconst props = defineProps<ListboxItemProps & { class?: HTMLAttributes['class'] }>()\nconst emits = defineEmits<ListboxItemEmits>()\n\nconst delegatedProps = reactiveOmit(props, 'class')\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n\nconst id = useId()\nconst { filterState, allItems, allGroups } = useCommand()\nconst groupContext = useCommandGroup()\n\nconst isRender = computed(() => {\n  if (!filterState.search) {\n    return true\n  } else {\n    const filteredCurrentItem = filterState.filtered.items.get(id)\n    // If the filtered items is undefined means not in the all times map yet\n    // Do the first render to add into the map\n    if (filteredCurrentItem === undefined) {\n      return true\n    }\n\n    // Check with filter\n    return filteredCurrentItem > 0\n  }\n})\n\nconst itemRef = ref()\nconst currentElement = useCurrentElement(itemRef)\nonMounted(() => {\n  if (!(currentElement.value instanceof HTMLElement)) return\n\n  // textValue to perform filter\n  allItems.value.set(id, currentElement.value.textContent ?? props.value?.toString() ?? '')\n\n  const groupId = groupContext?.id\n  if (groupId) {\n    if (!allGroups.value.has(groupId)) {\n      allGroups.value.set(groupId, new Set([id]))\n    } else {\n      allGroups.value.get(groupId)?.add(id)\n    }\n  }\n})\nonUnmounted(() => {\n  allItems.value.delete(id)\n})\n</script>\n\n<template>\n  <ListboxItem\n    v-if=\"isRender\"\n    v-bind=\"forwarded\"\n    :id=\"id\"\n    ref=\"itemRef\"\n    data-uipkge\n    data-slot=\"command-item\"\n    :class=\"\n      cn(\n        'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg:not([class*=\\'text-\\'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\\'size-\\'])]:size-4',\n        props.class,\n      )\n    \"\n    @select=\"\n      () => {\n        filterState.search = ''\n      }\n    \"\n  >\n    <slot />\n  </ListboxItem>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/command/CommandItem.vue"
    },
    {
      "path": "packages/registry-vue/components/command/CommandList.vue",
      "content": "<script setup lang=\"ts\">\nimport type { ListboxContentProps } from 'reka-ui'\nimport type { HTMLAttributes } from 'vue'\nimport { reactiveOmit } from '@vueuse/core'\nimport { ListboxContent, useForwardProps } from 'reka-ui'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<ListboxContentProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = reactiveOmit(props, 'class')\n\nconst forwarded = useForwardProps(delegatedProps)\n</script>\n\n<template>\n  <ListboxContent\n    data-uipkge\n    data-slot=\"command-list\"\n    v-bind=\"forwarded\"\n    :class=\"cn('max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto', props.class)\"\n  >\n    <div role=\"presentation\">\n      <slot />\n    </div>\n  </ListboxContent>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/command/CommandList.vue"
    },
    {
      "path": "packages/registry-vue/components/command/CommandSeparator.vue",
      "content": "<script setup lang=\"ts\">\nimport type { SeparatorProps } from 'reka-ui'\nimport type { HTMLAttributes } from 'vue'\nimport { reactiveOmit } from '@vueuse/core'\nimport { Separator } from 'reka-ui'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<SeparatorProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = reactiveOmit(props, 'class')\n</script>\n\n<template>\n  <Separator\n    data-uipkge\n    data-slot=\"command-separator\"\n    v-bind=\"delegatedProps\"\n    :class=\"cn('bg-border -mx-1 h-px', props.class)\"\n  >\n    <slot />\n  </Separator>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/command/CommandSeparator.vue"
    },
    {
      "path": "packages/registry-vue/components/command/CommandShortcut.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n</script>\n\n<template>\n  <span\n    data-uipkge\n    data-slot=\"command-shortcut\"\n    :class=\"cn('text-muted-foreground ml-auto text-xs tracking-widest', props.class)\"\n  >\n    <slot />\n  </span>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/command/CommandShortcut.vue"
    },
    {
      "path": "packages/registry-vue/components/command/index.ts",
      "content": "import type { Ref } from 'vue'\nimport { createContext } from 'reka-ui'\n\nexport { default as Command } from './Command.vue'\nexport { default as CommandDialog } from './CommandDialog.vue'\nexport { default as CommandEmpty } from './CommandEmpty.vue'\nexport { default as CommandGroup } from './CommandGroup.vue'\nexport { default as CommandInput } from './CommandInput.vue'\nexport { default as CommandItem } from './CommandItem.vue'\nexport { default as CommandList } from './CommandList.vue'\nexport { default as CommandSeparator } from './CommandSeparator.vue'\nexport { default as CommandShortcut } from './CommandShortcut.vue'\n\nexport const [useCommand, provideCommandContext] = createContext<{\n  allItems: Ref<Map<string, string>>\n  allGroups: Ref<Map<string, Set<string>>>\n  filterState: {\n    search: string\n    filtered: { count: number; items: Map<string, number>; groups: Set<string> }\n  }\n}>('Command')\n\nexport const [useCommandGroup, provideCommandGroupContext] = createContext<{\n  id?: string\n}>('CommandGroup')\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/command/index.ts"
    }
  ],
  "dependencies": [
    "@vueuse/core",
    "lucide-vue-next",
    "reka-ui"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/dialog.json"
  ],
  "description": "Searchable command palette à la Cmd-K — keyboard-driven menu with grouped items, icons, shortcuts, and fuzzy filtering. Use as a global launcher (mounted in a Dialog) or inline as a typeahead select.",
  "categories": [
    "overlay"
  ]
}