{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "following-pointer",
  "title": "Following Pointer",
  "description": "Swaps the cursor for an arrow and a name tag that trail the pointer inside a region.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/velora/following-pointer.tsx",
      "content": "\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n  AnimatePresence,\n  motion,\n  useMotionValue,\n  useReducedMotion,\n  useSpring,\n} from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst SPRING = { stiffness: 520, damping: 38, mass: 0.5 };\n\ninterface FollowingPointerProps\n  extends Omit<React.HTMLAttributes<HTMLDivElement>, \"color\"> {\n  /** Content of the name tag next to the arrow; leave empty for an arrow only */\n  label?: React.ReactNode;\n  /** Any CSS colour for the arrow and tag */\n  color?: string;\n  children: React.ReactNode;\n}\n\n/**\n * Wrap a region to swap the system cursor for a coloured arrow and name tag,\n * like a collaborator's cursor, that trail the pointer on a spring. It only\n * takes over for a mouse on a fine-pointer device; touch keeps the defaults,\n * and the overlay never blocks clicks.\n */\nexport function FollowingPointer({\n  label,\n  color = \"var(--brand)\",\n  className,\n  children,\n  ...props\n}: FollowingPointerProps) {\n  const ref = useRef<HTMLDivElement>(null);\n  const last = useRef({ x: 0, y: 0 });\n  const [active, setActive] = useState(false);\n  const reducedMotion = useReducedMotion();\n\n  const x = useMotionValue(0);\n  const y = useMotionValue(0);\n  const springX = useSpring(x, SPRING);\n  const springY = useSpring(y, SPRING);\n\n  // Position relative to the region, from the last known client point.\n  const place = useCallback((jump: boolean) => {\n    const rect = ref.current?.getBoundingClientRect();\n    if (!rect) return;\n    const nx = last.current.x - rect.left;\n    const ny = last.current.y - rect.top;\n    x.set(nx);\n    y.set(ny);\n    if (jump) {\n      springX.jump(nx);\n      springY.jump(ny);\n    }\n  }, [x, y, springX, springY]);\n\n  const track = (event: React.PointerEvent<HTMLDivElement>) => {\n    if (event.pointerType !== \"mouse\" || !matchMedia(\"(pointer: fine)\").matches) return;\n    last.current = { x: event.clientX, y: event.clientY };\n    place(!active);\n    setActive(true);\n  };\n\n  // Keep the arrow under the (hidden) pointer while the page scrolls.\n  useEffect(() => {\n    if (!active) return;\n    const onScroll = () => place(false);\n    addEventListener(\"scroll\", onScroll, { capture: true, passive: true });\n    return () => removeEventListener(\"scroll\", onScroll, { capture: true });\n  }, [active, place]);\n\n  return (\n    <div\n      ref={ref}\n      data-slot=\"following-pointer\"\n      className={cn(\"relative\", active && \"cursor-none! [&_*]:cursor-none!\", className)}\n      {...props}\n      onPointerMove={(event) => {\n        track(event);\n        props.onPointerMove?.(event);\n      }}\n      onPointerLeave={(event) => {\n        setActive(false);\n        props.onPointerLeave?.(event);\n      }}\n    >\n      {children}\n      <div\n        aria-hidden\n        className=\"pointer-events-none absolute inset-0 z-50 overflow-hidden rounded-[inherit]\"\n      >\n        <AnimatePresence>\n          {active && (\n            <motion.div\n              className=\"absolute top-0 left-0\"\n              style={reducedMotion ? { x, y } : { x: springX, y: springY }}\n              initial={reducedMotion ? false : { scale: 0, opacity: 0 }}\n              animate={{ scale: 1, opacity: 1 }}\n              exit={reducedMotion ? undefined : { scale: 0, opacity: 0 }}\n              transition={{ duration: 0.15 }}\n            >\n              <svg\n                viewBox=\"0 0 20 20\"\n                className=\"absolute -top-0.5 -left-0.5 size-5 drop-shadow-sm\"\n              >\n                <path\n                  d=\"M2.5 2.5 17 8.4l-6.4 2.2L8.4 17Z\"\n                  fill={color}\n                  stroke=\"white\"\n                  strokeWidth=\"1.5\"\n                  strokeLinejoin=\"round\"\n                />\n              </svg>\n              {label != null && label !== \"\" && (\n                <div\n                  style={{ backgroundColor: color }}\n                  className=\"absolute top-4 left-3.5 rounded-full px-2.5 py-1 text-xs font-medium whitespace-nowrap text-brand-foreground shadow-md ring-1 ring-white/70\"\n                >\n                  {label}\n                </div>\n              )}\n            </motion.div>\n          )}\n        </AnimatePresence>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/velora/following-pointer.tsx"
    }
  ],
  "type": "registry:ui"
}