{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "cursor-trail",
  "title": "Cursor Trail",
  "description": "A fading mark behind the pointer, drawn only inside its own box.",
  "registryDependencies": [
    "utils",
    "https://ui.tinkererslabs.com/r/render-surface.json"
  ],
  "files": [
    {
      "path": "registry/default/cursor-trail/cursor-trail.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  RenderSurface,\n  useThemeColors,\n  type SurfaceColor,\n} from \"@/registry/default/render-surface/render-surface\"\nimport { cn } from \"@/lib/utils\"\n\nexport type CursorTrailProps = React.HTMLAttributes<HTMLDivElement> & {\n  color?: string\n  /** Widest the trail gets, in pixels. */\n  size?: number\n  /** Seconds a mark takes to fade. */\n  life?: number\n  surfaceClassName?: string\n}\n\ntype Mark = { x: number; y: number; age: number }\n\n/**\n * Leaves a fading mark behind the pointer inside its own box. It draws only\n * while the pointer is over it, so it costs nothing on the rest of the page.\n */\nexport function CursorTrail({\n  color = \"--primary\",\n  size = 26,\n  life = 0.7,\n  className,\n  surfaceClassName,\n  children,\n  ...rootProps\n}: CursorTrailProps) {\n  const rootRef = React.useRef<HTMLDivElement>(null)\n  const marks = React.useRef<Mark[]>([])\n  const pointer = React.useRef({ x: 0, y: 0, down: false })\n  const [drawing, setDrawing] = React.useState(false)\n\n  const tokens = React.useMemo(\n    () => (color.startsWith(\"--\") ? [color] : []),\n    [color]\n  )\n\n  const resolved = useThemeColors(rootRef, tokens)\n  const ink = color.startsWith(\"--\") ? resolved[color] : undefined\n\n  const setup = React.useCallback(() => null, [])\n\n  const draw = React.useCallback(\n    ({\n      context,\n      size: box,\n      delta,\n    }: {\n      context: CanvasRenderingContext2D\n      size: { width: number; height: number; dpr: number }\n      delta: number\n    }) => {\n      if (!ink) return\n\n      context.setTransform(box.dpr, 0, 0, box.dpr, 0, 0)\n      context.clearRect(0, 0, box.width, box.height)\n\n      const living: Mark[] = []\n\n      for (const mark of marks.current) {\n        mark.age += delta\n        if (mark.age >= life) continue\n        living.push(mark)\n\n        const remaining = 1 - mark.age / life\n        const radius = (size / 2) * remaining\n        const [r, g, b] = ink as SurfaceColor\n\n        context.fillStyle = `rgba(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(b * 255)}, ${remaining * 0.5})`\n        context.beginPath()\n        context.arc(mark.x, mark.y, Math.max(radius, 0.5), 0, Math.PI * 2)\n        context.fill()\n      }\n\n      marks.current = living\n      if (living.length === 0 && !pointer.current.down) setDrawing(false)\n    },\n    [ink, life, size]\n  )\n\n  return (\n    <div\n      ref={rootRef}\n      data-slot=\"cursor-trail\"\n      className={cn(\"relative isolate overflow-hidden\", className)}\n      onPointerMove={(event) => {\n        const rect = event.currentTarget.getBoundingClientRect()\n        pointer.current.x = event.clientX - rect.left\n        pointer.current.y = event.clientY - rect.top\n        pointer.current.down = true\n\n        if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) {\n          return\n        }\n\n        marks.current.push({\n          x: pointer.current.x,\n          y: pointer.current.y,\n          age: 0,\n        })\n\n        setDrawing(true)\n      }}\n      onPointerLeave={() => {\n        pointer.current.down = false\n      }}\n      {...rootProps}\n    >\n      {ink !== undefined && (\n        <RenderSurface<null, \"2d\">\n          setup={setup}\n          draw={draw}\n          paused={!drawing}\n          className={cn(\n            \"pointer-events-none absolute inset-0 z-10\",\n            surfaceClassName\n          )}\n        />\n      )}\n      {children}\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "scene",
    "pointer",
    "canvas"
  ],
  "type": "registry:ui"
}