{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "use-gl-palette",
  "title": "useGlPalette",
  "description": "Reads CSS colour tokens for WebGL and follows theme and skin changes.",
  "files": [
    {
      "path": "registry/spotcheck/hooks/use-gl-palette.ts",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nconst WATCHED_ATTRIBUTES = [\"class\", \"style\", \"data-theme\", \"data-skin\"]\n\n/** Resolves any CSS colour (oklch, color-mix, light-dark) to a hex string that three.js can read. */\nexport function toHex(cssColor: string) {\n  const context = document.createElement(\"canvas\").getContext(\"2d\", { willReadFrequently: true })\n  if (!context) return \"#000000\"\n  context.fillStyle = cssColor\n  context.fillRect(0, 0, 1, 1)\n  const [r = 0, g = 0, b = 0] = context.getImageData(0, 0, 1, 1).data\n  return `#${[r, g, b].map((channel) => channel.toString(16).padStart(2, \"0\")).join(\"\")}`\n}\n\nfunction readTokens<T extends string>(element: Element, tokens: readonly T[]) {\n  // A probe inherits the element's scope, so nested data-theme and data-skin values are honoured.\n  const probe = document.createElement(\"span\")\n  probe.hidden = true\n  element.appendChild(probe)\n  const palette = {} as Record<T, string>\n  for (const token of tokens) {\n    probe.style.color = `var(${token})`\n    palette[token] = toHex(getComputedStyle(probe).color)\n  }\n  probe.remove()\n  return palette\n}\n\n/** Reads CSS colour tokens for a WebGL scene and re-reads them when the theme or skin changes. */\nexport function useGlPalette<T extends string>(\n  ref: React.RefObject<Element | null>,\n  tokens: readonly T[],\n) {\n  const [palette, setPalette] = React.useState<Record<T, string> | null>(null)\n\n  React.useEffect(() => {\n    const element = ref.current\n    if (!element) return\n\n    const update = () => setPalette(readTokens(element, tokens))\n    update()\n\n    const observer = new MutationObserver(update)\n    for (let node: Element | null = element.parentElement; node; node = node.parentElement) {\n      observer.observe(node, { attributes: true, attributeFilter: WATCHED_ATTRIBUTES })\n    }\n    const scheme = window.matchMedia(\"(prefers-color-scheme: dark)\")\n    scheme.addEventListener(\"change\", update)\n\n    return () => {\n      observer.disconnect()\n      scheme.removeEventListener(\"change\", update)\n    }\n  }, [ref, tokens])\n\n  return palette\n}\n",
      "type": "registry:hook"
    }
  ],
  "type": "registry:hook"
}