{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"webgl-provider","type":"registry:component","title":"Webgl Provider","description":"A single shared WebGL canvas for the whole app.","meta":{"pro":false},"docs":"Usage: https://atelier-ui.com/r/webgl-provider.md\nDocs: https://atelier-ui.com/docs/foundation/primitive/webgl-provider","dependencies":["three","@types/three","@react-three/fiber","@react-three/postprocessing","postprocessing","motion"],"registryDependencies":["https://atelier-ui.com/r/webgl-portal.json","https://atelier-ui.com/r/agent-rules.json"],"files":[{"path":"registry/webgl-provider/webgl-provider.tsx","type":"registry:component","target":"components/webgl-provider/webgl-provider.tsx","content":"\"use client\"\n\nimport { advance, Canvas, type CanvasProps, useStore, useThree } from \"@react-three/fiber\"\nimport { EffectComposer } from \"@react-three/postprocessing\"\nimport { cancelFrame, type FrameData, frame } from \"motion\"\nimport { type ComponentRef, type ReactNode, useEffect, useRef, useState } from \"react\"\nimport type { Camera, Scene } from \"three\"\nimport { effectTeleport, WebglPortal } from \"../webgl-portal/webgl-portal\"\n\ntype WebglProviderProps = Omit<CanvasProps, \"children\" | \"eventSource\"> & {\n    children: ReactNode\n    className?: string\n    contained?: boolean\n}\n\ntype WebglReadyOptions = {\n    scene?: Scene\n    camera?: Camera\n    enabled?: boolean\n    onReady?: () => void\n}\n\nexport function useWebglReady({ scene, camera, enabled = true, onReady }: WebglReadyOptions = {}) {\n    const [ready, setReady] = useState(false)\n    const gl = useThree((state) => state.gl)\n    const defaultScene = useThree((state) => state.scene)\n    const defaultCamera = useThree((state) => state.camera)\n    const onReadyRef = useRef(onReady)\n    onReadyRef.current = onReady\n\n    const targetScene = scene ?? defaultScene\n    const targetCamera = camera ?? defaultCamera\n\n    useEffect(() => {\n        if (!enabled) return\n        let active = true\n\n        gl.compileAsync(targetScene, targetCamera).then(() => {\n            if (!active) return\n            requestAnimationFrame(() => {\n                if (!active) return\n                setReady(true)\n                onReadyRef.current?.()\n            })\n        })\n\n        return () => {\n            active = false\n        }\n    }, [gl, targetScene, targetCamera, enabled])\n\n    return ready\n}\n\n// Renders in Motion's `postRender` phase, after Lenis and Motion have\n// updated. One shared driver serves every mounted provider.\ntype CanvasStore = ReturnType<typeof useStore>\nconst canvasStores = new Set<CanvasStore>()\nlet clockStart: number | null = null\n\nfunction tick(data: FrameData) {\n    if (clockStart === null) clockStart = data.timestamp\n\n    // frameloop=\"never\" expects the elapsed clock time in seconds.\n    const elapsed = (data.timestamp - clockStart) / 1000\n\n    let runGlobalEffects = true\n    for (const store of canvasStores) {\n        const state = store.getState()\n        if (state.internal.active) {\n            advance(elapsed, runGlobalEffects, state)\n            runGlobalEffects = false\n        }\n    }\n}\n\nfunction MotionFrameloop() {\n    const store = useStore()\n\n    useEffect(() => {\n        canvasStores.add(store)\n        if (canvasStores.size === 1) frame.postRender(tick, true)\n        return () => {\n            canvasStores.delete(store)\n            if (canvasStores.size === 0) cancelFrame(tick)\n        }\n    }, [store])\n\n    return null\n}\n\nfunction Effects() {\n    const effects = effectTeleport.useItems()\n    const gl = useThree((state) => state.gl)\n    const mounted = effects.length > 0\n\n    // EffectComposer sets `renderer.autoClear = false` and never restores it;\n    // without this the canvas keeps its last frame once the composer unmounts.\n    useEffect(() => {\n        if (!mounted) return\n        return () => {\n            gl.autoClear = true\n        }\n    }, [mounted, gl])\n\n    if (!mounted) return null\n\n    return (\n        <EffectComposer key={effects.length}>\n            <effectTeleport.Out />\n        </EffectComposer>\n    )\n}\n\nexport function WebglProvider({\n    children,\n    className,\n    style,\n    contained = false,\n    ...canvasProps\n}: WebglProviderProps) {\n    const [eventSource, setEventSource] = useState<ComponentRef<\"div\"> | null>(null)\n\n    return (\n        <div\n            ref={setEventSource}\n            data-atelier-webgl=\"\"\n            className={className}\n            style={contained ? { position: \"relative\" } : { display: \"contents\" }}\n        >\n            <Canvas\n                eventPrefix=\"client\"\n                dpr={[1, 2]}\n                {...canvasProps}\n                frameloop=\"never\"\n                eventSource={eventSource ?? undefined}\n                style={{\n                    position: contained ? \"absolute\" : \"fixed\",\n                    inset: 0,\n                    pointerEvents: \"none\",\n                    ...style,\n                }}\n            >\n                <MotionFrameloop />\n                <WebglPortal />\n                <Effects />\n            </Canvas>\n\n            {children}\n        </div>\n    )\n}\n"}]}