{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "context-lens",
  "type": "registry:component",
  "title": "Context Lens",
  "description": "Vignette overlay that dims the canvas outside a circular focus region.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/context-lens/context-lens.tsx",
      "content": "\"use client\";\n\nimport { type ComponentPropsWithoutRef, forwardRef, useId } from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\n/**\n * Focus point — pixel coordinates relative to the canvas viewport.\n *\n * @public\n */\nexport type ContextLensFocus = {\n  /** Center X of the lens. */\n  cx: number;\n  /** Center Y of the lens. */\n  cy: number;\n  /** Inner radius — fully transparent inside this circle. */\n  inner: number;\n  /** Outer radius — full dim beyond this circle. */\n  outer: number;\n};\n\n/**\n * Localizable strings.\n *\n * @public\n */\nexport type ContextLensLabels = {\n  /** Aria-label for the lens layer. Defaults to `\"Context lens\"`. */\n  region?: string;\n};\n\nconst DEFAULT_LABELS = {\n  region: \"Context lens\",\n} as const satisfies Required<ContextLensLabels>;\n\n/**\n * Props for {@link ContextLens}.\n *\n * @public\n */\nexport type ContextLensProps = {\n  /** Lens geometry. When `null`, no lens renders (full surface unobscured). */\n  focus: ContextLensFocus | null;\n  /** Localizable strings. */\n  labels?: ContextLensLabels;\n  /** Dim opacity outside the outer ring (0..1). Defaults to `0.55`. */\n  opacity?: number;\n} & ComponentPropsWithoutRef<\"svg\">;\n\nconst clamp01 = (value: number): number => {\n  if (value < 0) {\n    return 0;\n  }\n  if (value > 1) {\n    return 1;\n  }\n  return value;\n};\n\nconst safeRadius = (value: number): number => (value < 0 ? 0 : value);\n\n/**\n * Vignette overlay that dims the canvas outside a circular focus\n * region. Use to draw the eye to a selection, an active run, or any\n * single object the user must attend to. Pure presentation; the host\n * computes the focus center + radii from the active selection's\n * bounding box.\n *\n * The lens is `pointer-events: none` — host gestures pass through.\n *\n * @example\n * ```tsx\n * <div className=\"relative h-screen w-screen\">\n *   <Canvas />\n *   <ContextLens focus={{ cx: 480, cy: 320, inner: 90, outer: 180 }} />\n * </div>\n * ```\n *\n * @public\n */\nexport const ContextLens = forwardRef<SVGSVGElement, ContextLensProps>(\n  (props, ref) => {\n    const { className, focus, labels, opacity = 0.55, ...rest } = props;\n    const maskId = useId();\n    if (!focus) {\n      return null;\n    }\n    const resolvedLabels = { ...DEFAULT_LABELS, ...labels };\n    const inner = safeRadius(focus.inner);\n    const outer = safeRadius(Math.max(focus.outer, inner));\n    const fillOpacity = clamp01(opacity);\n    return (\n      <svg\n        aria-hidden=\"true\"\n        aria-label={resolvedLabels.region}\n        className={cn(\n          \"pointer-events-none absolute inset-0 z-20 h-full w-full\",\n          className,\n        )}\n        data-context-lens\n        ref={ref}\n        {...rest}\n      >\n        <defs>\n          <radialGradient\n            cx={focus.cx}\n            cy={focus.cy}\n            data-context-lens-gradient\n            gradientUnits=\"userSpaceOnUse\"\n            id={maskId}\n            r={outer === 0 ? 1 : outer}\n          >\n            <stop offset=\"0%\" stopColor=\"black\" stopOpacity={0} />\n            <stop\n              offset={outer === 0 ? \"100%\" : `${(inner / outer) * 100}%`}\n              stopColor=\"black\"\n              stopOpacity={0}\n            />\n            <stop offset=\"100%\" stopColor=\"black\" stopOpacity={1} />\n          </radialGradient>\n        </defs>\n        <rect\n          data-context-lens-dim\n          fill=\"black\"\n          fillOpacity={fillOpacity}\n          height=\"100%\"\n          mask={`url(#${maskId}-mask)`}\n          width=\"100%\"\n          x={0}\n          y={0}\n        />\n        <mask id={`${maskId}-mask`}>\n          <rect fill=\"white\" height=\"100%\" width=\"100%\" x={0} y={0} />\n          <circle\n            cx={focus.cx}\n            cy={focus.cy}\n            fill={`url(#${maskId})`}\n            r={outer}\n          />\n        </mask>\n      </svg>\n    );\n  },\n);\nContextLens.displayName = \"ContextLens\";\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
