{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "Galaxy-TS-CSS",
	"title": "Galaxy",
	"description": "Parallax realistic starfield with pointer interactions.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:file",
			"path": "Galaxy.css",
			"target": "@components/Galaxy.css",
			"content": ".galaxy-container {\n  width: 100%;\n  height: 100%;\n  position: relative;\n}\n"
		},
		{
			"type": "registry:component",
			"path": "Galaxy.tsx",
			"content": "import { Renderer, Program, Mesh, Color, Triangle } from 'ogl';\nimport { useEffect, useRef } from 'react';\nimport './Galaxy.css';\n\nconst vertexShader = `\nattribute vec2 uv;\nattribute vec2 position;\n\nvarying vec2 vUv;\n\nvoid main() {\n  vUv = uv;\n  gl_Position = vec4(position, 0, 1);\n}\n`;\n\nconst fragmentShader = `\nprecision highp float;\n\nuniform float uTime;\nuniform vec3 uResolution;\nuniform vec2 uFocal;\nuniform vec2 uRotation;\nuniform float uStarSpeed;\nuniform float uDensity;\nuniform float uHueShift;\nuniform float uSpeed;\nuniform vec2 uMouse;\nuniform float uGlowIntensity;\nuniform float uSaturation;\nuniform bool uMouseRepulsion;\nuniform float uTwinkleIntensity;\nuniform float uRotationSpeed;\nuniform float uRepulsionStrength;\nuniform float uMouseActiveFactor;\nuniform float uAutoCenterRepulsion;\nuniform bool uTransparent;\nuniform float uLightMode;\n\nvarying vec2 vUv;\n\n#define NUM_LAYER 4.0\n#define STAR_COLOR_CUTOFF 0.2\n#define MAT45 mat2(0.7071, -0.7071, 0.7071, 0.7071)\n#define PERIOD 3.0\n\nfloat Hash21(vec2 p) {\n  p = fract(p * vec2(123.34, 456.21));\n  p += dot(p, p + 45.32);\n  return fract(p.x * p.y);\n}\n\nfloat tri(float x) {\n  return abs(fract(x) * 2.0 - 1.0);\n}\n\nfloat tris(float x) {\n  float t = fract(x);\n  return 1.0 - smoothstep(0.0, 1.0, abs(2.0 * t - 1.0));\n}\n\nfloat trisn(float x) {\n  float t = fract(x);\n  return 2.0 * (1.0 - smoothstep(0.0, 1.0, abs(2.0 * t - 1.0))) - 1.0;\n}\n\nvec3 hsv2rgb(vec3 c) {\n  vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n  vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n  return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n}\n\nfloat Star(vec2 uv, float flare) {\n  float d = length(uv);\n  float m = (0.05 * uGlowIntensity) / d;\n  float rays = smoothstep(0.0, 1.0, 1.0 - abs(uv.x * uv.y * 1000.0));\n  m += rays * flare * uGlowIntensity;\n  uv *= MAT45;\n  rays = smoothstep(0.0, 1.0, 1.0 - abs(uv.x * uv.y * 1000.0));\n  m += rays * 0.3 * flare * uGlowIntensity;\n  m *= smoothstep(1.0, 0.2, d);\n  return m;\n}\n\nvec3 StarLayer(vec2 uv) {\n  vec3 col = vec3(0.0);\n\n  vec2 gv = fract(uv) - 0.5; \n  vec2 id = floor(uv);\n\n  for (int y = -1; y <= 1; y++) {\n    for (int x = -1; x <= 1; x++) {\n      vec2 offset = vec2(float(x), float(y));\n      vec2 si = id + vec2(float(x), float(y));\n      float seed = Hash21(si);\n      float size = fract(seed * 345.32);\n      float glossLocal = tri(uStarSpeed / (PERIOD * seed + 1.0));\n      float flareSize = smoothstep(0.9, 1.0, size) * glossLocal;\n\n      float red = smoothstep(STAR_COLOR_CUTOFF, 1.0, Hash21(si + 1.0)) + STAR_COLOR_CUTOFF;\n      float blu = smoothstep(STAR_COLOR_CUTOFF, 1.0, Hash21(si + 3.0)) + STAR_COLOR_CUTOFF;\n      float grn = min(red, blu) * seed;\n      vec3 base = vec3(red, grn, blu);\n      \n      float hue = atan(base.g - base.r, base.b - base.r) / (2.0 * 3.14159) + 0.5;\n      hue = fract(hue + uHueShift / 360.0);\n      float sat = length(base - vec3(dot(base, vec3(0.299, 0.587, 0.114)))) * uSaturation;\n      float val = max(max(base.r, base.g), base.b);\n      base = hsv2rgb(vec3(hue, sat, val));\n\n      vec2 pad = vec2(tris(seed * 34.0 + uTime * uSpeed / 10.0), tris(seed * 38.0 + uTime * uSpeed / 30.0)) - 0.5;\n\n      float star = Star(gv - offset - pad, flareSize);\n      vec3 color = base;\n\n      float twinkle = trisn(uTime * uSpeed + seed * 6.2831) * 0.5 + 1.0;\n      twinkle = mix(1.0, twinkle, uTwinkleIntensity);\n      star *= twinkle;\n      \n      col += star * size * color;\n    }\n  }\n\n  return col;\n}\n\nvoid main() {\n  vec2 focalPx = uFocal * uResolution.xy;\n  vec2 uv = (vUv * uResolution.xy - focalPx) / uResolution.y;\n\n  vec2 mouseNorm = uMouse - vec2(0.5);\n  \n  if (uAutoCenterRepulsion > 0.0) {\n    vec2 centerUV = vec2(0.0, 0.0);\n    float centerDist = length(uv - centerUV);\n    vec2 repulsion = normalize(uv - centerUV) * (uAutoCenterRepulsion / (centerDist + 0.1));\n    uv += repulsion * 0.05;\n  } else if (uMouseRepulsion) {\n    vec2 mousePosUV = (uMouse * uResolution.xy - focalPx) / uResolution.y;\n    float mouseDist = length(uv - mousePosUV);\n    vec2 repulsion = normalize(uv - mousePosUV) * (uRepulsionStrength / (mouseDist + 0.1));\n    uv += repulsion * 0.05 * uMouseActiveFactor;\n  } else {\n    vec2 mouseOffset = mouseNorm * 0.1 * uMouseActiveFactor;\n    uv += mouseOffset;\n  }\n\n  float autoRotAngle = uTime * uRotationSpeed;\n  mat2 autoRot = mat2(cos(autoRotAngle), -sin(autoRotAngle), sin(autoRotAngle), cos(autoRotAngle));\n  uv = autoRot * uv;\n\n  uv = mat2(uRotation.x, -uRotation.y, uRotation.y, uRotation.x) * uv;\n\n  vec3 col = vec3(0.0);\n\n  for (float i = 0.0; i < 1.0; i += 1.0 / NUM_LAYER) {\n    float depth = fract(i + uStarSpeed * uSpeed);\n    float scale = mix(20.0 * uDensity, 0.5 * uDensity, depth);\n    float fade = depth * smoothstep(1.0, 0.9, depth);\n    col += StarLayer(uv * scale + i * 453.32) * fade;\n  }\n\n  if (uLightMode > 0.5) {\n    float energy = max(max(col.r, col.g), col.b);\n    float coverage = clamp(smoothstep(0.0, 0.42, energy) * 0.92, 0.0, 0.92);\n    vec3 ink = clamp(col * 0.48, 0.0, 0.82);\n    gl_FragColor = vec4(mix(vec3(1.0), ink, coverage), 1.0);\n  } else if (uTransparent) {\n    float alpha = length(col);\n    alpha = smoothstep(0.0, 0.3, alpha);\n    alpha = min(alpha, 1.0);\n    gl_FragColor = vec4(col, alpha);\n  } else {\n    gl_FragColor = vec4(col, 1.0);\n  }\n}\n`;\n\ninterface GalaxyProps {\n  focal?: [number, number];\n  rotation?: [number, number];\n  starSpeed?: number;\n  density?: number;\n  hueShift?: number;\n  disableAnimation?: boolean;\n  speed?: number;\n  mouseInteraction?: boolean;\n  glowIntensity?: number;\n  saturation?: number;\n  mouseRepulsion?: boolean;\n  twinkleIntensity?: number;\n  rotationSpeed?: number;\n  repulsionStrength?: number;\n  autoCenterRepulsion?: number;\n  transparent?: boolean;\n  lightMode?: boolean;\n}\n\nexport default function Galaxy({\n  focal = [0.5, 0.5],\n  rotation = [1.0, 0.0],\n  starSpeed = 0.5,\n  density = 1,\n  hueShift = 140,\n  disableAnimation = false,\n  speed = 1.0,\n  mouseInteraction = true,\n  glowIntensity = 0.3,\n  saturation = 0.0,\n  mouseRepulsion = true,\n  repulsionStrength = 2,\n  twinkleIntensity = 0.3,\n  rotationSpeed = 0.1,\n  autoCenterRepulsion = 0,\n  transparent = true,\n  lightMode = false,\n  ...rest\n}: GalaxyProps) {\n  const ctnDom = useRef<HTMLDivElement>(null);\n  const targetMousePos = useRef({ x: 0.5, y: 0.5 });\n  const smoothMousePos = useRef({ x: 0.5, y: 0.5 });\n  const targetMouseActive = useRef(0.0);\n  const smoothMouseActive = useRef(0.0);\n\n  useEffect(() => {\n    if (!ctnDom.current) return;\n    const ctn = ctnDom.current;\n    const renderer = new Renderer({\n      alpha: transparent,\n      premultipliedAlpha: false\n    });\n    const gl = renderer.gl;\n\n    if (lightMode) {\n      gl.clearColor(1, 1, 1, 1);\n    } else if (transparent) {\n      gl.enable(gl.BLEND);\n      gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\n      gl.clearColor(0, 0, 0, 0);\n    } else {\n      gl.clearColor(0, 0, 0, 1);\n    }\n\n    let program: Program;\n\n    function resize() {\n      const scale = 1;\n      renderer.setSize(ctn.offsetWidth * scale, ctn.offsetHeight * scale);\n      if (program) {\n        program.uniforms.uResolution.value = new Color(\n          gl.canvas.width,\n          gl.canvas.height,\n          gl.canvas.width / gl.canvas.height\n        );\n      }\n    }\n    window.addEventListener('resize', resize, false);\n    resize();\n\n    const geometry = new Triangle(gl);\n    program = new Program(gl, {\n      vertex: vertexShader,\n      fragment: fragmentShader,\n      uniforms: {\n        uTime: { value: 0 },\n        uResolution: {\n          value: new Color(gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height)\n        },\n        uFocal: { value: new Float32Array(focal) },\n        uRotation: { value: new Float32Array(rotation) },\n        uStarSpeed: { value: starSpeed },\n        uDensity: { value: density },\n        uHueShift: { value: hueShift },\n        uSpeed: { value: speed },\n        uMouse: {\n          value: new Float32Array([smoothMousePos.current.x, smoothMousePos.current.y])\n        },\n        uGlowIntensity: { value: glowIntensity },\n        uSaturation: { value: saturation },\n        uMouseRepulsion: { value: mouseRepulsion },\n        uTwinkleIntensity: { value: twinkleIntensity },\n        uRotationSpeed: { value: rotationSpeed },\n        uRepulsionStrength: { value: repulsionStrength },\n        uMouseActiveFactor: { value: 0.0 },\n        uAutoCenterRepulsion: { value: autoCenterRepulsion },\n        uTransparent: { value: transparent },\n        uLightMode: { value: lightMode ? 1 : 0 }\n      }\n    });\n\n    const mesh = new Mesh(gl, { geometry, program });\n    let animateId: number;\n\n    function update(t: number) {\n      animateId = requestAnimationFrame(update);\n      if (!disableAnimation) {\n        program.uniforms.uTime.value = t * 0.001;\n        program.uniforms.uStarSpeed.value = (t * 0.001 * starSpeed) / 10.0;\n      }\n\n      const lerpFactor = 0.05;\n      smoothMousePos.current.x += (targetMousePos.current.x - smoothMousePos.current.x) * lerpFactor;\n      smoothMousePos.current.y += (targetMousePos.current.y - smoothMousePos.current.y) * lerpFactor;\n\n      smoothMouseActive.current += (targetMouseActive.current - smoothMouseActive.current) * lerpFactor;\n\n      program.uniforms.uMouse.value[0] = smoothMousePos.current.x;\n      program.uniforms.uMouse.value[1] = smoothMousePos.current.y;\n      program.uniforms.uMouseActiveFactor.value = smoothMouseActive.current;\n\n      renderer.render({ scene: mesh });\n    }\n    animateId = requestAnimationFrame(update);\n    ctn.appendChild(gl.canvas);\n\n    function handleMouseMove(e: MouseEvent) {\n      const rect = ctn.getBoundingClientRect();\n      const x = (e.clientX - rect.left) / rect.width;\n      const y = 1.0 - (e.clientY - rect.top) / rect.height;\n      targetMousePos.current = { x, y };\n      targetMouseActive.current = 1.0;\n    }\n\n    function handleMouseLeave() {\n      targetMouseActive.current = 0.0;\n    }\n\n    if (mouseInteraction) {\n      ctn.addEventListener('mousemove', handleMouseMove);\n      ctn.addEventListener('mouseleave', handleMouseLeave);\n    }\n\n    return () => {\n      cancelAnimationFrame(animateId);\n      window.removeEventListener('resize', resize);\n      if (mouseInteraction) {\n        ctn.removeEventListener('mousemove', handleMouseMove);\n        ctn.removeEventListener('mouseleave', handleMouseLeave);\n      }\n      ctn.removeChild(gl.canvas);\n      gl.getExtension('WEBGL_lose_context')?.loseContext();\n    };\n  }, [\n    focal,\n    rotation,\n    starSpeed,\n    density,\n    hueShift,\n    disableAnimation,\n    speed,\n    mouseInteraction,\n    glowIntensity,\n    saturation,\n    mouseRepulsion,\n    twinkleIntensity,\n    rotationSpeed,\n    repulsionStrength,\n    autoCenterRepulsion,\n    transparent,\n    lightMode\n  ]);\n\n  return <div ref={ctnDom} className=\"galaxy-container\" {...rest} />;\n}\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"ogl@^1.0.11"
	]
}