{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "command-palette",
  "title": "Command Palette",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/command-palette/CommandPalette.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, type Component } from 'vue'\nimport { LayoutDashboard, FileText, Inbox, Settings, Users, KanbanSquare, Search } from 'lucide-vue-next'\nimport {\n  CommandDialog,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n  CommandSeparator,\n  CommandShortcut,\n} from '@/components/ui/command'\n\nexport interface CommandPaletteItem {\n  label: string\n  value?: string\n  hint?: string\n  icon?: Component\n  onSelect?: () => void\n}\n\nexport interface CommandPaletteGroup {\n  heading: string\n  items: CommandPaletteItem[]\n}\n\nconst props = withDefaults(\n  defineProps<{\n    groups?: CommandPaletteGroup[]\n    placeholder?: string\n    triggerLabel?: string\n    showTrigger?: boolean\n  }>(),\n  {\n    placeholder: 'Search pages, commands…',\n    triggerLabel: 'Search pages, commands…',\n    showTrigger: true,\n    groups: () => [\n      {\n        heading: 'Navigate',\n        items: [\n          { label: 'Dashboard', hint: '/dashboard', icon: LayoutDashboard },\n          { label: 'Inbox', hint: '/inbox', icon: Inbox },\n          { label: 'Kanban', hint: '/kanban', icon: KanbanSquare },\n          { label: 'Team', hint: '/team', icon: Users },\n        ],\n      },\n      {\n        heading: 'Settings',\n        items: [\n          { label: 'Profile', hint: '/profile', icon: FileText },\n          { label: 'Settings', hint: '/settings', icon: Settings },\n        ],\n      },\n    ],\n  },\n)\n\nconst emit = defineEmits<{\n  (e: 'select', item: CommandPaletteItem): void\n}>()\n\nconst open = ref(false)\n\nfunction show() {\n  open.value = true\n}\n\nfunction hide() {\n  open.value = false\n}\n\nfunction toggle() {\n  open.value = !open.value\n}\n\nfunction pick(item: CommandPaletteItem) {\n  hide()\n  item.onSelect?.()\n  emit('select', item)\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n  if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {\n    e.preventDefault()\n    toggle()\n  }\n}\n\nconst triggerShortcut = computed(() => {\n  if (typeof navigator === 'undefined') return '⌘K'\n  return /Mac|iPhone|iPad/i.test(navigator.platform) ? '⌘K' : 'Ctrl K'\n})\n\nonMounted(() => window.addEventListener('keydown', onKeydown))\nonBeforeUnmount(() => window.removeEventListener('keydown', onKeydown))\n\ndefineExpose({ show, hide, toggle })\n</script>\n\n<template>\n  <button\n    v-if=\"showTrigger\"\n    type=\"button\"\n    class=\"bg-secondary/50 hover:bg-secondary text-muted-foreground focus-visible:ring-ring relative hidden h-8 w-full items-center gap-2 rounded-lg border border-transparent px-2.5 text-sm shadow-none transition-colors focus-visible:ring-1 focus-visible:outline-none sm:flex md:w-[220px] lg:w-[300px]\"\n    aria-label=\"Open command palette\"\n    @click=\"show\"\n  >\n    <Search class=\"size-3.5 shrink-0\" />\n    <span class=\"flex-1 truncate text-left\">{{ triggerLabel }}</span>\n    <kbd\n      class=\"bg-muted/80 text-muted-foreground pointer-events-none flex h-5 items-center justify-center rounded-md border px-1.5 font-mono text-[10px] font-medium\"\n    >\n      <span>{{ triggerShortcut }}</span>\n    </kbd>\n  </button>\n\n  <CommandDialog v-model:open=\"open\" title=\"Command palette\" description=\"Search pages and run commands\">\n    <CommandInput :placeholder=\"placeholder\" />\n    <CommandList class=\"max-h-[480px]\">\n      <CommandEmpty>No matches.</CommandEmpty>\n      <template v-for=\"(group, gi) in groups\" :key=\"group.heading\">\n        <CommandGroup :heading=\"group.heading\">\n          <CommandItem\n            v-for=\"item in group.items\"\n            :key=\"`${group.heading}-${item.label}`\"\n            :value=\"`${group.heading} ${item.label} ${item.hint ?? ''}`\"\n            @select=\"pick(item)\"\n          >\n            <component :is=\"item.icon\" v-if=\"item.icon\" class=\"size-4\" />\n            <span>{{ item.label }}</span>\n            <CommandShortcut v-if=\"item.hint\" class=\"text-muted-foreground/70\">{{ item.hint }}</CommandShortcut>\n          </CommandItem>\n        </CommandGroup>\n        <CommandSeparator v-if=\"gi < groups.length - 1\" />\n      </template>\n    </CommandList>\n  </CommandDialog>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/CommandPalette.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/command.json"
  ],
  "description": "Header-grade search/command palette. Bundles the slim trigger button (with platform-aware ⌘K/Ctrl-K kbd hint) AND the modal CommandDialog into one block; consumers drop it once and the global keyboard shortcut wires itself. Takes a `groups` array of `{ heading, items: [{ label, hint, icon, onSelect }] }` and emits `select`. `show-trigger=false` hides the inline button so the consumer can fire it from elsewhere via the exposed `show()` / `toggle()` methods.",
  "categories": [
    "dashboard",
    "overlay",
    "navigation"
  ]
}