{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "image-editor-command-menu",
  "title": "Command Menu",
  "description": "Cmd+K palette for tools, history and filters.",
  "dependencies": [
    "lucide-react",
    "photocn@^0.1.1"
  ],
  "registryDependencies": [
    "command",
    "https://photocn.dev/r/image-editor-toolbar.json"
  ],
  "files": [
    {
      "path": "src/registry/image-editor/command-menu.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useState, type ComponentType, type SVGProps } from \"react\";\nimport {\n  EyeOffIcon,\n  ImagePlusIcon,\n  Redo2Icon,\n  RotateCcwIcon,\n  Undo2Icon,\n  WandSparklesIcon,\n} from \"lucide-react\";\nimport { useImageEditor } from \"photocn/react\";\n\nimport { editorToolbarDefaultTools } from \"./toolbar\";\n\nimport {\n  Command,\n  CommandDialog,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n  CommandSeparator,\n  CommandShortcut,\n} from \"@/components/ui/command\";\n\nexport interface EditorCommandItem {\n  value: string;\n  label: string;\n  keywords?: string[];\n  shortcut?: string;\n  disabled?: boolean;\n  icon?: ComponentType<SVGProps<SVGSVGElement>>;\n  onSelect?: (value: string) => void;\n}\n\nexport interface EditorCommandGroup {\n  heading?: string;\n  items: readonly EditorCommandItem[];\n}\n\nexport interface EditorCommandDialogProps {\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  groups: readonly EditorCommandGroup[];\n  title?: string;\n  description?: string;\n  placeholder?: string;\n  emptyLabel?: string;\n  className?: string;\n}\n\nexport function EditorCommandDialog({\n  open,\n  onOpenChange,\n  groups,\n  title = \"Editor commands\",\n  description = \"Search editor tools and actions.\",\n  placeholder = \"Search commands...\",\n  emptyLabel = \"No commands found.\",\n  className,\n}: EditorCommandDialogProps) {\n  return (\n    <CommandDialog\n      className={className}\n      description={description}\n      onOpenChange={onOpenChange}\n      open={open}\n      title={title}\n    >\n      <Command>\n        <CommandInput placeholder={placeholder} />\n        <CommandList>\n          <CommandEmpty>{emptyLabel}</CommandEmpty>\n          {groups.map((group, groupIndex) => (\n            <CommandGroup\n              heading={group.heading}\n              key={group.heading ?? `group-${groupIndex}`}\n            >\n              {group.items.map((item) => {\n                const Icon = item.icon;\n  \n                return (\n                  <CommandItem\n                    disabled={item.disabled}\n                    key={item.value}\n                    keywords={item.keywords}\n                    onSelect={(value) => item.onSelect?.(value)}\n                    value={item.value}\n                  >\n                    {Icon ? <Icon aria-hidden=\"true\" /> : null}\n                    <span>{item.label}</span>\n                    {item.shortcut ? (\n                      <CommandShortcut>{item.shortcut}</CommandShortcut>\n                    ) : null}\n                  </CommandItem>\n                );\n              })}\n              {groupIndex < groups.length - 1 ? <CommandSeparator /> : null}\n            </CommandGroup>\n          ))}\n        </CommandList>\n      </Command>\n    </CommandDialog>\n  );\n}\n\nexport interface ImageEditorCommandMenuProps {\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  /** Toggle with Cmd/Ctrl+K. Default `true`. */\n  shortcut?: boolean;\n  /** Extra groups appended after the built-in ones. */\n  groups?: readonly EditorCommandGroup[];\n}\n\n/** Command palette for tools, history and filter presets (Cmd/Ctrl+K). */\nexport function ImageEditorCommandMenu({\n  open: openProp,\n  onOpenChange,\n  shortcut = true,\n  groups = [],\n}: ImageEditorCommandMenuProps) {\n  const editor = useImageEditor();\n  const [openState, setOpenState] = useState(false);\n  const open = openProp ?? openState;\n  const setOpen = (next: boolean) => {\n    setOpenState(next);\n    onOpenChange?.(next);\n  };\n\n  useEffect(() => {\n    if (!shortcut) return;\n    const onKeyDown = (event: KeyboardEvent) => {\n      if (event.key.toLowerCase() === \"k\" && (event.metaKey || event.ctrlKey)) {\n        const root = editor.rootRef.current;\n        const inside =\n          !root || root.contains(document.activeElement) || document.activeElement === document.body;\n        if (!inside) return;\n        event.preventDefault();\n        setOpenState((value) => !value);\n      }\n    };\n    window.addEventListener(\"keydown\", onKeyDown);\n    return () => window.removeEventListener(\"keydown\", onKeyDown);\n  }, [shortcut, editor.rootRef]);\n\n  const run = (fn: () => void) => () => {\n    fn();\n    setOpen(false);\n  };\n\n  const builtIn: EditorCommandGroup[] = [\n    {\n      heading: \"Tools\",\n      items: editorToolbarDefaultTools.map((tool) => ({\n        value: `tool:${tool.value}`,\n        label: tool.label,\n        icon: tool.icon,\n        onSelect: run(() => editor.setTool(tool.value)),\n      })),\n    },\n    {\n      heading: \"Edit\",\n      items: [\n        {\n          value: \"undo\",\n          label: \"Undo\",\n          icon: Undo2Icon,\n          shortcut: \"⌘Z\",\n          disabled: !editor.history.canUndo,\n          onSelect: run(editor.history.undo),\n        },\n        {\n          value: \"redo\",\n          label: \"Redo\",\n          icon: Redo2Icon,\n          shortcut: \"⇧⌘Z\",\n          disabled: !editor.history.canRedo,\n          onSelect: run(editor.history.redo),\n        },\n        {\n          value: \"open\",\n          label: \"Open image…\",\n          icon: ImagePlusIcon,\n          onSelect: run(() => void editor.openFile()),\n        },\n        {\n          value: \"reset\",\n          label: \"Reset all edits\",\n          icon: RotateCcwIcon,\n          onSelect: run(editor.resetAll),\n        },\n      ],\n    },\n    {\n      heading: \"Filters\",\n      items: [\n        {\n          value: \"filter:none\",\n          label: \"No filter\",\n          icon: EyeOffIcon,\n          onSelect: run(() => void editor.filters.select(null)),\n        },\n        ...editor.filters.presets.map((preset) => ({\n          value: `filter:${preset.label}`,\n          label: `Filter: ${preset.label}`,\n          keywords: [\"filter\", \"lut\", preset.label],\n          icon: WandSparklesIcon,\n          onSelect: run(() => void editor.filters.select(preset)),\n        })),\n      ],\n    },\n  ];\n\n  return (\n    <EditorCommandDialog\n      groups={[...builtIn, ...groups]}\n      onOpenChange={setOpen}\n      open={open}\n    />\n  );\n}\n\n",
      "type": "registry:component",
      "target": "components/image-editor/command-menu.tsx"
    }
  ],
  "categories": [
    "image-editor"
  ],
  "type": "registry:component"
}