{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pixel-image",
  "type": "registry:ui",
  "title": "Pixel Image",
  "description": "A component that displays an image with a pixelated effect, creating a retro aesthetic.",
  "files": [
    {
      "path": "registry/magicui/pixel-image.tsx",
      "content": "\"use client\"\n\nimport { useEffect, useMemo, useState } from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype Grid = {\n  rows: number\n  cols: number\n}\n\nconst DEFAULT_GRIDS: Record<string, Grid> = {\n  \"6x4\": { rows: 4, cols: 6 },\n  \"8x8\": { rows: 8, cols: 8 },\n  \"8x3\": { rows: 3, cols: 8 },\n  \"4x6\": { rows: 6, cols: 4 },\n  \"3x8\": { rows: 8, cols: 3 },\n}\n\ntype PredefinedGridKey = keyof typeof DEFAULT_GRIDS\n\ninterface PixelImageProps {\n  src: string\n  grid?: PredefinedGridKey\n  customGrid?: Grid\n  grayscaleAnimation?: boolean\n  pixelFadeInDuration?: number // in ms\n  maxAnimationDelay?: number // in ms\n  colorRevealDelay?: number // in ms\n}\n\nexport const PixelImage = ({\n  src,\n  grid = \"6x4\",\n  grayscaleAnimation = true,\n  pixelFadeInDuration = 1000,\n  maxAnimationDelay = 1200,\n  colorRevealDelay = 1300,\n  customGrid,\n}: PixelImageProps) => {\n  const [isVisible, setIsVisible] = useState(false)\n  const [showColor, setShowColor] = useState(false)\n\n  const MIN_GRID = 1\n  const MAX_GRID = 16\n\n  const { rows, cols } = useMemo(() => {\n    const isValidGrid = (grid?: Grid) => {\n      if (!grid) return false\n      const { rows, cols } = grid\n      return (\n        Number.isInteger(rows) &&\n        Number.isInteger(cols) &&\n        rows >= MIN_GRID &&\n        cols >= MIN_GRID &&\n        rows <= MAX_GRID &&\n        cols <= MAX_GRID\n      )\n    }\n\n    return isValidGrid(customGrid) ? customGrid! : DEFAULT_GRIDS[grid]\n  }, [customGrid, grid])\n\n  useEffect(() => {\n    setIsVisible(true)\n    const colorTimeout = setTimeout(() => {\n      setShowColor(true)\n    }, colorRevealDelay)\n    return () => clearTimeout(colorTimeout)\n  }, [colorRevealDelay])\n\n  const pieces = useMemo(() => {\n    const total = rows * cols\n    return Array.from({ length: total }, (_, index) => {\n      const row = Math.floor(index / cols)\n      const col = index % cols\n\n      const clipPath = `polygon(\n        ${col * (100 / cols)}% ${row * (100 / rows)}%,\n        ${(col + 1) * (100 / cols)}% ${row * (100 / rows)}%,\n        ${(col + 1) * (100 / cols)}% ${(row + 1) * (100 / rows)}%,\n        ${col * (100 / cols)}% ${(row + 1) * (100 / rows)}%\n      )`\n\n      const delay = Math.random() * maxAnimationDelay\n      return {\n        clipPath,\n        delay,\n      }\n    })\n  }, [rows, cols, maxAnimationDelay])\n\n  return (\n    <div className=\"relative h-72 w-72 select-none md:h-96 md:w-96\">\n      {pieces.map((piece, index) => (\n        <div\n          key={index}\n          className={cn(\n            \"absolute inset-0 transition-all ease-out\",\n            isVisible ? \"opacity-100\" : \"opacity-0\"\n          )}\n          style={{\n            clipPath: piece.clipPath,\n            transitionDelay: `${piece.delay}ms`,\n            transitionDuration: `${pixelFadeInDuration}ms`,\n          }}\n        >\n          <img\n            src={src}\n            alt={`Pixel image piece ${index + 1}`}\n            className={cn(\n              \"z-1 rounded-[2.5rem] object-cover\",\n              grayscaleAnimation && (showColor ? \"grayscale-0\" : \"grayscale\")\n            )}\n            style={{\n              transition: grayscaleAnimation\n                ? `filter ${pixelFadeInDuration}ms cubic-bezier(0.4, 0, 0.2, 1)`\n                : \"none\",\n            }}\n            draggable={false}\n          />\n        </div>\n      ))}\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ]
}