{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "liquid-cursor-shadcnui",
  "type": "registry:ui",
  "title": "Liquid Cursor",
  "description": "A liquid-like cursor effect that mixes colors on movement",
  "dependencies": [
    "framer-motion",
    "react"
  ],
  "files": [
    {
      "path": "@uitripled/react-shadcn/src/components/liquid-cursor.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useRef } from \"react\";\n\ninterface Point {\n  x: number;\n  y: number;\n  vx: number;\n  vy: number;\n  life: number;\n  maxLife: number;\n  radius: number;\n  color: string;\n}\n\nconst COLORS = [\n  \"#FF0080\", // Pink\n  \"#7928CA\", // Purple\n  \"#0070F3\", // Blue\n  \"#00DFD8\", // Cyan\n  \"#FF4D4D\", // Red\n  \"#FFD700\", // Gold\n];\n\nexport const LiquidCursor = () => {\n  const canvasRef = useRef<HTMLCanvasElement>(null);\n  const pointsRef = useRef<Point[]>([]);\n  const mouseRef = useRef({ x: 0, y: 0, lastX: 0, lastY: 0, moved: false });\n  const rafRef = useRef<number | null>(null);\n\n  useEffect(() => {\n    const canvas = canvasRef.current;\n    if (!canvas) return;\n\n    const ctx = canvas.getContext(\"2d\");\n    if (!ctx) return;\n\n    const resize = () => {\n      canvas.width = window.innerWidth;\n      canvas.height = window.innerHeight;\n    };\n    window.addEventListener(\"resize\", resize);\n    resize();\n\n    const handleMouseMove = (e: MouseEvent) => {\n      mouseRef.current.x = e.clientX;\n      mouseRef.current.y = e.clientY;\n      mouseRef.current.moved = true;\n    };\n    window.addEventListener(\"mousemove\", handleMouseMove);\n\n    const addPoint = (x: number, y: number) => {\n      const angle = Math.random() * Math.PI * 2;\n      const speed = Math.random() * 0.5;\n      const color = COLORS[Math.floor(Math.random() * COLORS.length)];\n\n      pointsRef.current.push({\n        x,\n        y,\n        vx: Math.cos(angle) * speed,\n        vy: Math.sin(angle) * speed,\n        life: 1,\n        maxLife: 1,\n        radius: Math.random() * 20 + 10, // Random radius between 10 and 30\n        color,\n      });\n    };\n\n    const animate = () => {\n      // Clear with a slight fade for trails, or full clear\n      ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n      // Add new points if mouse moved\n      if (mouseRef.current.moved) {\n        const dist = Math.hypot(\n          mouseRef.current.x - mouseRef.current.lastX,\n          mouseRef.current.y - mouseRef.current.lastY\n        );\n\n        // Interpolate points for smooth lines\n        if (dist > 0) {\n          const steps = Math.min(dist, 20); // Limit steps to avoid lag\n          for (let i = 0; i < steps; i += 2) {\n            const t = i / steps;\n            const x =\n              mouseRef.current.lastX +\n              (mouseRef.current.x - mouseRef.current.lastX) * t;\n            const y =\n              mouseRef.current.lastY +\n              (mouseRef.current.y - mouseRef.current.lastY) * t;\n            // Only add points occasionally to avoid overcrowding\n            if (Math.random() > 0.5) addPoint(x, y);\n          }\n        }\n\n        mouseRef.current.lastX = mouseRef.current.x;\n        mouseRef.current.lastY = mouseRef.current.y;\n        mouseRef.current.moved = false; // Reset moved flag until next event\n      } else {\n        // If mouse stopped, we update last position to current to be safe\n        mouseRef.current.lastX = mouseRef.current.x;\n        mouseRef.current.lastY = mouseRef.current.y;\n      }\n\n      // Update and draw points\n      for (let i = pointsRef.current.length - 1; i >= 0; i--) {\n        const p = pointsRef.current[i];\n\n        p.life -= 0.01; // Decay rate\n        p.x += p.vx;\n        p.y += p.vy;\n        p.radius *= 0.99; // Shrink slightly\n\n        if (p.life <= 0 || p.radius < 0.5) {\n          pointsRef.current.splice(i, 1);\n          continue;\n        }\n\n        ctx.beginPath();\n        ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);\n        ctx.fillStyle = p.color;\n        // Use globalAlpha for fading\n        ctx.globalAlpha = p.life;\n        ctx.fill();\n        ctx.globalAlpha = 1;\n      }\n\n      rafRef.current = requestAnimationFrame(animate);\n    };\n\n    animate();\n\n    return () => {\n      window.removeEventListener(\"resize\", resize);\n      window.removeEventListener(\"mousemove\", handleMouseMove);\n      if (rafRef.current) cancelAnimationFrame(rafRef.current);\n    };\n  }, []);\n\n  return (\n    <div className=\"fixed inset-0 pointer-events-none z-[9999] overflow-hidden\">\n      {/* SVG Filter for the Gooey Effect */}\n      <svg className=\"hidden\">\n        <defs>\n          <filter id=\"liquid-cursor-filter\">\n            <feGaussianBlur\n              in=\"SourceGraphic\"\n              stdDeviation=\"10\"\n              result=\"blur\"\n            />\n            <feColorMatrix\n              in=\"blur\"\n              mode=\"matrix\"\n              values=\"1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 18 -7\"\n              result=\"goo\"\n            />\n            <feComposite in=\"SourceGraphic\" in2=\"goo\" operator=\"atop\" />\n          </filter>\n        </defs>\n      </svg>\n\n      <canvas\n        ref={canvasRef}\n        className=\"w-full h-full block\"\n        style={{ filter: \"url(#liquid-cursor-filter)\" }}\n      />\n    </div>\n  );\n};\n",
      "type": "registry:ui",
      "target": "components/uitripled/liquid-cursor-shadcnui.tsx"
    }
  ]
}