{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "image-editor",
  "title": "Image Editor",
  "description": "The complete photo editor: toolbar, canvas, tool switcher, histogram and every panel. Drop it in and go.",
  "dependencies": [
    "lucide-react",
    "photocn@^0.1.1"
  ],
  "registryDependencies": [
    "badge",
    "https://photocn.dev/r/image-editor-canvas.json",
    "https://photocn.dev/r/image-editor-compact-layout.json",
    "https://photocn.dev/r/image-editor-export.json",
    "https://photocn.dev/r/image-editor-histogram.json",
    "https://photocn.dev/r/image-editor-layout.json",
    "https://photocn.dev/r/image-editor-recipes.json",
    "https://photocn.dev/r/image-editor-tool-panel.json",
    "https://photocn.dev/r/image-editor-toolbar.json",
    "tooltip"
  ],
  "files": [
    {
      "path": "src/registry/image-editor/image-editor.tsx",
      "content": "\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { AlertCircleIcon } from \"lucide-react\";\nimport {\n  errorMessage,\n  ImageEditorProvider,\n  useImageEditor,\n  useImageEditorState,\n  type ImageEditorApi,\n  type ImageEditorExportResult,\n  type ImageEditorToolId,\n  type UseImageEditorStateOptions,\n} from \"photocn/react\";\n\nimport { Badge } from \"@/components/ui/badge\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport { cn } from \"@/lib/utils\";\n\nimport { ImageEditorCanvas } from \"./canvas\";\nimport { ImageEditorExportDialog } from \"./export-dialog\";\nimport { ImageEditorHistogram } from \"./histogram\";\nimport { ImageEditorCompactLayout } from \"./compact-layout\";\nimport {\n  COMPACT_WIDTH,\n  ImageEditorLayout,\n  MOBILE_QUERY,\n  useElementWidth,\n  useMediaQuery,\n} from \"./layout\";\nimport { ImageEditorRecipes } from \"./recipes\";\nimport { ImageEditorToolPanel } from \"./tool-panel\";\nimport {\n  editorToolbarDefaultTools,\n  ImageEditorCompareButton,\n  ImageEditorOpenButton,\n  ImageEditorRedoButton,\n  ImageEditorResetButton,\n  ImageEditorToolbar,\n  ImageEditorUndoButton,\n  type EditorToolbarTool,\n} from \"./toolbar\";\n\nexport interface ImageEditorProps extends UseImageEditorStateOptions {\n  /** Use state you created with `useImageEditorState()` instead of owning it. */\n  editor?: ImageEditorApi;\n  className?: string;\n  /** Tools shown in the sidebar switcher. Default: every built-in tool. */\n  tools?: readonly EditorToolbarTool[];\n  /** Replace or add sidebar panels per tool id. */\n  panels?: Partial<Record<ImageEditorToolId, ReactNode>>;\n  /** Show the \"Open\" button and empty-state picker. Default `true`. */\n  showOpenButton?: boolean;\n  /** Show recipe save/load buttons. Default `true`. */\n  showRecipes?: boolean;\n  /** Show the histogram above the tool panel. Default `true`. */\n  showHistogram?: boolean;\n  /** Extra toolbar content, rendered before the Export button. */\n  toolbarExtra?: ReactNode;\n  /**\n   * \"auto\" (default) switches to the phone layout (bottom tools + drawer)\n   * when the editor itself is narrower than 640px.\n   */\n  layout?: \"auto\" | \"desktop\" | \"compact\";\n  /** Extra content under the active tool panel. */\n  sidebarExtra?: ReactNode;\n  /** Adds a \"Save\" button to the export dialog (e.g. upload the result). */\n  onSave?: (result: ImageEditorExportResult) => void | Promise<void>;\n  /** Rendered inside the provider — e.g. `<ImageEditorCommandMenu />`. */\n  children?: ReactNode;\n}\n\n/**\n * The complete editor: toolbar, canvas, tool switcher, histogram and panels.\n * Every piece is also exported on its own — copy this file's layout and\n * rearrange them to build your own editor.\n */\nexport function ImageEditor({ editor, ...props }: ImageEditorProps) {\n  if (editor) return <ImageEditorView editor={editor} {...props} />;\n  return <OwnedImageEditor {...props} />;\n}\n\nfunction OwnedImageEditor(props: Omit<ImageEditorProps, \"editor\">) {\n  const {\n    className: _className,\n    tools: _tools,\n    panels: _panels,\n    showOpenButton: _showOpenButton,\n    showRecipes: _showRecipes,\n    showHistogram: _showHistogram,\n    toolbarExtra: _toolbarExtra,\n    sidebarExtra: _sidebarExtra,\n    onSave: _onSave,\n    layout: _layout,\n    children: _children,\n    ...options\n  } = props;\n  const editor = useImageEditorState(options);\n  return <ImageEditorView editor={editor} {...props} />;\n}\n\nfunction ImageEditorView({\n  editor,\n  className,\n  tools = editorToolbarDefaultTools,\n  panels,\n  showOpenButton = true,\n  showRecipes = true,\n  showHistogram = true,\n  toolbarExtra,\n  sidebarExtra,\n  onSave,\n  layout = \"auto\",\n  children,\n}: ImageEditorProps & { editor: ImageEditorApi }) {\n  const width = useElementWidth(editor.rootRef);\n  const isMobile = useMediaQuery(MOBILE_QUERY);\n  const compact =\n    layout === \"compact\" || (layout === \"auto\" && (width ? width < COMPACT_WIDTH : isMobile));\n  // Don't mount a layout (and its canvas) until the editor has been measured:\n  // a narrow editor on a wide screen would otherwise start in the desktop\n  // layout and rebuild everything a frame later. A hidden editor (width 0)\n  // falls back to the screen size.\n  const ready = layout !== \"auto\" || width !== null;\n  return (\n    <ImageEditorProvider editor={editor}>\n      <TooltipProvider>\n        <div\n          className={cn(\"@container/editor h-full min-h-[560px] w-full\", className)}\n          data-layout={compact ? \"compact\" : \"desktop\"}\n          data-slot=\"image-editor\"\n          ref={editor.rootRef}\n        >\n          {!ready ? null : compact ? (\n            <ImageEditorCompactLayout\n              onSave={onSave}\n              panels={panels}\n              showOpenButton={showOpenButton}\n              showRecipes={showRecipes}\n              toolbarExtra={toolbarExtra}\n              tools={tools}\n            />\n          ) : (\n          <ImageEditorLayout\n            className=\"h-full\"\n            sidebar={\n              <>\n                <div className=\"border-b bg-background p-3\">\n                  <ImageEditorToolbar tools={tools} />\n                </div>\n                {showHistogram ? (\n                  <ImageEditorHistogram className=\"border-b bg-muted/30 px-3 py-2\" />\n                ) : null}\n                <ImageEditorToolPanel panels={panels} />\n                {sidebarExtra}\n              </>\n            }\n            sidebarDefaultSize=\"338px\"\n            toolbar={\n              <div className=\"flex w-full min-w-0 flex-wrap items-center gap-2\">\n                <ImageEditorUndoButton />\n                <ImageEditorRedoButton />\n                {showOpenButton ? <ImageEditorOpenButton /> : null}\n                <ImageEditorStatusBadge />\n                <div className=\"ml-auto flex flex-wrap items-center gap-2\">\n                  {showRecipes ? <ImageEditorRecipes /> : null}\n                  <ImageEditorResetButton />\n                  <ImageEditorCompareButton />\n                  {toolbarExtra}\n                  <ImageEditorExportDialog onSave={onSave} />\n                </div>\n              </div>\n            }\n          >\n            <ImageEditorCanvas showOpenButton={showOpenButton} />\n          </ImageEditorLayout>\n          )}\n          {children}\n        </div>\n      </TooltipProvider>\n    </ImageEditorProvider>\n  );\n}\n\n/** Loading / error indicator; renders nothing once the editor is ready. */\nexport function ImageEditorStatusBadge() {\n  const editor = useImageEditor();\n  if (editor.status === \"error\") {\n    const message = errorMessage(editor.error);\n    return (\n      <TooltipProvider>\n      <Tooltip>\n        <TooltipTrigger\n          render={\n            <Badge className=\"max-w-48\" variant=\"destructive\">\n              <AlertCircleIcon aria-hidden=\"true\" />\n              <span className=\"truncate\">{message}</span>\n            </Badge>\n          }\n        />\n        <TooltipContent className=\"max-w-sm\">{message}</TooltipContent>\n      </Tooltip>\n      </TooltipProvider>\n    );\n  }\n  if (editor.status === \"loading\") {\n    return <Badge variant=\"outline\">Loading</Badge>;\n  }\n  return null;\n}\n",
      "type": "registry:component",
      "target": "components/image-editor/image-editor.tsx"
    }
  ],
  "categories": [
    "image-editor"
  ],
  "type": "registry:block"
}