{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "context-lens",
  "title": "Context Lens",
  "description": "Vignette overlay that dims the canvas outside a circular focus region.",
  "dependencies": [
    "@vllnt/ui@^0.3.0"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/context-lens/context-lens.tsx",
      "content": "\"use client\";\n\nimport { type ComponentPropsWithoutRef, 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 = ({\n  ref,\n  ...props\n}: ContextLensProps & { ref?: React.Ref<SVGSVGElement> }) => {\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};\nContextLens.displayName = \"ContextLens\";\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component",
  "version": "0.3.0",
  "stability": "stable"
}
