{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "clicky",
  "title": "Clicky",
  "description": "A 3D hand cursor that pops in and presses any element to show people where to click.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/clover-ui/clicky.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion, useAnimate, useInView, useReducedMotion } from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport type ClickyColor = \"purple\" | \"pink\" | \"blue\" | \"emerald\" | \"orange\" | \"default\";\n\nconst palette: Record<ClickyColor, { line: string; edge: string; from: string; to: string }> = {\n  purple: { line: \"#8b5cf6\", edge: \"#6d28d9\", from: \"#a855f7\", to: \"#ec4899\" },\n  pink: { line: \"#ec4899\", edge: \"#be185d\", from: \"#f472b6\", to: \"#fb7185\" },\n  blue: { line: \"#3b82f6\", edge: \"#1d4ed8\", from: \"#60a5fa\", to: \"#22d3ee\" },\n  emerald: { line: \"#10b981\", edge: \"#047857\", from: \"#34d399\", to: \"#a3e635\" },\n  orange: { line: \"#f97316\", edge: \"#c2410c\", from: \"#fb923c\", to: \"#facc15\" },\n  default: { line: \"#52525b\", edge: \"#27272a\", from: \"#a1a1aa\", to: \"#71717a\" },\n};\n\nexport interface ClickyProps extends Omit<React.ComponentProps<\"span\">, \"color\"> {\n  children: React.ReactNode;\n  color?: ClickyColor;\n  size?: number;\n  anchor?: { x: number; y: number };\n  delay?: number;\n  loop?: boolean;\n  interval?: number;\n  autoClick?: boolean;\n}\n\nconst TIP = { x: 13, y: 2.5 };\n\nconst HAND =\n  \"M11 16V4.5a2 2 0 0 1 4 0V12a2 2 0 0 1 4 0v1a2 2 0 0 1 4 0v1a2 2 0 0 1 4 0v7c0 4.5-3 8-7.5 8h-3c-2.7 0-4.3-1-5.9-3l-5.2-6.4a1.9 1.9 0 0 1 2.8-2.6L11 19.5Z\";\n\nfunction Clicky({\n  children,\n  color = \"purple\",\n  size = 40,\n  anchor = { x: 62, y: 58 },\n  delay = 0.2,\n  loop = true,\n  interval = 2.4,\n  autoClick = false,\n  className,\n  ref,\n  ...props\n}: ClickyProps) {\n  const [scope, animate] = useAnimate<HTMLSpanElement>();\n  React.useImperativeHandle(ref, () => scope.current, [scope]);\n  const targetRef = React.useRef<HTMLSpanElement>(null);\n  const inView = useInView(scope, { once: true, amount: 0.6 });\n  const reduceMotion = useReducedMotion();\n  const colors = palette[color];\n\n  React.useEffect(() => {\n    if (!inView) return;\n    let cancelled = false;\n    const timers = new Set<ReturnType<typeof setTimeout>>();\n    const wait = (seconds: number) =>\n      new Promise<void>((resolve) => {\n        const timer = setTimeout(() => {\n          timers.delete(timer);\n          resolve();\n        }, seconds * 1000);\n        timers.add(timer);\n      });\n    const quick = { duration: 0.12, ease: \"easeOut\" } as const;\n    const release = { type: \"spring\", stiffness: 520, damping: 26 } as const;\n\n    async function press() {\n      const target = targetRef.current;\n      await Promise.all([\n        animate(\"[data-hand]\", { scale: 0.86, rotate: -4 }, quick),\n        target && animate(target, { scale: 0.97 }, quick),\n      ]);\n      if (cancelled) return;\n      animate(\n        \"[data-glow]\",\n        { scale: [0.4, 1.8], opacity: [0.55, 0] },\n        { duration: 0.7, ease: \"easeOut\" },\n      );\n      if (autoClick) (target?.firstElementChild as HTMLElement | null)?.click();\n      await Promise.all([\n        animate(\"[data-hand]\", { scale: 1, rotate: 0 }, release),\n        target && animate(target, { scale: 1 }, release),\n      ]);\n    }\n\n    async function run() {\n      await wait(reduceMotion ? 0 : delay);\n      if (cancelled) return;\n      await animate(\n        \"[data-cursor]\",\n        { opacity: 1, scale: 1 },\n        reduceMotion ? { duration: 0 } : { duration: 0.18, ease: \"easeOut\" },\n      );\n      if (reduceMotion) return;\n      while (!cancelled) {\n        await press();\n        if (!loop || cancelled) break;\n        await wait(interval);\n      }\n    }\n\n    run();\n    return () => {\n      cancelled = true;\n      timers.forEach(clearTimeout);\n    };\n  }, [inView, animate, autoClick, delay, interval, loop, reduceMotion]);\n\n  const scale = size / 32;\n  const tipX = TIP.x * scale;\n  const tipY = TIP.y * scale;\n\n  return (\n    <span ref={scope} className={cn(\"relative inline-block\", className)} {...props}>\n      <span ref={targetRef} className=\"inline-block\">\n        {children}\n      </span>\n\n      <span\n        aria-hidden\n        className=\"pointer-events-none absolute z-10\"\n        style={{ left: `${anchor.x}%`, top: `${anchor.y}%` }}\n      >\n        <motion.span\n          data-cursor\n          initial={{ opacity: 0, scale: 0.6 }}\n          className=\"absolute top-0 left-0 block\"\n        >\n          <motion.span\n            data-glow\n            className=\"absolute size-12 rounded-full opacity-0 blur-md\"\n            style={{\n              left: -24,\n              top: -24,\n              background: `radial-gradient(circle, ${colors.from}, ${colors.to} 60%, transparent 70%)`,\n            }}\n          />\n          <span className=\"relative block\" style={{ marginLeft: -tipX, marginTop: -tipY }}>\n            <motion.svg\n              data-hand\n              viewBox=\"0 0 32 32\"\n              width={size}\n              height={size}\n              className=\"overflow-visible drop-shadow-[0_6px_10px_rgba(0,0,0,0.18)]\"\n              style={{ originX: `${(TIP.x / 32) * 100}%`, originY: \"10%\" }}\n            >\n              <path\n                d={HAND}\n                transform=\"translate(1.4 1.8)\"\n                fill={colors.edge}\n                stroke={colors.edge}\n                strokeWidth={1.6}\n                strokeLinejoin=\"round\"\n              />\n              <path\n                d={HAND}\n                fill=\"#ffffff\"\n                stroke={colors.line}\n                strokeWidth={1.6}\n                strokeLinejoin=\"round\"\n              />\n              <path\n                d=\"M15 12.5v4M19 13.5v3.5M23 14.5v3\"\n                stroke={colors.line}\n                strokeWidth={1.4}\n                strokeLinecap=\"round\"\n              />\n            </motion.svg>\n          </span>\n        </motion.span>\n      </span>\n    </span>\n  );\n}\n\nexport { Clicky };\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}