{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "abstract-cube-isometric-loader",
  "type": "registry:ui",
  "title": "Abstract Cube Isometric",
  "description": "An isometric 3D wireframe cube rotating through perspective angles.",
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/ui/abstract-cube-isometric-loader.tsx",
      "content": "\"use client\";\n\nimport React, { useMemo } from \"react\";\n\nexport interface GradientConfig {\n  from: string;\n  via?: string;\n  to: string;\n  direction?: \"diagonal\" | \"horizontal\" | \"vertical\";\n}\n\nexport interface AbstractCubeIsometricLoaderProps {\n  /** Size of each dot in pixels. Defaults to 5 */\n  dotSize?: number;\n  /** Gap between dots in pixels. Defaults to 4 */\n  gap?: number;\n  /** Duration of one full animation cycle in seconds. Defaults to 1.2 */\n  speed?: number;\n  /** Color of active dots (e.g. \"currentColor\", \"#3b82f6\"). Defaults to \"currentColor\" */\n  color?: string;\n  /** Multi-stop gradient color configuration */\n  gradient?: GradientConfig;\n  /** Enable bloom / glow effect. Defaults to false */\n  bloom?: boolean;\n  /** Inactive dot opacity (0 - 1). Defaults to 0.08 */\n  opacity?: number;\n  /** Optional custom class name */\n  className?: string;\n}\n\nconst MATRIX = [\"00100\",\"01110\",\"10101\",\"11011\",\"01110\"] as const;\nconst ROWS = MATRIX.length;\nconst COLS = MATRIX[0]?.length || 5;\n\nconst colorCache = new Map<string, [number, number, number] | null>();\n\nfunction parseColorToRgb(color: string): [number, number, number] | null {\n  if (!color || color === \"currentColor\") return null;\n  if (colorCache.has(color)) return colorCache.get(color)!;\n\n  let result: [number, number, number] | null = null;\n  if (color.startsWith(\"#\")) {\n    let hex = color.slice(1);\n    if (hex.length === 3) {\n      hex = hex.split(\"\").map((c) => c + c).join(\"\");\n    }\n    if (hex.length === 6) {\n      const num = parseInt(hex, 16);\n      result = [(num >> 16) & 255, (num >> 8) & 255, num & 255];\n    }\n  } else {\n    const rgbMatch = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/i);\n    if (rgbMatch) {\n      result = [\n        parseInt(rgbMatch[1], 10),\n        parseInt(rgbMatch[2], 10),\n        parseInt(rgbMatch[3], 10),\n      ];\n    }\n  }\n  colorCache.set(color, result);\n  return result;\n}\n\nfunction interpolateColor(\n  c1: [number, number, number],\n  c2: [number, number, number],\n  factor: number\n): string {\n  const r = Math.round(c1[0] + factor * (c2[0] - c1[0]));\n  const g = Math.round(c1[1] + factor * (c2[1] - c1[1]));\n  const b = Math.round(c1[2] + factor * (c2[2] - c1[2]));\n  return `rgb(${r}, ${g}, ${b})`;\n}\n\nfunction getMultiGradientColor(\n  gradient: GradientConfig,\n  col: number,\n  row: number,\n  totalCols: number,\n  totalRows: number\n): string | undefined {\n  const cFrom = parseColorToRgb(gradient.from);\n  const cTo = parseColorToRgb(gradient.to);\n  const cVia = gradient.via ? parseColorToRgb(gradient.via) : null;\n\n  if (!cFrom || !cTo) return undefined;\n\n  let progress = 0;\n  const maxDim = Math.max(1, totalCols - 1);\n  const maxRow = Math.max(1, totalRows - 1);\n\n  if (gradient.direction === \"horizontal\") {\n    progress = col / maxDim;\n  } else if (gradient.direction === \"vertical\") {\n    progress = row / maxRow;\n  } else {\n    progress = (col + row) / (maxDim + maxRow);\n  }\n\n  progress = Math.max(0, Math.min(1, progress));\n\n  if (!cVia) {\n    return interpolateColor(cFrom, cTo, progress);\n  }\n\n  if (progress < 0.5) {\n    const factor = progress / 0.5;\n    return interpolateColor(cFrom, cVia, factor);\n  } else {\n    const factor = (progress - 0.5) / 0.5;\n    return interpolateColor(cVia, cTo, factor);\n  }\n}\n\nexport function AbstractCubeIsometricLoader({\n  dotSize = 5,\n  gap = 4,\n  speed = 1.2,\n  color = \"currentColor\",\n  gradient,\n  bloom = false,\n  opacity = 0.08,\n  className = \"\",\n}: AbstractCubeIsometricLoaderProps) {\n  const dots = useMemo(() => {\n    const list: {\n      key: string;\n      active: boolean;\n      delay: number;\n      color?: string;\n      x: number;\n      y: number;\n    }[] = [];\n\n    for (let r = 0; r < ROWS; r++) {\n      for (let c = 0; c < COLS; c++) {\n        const active = MATRIX[r]?.[c] === \"1\";\n        const delay = (r * COLS + c) * 0.04;\n\n        let dotColor: string | undefined = undefined;\n        if (gradient) {\n          dotColor = getMultiGradientColor(gradient, c, r, COLS, ROWS);\n        } else if (color && color !== \"currentColor\") {\n          dotColor = color;\n        }\n\n        list.push({\n          key: `${r}-${c}`,\n          active,\n          delay,\n          color: dotColor,\n          x: c + 1,\n          y: r + 1,\n        });\n      }\n    }\n    return list;\n  }, [color, gradient]);\n\n  let gradientBackground: string | undefined = undefined;\n  if (gradient) {\n    const angle =\n      gradient.direction === \"horizontal\"\n        ? \"to right\"\n        : gradient.direction === \"vertical\"\n          ? \"to bottom\"\n          : \"135deg\";\n    if (gradient.via) {\n      gradientBackground = `linear-gradient(${angle}, ${gradient.from}, ${gradient.via}, ${gradient.to})`;\n    } else {\n      gradientBackground = `linear-gradient(${angle}, ${gradient.from}, ${gradient.to})`;\n    }\n  }\n\n  return (\n    <div\n      className={`atom-loader dot-loader ${bloom ? \"has-bloom\" : \"\"} ${className}`}\n      data-motion=\"orbit\"\n      style={\n        {\n          \"--dot-size\": `${dotSize}px`,\n          \"--gap\": `${gap}px`,\n          \"--cols\": COLS,\n          \"--rows\": ROWS,\n          \"--speed\": `${speed}s`,\n          \"--idle-opacity\": opacity,\n          \"--dot-color\": color === \"currentColor\" ? \"currentColor\" : color,\n          \"--dot-bg\": gradientBackground || undefined,\n        } as React.CSSProperties\n      }\n      aria-label=\"Loading: Abstract Cube Isometric\"\n      role=\"status\"\n    >\n      {dots.map((dot) => (\n        <span\n          key={dot.key}\n          className={dot.active ? \"active\" : \"\"}\n          style={\n            {\n              \"--x\": dot.x,\n              \"--y\": dot.y,\n              \"--delay\": `${dot.delay}s`,\n              ...(dot.color\n                ? {\n                    \"--dot-color\": dot.color,\n                    backgroundColor: dot.active ? dot.color : undefined,\n                  }\n                : {}),\n            } as React.CSSProperties\n          }\n        />\n      ))}\n    </div>\n  );\n}\n\nexport default AbstractCubeIsometricLoader;\n",
      "type": "registry:ui"
    }
  ]
}