{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "Orb-JS-TW",
	"title": "Orb",
	"description": "Floating energy orb with customizable hover effect.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "Orb/Orb.jsx",
			"content": "import { Mesh, Program, Renderer, Triangle, Vec3 } from 'ogl';\nimport { useEffect, useRef } from 'react';\n\nexport default function Orb({\n  hue = 0,\n  hoverIntensity = 0.2,\n  rotateOnHover = true,\n  forceHoverState = false,\n  backgroundColor = '#000000'\n}) {\n  const ctnDom = useRef(null);\n\n  const vert = /* glsl */ `\n    precision highp float;\n    attribute vec2 position;\n    attribute vec2 uv;\n    varying vec2 vUv;\n    void main() {\n      vUv = uv;\n      gl_Position = vec4(position, 0.0, 1.0);\n    }\n  `;\n\n  const frag = /* glsl */ `\n    precision highp float;\n\n    uniform float iTime;\n    uniform vec3 iResolution;\n    uniform float hue;\n    uniform float hover;\n    uniform float rot;\n    uniform float hoverIntensity;\n    uniform vec3 backgroundColor;\n    varying vec2 vUv;\n\n    vec3 rgb2yiq(vec3 c) {\n      float y = dot(c, vec3(0.299, 0.587, 0.114));\n      float i = dot(c, vec3(0.596, -0.274, -0.322));\n      float q = dot(c, vec3(0.211, -0.523, 0.312));\n      return vec3(y, i, q);\n    }\n    \n    vec3 yiq2rgb(vec3 c) {\n      float r = c.x + 0.956 * c.y + 0.621 * c.z;\n      float g = c.x - 0.272 * c.y - 0.647 * c.z;\n      float b = c.x - 1.106 * c.y + 1.703 * c.z;\n      return vec3(r, g, b);\n    }\n    \n    vec3 adjustHue(vec3 color, float hueDeg) {\n      float hueRad = hueDeg * 3.14159265 / 180.0;\n      vec3 yiq = rgb2yiq(color);\n      float cosA = cos(hueRad);\n      float sinA = sin(hueRad);\n      float i = yiq.y * cosA - yiq.z * sinA;\n      float q = yiq.y * sinA + yiq.z * cosA;\n      yiq.y = i;\n      yiq.z = q;\n      return yiq2rgb(yiq);\n    }\n\n    vec3 hash33(vec3 p3) {\n      p3 = fract(p3 * vec3(0.1031, 0.11369, 0.13787));\n      p3 += dot(p3, p3.yxz + 19.19);\n      return -1.0 + 2.0 * fract(vec3(\n        p3.x + p3.y,\n        p3.x + p3.z,\n        p3.y + p3.z\n      ) * p3.zyx);\n    }\n\n    float snoise3(vec3 p) {\n      const float K1 = 0.333333333;\n      const float K2 = 0.166666667;\n      vec3 i = floor(p + (p.x + p.y + p.z) * K1);\n      vec3 d0 = p - (i - (i.x + i.y + i.z) * K2);\n      vec3 e = step(vec3(0.0), d0 - d0.yzx);\n      vec3 i1 = e * (1.0 - e.zxy);\n      vec3 i2 = 1.0 - e.zxy * (1.0 - e);\n      vec3 d1 = d0 - (i1 - K2);\n      vec3 d2 = d0 - (i2 - K1);\n      vec3 d3 = d0 - 0.5;\n      vec4 h = max(0.6 - vec4(\n        dot(d0, d0),\n        dot(d1, d1),\n        dot(d2, d2),\n        dot(d3, d3)\n      ), 0.0);\n      vec4 n = h * h * h * h * vec4(\n        dot(d0, hash33(i)),\n        dot(d1, hash33(i + i1)),\n        dot(d2, hash33(i + i2)),\n        dot(d3, hash33(i + 1.0))\n      );\n      return dot(vec4(31.316), n);\n    }\n\n    vec4 extractAlpha(vec3 colorIn) {\n      float a = max(max(colorIn.r, colorIn.g), colorIn.b);\n      return vec4(colorIn.rgb / (a + 1e-5), a);\n    }\n\n    const vec3 baseColor1 = vec3(0.611765, 0.262745, 0.996078);\n    const vec3 baseColor2 = vec3(0.298039, 0.760784, 0.913725);\n    const vec3 baseColor3 = vec3(0.062745, 0.078431, 0.600000);\n    const float innerRadius = 0.6;\n    const float noiseScale = 0.65;\n\n    float light1(float intensity, float attenuation, float dist) {\n      return intensity / (1.0 + dist * attenuation);\n    }\n    float light2(float intensity, float attenuation, float dist) {\n      return intensity / (1.0 + dist * dist * attenuation);\n    }\n\n    vec4 draw(vec2 uv) {\n      vec3 color1 = adjustHue(baseColor1, hue);\n      vec3 color2 = adjustHue(baseColor2, hue);\n      vec3 color3 = adjustHue(baseColor3, hue);\n      \n      float ang = atan(uv.y, uv.x);\n      float len = length(uv);\n      float invLen = len > 0.0 ? 1.0 / len : 0.0;\n\n            float bgLuminance = dot(backgroundColor, vec3(0.299, 0.587, 0.114));\n      \n      float n0 = snoise3(vec3(uv * noiseScale, iTime * 0.5)) * 0.5 + 0.5;\n      float r0 = mix(mix(innerRadius, 1.0, 0.4), mix(innerRadius, 1.0, 0.6), n0);\n      float d0 = distance(uv, (r0 * invLen) * uv);\n      float v0 = light1(1.0, 10.0, d0);\n      v0 *= smoothstep(r0 * 1.05, r0, len);\n            float innerFade = smoothstep(r0 * 0.8, r0 * 0.95, len);\n      v0 *= mix(innerFade, 1.0, bgLuminance * 0.7);\n      float cl = cos(ang + iTime * 2.0) * 0.5 + 0.5;\n      \n      float a = iTime * -1.0;\n      vec2 pos = vec2(cos(a), sin(a)) * r0;\n      float d = distance(uv, pos);\n      float v1 = light2(1.5, 5.0, d);\n      v1 *= light1(1.0, 50.0, d0);\n      \n      float v2 = smoothstep(1.0, mix(innerRadius, 1.0, n0 * 0.5), len);\n      float v3 = smoothstep(innerRadius, mix(innerRadius, 1.0, 0.5), len);\n      \n      vec3 colBase = mix(color1, color2, cl);\n      float fadeAmount = mix(1.0, 0.1, bgLuminance);\n      \n      vec3 darkCol = mix(color3, colBase, v0);\n      darkCol = (darkCol + v1) * v2 * v3;\n      darkCol = clamp(darkCol, 0.0, 1.0);\n      \n      vec3 lightCol = (colBase + v1) * mix(1.0, v2 * v3, fadeAmount);\n      lightCol = mix(backgroundColor, lightCol, v0);\n      lightCol = clamp(lightCol, 0.0, 1.0);\n      \n      vec3 finalCol = mix(darkCol, lightCol, bgLuminance);\n      \n      return extractAlpha(finalCol);\n    }\n\n    vec4 mainImage(vec2 fragCoord) {\n      vec2 center = iResolution.xy * 0.5;\n      float size = min(iResolution.x, iResolution.y);\n      vec2 uv = (fragCoord - center) / size * 2.0;\n      \n      float angle = rot;\n      float s = sin(angle);\n      float c = cos(angle);\n      uv = vec2(c * uv.x - s * uv.y, s * uv.x + c * uv.y);\n      \n      uv.x += hover * hoverIntensity * 0.1 * sin(uv.y * 10.0 + iTime);\n      uv.y += hover * hoverIntensity * 0.1 * sin(uv.x * 10.0 + iTime);\n      \n      return draw(uv);\n    }\n\n    void main() {\n      vec2 fragCoord = vUv * iResolution.xy;\n      vec4 col = mainImage(fragCoord);\n      gl_FragColor = vec4(col.rgb * col.a, col.a);\n    }\n  `;\n\n  useEffect(() => {\n    const container = ctnDom.current;\n    if (!container) return;\n\n    const renderer = new Renderer({ alpha: true, premultipliedAlpha: false });\n    const gl = renderer.gl;\n    gl.clearColor(0, 0, 0, 0);\n    container.appendChild(gl.canvas);\n\n    const geometry = new Triangle(gl);\n    const program = new Program(gl, {\n      vertex: vert,\n      fragment: frag,\n      uniforms: {\n        iTime: { value: 0 },\n        iResolution: {\n          value: new Vec3(gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height)\n        },\n        hue: { value: hue },\n        hover: { value: 0 },\n        rot: { value: 0 },\n        hoverIntensity: { value: hoverIntensity },\n        backgroundColor: { value: hexToVec3(backgroundColor) }\n      }\n    });\n\n    const mesh = new Mesh(gl, { geometry, program });\n\n    function resize() {\n      if (!container) return;\n      const dpr = window.devicePixelRatio || 1;\n      const width = container.clientWidth;\n      const height = container.clientHeight;\n      renderer.setSize(width * dpr, height * dpr);\n      gl.canvas.style.width = width + 'px';\n      gl.canvas.style.height = height + 'px';\n      program.uniforms.iResolution.value.set(gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height);\n    }\n    window.addEventListener('resize', resize);\n    resize();\n\n    let targetHover = 0;\n    let lastTime = 0;\n    let currentRot = 0;\n    const rotationSpeed = 0.3;\n\n    const handleMouseMove = e => {\n      const rect = container.getBoundingClientRect();\n      const x = e.clientX - rect.left;\n      const y = e.clientY - rect.top;\n      const width = rect.width;\n      const height = rect.height;\n      const size = Math.min(width, height);\n      const centerX = width / 2;\n      const centerY = height / 2;\n      const uvX = ((x - centerX) / size) * 2.0;\n      const uvY = ((y - centerY) / size) * 2.0;\n\n      if (Math.sqrt(uvX * uvX + uvY * uvY) < 0.8) {\n        targetHover = 1;\n      } else {\n        targetHover = 0;\n      }\n    };\n\n    const handleMouseLeave = () => {\n      targetHover = 0;\n    };\n\n    container.addEventListener('mousemove', handleMouseMove);\n    container.addEventListener('mouseleave', handleMouseLeave);\n\n    let rafId;\n    const update = t => {\n      rafId = requestAnimationFrame(update);\n      const dt = (t - lastTime) * 0.001;\n      lastTime = t;\n      program.uniforms.iTime.value = t * 0.001;\n      program.uniforms.hue.value = hue;\n      program.uniforms.hoverIntensity.value = hoverIntensity;\n      program.uniforms.backgroundColor.value = hexToVec3(backgroundColor);\n\n      const effectiveHover = forceHoverState ? 1 : targetHover;\n      program.uniforms.hover.value += (effectiveHover - program.uniforms.hover.value) * 0.1;\n\n      if (rotateOnHover && effectiveHover > 0.5) {\n        currentRot += dt * rotationSpeed;\n      }\n      program.uniforms.rot.value = currentRot;\n\n      renderer.render({ scene: mesh });\n    };\n    rafId = requestAnimationFrame(update);\n\n    return () => {\n      cancelAnimationFrame(rafId);\n      window.removeEventListener('resize', resize);\n      container.removeEventListener('mousemove', handleMouseMove);\n      container.removeEventListener('mouseleave', handleMouseLeave);\n      container.removeChild(gl.canvas);\n      gl.getExtension('WEBGL_lose_context')?.loseContext();\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [hue, hoverIntensity, rotateOnHover, forceHoverState, backgroundColor]);\n\n  return <div ref={ctnDom} className=\"w-full h-full\" />;\n}\n\nfunction hslToRgb(h, s, l) {\n  let r, g, b;\n\n  if (s === 0) {\n    r = g = b = l;\n  } else {\n    const hue2rgb = (p, q, t) => {\n      if (t < 0) t += 1;\n      if (t > 1) t -= 1;\n      if (t < 1 / 6) return p + (q - p) * 6 * t;\n      if (t < 1 / 2) return q;\n      if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\n      return p;\n    };\n\n    const q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n    const p = 2 * l - q;\n    r = hue2rgb(p, q, h + 1 / 3);\n    g = hue2rgb(p, q, h);\n    b = hue2rgb(p, q, h - 1 / 3);\n  }\n\n  return new Vec3(r, g, b);\n}\n\nfunction hexToVec3(color) {\n  if (color.startsWith('#')) {\n    const r = parseInt(color.slice(1, 3), 16) / 255;\n    const g = parseInt(color.slice(3, 5), 16) / 255;\n    const b = parseInt(color.slice(5, 7), 16) / 255;\n    return new Vec3(r, g, b);\n  }\n\n  const rgbMatch = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/);\n  if (rgbMatch) {\n    return new Vec3(parseInt(rgbMatch[1]) / 255, parseInt(rgbMatch[2]) / 255, parseInt(rgbMatch[3]) / 255);\n  }\n\n  const hslMatch = color.match(/hsla?\\((\\d+),\\s*(\\d+)%,\\s*(\\d+)%/);\n  if (hslMatch) {\n    const h = parseInt(hslMatch[1]) / 360;\n    const s = parseInt(hslMatch[2]) / 100;\n    const l = parseInt(hslMatch[3]) / 100;\n    return hslToRgb(h, s, l);\n  }\n\n  return new Vec3(0, 0, 0);\n}\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"ogl@^1.0.11"
	]
}