{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "image-compare",
  "title": "Image Compare",
  "description": "A before/after image slider — drag the divider to compare both sides in real time",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "src/registry/blocks/image-compare/output-showcase.tsx",
      "content": "\"use client\";\n\nimport { useRef, useState, useCallback, useEffect } from \"react\";\nimport { ChevronsLeftRight } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface ImageCompareProps {\n  label?: string;\n  title?: string;\n  description?: string;\n  beforeImage?: string;\n  afterImage?: string;\n  beforeLabel?: string;\n  afterLabel?: string;\n  initialPosition?: number;\n  className?: string;\n}\n\nconst ImageCompare = ({\n  label = \"Before & After\",\n  title = \"See the difference, instantly.\",\n  description = \"Drag the slider to compare both sides and see the result in real time.\",\n  beforeImage = \"https://www.shadcnship.com/images/placeholders/hero-architecture-10.webp\",\n  afterImage = \"https://www.shadcnship.com/images/placeholders/hero-architecture-12.webp\",\n  beforeLabel = \"Before\",\n  afterLabel = \"After\",\n  initialPosition = 50,\n  className,\n}: ImageCompareProps) => {\n  const [position, setPosition] = useState(initialPosition);\n  const [isDragging, setIsDragging] = useState(false);\n  const containerRef = useRef<HTMLDivElement>(null);\n\n  const updatePosition = useCallback((clientX: number) => {\n    if (!containerRef.current) return;\n    const rect = containerRef.current.getBoundingClientRect();\n    const x = clientX - rect.left;\n    const pct = Math.min(Math.max((x / rect.width) * 100, 0), 100);\n    setPosition(pct);\n  }, []);\n\n  useEffect(() => {\n    if (!isDragging) return;\n    const onMouseMove = (e: MouseEvent) => updatePosition(e.clientX);\n    const onMouseUp = () => setIsDragging(false);\n    window.addEventListener(\"mousemove\", onMouseMove);\n    window.addEventListener(\"mouseup\", onMouseUp);\n    return () => {\n      window.removeEventListener(\"mousemove\", onMouseMove);\n      window.removeEventListener(\"mouseup\", onMouseUp);\n    };\n  }, [isDragging, updatePosition]);\n\n  return (\n    <section className={cn(\"container mx-auto px-8 py-12 md:py-24\", className)}>\n      <div className=\"mx-auto flex max-w-2xl flex-col items-center gap-4 text-center\">\n        {label && (\n          <p className=\"text-sm font-semibold tracking-widest text-muted-foreground uppercase\">\n            {label}\n          </p>\n        )}\n        <h2 className=\"text-4xl font-medium tracking-tight md:text-5xl\">\n          {title}\n        </h2>\n        {description && (\n          <p className=\"text-muted-foreground md:text-lg\">{description}</p>\n        )}\n      </div>\n\n      <div className=\"mx-auto mt-12 max-w-5xl\">\n        <div\n          ref={containerRef}\n          className={cn(\n            \"relative aspect-square overflow-hidden rounded-2xl select-none md:aspect-video\",\n            isDragging ? \"cursor-grabbing\" : \"cursor-col-resize\",\n          )}\n          onMouseDown={(e) => {\n            setIsDragging(true);\n            updatePosition(e.clientX);\n          }}\n          onTouchStart={(e) => {\n            setIsDragging(true);\n            updatePosition(e.touches[0].clientX);\n          }}\n          onTouchMove={(e) => updatePosition(e.touches[0].clientX)}\n          onTouchEnd={() => setIsDragging(false)}\n        >\n          {/* After image — base layer (right side) */}\n          <img\n            src={afterImage}\n            alt={afterLabel}\n            draggable={false}\n            className=\"absolute inset-0 h-full w-full object-cover\"\n          />\n\n          {/* Before image — clipped to the left of the slider */}\n          <img\n            src={beforeImage}\n            alt={beforeLabel}\n            draggable={false}\n            className=\"absolute inset-0 h-full w-full object-cover\"\n            style={{ clipPath: `inset(0 ${100 - position}% 0 0)` }}\n          />\n\n          {/* Vertical divider line */}\n          <div\n            className=\"pointer-events-none absolute inset-y-0 w-px bg-white\"\n            style={{\n              left: `${position}%`,\n              transform: \"translateX(-50%)\",\n            }}\n          />\n\n          {/* Handle */}\n          <div\n            className=\"pointer-events-none absolute top-1/2 flex size-10 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full bg-white shadow-lg\"\n            style={{ left: `${position}%` }}\n          >\n            <ChevronsLeftRight className=\"size-4 text-foreground dark:text-background\" />\n          </div>\n\n          {/* Before label */}\n          <div className=\"pointer-events-none absolute top-4 left-4\">\n            <span className=\"rounded-full bg-foreground px-3 py-1.5 text-sm font-medium text-background\">\n              {beforeLabel}\n            </span>\n          </div>\n\n          {/* After label */}\n          <div className=\"pointer-events-none absolute top-4 right-4\">\n            <span className=\"rounded-full bg-foreground px-3 py-1.5 text-sm font-medium text-background\">\n              {afterLabel}\n            </span>\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n};\n\nexport default ImageCompare;\nexport type { ImageCompareProps };\n",
      "type": "registry:component",
      "target": "components/image-compare.tsx"
    }
  ],
  "meta": {
    "image": "/r/previews/image-compare.webp",
    "imageDark": "/r/previews/image-compare-dark.webp"
  },
  "categories": [
    "feature"
  ],
  "type": "registry:block"
}