{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "infinite-plane",
  "type": "registry:component",
  "title": "Infinite Plane",
  "description": "Tiled pannable backdrop for the canvas with dot or grid pattern that drifts with the viewport.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/infinite-plane/infinite-plane.tsx",
      "content": "\"use client\";\n\nimport {\n  type ComponentPropsWithoutRef,\n  forwardRef,\n  type ReactNode,\n} from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\n/**\n * Pattern style for the plane backdrop.\n *\n * @public\n */\nexport type InfinitePlanePattern = \"blank\" | \"dot\" | \"grid\";\n\n/**\n * Localizable strings.\n *\n * @public\n */\nexport type InfinitePlaneLabels = {\n  /** Aria-label override. Defaults to `\"Infinite plane\"`. */\n  region?: string;\n};\n\nconst DEFAULT_LABELS = {\n  region: \"Infinite plane\",\n} as const satisfies Required<InfinitePlaneLabels>;\n\n/**\n * Props for {@link InfinitePlane}.\n *\n * @public\n */\nexport type InfinitePlaneProps = {\n  /** Children render in the plane's coordinate space. */\n  children?: ReactNode;\n  /** Localizable strings. */\n  labels?: InfinitePlaneLabels;\n  /** Backdrop pattern. Defaults to `\"dot\"`. */\n  pattern?: InfinitePlanePattern;\n  /** Pattern grid spacing in pixels. Defaults to `32`. */\n  spacing?: number;\n  /** Optional offset applied to the pattern (drift with viewport translation). Defaults to `{ x: 0, y: 0 }`. */\n  translate?: { x: number; y: number };\n  /** Zoom factor — drives the pattern's effective spacing. Defaults to `1`. */\n  zoom?: number;\n} & ComponentPropsWithoutRef<\"div\">;\n\nconst safeSpacing = (value: number): number => (value < 4 ? 4 : value);\n\nconst safeZoom = (value: number): number => {\n  if (value < 0.1) {\n    return 0.1;\n  }\n  if (value > 10) {\n    return 10;\n  }\n  return value;\n};\n\nconst buildBackground = (input: {\n  pattern: InfinitePlanePattern;\n  spacing: number;\n  translate: { x: number; y: number };\n  zoom: number;\n}): React.CSSProperties => {\n  if (input.pattern === \"blank\") {\n    return {};\n  }\n  const size = safeSpacing(input.spacing) * safeZoom(input.zoom);\n  const pos = `${input.translate.x}px ${input.translate.y}px`;\n  if (input.pattern === \"grid\") {\n    return {\n      backgroundImage:\n        \"linear-gradient(to right, hsl(var(--border)) 1px, transparent 1px), linear-gradient(to bottom, hsl(var(--border)) 1px, transparent 1px)\",\n      backgroundPosition: pos,\n      backgroundSize: `${size}px ${size}px`,\n    };\n  }\n  return {\n    backgroundImage:\n      \"radial-gradient(circle, hsl(var(--border)) 1px, transparent 1px)\",\n    backgroundPosition: pos,\n    backgroundSize: `${size}px ${size}px`,\n  };\n};\n\n/**\n * Tiled pannable backdrop for the canvas. Renders a `dot` or `grid`\n * pattern that drifts with the viewport translate + scales with the\n * zoom, plus a slot for spatial children that share its coordinate\n * space.\n *\n * Pure presentation; the host owns the viewport transform and supplies\n * `translate` + `zoom` from its pan / zoom controller.\n *\n * @example\n * ```tsx\n * <InfinitePlane translate={{ x: pan.x, y: pan.y }} zoom={zoom}>\n *   <ObjectCard …/>\n *   <ObjectCard …/>\n * </InfinitePlane>\n * ```\n *\n * @public\n */\nexport const InfinitePlane = forwardRef<HTMLDivElement, InfinitePlaneProps>(\n  (props, ref) => {\n    const {\n      children,\n      className,\n      labels,\n      pattern = \"dot\",\n      spacing = 32,\n      translate = { x: 0, y: 0 },\n      zoom = 1,\n      ...rest\n    } = props;\n    const resolvedLabels = { ...DEFAULT_LABELS, ...labels };\n    const background = buildBackground({ pattern, spacing, translate, zoom });\n    return (\n      <div\n        aria-label={resolvedLabels.region}\n        className={cn(\n          \"relative h-full w-full overflow-hidden bg-background\",\n          className,\n        )}\n        data-infinite-plane\n        data-infinite-plane-pattern={pattern}\n        ref={ref}\n        role=\"region\"\n        style={background}\n        {...rest}\n      >\n        {children}\n      </div>\n    );\n  },\n);\nInfinitePlane.displayName = \"InfinitePlane\";\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
