{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "eye-tracking",
  "type": "registry:block",
  "title": "Eye tracking",
  "description": "Eye tracking",
  "files": [
    {
      "path": "components/usages/eyetrackingusage.tsx",
      "content": "import { EyeTracking } from \"@/registry/open-source/eye-tracking\"\n\nexport default function EyeTrackingUsage() {\n    return (\n        <div>\n            <EyeTracking\n                eyeCount={3}\n                eyeSize={100}\n                gap={30}\n                irisColor=\"#8B5CF6\"\n                irisColorSecondary=\"#A78BFA\"\n            />\n        </div>\n    )\n}",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/eyetrackingusage.tsx",
      "content": "import { EyeTracking } from \"@/registry/open-source/eye-tracking\"\n\nexport default function EyeTrackingUsage() {\n    return (\n        <div>\n            <EyeTracking\n                eyeCount={3}\n                eyeSize={100}\n                gap={30}\n                irisColor=\"#8B5CF6\"\n                irisColorSecondary=\"#A78BFA\"\n            />\n        </div>\n    )\n}",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/eye-tracking.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { motion, useMotionValue, useSpring, useTransform } from \"motion/react\"\nimport { cn } from \"@/registry/utilities/cn\"\n\n// Credit:\n// https://www.componentry.fun/docs/components/eye-tracking\n\ninterface EyeTrackingProps {\n    /** Additional CSS classes */\n    className?: string\n    /** Size of each eye in pixels */\n    eyeSize?: number\n    /** Gap between eyes in pixels */\n    gap?: number\n    /** Color of the iris */\n    irisColor?: string\n    /** Secondary iris color for gradient */\n    irisColorSecondary?: string\n    /** Pupil color */\n    pupilColor?: string\n    /** Sclera (white) color */\n    scleraColor?: string\n    /** How far the pupil can travel (0-1) */\n    pupilRange?: number\n    /** Enable the reflection/glint effect */\n    showReflection?: boolean\n    /** Enable iris detail pattern */\n    showIrisDetail?: boolean\n    /** Enable subtle idle animation when cursor is away */\n    idleAnimation?: boolean\n    /** Blink interval in milliseconds (0 to disable) */\n    blinkInterval?: number\n    /** Number of eyes */\n    eyeCount?: number\n    /** Variant style */\n    variant?: \"realistic\" | \"cartoon\" | \"minimal\" | \"cyber\"\n    /** Enable reactive pupil dilation */\n    reactivePupil?: boolean\n    /** Eyelid visibility */\n    showEyelids?: boolean\n}\n\ninterface EyeProps {\n    eyeSize: number\n    irisColor: string\n    irisColorSecondary: string\n    pupilColor: string\n    scleraColor: string\n    pupilRange: number\n    showReflection: boolean\n    showIrisDetail: boolean\n    blinkInterval: number\n    variant: \"realistic\" | \"cartoon\" | \"minimal\" | \"cyber\"\n    reactivePupil: boolean\n    showEyelids: boolean\n    mouseX: React.MutableRefObject<number>\n    mouseY: React.MutableRefObject<number>\n    index: number\n}\n\nfunction Eye({\n    eyeSize,\n    irisColor,\n    irisColorSecondary,\n    pupilColor,\n    scleraColor,\n    pupilRange,\n    showReflection,\n    showIrisDetail,\n    blinkInterval,\n    variant,\n    reactivePupil,\n    showEyelids,\n    mouseX,\n    mouseY,\n    index,\n}: EyeProps) {\n    const eyeRef = React.useRef<HTMLDivElement>(null)\n    const [isBlinking, setIsBlinking] = React.useState(false)\n    const [pupilScale, setPupilScale] = React.useState(1)\n\n    const x = useMotionValue(0)\n    const y = useMotionValue(0)\n    const springX = useSpring(x, { stiffness: 300, damping: 25, mass: 0.5 })\n    const springY = useSpring(y, { stiffness: 300, damping: 25, mass: 0.5 })\n\n    const irisSize = eyeSize * 0.45\n    const pupilSize = irisSize * 0.5\n    const maxOffset = (eyeSize / 2 - irisSize / 2) * pupilRange\n\n    // Blink animation\n    React.useEffect(() => {\n        if (blinkInterval <= 0) return\n\n        const blink = () => {\n            setIsBlinking(true)\n            setTimeout(() => setIsBlinking(false), 150)\n        }\n\n        // Random offset per eye so they don't all blink at the exact same time\n        const randomOffset = Math.random() * 200\n        const timeout = setTimeout(() => {\n            blink()\n            const interval = setInterval(blink, blinkInterval + Math.random() * 1000)\n            return () => clearInterval(interval)\n        }, randomOffset)\n\n        const interval = setInterval(blink, blinkInterval + Math.random() * 1000)\n        return () => {\n            clearTimeout(timeout)\n            clearInterval(interval)\n        }\n    }, [blinkInterval])\n\n    // Track mouse position and update pupil\n    React.useEffect(() => {\n        let animFrame: number\n\n        const update = () => {\n            if (!eyeRef.current) {\n                animFrame = requestAnimationFrame(update)\n                return\n            }\n\n            const rect = eyeRef.current.getBoundingClientRect()\n            const eyeCenterX = rect.left + rect.width / 2\n            const eyeCenterY = rect.top + rect.height / 2\n\n            const dx = mouseX.current - eyeCenterX\n            const dy = mouseY.current - eyeCenterY\n            const distance = Math.sqrt(dx * dx + dy * dy)\n            const angle = Math.atan2(dy, dx)\n\n            const clampedDistance = Math.min(distance, maxOffset * 3)\n            const normalizedDistance = clampedDistance / (maxOffset * 3)\n            const offset = normalizedDistance * maxOffset\n\n            x.set(Math.cos(angle) * offset)\n            y.set(Math.sin(angle) * offset)\n\n            // Reactive pupil dilation based on distance\n            if (reactivePupil) {\n                const proximityScale = distance < 200 ? 1.3 - (distance / 200) * 0.3 : 0.85 + (Math.min(distance, 800) / 800) * 0.15\n                setPupilScale(proximityScale)\n            }\n\n            animFrame = requestAnimationFrame(update)\n        }\n\n        animFrame = requestAnimationFrame(update)\n        return () => cancelAnimationFrame(animFrame)\n    }, [x, y, maxOffset, reactivePupil, mouseX, mouseY])\n\n    // Rotation transform for iris detail\n    const irisRotation = useTransform(springX, [-maxOffset, maxOffset], [-15, 15])\n\n    const eyeAspect = variant === \"cartoon\" ? 1 : 0.85\n    const borderRadius = variant === \"minimal\" ? \"50%\" : variant === \"cartoon\" ? \"50%\" : \"50%\"\n\n    const scleraGradient =\n        variant === \"realistic\"\n            ? `radial-gradient(circle at 35% 35%, ${scleraColor} 0%, ${scleraColor}ee 60%, ${scleraColor}cc 100%)`\n            : variant === \"cyber\"\n                ? `radial-gradient(circle at 50% 50%, #0a0a1a 0%, #111128 100%)`\n                : scleraColor\n\n    const eyeWidth = eyeSize\n    const eyeHeight = eyeSize * eyeAspect\n\n    return (\n        <motion.div\n            ref={eyeRef}\n            className={cn(\n                \"relative overflow-hidden\",\n                variant === \"cyber\" && \"border border-cyan-500/30\"\n            )}\n            style={{\n                width: eyeWidth,\n                height: eyeHeight,\n                borderRadius,\n                background: scleraGradient,\n                boxShadow:\n                    variant === \"realistic\"\n                        ? \"inset 0 2px 8px rgba(0,0,0,0.15), inset 0 -1px 4px rgba(0,0,0,0.05), 0 4px 20px rgba(0,0,0,0.1)\"\n                        : variant === \"cyber\"\n                            ? \"inset 0 0 30px rgba(0,200,255,0.1), 0 0 20px rgba(0,200,255,0.15)\"\n                            : variant === \"cartoon\"\n                                ? \"inset 0 4px 12px rgba(0,0,0,0.1), 0 6px 24px rgba(0,0,0,0.15)\"\n                                : \"0 2px 10px rgba(0,0,0,0.1)\",\n            }}\n            animate={{\n                scaleY: isBlinking ? 0.05 : 1,\n            }}\n            transition={{\n                scaleY: { duration: 0.1, ease: \"easeInOut\" },\n            }}\n        >\n            {/* Blood vessel details for realistic variant */}\n            {variant === \"realistic\" && (\n                <div className=\"absolute inset-0 overflow-hidden rounded-full opacity-[0.07]\">\n                    {[...Array(6)].map((_, i) => (\n                        <div\n                            key={i}\n                            className=\"absolute bg-red-500\"\n                            style={{\n                                width: \"1px\",\n                                height: eyeSize * 0.4,\n                                left: `${20 + i * 12}%`,\n                                top: `${10 + (i % 3) * 15}%`,\n                                transform: `rotate(${-30 + i * 20}deg)`,\n                                opacity: 0.3 + Math.random() * 0.4,\n                            }}\n                        />\n                    ))}\n                </div>\n            )}\n\n            {/* Iris */}\n            <motion.div\n                className=\"absolute\"\n                style={{\n                    width: irisSize,\n                    height: irisSize,\n                    borderRadius: \"50%\",\n                    left: eyeWidth / 2 - irisSize / 2,\n                    top: eyeHeight / 2 - irisSize / 2,\n                    x: springX,\n                    y: springY,\n                    background:\n                        variant === \"cyber\"\n                            ? `conic-gradient(from 0deg, ${irisColor}, ${irisColorSecondary}, ${irisColor})`\n                            : `radial-gradient(circle at 40% 40%, ${irisColorSecondary}, ${irisColor} 60%, ${irisColor}dd 100%)`,\n                    boxShadow:\n                        variant === \"realistic\"\n                            ? `inset 0 2px 6px rgba(0,0,0,0.3), 0 0 0 1px rgba(0,0,0,0.1)`\n                            : variant === \"cyber\"\n                                ? `0 0 15px ${irisColor}66, inset 0 0 10px ${irisColor}33`\n                                : `inset 0 1px 4px rgba(0,0,0,0.2)`,\n                }}\n            >\n                {/* Iris detail pattern */}\n                {showIrisDetail && (\n                    <motion.div\n                        className=\"absolute inset-0 rounded-full overflow-hidden\"\n                        style={{ rotate: irisRotation }}\n                    >\n                        {variant === \"cyber\" ? (\n                            // Cyber circuit pattern\n                            <>\n                                <div\n                                    className=\"absolute inset-[15%] rounded-full border border-dashed opacity-40\"\n                                    style={{ borderColor: irisColor }}\n                                />\n                                <div\n                                    className=\"absolute inset-[30%] rounded-full border opacity-30\"\n                                    style={{ borderColor: irisColorSecondary }}\n                                />\n                                {[...Array(8)].map((_, i) => (\n                                    <div\n                                        key={i}\n                                        className=\"absolute left-1/2 top-1/2 origin-left opacity-25\"\n                                        style={{\n                                            width: irisSize * 0.45,\n                                            height: \"1px\",\n                                            background: irisColor,\n                                            transform: `rotate(${i * 45}deg)`,\n                                        }}\n                                    />\n                                ))}\n                            </>\n                        ) : (\n                            // Realistic iris fibers\n                            <>\n                                {[...Array(24)].map((_, i) => (\n                                    <div\n                                        key={i}\n                                        className=\"absolute left-1/2 top-1/2 origin-left\"\n                                        style={{\n                                            width: irisSize * 0.45,\n                                            height: \"1px\",\n                                            background: `linear-gradient(to right, transparent 20%, ${irisColor}44 50%, transparent 80%)`,\n                                            transform: `rotate(${i * 15}deg)`,\n                                            opacity: 0.3 + (i % 3) * 0.15,\n                                        }}\n                                    />\n                                ))}\n                                <div\n                                    className=\"absolute inset-[20%] rounded-full\"\n                                    style={{\n                                        border: `1px solid ${irisColor}33`,\n                                    }}\n                                />\n                            </>\n                        )}\n                    </motion.div>\n                )}\n\n                {/* Pupil */}\n                <motion.div\n                    className=\"absolute rounded-full\"\n                    style={{\n                        width: pupilSize,\n                        height: pupilSize,\n                        left: irisSize / 2 - pupilSize / 2,\n                        top: irisSize / 2 - pupilSize / 2,\n                        background:\n                            variant === \"cyber\"\n                                ? `radial-gradient(circle, ${pupilColor} 40%, transparent 100%)`\n                                : pupilColor,\n                        boxShadow:\n                            variant === \"cyber\"\n                                ? `0 0 10px ${irisColor}88`\n                                : undefined,\n                    }}\n                    animate={{\n                        scale: reactivePupil ? pupilScale : 1,\n                    }}\n                    transition={{ duration: 0.3, ease: \"easeOut\" }}\n                />\n\n                {/* Primary light reflection */}\n                {showReflection && (\n                    <>\n                        <div\n                            className=\"absolute rounded-full\"\n                            style={{\n                                width: pupilSize * 0.35,\n                                height: pupilSize * 0.35,\n                                left: irisSize * 0.3,\n                                top: irisSize * 0.25,\n                                background:\n                                    variant === \"cyber\"\n                                        ? `radial-gradient(circle, rgba(0,255,255,0.9), transparent)`\n                                        : \"radial-gradient(circle, rgba(255,255,255,0.95), rgba(255,255,255,0.6))\",\n                                filter: \"blur(0.5px)\",\n                            }}\n                        />\n                        {/* Secondary smaller reflection */}\n                        <div\n                            className=\"absolute rounded-full\"\n                            style={{\n                                width: pupilSize * 0.15,\n                                height: pupilSize * 0.15,\n                                left: irisSize * 0.58,\n                                top: irisSize * 0.6,\n                                background:\n                                    variant === \"cyber\"\n                                        ? \"rgba(0,255,255,0.5)\"\n                                        : \"rgba(255,255,255,0.7)\",\n                            }}\n                        />\n                    </>\n                )}\n            </motion.div>\n\n            {/* Top eyelid shadow */}\n            {showEyelids && variant !== \"minimal\" && (\n                <>\n                    <div\n                        className=\"pointer-events-none absolute inset-x-0 top-0\"\n                        style={{\n                            height: eyeHeight * 0.35,\n                            background:\n                                variant === \"cyber\"\n                                    ? \"linear-gradient(to bottom, rgba(0,10,30,0.6) 0%, transparent 100%)\"\n                                    : \"linear-gradient(to bottom, rgba(0,0,0,0.08) 0%, transparent 100%)\",\n                            borderRadius: \"50% 50% 0 0\",\n                        }}\n                    />\n                    <div\n                        className=\"pointer-events-none absolute inset-x-0 bottom-0\"\n                        style={{\n                            height: eyeHeight * 0.2,\n                            background:\n                                variant === \"cyber\"\n                                    ? \"linear-gradient(to top, rgba(0,10,30,0.4) 0%, transparent 100%)\"\n                                    : \"linear-gradient(to top, rgba(0,0,0,0.04) 0%, transparent 100%)\",\n                            borderRadius: \"0 0 50% 50%\",\n                        }}\n                    />\n                </>\n            )}\n\n            {/* Cyber scan line */}\n            {variant === \"cyber\" && (\n                <motion.div\n                    className=\"pointer-events-none absolute inset-x-0 h-[2px]\"\n                    style={{\n                        background: `linear-gradient(to right, transparent, ${irisColor}44, transparent)`,\n                    }}\n                    animate={{\n                        top: [0, eyeHeight, 0],\n                    }}\n                    transition={{\n                        duration: 3,\n                        repeat: Infinity,\n                        ease: \"linear\",\n                    }}\n                />\n            )}\n        </motion.div>\n    )\n}\n\nexport function EyeTracking({\n    className,\n    eyeSize = 120,\n    gap = 40,\n    irisColor = \"#4A6741\",\n    irisColorSecondary = \"#6B8F62\",\n    pupilColor = \"#0a0a0a\",\n    scleraColor = \"#F5F0EB\",\n    pupilRange = 0.7,\n    showReflection = true,\n    showIrisDetail = true,\n    idleAnimation = true,\n    blinkInterval = 4000,\n    eyeCount = 2,\n    variant = \"realistic\",\n    reactivePupil = true,\n    showEyelids = true,\n}: EyeTrackingProps) {\n    const mouseX = React.useRef(typeof window !== \"undefined\" ? window.innerWidth / 2 : 0)\n    const mouseY = React.useRef(typeof window !== \"undefined\" ? window.innerHeight / 2 : 0)\n    const [isMounted, setIsMounted] = React.useState(false)\n\n    // Default colors per variant\n    const resolvedIrisColor =\n        variant === \"cyber\" ? (irisColor === \"#4A6741\" ? \"#00d4ff\" : irisColor) : irisColor\n    const resolvedIrisSecondary =\n        variant === \"cyber\"\n            ? irisColorSecondary === \"#6B8F62\"\n                ? \"#0088ff\"\n                : irisColorSecondary\n            : irisColorSecondary\n    const resolvedPupilColor =\n        variant === \"cyber\" ? (pupilColor === \"#0a0a0a\" ? \"#001122\" : pupilColor) : pupilColor\n    const resolvedScleraColor =\n        variant === \"cyber\" ? (scleraColor === \"#F5F0EB\" ? \"#0a0a1a\" : scleraColor) : scleraColor\n\n    React.useEffect(() => {\n        setIsMounted(true)\n    }, [])\n\n    // Global mouse tracker\n    React.useEffect(() => {\n        const handleMouseMove = (e: MouseEvent) => {\n            mouseX.current = e.clientX\n            mouseY.current = e.clientY\n        }\n\n        const handleTouchMove = (e: TouchEvent) => {\n            if (e.touches[0]) {\n                mouseX.current = e.touches[0].clientX\n                mouseY.current = e.touches[0].clientY\n            }\n        }\n\n        window.addEventListener(\"mousemove\", handleMouseMove, { passive: true })\n        window.addEventListener(\"touchmove\", handleTouchMove, { passive: true })\n        return () => {\n            window.removeEventListener(\"mousemove\", handleMouseMove)\n            window.removeEventListener(\"touchmove\", handleTouchMove)\n        }\n    }, [])\n\n    // Idle animation - subtle random eye movement when no cursor activity\n    React.useEffect(() => {\n        if (!idleAnimation) return\n\n        let idleTimeout: ReturnType<typeof setTimeout>\n        let idleInterval: ReturnType<typeof setInterval>\n        let lastX = mouseX.current\n        let lastY = mouseY.current\n\n        const checkIdle = () => {\n            if (mouseX.current === lastX && mouseY.current === lastY) {\n                // Start idle micro-movements\n                idleInterval = setInterval(() => {\n                    const currentX = mouseX.current\n                    const currentY = mouseY.current\n                    mouseX.current = currentX + (Math.random() - 0.5) * 30\n                    mouseY.current = currentY + (Math.random() - 0.5) * 30\n                    // Restore after brief moment\n                    setTimeout(() => {\n                        mouseX.current = currentX\n                        mouseY.current = currentY\n                    }, 500)\n                }, 2000)\n            }\n            lastX = mouseX.current\n            lastY = mouseY.current\n        }\n\n        idleTimeout = setInterval(checkIdle, 3000)\n\n        return () => {\n            clearInterval(idleTimeout)\n            clearInterval(idleInterval)\n        }\n    }, [idleAnimation])\n\n    if (!isMounted) {\n        return (\n            <div\n                className={cn(\"flex items-center justify-center\", className)}\n                style={{ gap }}\n            >\n                {[...Array(eyeCount)].map((_, i) => (\n                    <div\n                        key={i}\n                        className=\"rounded-full bg-neutral-200 dark:bg-neutral-800\"\n                        style={{ width: eyeSize, height: eyeSize * 0.85 }}\n                    />\n                ))}\n            </div>\n        )\n    }\n\n    return (\n        <div\n            className={cn(\"flex items-center justify-center\", className)}\n            style={{ gap }}\n        >\n            {[...Array(eyeCount)].map((_, i) => (\n                <Eye\n                    key={i}\n                    index={i}\n                    eyeSize={eyeSize}\n                    irisColor={resolvedIrisColor}\n                    irisColorSecondary={resolvedIrisSecondary}\n                    pupilColor={resolvedPupilColor}\n                    scleraColor={resolvedScleraColor}\n                    pupilRange={pupilRange}\n                    showReflection={showReflection}\n                    showIrisDetail={showIrisDetail}\n                    blinkInterval={blinkInterval}\n                    variant={variant}\n                    reactivePupil={reactivePupil}\n                    showEyelids={showEyelids}\n                    mouseX={mouseX}\n                    mouseY={mouseY}\n                />\n            ))}\n        </div>\n    )\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/utilities/cn.ts",
      "content": "import { ClassValue, clsx } from \"clsx\";\r\nimport { twMerge } from \"tailwind-merge\";\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n\treturn twMerge(clsx(inputs));\r\n}\r\n",
      "type": "registry:ui"
    }
  ]
}