{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "command-menu",
  "title": "Command Menu",
  "description": "⌘K command palette for nav, posts, connect, and theme.",
  "dependencies": [
    "cmdk",
    "lucide-react",
    "next",
    "next-themes"
  ],
  "files": [
    {
      "path": "registry/default/blocks/command-menu.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Command } from \"cmdk\"\nimport { useRouter } from \"next/navigation\"\nimport { useTheme } from \"next-themes\"\nimport {\n  ArrowRight,\n  ArrowUpRight,\n  Github,\n  Home,\n  Mail,\n  Moon,\n  Sun,\n} from \"lucide-react\"\n\n/** Edit email, nav, and connect links after install. */\nconst CONTENT = {\n  email: \"hello@example.com\",\n  nav: [\n    { label: \"Home\", href: \"/\" },\n    { label: \"Projects\", href: \"/projects\" },\n    { label: \"Writing\", href: \"/writing\" },\n    { label: \"Log\", href: \"/log\" },\n    { label: \"Resume\", href: \"/resume\" },\n    { label: \"Contact\", href: \"/contact\" },\n  ] as Array<{ label: string; href: string; external?: boolean }>,\n  connect: [\n    {\n      label: \"GitHub\",\n      href: \"https://github.com\",\n      external: true,\n    },\n    {\n      label: \"X / Twitter\",\n      href: \"https://x.com\",\n      external: true,\n    },\n  ] as Array<{ label: string; href: string; external?: boolean }>,\n}\n\nexport type CommandMenuPost = { slug: string; title: string; href?: string }\nexport type CommandMenuPage = {\n  label: string\n  href: string\n  external?: boolean\n}\n\nexport function CommandMenu({\n  email = CONTENT.email,\n  nav = CONTENT.nav,\n  connect = CONTENT.connect,\n  posts,\n  pages,\n}: {\n  email?: string\n  nav?: CommandMenuPage[]\n  connect?: CommandMenuPage[]\n  posts?: CommandMenuPost[]\n  pages?: CommandMenuPage[]\n} = {}) {\n  const router = useRouter()\n  const { resolvedTheme, setTheme } = useTheme()\n  const [open, setOpen] = React.useState(false)\n  const [copied, setCopied] = React.useState(false)\n\n  React.useEffect(() => {\n    const onKeyDown = (e: KeyboardEvent) => {\n      const isToggle =\n        (e.key === \"k\" || e.key === \"K\") && (e.metaKey || e.ctrlKey)\n      if (isToggle) {\n        e.preventDefault()\n        setOpen((v) => !v)\n      }\n    }\n    window.addEventListener(\"keydown\", onKeyDown)\n    return () => window.removeEventListener(\"keydown\", onKeyDown)\n  }, [])\n\n  React.useEffect(() => {\n    if (!open) return\n    const y = window.scrollY\n    const { style } = document.body\n    const prev = {\n      overflow: style.overflow,\n      position: style.position,\n      top: style.top,\n      width: style.width,\n      left: style.left,\n      right: style.right,\n    }\n    style.overflow = \"hidden\"\n    style.position = \"fixed\"\n    style.top = `-${y}px`\n    style.left = \"0\"\n    style.right = \"0\"\n    style.width = \"100%\"\n    return () => {\n      style.overflow = prev.overflow\n      style.position = prev.position\n      style.top = prev.top\n      style.width = prev.width\n      style.left = prev.left\n      style.right = prev.right\n      window.scrollTo(0, y)\n    }\n  }, [open])\n\n  const run = React.useCallback((action: () => void) => {\n    setOpen(false)\n    requestAnimationFrame(action)\n  }, [])\n\n  const copyEmail = React.useCallback(() => {\n    navigator.clipboard?.writeText(email).catch(() => {})\n    setCopied(true)\n    setTimeout(() => setCopied(false), 1600)\n  }, [email])\n\n  const go = (href: string, external?: boolean) => {\n    if (external || /^https?:\\/\\//.test(href) || href.startsWith(\"mailto:\")) {\n      window.open(href, \"_blank\", \"noopener,noreferrer\")\n      return\n    }\n    router.push(href)\n  }\n\n  return (\n    <Command.Dialog\n      open={open}\n      onOpenChange={setOpen}\n      label=\"Command menu\"\n      loop\n    >\n      <div className=\"cmdk-input-row\">\n        <Command.Input\n          placeholder=\"Type a command or search…\"\n          className=\"cmdk-input\"\n        />\n        <kbd className=\"cmdk-kbd\">esc</kbd>\n      </div>\n\n      <Command.List className=\"cmdk-list\">\n        <Command.Empty className=\"cmdk-empty\">\n          Nothing matches — try a page name or ⌘K again.\n        </Command.Empty>\n\n        <Command.Group heading=\"Go\" className=\"cmdk-group\">\n          {nav.map((item) => (\n            <CmdItem\n              key={item.href}\n              icon={<Home size={14} />}\n              label={item.label}\n              shortcut={\n                item.external ? <ArrowUpRight size=\"0.9em\" /> : undefined\n              }\n              onSelect={() => run(() => go(item.href, item.external))}\n            />\n          ))}\n        </Command.Group>\n\n        {pages && pages.length > 0 ? (\n          <Command.Group heading=\"Pages\" className=\"cmdk-group\">\n            {pages.map((p) => (\n              <CmdItem\n                key={p.href}\n                icon={<ArrowRight size={14} />}\n                label={p.label}\n                shortcut={\n                  p.external ? <ArrowUpRight size=\"0.9em\" /> : undefined\n                }\n                onSelect={() => run(() => go(p.href, p.external))}\n              />\n            ))}\n          </Command.Group>\n        ) : null}\n\n        {posts && posts.length > 0 ? (\n          <Command.Group heading=\"Posts\" className=\"cmdk-group\">\n            {posts.map((p) => {\n              const href = p.href ?? `/writing/${p.slug}`\n              const external = /^https?:\\/\\//.test(href)\n              return (\n                <CmdItem\n                  key={p.slug}\n                  icon={<ArrowRight size={14} />}\n                  label={p.title}\n                  shortcut={\n                    external ? <ArrowUpRight size=\"0.9em\" /> : undefined\n                  }\n                  onSelect={() => run(() => go(href, external))}\n                />\n              )\n            })}\n          </Command.Group>\n        ) : null}\n\n        <Command.Group heading=\"Connect\" className=\"cmdk-group\">\n          {connect.map((item) => (\n            <CmdItem\n              key={item.href}\n              icon={<Github size={14} />}\n              label={item.label}\n              shortcut={<ArrowUpRight size=\"0.9em\" />}\n              onSelect={() => run(() => go(item.href, item.external ?? true))}\n            />\n          ))}\n          <CmdItem\n            icon={<Mail size={14} />}\n            label={copied ? \"Email copied\" : \"Copy email\"}\n            onSelect={copyEmail}\n          />\n        </Command.Group>\n\n        <Command.Group heading=\"Appearance\" className=\"cmdk-group\">\n          <CmdItem\n            icon={\n              resolvedTheme === \"dark\" ? <Sun size={14} /> : <Moon size={14} />\n            }\n            label={resolvedTheme === \"dark\" ? \"Light mode\" : \"Dark mode\"}\n            onSelect={() =>\n              run(() => setTheme(resolvedTheme === \"dark\" ? \"light\" : \"dark\"))\n            }\n          />\n        </Command.Group>\n      </Command.List>\n    </Command.Dialog>\n  )\n}\n\nfunction CmdItem({\n  icon,\n  label,\n  shortcut,\n  onSelect,\n}: {\n  icon: React.ReactNode\n  label: string\n  shortcut?: React.ReactNode\n  onSelect: () => void\n}) {\n  return (\n    <Command.Item onSelect={onSelect} className=\"cmdk-item\" value={label}>\n      <span className=\"cmdk-item-icon\" aria-hidden>\n        {icon}\n      </span>\n      <span className=\"cmdk-item-label\">{label}</span>\n      {shortcut ? (\n        <span className=\"cmdk-item-shortcut\" aria-hidden>\n          {shortcut}\n        </span>\n      ) : null}\n    </Command.Item>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/blocks/command-menu.tsx"
    }
  ],
  "type": "registry:block"
}