{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "cursor-lens",
  "title": "Cursor Lens",
  "description": "A circular pointer magnifier for arbitrary React content.",
  "registryDependencies": [
    "@rivelle/utils"
  ],
  "files": [
    {
      "path": "registry/nova/effects/cursor-lens.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\ntype CursorLensProps = React.ComponentProps<\"div\"> & {\n  lensClassName?: string;\n  size?: number;\n  zoom?: number;\n};\n\nfunction CursorLens({\n  children,\n  className,\n  lensClassName,\n  onPointerEnter,\n  onPointerLeave,\n  onPointerMove,\n  size = 180,\n  zoom = 1.75,\n  ...props\n}: CursorLensProps) {\n  const [active, setActive] = React.useState(false);\n  const [position, setPosition] = React.useState({ x: 0, y: 0 });\n  const [bounds, setBounds] = React.useState({ width: 0, height: 0 });\n  const frame = React.useRef<number | null>(null);\n\n  React.useEffect(\n    () => () => {\n      if (frame.current !== null) cancelAnimationFrame(frame.current);\n    },\n    [],\n  );\n\n  return (\n    <div\n      className={cn(\n        \"relative isolate cursor-none overflow-hidden rounded-[2rem] border border-foreground/10 bg-card\",\n        className,\n      )}\n      data-slot=\"cursor-lens\"\n      onPointerEnter={(event) => {\n        const rect = event.currentTarget.getBoundingClientRect();\n        setBounds({ width: rect.width, height: rect.height });\n        setActive(true);\n        onPointerEnter?.(event);\n      }}\n      onPointerLeave={(event) => {\n        setActive(false);\n        onPointerLeave?.(event);\n      }}\n      onPointerMove={(event) => {\n        const rect = event.currentTarget.getBoundingClientRect();\n        const next = {\n          x: event.clientX - rect.left,\n          y: event.clientY - rect.top,\n        };\n        if (frame.current !== null) cancelAnimationFrame(frame.current);\n        frame.current = requestAnimationFrame(() => setPosition(next));\n        onPointerMove?.(event);\n      }}\n      {...props}\n    >\n      <div className=\"h-full\">{children}</div>\n      <div\n        aria-hidden=\"true\"\n        className={cn(\n          \"pointer-events-none absolute left-0 top-0 z-20 overflow-hidden rounded-full border border-background/50 bg-background shadow-[0_18px_70px_-20px_var(--foreground)] transition-opacity duration-200 will-change-transform [backface-visibility:hidden] motion-reduce:hidden\",\n          active ? \"opacity-100\" : \"opacity-0\",\n          lensClassName,\n        )}\n        style={{\n          height: size,\n          transform: `translate3d(${position.x - size / 2}px, ${position.y - size / 2}px, 0)`,\n          width: size,\n        }}\n      >\n        <div\n          className=\"absolute left-0 top-0 will-change-transform [backface-visibility:hidden]\"\n          style={{\n            height: bounds.height,\n            transform: `translate3d(${size / 2 - position.x}px, ${size / 2 - position.y}px, 0) scale(${zoom})`,\n            transformOrigin: `${position.x}px ${position.y}px`,\n            width: bounds.width,\n          }}\n        >\n          {children}\n        </div>\n        <div className=\"absolute inset-0 rounded-full ring-1 ring-inset ring-foreground/10\" />\n      </div>\n    </div>\n  );\n}\n\nexport { CursorLens };\nexport type { CursorLensProps };\n",
      "type": "registry:ui",
      "target": "@effects/cursor-lens.tsx"
    }
  ],
  "type": "registry:ui"
}