{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "LaserFlow-TS-TW",
	"title": "LaserFlow",
	"description": "Dynamic laser light that flows onto a surface, customizable effect.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "LaserFlow/LaserFlow.tsx",
			"content": "import React, { useEffect, useRef } from 'react';\nimport * as THREE from 'three';\n\ntype Props = {\n  className?: string;\n  style?: React.CSSProperties;\n  wispDensity?: number;\n  dpr?: number;\n  mouseSmoothTime?: number;\n  mouseTiltStrength?: number;\n  horizontalBeamOffset?: number;\n  verticalBeamOffset?: number;\n  flowSpeed?: number;\n  verticalSizing?: number;\n  horizontalSizing?: number;\n  fogIntensity?: number;\n  fogScale?: number;\n  wispSpeed?: number;\n  wispIntensity?: number;\n  flowStrength?: number;\n  decay?: number;\n  falloffStart?: number;\n  fogFallSpeed?: number;\n  color?: string;\n  backgroundColor?: string;\n};\n\nconst VERT = `\nprecision highp float;\nattribute vec3 position;\nvoid main(){\n  gl_Position = vec4(position, 1.0);\n}\n`;\n\nconst FRAG = `\n#ifdef GL_ES\n#extension GL_OES_standard_derivatives : enable\n#endif\nprecision highp float;\nprecision mediump int;\n\nuniform float iTime;\nuniform vec3 iResolution;\nuniform vec4 iMouse;\nuniform float uWispDensity;\nuniform float uTiltScale;\nuniform float uFlowTime;\nuniform float uFogTime;\nuniform float uBeamXFrac;\nuniform float uBeamYFrac;\nuniform float uFlowSpeed;\nuniform float uVLenFactor;\nuniform float uHLenFactor;\nuniform float uFogIntensity;\nuniform float uFogScale;\nuniform float uWSpeed;\nuniform float uWIntensity;\nuniform float uFlowStrength;\nuniform float uDecay;\nuniform float uFalloffStart;\nuniform float uFogFallSpeed;\nuniform vec3 uColor;\nuniform float uFade;\n\n// Core beam/flare shaping and dynamics\n#define PI 3.14159265359\n#define TWO_PI 6.28318530718\n#define EPS 1e-6\n#define EDGE_SOFT (DT_LOCAL*4.0)\n#define DT_LOCAL 0.0038\n#define TAP_RADIUS 6\n#define R_H 150.0\n#define R_V 150.0\n#define FLARE_HEIGHT 16.0\n#define FLARE_AMOUNT 8.0\n#define FLARE_EXP 2.0\n#define TOP_FADE_START 0.1\n#define TOP_FADE_EXP 1.0\n#define FLOW_PERIOD 0.5\n#define FLOW_SHARPNESS 1.5\n\n// Wisps (animated micro-streaks) that travel along the beam\n#define W_BASE_X 1.5\n#define W_LAYER_GAP 0.25\n#define W_LANES 10\n#define W_SIDE_DECAY 0.5\n#define W_HALF 0.01\n#define W_AA 0.15\n#define W_CELL 20.0\n#define W_SEG_MIN 0.01\n#define W_SEG_MAX 0.55\n#define W_CURVE_AMOUNT 15.0\n#define W_CURVE_RANGE (FLARE_HEIGHT - 3.0)\n#define W_BOTTOM_EXP 10.0\n\n// Volumetric fog controls\n#define FOG_ON 1\n#define FOG_CONTRAST 1.2\n#define FOG_SPEED_U 0.1\n#define FOG_SPEED_V -0.1\n#define FOG_OCTAVES 5\n#define FOG_BOTTOM_BIAS 0.8\n#define FOG_TILT_TO_MOUSE 0.05\n#define FOG_TILT_DEADZONE 0.01\n#define FOG_TILT_MAX_X 0.35\n#define FOG_TILT_SHAPE 1.5\n#define FOG_BEAM_MIN 0.0\n#define FOG_BEAM_MAX 0.75\n#define FOG_MASK_GAMMA 0.5\n#define FOG_EXPAND_SHAPE 12.2\n#define FOG_EDGE_MIX 0.5\n\n// Horizontal vignette for the fog volume\n#define HFOG_EDGE_START 0.20\n#define HFOG_EDGE_END 0.98\n#define HFOG_EDGE_GAMMA 1.4\n#define HFOG_Y_RADIUS 25.0\n#define HFOG_Y_SOFT 60.0\n\n// Beam extents and edge masking\n#define EDGE_X0 0.22\n#define EDGE_X1 0.995\n#define EDGE_X_GAMMA 1.25\n#define EDGE_LUMA_T0 0.0\n#define EDGE_LUMA_T1 2.0\n#define DITHER_STRENGTH 1.0\n\n    float g(float x){return x<=0.00031308?12.92*x:1.055*pow(x,1.0/2.4)-0.055;}\n    float bs(vec2 p,vec2 q,float powr){\n        float d=distance(p,q),f=powr*uFalloffStart,r=(f*f)/(d*d+EPS);\n        return powr*min(1.0,r);\n    }\n    float bsa(vec2 p,vec2 q,float powr,vec2 s){\n        vec2 d=p-q; float dd=(d.x*d.x)/(s.x*s.x)+(d.y*d.y)/(s.y*s.y),f=powr*uFalloffStart,r=(f*f)/(dd+EPS);\n        return powr*min(1.0,r);\n    }\n    float tri01(float x){float f=fract(x);return 1.0-abs(f*2.0-1.0);}\n    float tauWf(float t,float tmin,float tmax){float a=smoothstep(tmin,tmin+EDGE_SOFT,t),b=1.0-smoothstep(tmax-EDGE_SOFT,tmax,t);return max(0.0,a*b);} \n    float h21(vec2 p){p=fract(p*vec2(123.34,456.21));p+=dot(p,p+34.123);return fract(p.x*p.y);}\n    float vnoise(vec2 p){\n        vec2 i=floor(p),f=fract(p);\n        float a=h21(i),b=h21(i+vec2(1,0)),c=h21(i+vec2(0,1)),d=h21(i+vec2(1,1));\n        vec2 u=f*f*(3.0-2.0*f);\n        return mix(mix(a,b,u.x),mix(c,d,u.x),u.y);\n    }\n    float fbm2(vec2 p){\n        float v=0.0,amp=0.6; mat2 m=mat2(0.86,0.5,-0.5,0.86);\n        for(int i=0;i<FOG_OCTAVES;++i){v+=amp*vnoise(p); p=m*p*2.03+17.1; amp*=0.52;}\n        return v;\n    }\n    float rGate(float x,float l){float a=smoothstep(0.0,W_AA,x),b=1.0-smoothstep(l,l+W_AA,x);return max(0.0,a*b);}\n    float flareY(float y){float t=clamp(1.0-(clamp(y,0.0,FLARE_HEIGHT)/max(FLARE_HEIGHT,EPS)),0.0,1.0);return pow(t,FLARE_EXP);}\n\n    float vWisps(vec2 uv,float topF){\n    float y=uv.y,yf=(y+uFlowTime*uWSpeed)/W_CELL;\n    float dRaw=clamp(uWispDensity,0.0,2.0),d=dRaw<=0.0?1.0:dRaw;\n    float lanesF=floor(float(W_LANES)*min(d,1.0)+0.5); // WebGL1-safe\n    int lanes=int(max(1.0,lanesF));\n    float sp=min(d,1.0),ep=max(d-1.0,0.0);\n    float fm=flareY(max(y,0.0)),rm=clamp(1.0-(y/max(W_CURVE_RANGE,EPS)),0.0,1.0),cm=fm*rm;\n    const float G=0.05; float xS=1.0+(FLARE_AMOUNT*W_CURVE_AMOUNT*G)*cm;\n    float sPix=clamp(y/R_V,0.0,1.0),bGain=pow(1.0-sPix,W_BOTTOM_EXP),sum=0.0;\n    for(int s=0;s<2;++s){\n        float sgn=s==0?-1.0:1.0;\n        for(int i=0;i<W_LANES;++i){\n            if(i>=lanes) break;\n            float off=W_BASE_X+float(i)*W_LAYER_GAP,xc=sgn*(off*xS);\n            float dx=abs(uv.x-xc),lat=1.0-smoothstep(W_HALF,W_HALF+W_AA,dx),amp=exp(-off*W_SIDE_DECAY);\n            float seed=h21(vec2(off,sgn*17.0)),yf2=yf+seed*7.0,ci=floor(yf2),fy=fract(yf2);\n            float seg=mix(W_SEG_MIN,W_SEG_MAX,h21(vec2(ci,off*2.3)));\n            float spR=h21(vec2(ci,off+sgn*31.0)),seg1=rGate(fy,seg)*step(spR,sp);\n            if(ep>0.0){float spR2=h21(vec2(ci*3.1+7.0,off*5.3+sgn*13.0)); float f2=fract(fy+0.5); seg1+=rGate(f2,seg*0.9)*step(spR2,ep);}\n            sum+=amp*lat*seg1;\n        }\n    }\n    float span=smoothstep(-3.0,0.0,y)*(1.0-smoothstep(R_V-6.0,R_V,y));\n    return uWIntensity*sum*topF*bGain*span;\n}\n\nvoid mainImage(out vec4 fc,in vec2 frag){\n    vec2 C=iResolution.xy*.5; float invW=1.0/max(C.x,1.0);\n    vec2 sc=(512.0/iResolution.xy)*.4;\n    vec2 uv=(frag-C)*sc,off=vec2(uBeamXFrac*iResolution.x*sc.x,uBeamYFrac*iResolution.y*sc.y);\n    vec2 uvc = uv - off;\n    float a=0.0,b=0.0;\n    float basePhase=1.5*PI+uDecay*.5; float tauMin=basePhase-uDecay; float tauMax=basePhase;\n    float cx=clamp(uvc.x/(R_H*uHLenFactor),-1.0,1.0),tH=clamp(TWO_PI-acos(cx),tauMin,tauMax);\n    for(int k=-TAP_RADIUS;k<=TAP_RADIUS;++k){\n        float tu=tH+float(k)*DT_LOCAL,wt=tauWf(tu,tauMin,tauMax); if(wt<=0.0) continue;\n        float spd=max(abs(sin(tu)),0.02),u=clamp((basePhase-tu)/max(uDecay,EPS),0.0,1.0),env=pow(1.0-abs(u*2.0-1.0),0.8);\n        vec2 p=vec2((R_H*uHLenFactor)*cos(tu),0.0);\n        a+=wt*bs(uvc,p,env*spd);\n    }\n    float yPix=uvc.y,cy=clamp(-yPix/(R_V*uVLenFactor),-1.0,1.0),tV=clamp(TWO_PI-acos(cy),tauMin,tauMax);\n    for(int k=-TAP_RADIUS;k<=TAP_RADIUS;++k){\n        float tu=tV+float(k)*DT_LOCAL,wt=tauWf(tu,tauMin,tauMax); if(wt<=0.0) continue;\n        float yb=(-R_V)*cos(tu),s=clamp(yb/R_V,0.0,1.0),spd=max(abs(sin(tu)),0.02);\n        float env=pow(1.0-s,0.6)*spd;\n        float cap=1.0-smoothstep(TOP_FADE_START,1.0,s); cap=pow(cap,TOP_FADE_EXP); env*=cap;\n        float ph=s/max(FLOW_PERIOD,EPS)+uFlowTime*uFlowSpeed;\n        float fl=pow(tri01(ph),FLOW_SHARPNESS);\n        env*=mix(1.0-uFlowStrength,1.0,fl);\n        float yp=(-R_V*uVLenFactor)*cos(tu),m=pow(smoothstep(FLARE_HEIGHT,0.0,yp),FLARE_EXP),wx=1.0+FLARE_AMOUNT*m;\n        vec2 sig=vec2(wx,1.0),p=vec2(0.0,yp);\n        float mask=step(0.0,yp);\n        b+=wt*bsa(uvc,p,mask*env,sig);\n    }\n    float sPix=clamp(yPix/R_V,0.0,1.0),topA=pow(1.0-smoothstep(TOP_FADE_START,1.0,sPix),TOP_FADE_EXP);\n    float L=a+b*topA;\n    float w=vWisps(vec2(uvc.x,yPix),topA);\n    float fog=0.0;\n#if FOG_ON\n    vec2 fuv=uvc*uFogScale;\n    float mAct=step(1.0,length(iMouse.xy)),nx=((iMouse.x-C.x)*invW)*mAct;\n    float ax = abs(nx);\n    float stMag = mix(ax, pow(ax, FOG_TILT_SHAPE), 0.35);\n    float st = sign(nx) * stMag * uTiltScale;\n    st = clamp(st, -FOG_TILT_MAX_X, FOG_TILT_MAX_X);\n    vec2 dir=normalize(vec2(st,1.0));\n    fuv+=uFogTime*uFogFallSpeed*dir;\n    vec2 prp=vec2(-dir.y,dir.x);\n    fuv+=prp*(0.08*sin(dot(uvc,prp)*0.08+uFogTime*0.9));\n    float n=fbm2(fuv+vec2(fbm2(fuv+vec2(7.3,2.1)),fbm2(fuv+vec2(-3.7,5.9)))*0.6);\n    n=pow(clamp(n,0.0,1.0),FOG_CONTRAST);\n    float pixW = 1.0 / max(iResolution.y, 1.0);\n#ifdef GL_OES_standard_derivatives\n    float wL = max(fwidth(L), pixW);\n#else\n    float wL = pixW;\n#endif\n    float m0=pow(smoothstep(FOG_BEAM_MIN - wL, FOG_BEAM_MAX + wL, L),FOG_MASK_GAMMA);\n    float bm=1.0-pow(1.0-m0,FOG_EXPAND_SHAPE); bm=mix(bm*m0,bm,FOG_EDGE_MIX);\n    float yP=1.0-smoothstep(HFOG_Y_RADIUS,HFOG_Y_RADIUS+HFOG_Y_SOFT,abs(yPix));\n    float nxF=abs((frag.x-C.x)*invW),hE=1.0-smoothstep(HFOG_EDGE_START,HFOG_EDGE_END,nxF); hE=pow(clamp(hE,0.0,1.0),HFOG_EDGE_GAMMA);\n    float hW=mix(1.0,hE,clamp(yP,0.0,1.0));\n    float bBias=mix(1.0,1.0-sPix,FOG_BOTTOM_BIAS);\n    float browserFogIntensity = uFogIntensity;\n    browserFogIntensity *= 1.8;\n    float radialFade = 1.0 - smoothstep(0.0, 0.7, length(uvc) / 120.0);\n    float safariFog = n * browserFogIntensity * bBias * bm * hW * radialFade;\n    fog = safariFog;\n#endif\n    float LF=L+fog;\n    float dith=(h21(frag)-0.5)*(DITHER_STRENGTH/255.0);\n    float tone=g(LF+w);\n    vec3 col=tone*uColor+dith;\n    float alpha=clamp(g(L+w*0.6)+dith*0.6,0.0,1.0);\n    float nxE=abs((frag.x-C.x)*invW),xF=pow(clamp(1.0-smoothstep(EDGE_X0,EDGE_X1,nxE),0.0,1.0),EDGE_X_GAMMA);\n    float scene=LF+max(0.0,w)*0.5,hi=smoothstep(EDGE_LUMA_T0,EDGE_LUMA_T1,scene);\n    float eM=mix(xF,1.0,hi);\n    col*=eM; alpha*=eM;\n    col*=uFade; alpha*=uFade;\n    fc=vec4(col,alpha);\n}\n\nvoid main(){\n  vec4 fc;\n  mainImage(fc, gl_FragCoord.xy);\n  gl_FragColor = fc;\n}\n`;\n\nfunction hexToRGB(hex: string) {\n  let c = hex.trim();\n  if (c[0] === '#') c = c.slice(1);\n  if (c.length === 3)\n    c = c\n      .split('')\n      .map(x => x + x)\n      .join('');\n  const n = parseInt(c.slice(0, 6), 16) || 0xffffff;\n  return { r: ((n >> 16) & 255) / 255, g: ((n >> 8) & 255) / 255, b: (n & 255) / 255 };\n}\n\nexport const LaserFlow: React.FC<Props> = ({\n  className,\n  style,\n  wispDensity = 1,\n  dpr,\n  mouseSmoothTime = 0.0,\n  mouseTiltStrength = 0.01,\n  horizontalBeamOffset = 0.1,\n  verticalBeamOffset = 0.0,\n  flowSpeed = 0.35,\n  verticalSizing = 2.0,\n  horizontalSizing = 0.5,\n  fogIntensity = 0.45,\n  fogScale = 0.3,\n  wispSpeed = 15.0,\n  wispIntensity = 5.0,\n  flowStrength = 0.25,\n  decay = 1.1,\n  falloffStart = 1.2,\n  fogFallSpeed = 0.6,\n  color = '#FF79C6',\n  backgroundColor = '#000000'\n}) => {\n  const mountRef = useRef<HTMLDivElement | null>(null);\n  const rendererRef = useRef<THREE.WebGLRenderer | null>(null);\n  const uniformsRef = useRef<any>(null);\n  const hasFadedRef = useRef(false);\n  const rectRef = useRef<DOMRect | null>(null);\n  const baseDprRef = useRef<number>(1);\n  const currentDprRef = useRef<number>(1);\n  const lastSizeRef = useRef({ width: 0, height: 0, dpr: 0 });\n  const fpsSamplesRef = useRef<number[]>([]);\n  const lastFpsCheckRef = useRef<number>(performance.now());\n  const emaDtRef = useRef<number>(16.7);\n  const pausedRef = useRef<boolean>(false);\n  const inViewRef = useRef<boolean>(true);\n\n  const mouseSmoothTimeRef = useRef(mouseSmoothTime);\n  useEffect(() => {\n    mouseSmoothTimeRef.current = mouseSmoothTime;\n  }, [mouseSmoothTime]);\n\n  useEffect(() => {\n    const mount = mountRef.current!;\n    const renderer = new THREE.WebGLRenderer({\n      antialias: false,\n      alpha: false,\n      depth: false,\n      stencil: false,\n      powerPreference: 'high-performance',\n      premultipliedAlpha: false,\n      preserveDrawingBuffer: false,\n      failIfMajorPerformanceCaveat: false,\n      logarithmicDepthBuffer: false\n    });\n    rendererRef.current = renderer;\n\n    baseDprRef.current = Math.min(dpr ?? (window.devicePixelRatio || 1), 2);\n    currentDprRef.current = baseDprRef.current;\n\n    renderer.setPixelRatio(currentDprRef.current);\n    renderer.shadowMap.enabled = false;\n    renderer.outputColorSpace = THREE.SRGBColorSpace;\n    renderer.setClearColor(0x000000, 1);\n    const canvas = renderer.domElement;\n    canvas.style.width = '100%';\n    canvas.style.height = '100%';\n    canvas.style.display = 'block';\n    const applyBackgroundMode = (value: string) => {\n      const background = new THREE.Color(value || '#000000');\n      const luma = background.r * 0.2126 + background.g * 0.7152 + background.b * 0.0722;\n      const lightBackground = luma > 0.72;\n      mount.style.backgroundColor = value || '#000000';\n      canvas.style.filter = lightBackground ? 'invert(1) hue-rotate(180deg)' : 'none';\n      canvas.style.mixBlendMode = lightBackground ? 'normal' : 'screen';\n    };\n    applyBackgroundMode(backgroundColor);\n    mount.appendChild(canvas);\n\n    const scene = new THREE.Scene();\n    const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);\n\n    const geometry = new THREE.BufferGeometry();\n    geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array([-1, -1, 0, 3, -1, 0, -1, 3, 0]), 3));\n\n    const uniforms = {\n      iTime: { value: 0 },\n      iResolution: { value: new THREE.Vector3(1, 1, 1) },\n      iMouse: { value: new THREE.Vector4(0, 0, 0, 0) },\n      uWispDensity: { value: wispDensity },\n      uTiltScale: { value: mouseTiltStrength },\n      uFlowTime: { value: 0 },\n      uFogTime: { value: 0 },\n      uBeamXFrac: { value: horizontalBeamOffset },\n      uBeamYFrac: { value: verticalBeamOffset },\n      uFlowSpeed: { value: flowSpeed },\n      uVLenFactor: { value: verticalSizing },\n      uHLenFactor: { value: horizontalSizing },\n      uFogIntensity: { value: fogIntensity },\n      uFogScale: { value: fogScale },\n      uWSpeed: { value: wispSpeed },\n      uWIntensity: { value: wispIntensity },\n      uFlowStrength: { value: flowStrength },\n      uDecay: { value: decay },\n      uFalloffStart: { value: falloffStart },\n      uFogFallSpeed: { value: fogFallSpeed },\n      uColor: { value: new THREE.Vector3(1, 1, 1) },\n      uFade: { value: hasFadedRef.current ? 1 : 0 }\n    };\n    uniformsRef.current = uniforms;\n\n    const material = new THREE.RawShaderMaterial({\n      vertexShader: VERT,\n      fragmentShader: FRAG,\n      uniforms,\n      transparent: false,\n      depthTest: false,\n      depthWrite: false,\n      blending: THREE.NormalBlending\n    });\n\n    const mesh = new THREE.Mesh(geometry, material);\n    mesh.frustumCulled = false;\n    scene.add(mesh);\n\n    const clock = new THREE.Clock();\n    let prevTime = 0;\n    let fade = hasFadedRef.current ? 1 : 0;\n\n    const mouseTarget = new THREE.Vector2(0, 0);\n    const mouseSmooth = new THREE.Vector2(0, 0);\n\n    const setSizeNow = () => {\n      const w = mount.clientWidth || 1;\n      const h = mount.clientHeight || 1;\n      const pr = currentDprRef.current;\n\n      const last = lastSizeRef.current;\n      const sizeChanged = Math.abs(w - last.width) > 0.5 || Math.abs(h - last.height) > 0.5;\n      const dprChanged = Math.abs(pr - last.dpr) > 0.01;\n      if (!sizeChanged && !dprChanged) return;\n\n      lastSizeRef.current = { width: w, height: h, dpr: pr };\n      renderer.setPixelRatio(pr);\n      renderer.setSize(w, h, false);\n      uniforms.iResolution.value.set(w * pr, h * pr, pr);\n      rectRef.current = canvas.getBoundingClientRect();\n\n      if (!pausedRef.current) {\n        renderer.render(scene, camera);\n      }\n    };\n\n    let resizeRaf = 0;\n    const scheduleResize = () => {\n      if (resizeRaf) cancelAnimationFrame(resizeRaf);\n      resizeRaf = requestAnimationFrame(setSizeNow);\n    };\n\n    setSizeNow();\n    const ro = new ResizeObserver(scheduleResize);\n    ro.observe(mount);\n\n    const io = new IntersectionObserver(\n      entries => {\n        inViewRef.current = entries[0]?.isIntersecting ?? true;\n      },\n      { root: null, threshold: 0 }\n    );\n    io.observe(mount);\n\n    const onVis = () => {\n      pausedRef.current = document.hidden;\n    };\n    document.addEventListener('visibilitychange', onVis, { passive: true });\n\n    const updateMouse = (clientX: number, clientY: number) => {\n      const rect = rectRef.current;\n      if (!rect) return;\n      const x = clientX - rect.left;\n      const y = clientY - rect.top;\n      const ratio = currentDprRef.current;\n      const hb = rect.height * ratio;\n      mouseTarget.set(x * ratio, hb - y * ratio);\n    };\n    const onMove = (ev: PointerEvent | MouseEvent) => updateMouse(ev.clientX, ev.clientY);\n    const onLeave = () => mouseTarget.set(0, 0);\n    canvas.addEventListener('pointermove', onMove as any, { passive: true });\n    canvas.addEventListener('pointerdown', onMove as any, { passive: true });\n    canvas.addEventListener('pointerenter', onMove as any, { passive: true });\n    canvas.addEventListener('pointerleave', onLeave as any, { passive: true });\n\n    const onCtxLost = (e: Event) => {\n      e.preventDefault();\n      pausedRef.current = true;\n    };\n    const onCtxRestored = () => {\n      pausedRef.current = false;\n      scheduleResize();\n    };\n    canvas.addEventListener('webglcontextlost', onCtxLost, false);\n    canvas.addEventListener('webglcontextrestored', onCtxRestored, false);\n\n    let raf = 0;\n\n    const clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, v));\n    const dprFloor = 0.6;\n    const lowerThresh = 50;\n    const upperThresh = 58;\n    let lastDprChange = 0;\n    const dprChangeCooldown = 2000;\n\n    const adjustDprIfNeeded = (now: number) => {\n      const elapsed = now - lastFpsCheckRef.current;\n      if (elapsed < 750) return;\n\n      const samples = fpsSamplesRef.current;\n      if (samples.length === 0) {\n        lastFpsCheckRef.current = now;\n        return;\n      }\n      const avgFps = samples.reduce((a, b) => a + b, 0) / samples.length;\n\n      let next = currentDprRef.current;\n      const base = baseDprRef.current;\n\n      if (avgFps < lowerThresh) {\n        next = clamp(currentDprRef.current * 0.85, dprFloor, base);\n      } else if (avgFps > upperThresh && currentDprRef.current < base) {\n        next = clamp(currentDprRef.current * 1.1, dprFloor, base);\n      }\n\n      if (Math.abs(next - currentDprRef.current) > 0.01 && now - lastDprChange > dprChangeCooldown) {\n        currentDprRef.current = next;\n        lastDprChange = now;\n        setSizeNow();\n      }\n\n      fpsSamplesRef.current = [];\n      lastFpsCheckRef.current = now;\n    };\n\n    const animate = () => {\n      raf = requestAnimationFrame(animate);\n      if (pausedRef.current || !inViewRef.current) return;\n\n      const t = clock.getElapsedTime();\n      const dt = Math.max(0, t - prevTime);\n      prevTime = t;\n\n      const dtMs = dt * 1000;\n      emaDtRef.current = emaDtRef.current * 0.9 + dtMs * 0.1;\n      const instFps = 1000 / Math.max(1, emaDtRef.current);\n      fpsSamplesRef.current.push(instFps);\n\n      uniforms.iTime.value = t;\n\n      const cdt = Math.min(0.033, Math.max(0.001, dt));\n      (uniforms.uFlowTime.value as number) += cdt;\n      (uniforms.uFogTime.value as number) += cdt;\n\n      if (!hasFadedRef.current) {\n        const fadeDur = 1.0;\n        fade = Math.min(1, fade + cdt / fadeDur);\n        uniforms.uFade.value = fade;\n        if (fade >= 1) hasFadedRef.current = true;\n      }\n\n      const tau = Math.max(1e-3, mouseSmoothTimeRef.current);\n      const alpha = 1 - Math.exp(-cdt / tau);\n      mouseSmooth.lerp(mouseTarget, alpha);\n      uniforms.iMouse.value.set(mouseSmooth.x, mouseSmooth.y, 0, 0);\n\n      renderer.render(scene, camera);\n\n      adjustDprIfNeeded(performance.now());\n    };\n\n    animate();\n\n    return () => {\n      cancelAnimationFrame(raf);\n      if (resizeRaf) cancelAnimationFrame(resizeRaf);\n\n      ro.disconnect();\n      io.disconnect();\n      document.removeEventListener('visibilitychange', onVis);\n      canvas.removeEventListener('pointermove', onMove as any);\n      canvas.removeEventListener('pointerdown', onMove as any);\n      canvas.removeEventListener('pointerenter', onMove as any);\n      canvas.removeEventListener('pointerleave', onLeave as any);\n      canvas.removeEventListener('webglcontextlost', onCtxLost);\n      canvas.removeEventListener('webglcontextrestored', onCtxRestored);\n\n      scene.clear();\n      geometry.dispose();\n      material.dispose();\n      renderer.dispose();\n      renderer.forceContextLoss();\n      if (mount.contains(canvas)) mount.removeChild(canvas);\n    };\n  }, [dpr]);\n\n  useEffect(() => {\n    const uniforms = uniformsRef.current;\n    if (!uniforms) return;\n\n    uniforms.uWispDensity.value = wispDensity;\n    uniforms.uTiltScale.value = mouseTiltStrength;\n    uniforms.uBeamXFrac.value = horizontalBeamOffset;\n    uniforms.uBeamYFrac.value = verticalBeamOffset;\n    uniforms.uFlowSpeed.value = flowSpeed;\n    uniforms.uVLenFactor.value = verticalSizing;\n    uniforms.uHLenFactor.value = horizontalSizing;\n    uniforms.uFogIntensity.value = fogIntensity;\n    uniforms.uFogScale.value = fogScale;\n    uniforms.uWSpeed.value = wispSpeed;\n    uniforms.uWIntensity.value = wispIntensity;\n    uniforms.uFlowStrength.value = flowStrength;\n    uniforms.uDecay.value = decay;\n    uniforms.uFalloffStart.value = falloffStart;\n    uniforms.uFogFallSpeed.value = fogFallSpeed;\n\n    const { r, g, b } = hexToRGB(color || '#FFFFFF');\n    uniforms.uColor.value.set(r, g, b);\n    const canvas = rendererRef.current?.domElement;\n    if (canvas) {\n      const background = new THREE.Color(backgroundColor || '#000000');\n      const luma = background.r * 0.2126 + background.g * 0.7152 + background.b * 0.0722;\n      const lightBackground = luma > 0.72;\n      const mount = mountRef.current;\n      if (mount) mount.style.backgroundColor = backgroundColor || '#000000';\n      canvas.style.filter = lightBackground ? 'invert(1) hue-rotate(180deg)' : 'none';\n      canvas.style.mixBlendMode = lightBackground ? 'normal' : 'screen';\n    }\n  }, [\n    wispDensity,\n    mouseTiltStrength,\n    horizontalBeamOffset,\n    verticalBeamOffset,\n    flowSpeed,\n    verticalSizing,\n    horizontalSizing,\n    fogIntensity,\n    fogScale,\n    wispSpeed,\n    wispIntensity,\n    flowStrength,\n    decay,\n    falloffStart,\n    fogFallSpeed,\n    color,\n    backgroundColor\n  ]);\n\n  return <div ref={mountRef} className={`w-full h-full relative ${className || ''}`} style={style} />;\n};\n\nexport default LaserFlow;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"three@^0.180.0"
	]
}