{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "Crosshair-TS-CSS",
	"title": "Crosshair",
	"description": "Custom crosshair cursor with tracking, and link hover effects.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "Crosshair/Crosshair.tsx",
			"content": "import React, { useEffect, useRef, type RefObject } from 'react';\nimport { gsap } from 'gsap';\n\nconst lerp = (a: number, b: number, n: number): number => (1 - n) * a + n * b;\n\nconst getMousePos = (e: Event, container?: HTMLElement | null): { x: number; y: number } => {\n  const mouseEvent = e as MouseEvent;\n  if (container) {\n    const bounds = container.getBoundingClientRect();\n    return {\n      x: mouseEvent.clientX - bounds.left,\n      y: mouseEvent.clientY - bounds.top\n    };\n  }\n  return { x: mouseEvent.clientX, y: mouseEvent.clientY };\n};\n\ninterface CrosshairProps {\n  color?: string;\n  containerRef?: RefObject<HTMLElement>;\n}\n\nconst Crosshair: React.FC<CrosshairProps> = ({ color = 'white', containerRef = null }) => {\n  const cursorRef = useRef<HTMLDivElement>(null);\n  const lineHorizontalRef = useRef<HTMLDivElement>(null);\n  const lineVerticalRef = useRef<HTMLDivElement>(null);\n  const filterXRef = useRef<SVGFETurbulenceElement>(null);\n  const filterYRef = useRef<SVGFETurbulenceElement>(null);\n\n  let mouse = { x: 0, y: 0 };\n\n  useEffect(() => {\n    const handleMouseMove = (ev: Event) => {\n      const mouseEvent = ev as MouseEvent;\n      mouse = getMousePos(mouseEvent, containerRef?.current);\n      if (containerRef?.current) {\n        const bounds = containerRef.current.getBoundingClientRect();\n        if (\n          mouseEvent.clientX < bounds.left ||\n          mouseEvent.clientX > bounds.right ||\n          mouseEvent.clientY < bounds.top ||\n          mouseEvent.clientY > bounds.bottom\n        ) {\n          gsap.to([lineHorizontalRef.current, lineVerticalRef.current].filter(Boolean), { opacity: 0 });\n        } else {\n          gsap.to([lineHorizontalRef.current, lineVerticalRef.current].filter(Boolean), { opacity: 1 });\n        }\n      }\n    };\n\n    const target: HTMLElement | Window = containerRef?.current || window;\n    target.addEventListener('mousemove', handleMouseMove);\n\n    const renderedStyles: {\n      [key: string]: { previous: number; current: number; amt: number };\n    } = {\n      tx: { previous: 0, current: 0, amt: 0.15 },\n      ty: { previous: 0, current: 0, amt: 0.15 }\n    };\n\n    gsap.set([lineHorizontalRef.current, lineVerticalRef.current].filter(Boolean), { opacity: 0 });\n\n    const onMouseMove = (_ev: Event) => {\n      renderedStyles.tx.previous = renderedStyles.tx.current = mouse.x;\n      renderedStyles.ty.previous = renderedStyles.ty.current = mouse.y;\n\n      gsap.to([lineHorizontalRef.current, lineVerticalRef.current].filter(Boolean), {\n        duration: 0.9,\n        ease: 'Power3.easeOut',\n        opacity: 1\n      });\n\n      requestAnimationFrame(render);\n\n      target.removeEventListener('mousemove', onMouseMove);\n    };\n\n    target.addEventListener('mousemove', onMouseMove);\n\n    const primitiveValues = { turbulence: 0 };\n\n    const tl = gsap\n      .timeline({\n        paused: true,\n        onStart: () => {\n          if (lineHorizontalRef.current) {\n            lineHorizontalRef.current.style.filter = 'url(#filter-noise-x)';\n          }\n          if (lineVerticalRef.current) {\n            lineVerticalRef.current.style.filter = 'url(#filter-noise-y)';\n          }\n        },\n        onUpdate: () => {\n          if (filterXRef.current && filterYRef.current) {\n            filterXRef.current.setAttribute('baseFrequency', primitiveValues.turbulence.toString());\n            filterYRef.current.setAttribute('baseFrequency', primitiveValues.turbulence.toString());\n          }\n        },\n        onComplete: () => {\n          if (lineHorizontalRef.current && lineVerticalRef.current) {\n            lineHorizontalRef.current.style.filter = 'none';\n            lineVerticalRef.current.style.filter = 'none';\n          }\n        }\n      })\n      .to(primitiveValues, {\n        duration: 0.5,\n        ease: 'power1',\n        startAt: { turbulence: 1 },\n        turbulence: 0\n      });\n\n    const enter = () => tl.restart();\n    const leave = () => {\n      tl.progress(1).kill();\n    };\n\n    const render = () => {\n      renderedStyles.tx.current = mouse.x;\n      renderedStyles.ty.current = mouse.y;\n\n      for (const key in renderedStyles) {\n        const style = renderedStyles[key];\n        style.previous = lerp(style.previous, style.current, style.amt);\n      }\n\n      if (lineHorizontalRef.current && lineVerticalRef.current) {\n        gsap.set(lineVerticalRef.current, { x: renderedStyles.tx.previous });\n        gsap.set(lineHorizontalRef.current, { y: renderedStyles.ty.previous });\n      }\n\n      requestAnimationFrame(render);\n    };\n\n    const links: NodeListOf<HTMLAnchorElement> = containerRef?.current\n      ? containerRef.current.querySelectorAll('a')\n      : document.querySelectorAll('a');\n\n    links.forEach(link => {\n      link.addEventListener('mouseenter', enter);\n      link.addEventListener('mouseleave', leave);\n    });\n\n    return () => {\n      target.removeEventListener('mousemove', handleMouseMove);\n      target.removeEventListener('mousemove', onMouseMove);\n      links.forEach(link => {\n        link.removeEventListener('mouseenter', enter);\n        link.removeEventListener('mouseleave', leave);\n      });\n    };\n  }, [containerRef]);\n\n  return (\n    <div\n      ref={cursorRef}\n      className=\"cursor\"\n      style={{\n        position: containerRef ? 'absolute' : 'fixed',\n        top: 0,\n        left: 0,\n        width: '100%',\n        height: '100%',\n        pointerEvents: 'none',\n        zIndex: 10000\n      }}\n    >\n      <svg\n        style={{\n          position: 'absolute',\n          left: 0,\n          top: 0,\n          width: '100%',\n          height: '100%'\n        }}\n      >\n        <defs>\n          <filter id=\"filter-noise-x\">\n            <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.000001\" numOctaves=\"1\" ref={filterXRef} />\n            <feDisplacementMap in=\"SourceGraphic\" scale=\"40\" />\n          </filter>\n          <filter id=\"filter-noise-y\">\n            <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.000001\" numOctaves=\"1\" ref={filterYRef} />\n            <feDisplacementMap in=\"SourceGraphic\" scale=\"40\" />\n          </filter>\n        </defs>\n      </svg>\n      <div\n        ref={lineHorizontalRef}\n        style={{\n          position: 'absolute',\n          width: '100%',\n          height: '1px',\n          background: color,\n          pointerEvents: 'none',\n          transform: 'translateY(50%)',\n          opacity: 0\n        }}\n      />\n      <div\n        ref={lineVerticalRef}\n        style={{\n          position: 'absolute',\n          height: '100%',\n          width: '1px',\n          background: color,\n          pointerEvents: 'none',\n          transform: 'translateX(50%)',\n          opacity: 0\n        }}\n      />\n    </div>\n  );\n};\n\nexport default Crosshair;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"gsap@^3.13.0"
	]
}