{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "FaultyTerminal-TS-TW",
	"title": "FaultyTerminal",
	"description": "A flickering CRT terminal of glowing glyphs, with phosphor trails under the cursor and glitches on click.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "FaultyTerminal/FaultyTerminal.tsx",
			"content": "'use client';\n\nimport { useEffect, useRef, type CSSProperties, type HTMLAttributes } from 'react';\n\ntype Vec2 = [number, number];\n\nexport type FaultyTerminalGlyphs = 'blocks' | 'dots' | 'characters';\nexport type FaultyTerminalClickEffect = 'glitch' | 'degauss' | 'none';\n\nexport interface FaultyTerminalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {\n  scale?: number;\n  gridMul?: Vec2;\n  digitSize?: number;\n  timeScale?: number;\n  pause?: boolean;\n  scanlineIntensity?: number;\n  glitchAmount?: number;\n  flickerAmount?: number;\n  noiseAmp?: number;\n  chromaticAberration?: number;\n  dither?: number | boolean;\n  curvature?: number;\n  tint?: string;\n  mouseReact?: boolean;\n  mouseStrength?: number;\n  pageLoadAnimation?: boolean;\n  brightness?: number;\n  glyphs?: FaultyTerminalGlyphs;\n  glow?: number;\n  persistence?: number;\n  scramble?: number;\n  mouseGlow?: number;\n  vignette?: number;\n  clickEffect?: FaultyTerminalClickEffect;\n  variation?: number;\n  lightMode?: boolean;\n  dpr?: number;\n  className?: string;\n  style?: CSSProperties;\n}\n\ntype Settings = {\n  scale: number;\n  gridX: number;\n  gridY: number;\n  digitSize: number;\n  timeScale: number;\n  pause: boolean;\n  scanlineIntensity: number;\n  glitchAmount: number;\n  flickerAmount: number;\n  noiseAmp: number;\n  chromaticAberration: number;\n  dither: number;\n  curvature: number;\n  tint: string;\n  mouseReact: boolean;\n  mouseStrength: number;\n  pageLoadAnimation: boolean;\n  brightness: number;\n  glyphs: number;\n  glow: number;\n  persistence: number;\n  scramble: number;\n  mouseGlow: number;\n  vignette: number;\n  clickEffect: number;\n  clickSeconds: number;\n  variation?: number;\n  lightMode: boolean;\n  dpr?: number;\n};\n\ntype Program = { program: WebGLProgram; uniforms: Record<string, WebGLUniformLocation | null> };\n\nconst INTRO_SECONDS = 2;\nconst MAX_CLICKS = 4;\nconst CLICK_SECONDS: Record<string, number> = { glitch: 0.7, degauss: 1.6 };\nconst GLYPHS: Record<string, number> = { blocks: 0, dots: 1, characters: 2 };\nconst CLICKS: Record<string, number> = { none: 0, glitch: 1, degauss: 2 };\n\nconst FONT = [\n  ['.###.', '#..##', '#.#.#', '##..#', '.###.'],\n  ['..#..', '.##..', '..#..', '..#..', '.###.'],\n  ['####.', '....#', '.###.', '#....', '#####'],\n  ['####.', '....#', '.###.', '....#', '####.'],\n  ['#..#.', '#..#.', '#####', '...#.', '...#.'],\n  ['#####', '#....', '####.', '....#', '####.'],\n  ['.###.', '#....', '####.', '#...#', '.###.'],\n  ['#####', '....#', '...#.', '..#..', '..#..'],\n  ['.###.', '#...#', '.###.', '#...#', '.###.'],\n  ['.###.', '#...#', '.####', '....#', '.###.'],\n  ['.###.', '#...#', '#####', '#...#', '#...#'],\n  ['####.', '#...#', '####.', '#...#', '####.'],\n  ['.####', '#....', '#....', '#....', '.####'],\n  ['####.', '#...#', '#...#', '#...#', '####.'],\n  ['#####', '#....', '####.', '#....', '#####'],\n  ['#####', '#....', '####.', '#....', '#....'],\n  ['.....', '..#..', '.###.', '..#..', '.....'],\n  ['.....', '.....', '.###.', '.....', '.....'],\n  ['.#...', '..#..', '...#.', '..#..', '.#...'],\n  ['....#', '...#.', '..#..', '.#...', '#....'],\n  ['.....', '#.#.#', '.###.', '#.#.#', '.....'],\n  ['.....', '#####', '.....', '#####', '.....'],\n  ['.....', '.....', '.....', '.....', '#####']\n];\n\nconst FONT_BITS = FONT.map(rows =>\n  rows.reduce((bits, row, r) => {\n    let value = bits;\n    for (let c = 0; c < 5; c++) if (row[c] === '#') value += 2 ** (r * 5 + c);\n    return value;\n  }, 0)\n);\n\nconst VERTEX = `#version 300 es\nvoid main() {\n  vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));\n  gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);\n}`;\n\nconst PATTERN = `\nfloat time;\n\nfloat noise(vec2 p) {\n  return sin(p.x * 10.0) * sin(p.y * (3.0 + sin(time * 0.090909))) + 0.2;\n}\n\nmat2 rotate(float angle) {\n  float c = cos(angle);\n  float s = sin(angle);\n  return mat2(c, -s, s, c);\n}\n\nfloat fbm(vec2 p) {\n  p *= 1.1;\n  float f = 0.0;\n  float amp = 0.5 * uNoiseAmp;\n  mat2 modify0 = rotate(time * 0.02);\n  f += amp * noise(p);\n  p = modify0 * p * 2.0;\n  amp *= 0.454545;\n  mat2 modify1 = rotate(time * 0.02);\n  f += amp * noise(p);\n  p = modify1 * p * 2.0;\n  amp *= 0.454545;\n  f += amp * noise(p);\n  return f;\n}\n\nfloat pattern(vec2 p) {\n  vec2 offset1 = vec2(1.0);\n  vec2 offset0 = vec2(0.0);\n  mat2 rot01 = rotate(0.1 * time);\n  mat2 rot1 = rotate(0.1);\n  vec2 q = vec2(fbm(p + offset1), fbm(rot01 * p + offset1));\n  vec2 r = vec2(fbm(rot1 * q + offset0), fbm(q + offset0));\n  return fbm(p + r);\n}\n`;\n\nconst CELL_FRAGMENT = `#version 300 es\nprecision highp float;\nprecision highp int;\n\nuniform float iTime;\nuniform vec2 uGridMul;\nuniform float uNoiseAmp;\nuniform float uScale;\nuniform vec2 uMouse;\nuniform float uMouseStrength;\nuniform float uUseMouse;\nuniform float uPageLoadProgress;\nuniform float uUsePageLoadAnimation;\nuniform ivec2 uOrigin;\nuniform sampler2D uPrev0;\nuniform sampler2D uPrev1;\nuniform float uDecay;\nuniform float uFresh;\nuniform float uHover;\n\nlayout(location = 0) out vec4 outRings;\nlayout(location = 1) out vec4 outExtra;\n${PATTERN}\nvoid main() {\n  time = iTime * 0.333333;\n  ivec2 texel = ivec2(gl_FragCoord.xy);\n  vec2 grid = uGridMul * 15.0;\n  vec2 s = vec2(texel + uOrigin) / grid;\n  float intensity = pattern(s * 0.1) * 1.3 - 0.03;\n  float lift = 0.0;\n  float strike = 0.0;\n\n  if (uUseMouse > 0.5) {\n    vec2 mouseWorld = uMouse * uScale;\n    float distToMouse = distance(s, mouseWorld);\n    float mouseInfluence = exp(-distToMouse * 8.0) * uMouseStrength * 10.0;\n    intensity += mouseInfluence;\n    float ripple = sin(distToMouse * 20.0 - iTime * 5.0) * 0.1 * mouseInfluence;\n    intensity += ripple;\n    lift = mouseInfluence;\n  }\n\n  if (uUsePageLoadAnimation > 0.5) {\n    float cellRandom = fract(sin(dot(s, vec2(12.9898, 78.233))) * 43758.5453);\n    float cellDelay = cellRandom * 0.8;\n    float cellProgress = clamp((uPageLoadProgress - cellDelay) / 0.2, 0.0, 1.0);\n    float fadeAlpha = smoothstep(0.0, 1.0, cellProgress);\n    intensity *= fadeAlpha;\n    strike = sin(3.14159265 * cellProgress) * step(0.1, intensity);\n  }\n\n  vec4 on0 = step(vec4(0.1), intensity - vec4(0.0, 1.0, 2.0, 4.0) * 0.0625);\n  vec2 on1 = step(vec2(0.1), intensity - vec2(5.0, 8.0) * 0.0625);\n  float level = clamp((intensity - 0.1) / 0.6, 0.0, 1.0);\n  float heat = max(clamp(lift / 3.0, 0.0, 1.0) * uHover, strike);\n\n  vec4 prev0 = texelFetch(uPrev0, texel, 0) * (1.0 - uFresh);\n  vec4 prev1 = texelFetch(uPrev1, texel, 0) * (1.0 - uFresh);\n  vec4 fade0 = max(prev0 * uDecay - 1.0 / 255.0, 0.0);\n  vec4 fade1 = max(prev1 * uDecay - 1.0 / 255.0, 0.0);\n\n  outRings = max(on0, fade0);\n  outExtra = vec4(max(on1, fade1.xy), max(level, fade1.z), max(heat, fade1.w));\n}`;\n\nconst fontArray = `const uint FONT[${FONT_BITS.length}] = uint[${FONT_BITS.length}](${FONT_BITS.map(\n  bits => `${bits}u`\n).join(', ')});`;\n\nconst MAIN_FRAGMENT = `#version 300 es\nprecision highp float;\nprecision highp int;\n\nuniform float iTime;\nuniform vec3 iResolution;\nuniform float uScale;\nuniform vec2 uGridMul;\nuniform float uDigitSize;\nuniform float uScanlineIntensity;\nuniform float uGlitchAmount;\nuniform float uFlickerAmount;\nuniform float uChromaticAberration;\nuniform float uDither;\nuniform float uCurvature;\nuniform vec3 uTint;\nuniform float uBrightness;\nuniform float uLightMode;\nuniform sampler2D uRings0;\nuniform sampler2D uRings1;\nuniform ivec2 uOrigin;\nuniform ivec2 uCellCount;\nuniform int uGlyphs;\nuniform float uGlow;\nuniform float uScramble;\nuniform float uMouseGlow;\nuniform float uVignette;\nuniform vec4 uClicks[${MAX_CLICKS}];\nuniform int uClickMode;\nuniform float uClickSeconds;\nuniform float uClock;\n\nout vec4 outColor;\n\n${fontArray}\n\nfloat time;\nfloat clickTear;\nfloat clickSplit;\nfloat pixelHeat;\n\nfloat hash21(vec2 p) {\n  p = fract(p * 234.56);\n  p += dot(p, p + 34.56);\n  return fract(p.x * p.y);\n}\n\nfloat hashCell(vec2 p) {\n  vec3 q = fract(vec3(p.xyx) * 0.1031);\n  q += dot(q, q.yzx + 33.33);\n  return fract((q.x + q.y) * q.z);\n}\n\nivec2 cellTexel(vec2 cell) {\n  return clamp(ivec2(cell) - uOrigin, ivec2(0), uCellCount - 1);\n}\n\nfloat ringValue(float n, vec4 r0, vec4 r1) {\n  if (n < 0.5) return r0.x;\n  if (n < 1.5) return r0.y;\n  if (n < 2.5) return r0.z;\n  if (n < 4.5) return r0.w;\n  if (n < 5.5) return r1.x;\n  return r1.y;\n}\n\nfloat digit(vec2 p) {\n  vec2 grid = uGridMul * 15.0;\n  vec2 cell = floor(p * grid);\n  ivec2 texel = cellTexel(cell);\n  vec4 r0 = texelFetch(uRings0, texel, 0);\n  vec4 r1 = texelFetch(uRings1, texel, 0);\n  float boost = 1.0 + r1.w * uMouseGlow * 1.3;\n  pixelHeat = max(pixelHeat, r1.w);\n  p = fract(p * grid);\n  p *= uDigitSize;\n\n  float px5 = p.x * 5.0;\n  float py5 = (1.0 - p.y) * 5.0;\n  float x = fract(px5);\n  float y = fract(py5);\n  float i = floor(py5) - 2.0;\n  float j = floor(px5) - 2.0;\n  float inside = step(0.0, p.x) * step(p.x, 1.0) * step(0.0, p.y) * step(p.y, 1.0);\n\n  if (uGlyphs == 2) {\n    float rate = 1.2 + r1.w * uScramble * 18.0;\n    float phase = hashCell(cell * 1.37 + 4.1);\n    float epoch = floor(iTime * rate + phase * 7.0);\n    float pick = hashCell(cell + vec2(epoch * 0.731, epoch * 1.913));\n    uint glyph = FONT[int(min(pick * float(FONT.length()), float(FONT.length()) - 1.0))];\n    int bit = int(i + 2.0) * 5 + int(j + 2.0);\n    float lit = float((glyph >> uint(clamp(bit, 0, 24))) & 1u);\n    float glow = max(r0.x, r1.z);\n    float shade = 0.35 + 0.65 * r1.z;\n    return inside * lit * glow * shade * boost * (0.2 + y * 0.8) * (0.75 + x * 0.25);\n  }\n\n  float n = i * i + j * j;\n  float on = ringValue(n, r0, r1);\n  if (uGlyphs == 1) {\n    vec2 d = vec2(x, y) - 0.5;\n    float radius = 0.36 + 0.06 * on;\n    float edge = fwidth(px5) * 0.75;\n    float dot = 1.0 - smoothstep(radius - edge, radius + edge, length(d));\n    return inside * on * dot * boost * (0.55 + 0.45 * (1.0 - length(d) / 0.5));\n  }\n  float brightness = on * (0.2 + y * 0.8) * (0.75 + x * 0.25);\n  return inside * brightness * boost;\n}\n\nfloat onOff(float a, float b, float c) {\n  return step(c, sin(iTime + a * cos(iTime * b))) * uFlickerAmount;\n}\n\nfloat displace(vec2 look) {\n  float y = look.y - mod(iTime * 0.25, 1.0);\n  float window = 1.0 / (1.0 + 50.0 * y * y);\n  return sin(look.y * 20.0 + iTime) * 0.0125 * onOff(4.0, 2.0, 0.8) * (1.0 + cos(iTime * 60.0)) * window;\n}\n\nvec2 getLight(vec2 p) {\n  float bar = step(mod(p.y + time * 20.0, 1.0), 0.2) * 0.4 + 1.0;\n  bar *= uScanlineIntensity;\n\n  float displacement = displace(p);\n  p.x += displacement;\n\n  if (uGlitchAmount != 1.0) {\n    float extra = displacement * (uGlitchAmount - 1.0);\n    p.x += extra;\n  }\n\n  p.x += clickTear;\n\n  float middle = digit(p);\n\n  const float off = 0.002;\n  float sum = digit(p + vec2(-off, -off)) + digit(p + vec2(0.0, -off)) + digit(p + vec2(off, -off)) +\n              digit(p + vec2(-off, 0.0)) + digit(p + vec2(0.0, 0.0)) + digit(p + vec2(off, 0.0)) +\n              digit(p + vec2(-off, off)) + digit(p + vec2(0.0, off)) + digit(p + vec2(off, off));\n\n  return vec2(0.9 * middle, sum * 0.1 * bar);\n}\n\nfloat glowAt(vec2 p) {\n  vec2 grid = uGridMul * 15.0;\n  vec2 cell = floor(p * grid);\n  vec2 cellPx = iResolution.xy / (uScale * grid);\n  float sigma = 0.5 * min(cellPx.x, cellPx.y);\n  float center = 0.5 * min(1.0, 1.0 / max(uDigitSize, 0.001));\n  float total = 0.0;\n  for (int oy = -1; oy <= 1; oy++) {\n    for (int ox = -1; ox <= 1; ox++) {\n      vec2 c = cell + vec2(float(ox), float(oy));\n      ivec2 texel = cellTexel(c);\n      vec4 r0 = texelFetch(uRings0, texel, 0);\n      vec4 r1 = texelFetch(uRings1, texel, 0);\n      float emission = uGlyphs == 2\n        ? max(r0.x, r1.z) * (0.35 + 0.65 * r1.z) * 0.45\n        : (r0.x + 4.0 * (r0.y + r0.z + r0.w + r1.y) + 8.0 * r1.x) / 25.0;\n      emission *= 1.0 + r1.w * uMouseGlow * 1.6;\n      vec2 d = (p - (c + center) / grid) * iResolution.xy / uScale;\n      total += emission * exp(-dot(d, d) / (2.0 * sigma * sigma));\n    }\n  }\n  return total;\n}\n\nvec2 barrel(vec2 uv) {\n  vec2 c = uv * 2.0 - 1.0;\n  float r2 = dot(c, c);\n  c *= 1.0 + uCurvature * r2;\n  return c * 0.5 + 0.5;\n}\n\nvoid main() {\n  time = iTime * 0.333333;\n  vec2 uv = gl_FragCoord.xy / iResolution.xy;\n\n  if (uCurvature != 0.0) {\n    uv = barrel(uv);\n  }\n\n  clickTear = 0.0;\n  clickSplit = 0.0;\n  if (uClickMode > 0) {\n    for (int k = 0; k < ${MAX_CLICKS}; k++) {\n      vec4 click = uClicks[k];\n      if (click.w <= 0.0) continue;\n      float age = uClock - click.z;\n      if (age < 0.0 || age > uClickSeconds) continue;\n      float life = age / uClickSeconds;\n      float envelope = (1.0 - life) * (1.0 - life);\n      if (uClickMode == 1) {\n        float tick = floor(age * 30.0);\n        float bands = 14.0 + 26.0 * hashCell(vec2(click.z, tick * 0.37));\n        float row = floor(uv.y * bands);\n        float pickRow = hashCell(vec2(row + tick * 7.0, click.z * 17.0));\n        float torn = step(0.42, pickRow);\n        float reach = exp(-abs(uv.y - click.y) * 2.5);\n        float shift = (hashCell(vec2(row * 1.7 + tick, click.z)) - 0.5) * 0.22;\n        clickTear += shift * torn * envelope * reach;\n        clickSplit = max(clickSplit, envelope * reach * (0.4 + 0.6 * torn));\n      } else {\n        vec2 aspect = vec2(iResolution.x / iResolution.y, 1.0);\n        vec2 delta = (uv - click.xy) * aspect;\n        float dist = length(delta);\n        float front = age * 1.6;\n        float band = exp(-pow((dist - front) / 0.18, 2.0));\n        float wave = sin((dist - front) * 40.0) * band;\n        uv += delta / aspect * wave * 0.05 * envelope;\n        clickSplit = max(clickSplit, envelope * band);\n      }\n    }\n  }\n\n  vec2 p = uv * uScale;\n  pixelHeat = 0.0;\n  vec2 lightG = getLight(p);\n  float heat = pixelHeat;\n  vec2 lightR = lightG;\n  vec2 lightB = lightG;\n\n  float split = uChromaticAberration + clickSplit * 6.0;\n  if (split != 0.0) {\n    vec2 ca = vec2(split) / iResolution.xy;\n    lightR = getLight(p + ca);\n    lightB = getLight(p - ca);\n  }\n\n  if (uGlow > 0.0) {\n    float bloom = uGlow * 0.2 * glowAt(p);\n    lightR.y += bloom;\n    lightG.y += bloom;\n    lightB.y += bloom;\n  }\n\n  vec3 core = vec3(lightR.x, lightG.x, lightB.x);\n  vec3 halo = vec3(lightR.y, lightG.y, lightB.y);\n  vec3 raw = core + halo;\n\n  vec3 luma = vec3(0.2126, 0.7152, 0.0722);\n  float peak = max(uTint.r, max(uTint.g, uTint.b));\n  float phosphor = clamp(uGlow, 0.0, 1.0);\n  vec3 vividTint = clamp(mix(vec3(dot(uTint, luma)), uTint, 1.0 + 0.8 * phosphor), 0.0, 1.0);\n  vec3 coreTint = mix(uTint, vec3(peak), phosphor * 0.45 * smoothstep(0.45, 0.9, core));\n  vec3 col = core * coreTint + halo * vividTint;\n  col += (vec3(1.0) - uTint) * min(raw, vec3(1.0)) * heat * uMouseGlow * 0.6;\n  col *= uBrightness;\n\n  if (uVignette > 0.0) {\n    float edge = 0.04 + 0.22 * uVignette;\n    vec2 fade = smoothstep(vec2(0.0), vec2(edge), uv) * smoothstep(vec2(0.0), vec2(edge), 1.0 - uv);\n    float mask = fade.x * fade.y;\n    mask = mask * mask * (3.0 - 2.0 * mask);\n    float keep = mix(1.0, mask, clamp(uVignette * 1.5, 0.0, 1.0));\n    col *= keep;\n    core *= keep;\n    halo *= keep;\n  }\n\n  if (uLightMode > 0.5) {\n    vec3 luma = vec3(0.2126, 0.7152, 0.0722);\n    float lum = dot(uTint, luma);\n    float peak = max(uTint.r, max(uTint.g, uTint.b));\n    vec3 hue = uTint / max(peak, 0.0001);\n    vec3 vivid = clamp(mix(vec3(dot(hue, luma)), hue, 1.6), 0.0, 1.0);\n    vivid = clamp(vivid * (0.3 / max(dot(vivid, luma), 0.0001)), 0.0, 1.0);\n    vec3 ink = mix(uTint, vivid, smoothstep(0.3, 0.6, lum));\n    vec3 cover = (core * 1.15 + halo * 0.4) * uBrightness * (1.0 + heat * uMouseGlow * 0.4);\n    if (uDither > 0.0) cover += (hash21(gl_FragCoord.xy) - 0.5) * (uDither * 0.003922);\n    cover = clamp(cover, 0.0, 0.94);\n    float alpha = max(cover.r, max(cover.g, cover.b));\n    outColor = vec4(alpha - cover * (1.0 - ink), alpha);\n    return;\n  }\n\n  if (uDither > 0.0) {\n    float rnd = hash21(gl_FragCoord.xy);\n    col += (rnd - 0.5) * (uDither * 0.003922);\n  }\n\n  col = clamp(col, 0.0, 1.0);\n  outColor = vec4(col, max(col.r, max(col.g, col.b)));\n}`;\n\nconst clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n\nconst compile = (gl: WebGL2RenderingContext, type: number, source: string) => {\n  const shader = gl.createShader(type);\n  if (!shader) return null;\n  gl.shaderSource(shader, source);\n  gl.compileShader(shader);\n  if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return shader;\n  gl.deleteShader(shader);\n  return null;\n};\n\nconst link = (gl: WebGL2RenderingContext, fragmentSource: string): Program | null => {\n  const vertex = compile(gl, gl.VERTEX_SHADER, VERTEX);\n  const fragment = compile(gl, gl.FRAGMENT_SHADER, fragmentSource);\n  const program = gl.createProgram();\n  if (!vertex || !fragment || !program) return null;\n  gl.attachShader(program, vertex);\n  gl.attachShader(program, fragment);\n  gl.linkProgram(program);\n  gl.deleteShader(vertex);\n  gl.deleteShader(fragment);\n  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n    gl.deleteProgram(program);\n    return null;\n  }\n  const uniforms: Record<string, WebGLUniformLocation | null> = {};\n  const count = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);\n  for (let i = 0; i < count; i++) {\n    const info = gl.getActiveUniform(program, i);\n    if (!info) continue;\n    const name = info.name.replace(/\\[0\\]$/, '');\n    uniforms[name] = gl.getUniformLocation(program, name);\n  }\n  return { program, uniforms };\n};\n\nconst hexToRgb = (hex: string): number[] => {\n  let h = String(hex || '#ffffff')\n    .replace('#', '')\n    .trim();\n  if (h.length === 3 || h.length === 4)\n    h = h\n      .slice(0, 3)\n      .split('')\n      .map(c => c + c)\n      .join('');\n  const num = parseInt(h.slice(0, 6), 16);\n  if (Number.isNaN(num)) return [1, 1, 1];\n  return [((num >> 16) & 255) / 255, ((num >> 8) & 255) / 255, (num & 255) / 255];\n};\n\nexport default function FaultyTerminal({\n  scale = 1,\n  gridMul = [2, 1],\n  digitSize = 1.5,\n  timeScale = 0.3,\n  pause = false,\n  scanlineIntensity = 0.3,\n  glitchAmount = 1,\n  flickerAmount = 1,\n  noiseAmp = 1,\n  chromaticAberration = 0,\n  dither = 0,\n  curvature = 0.2,\n  tint = '#ffffff',\n  mouseReact = true,\n  mouseStrength = 0.2,\n  pageLoadAnimation = true,\n  brightness = 1,\n  glyphs = 'blocks',\n  glow = 0.5,\n  persistence = 0.5,\n  scramble = 0.5,\n  mouseGlow = 0.6,\n  vignette = 0.25,\n  clickEffect = 'glitch',\n  variation,\n  lightMode = false,\n  dpr,\n  className = '',\n  style,\n  ...rest\n}: FaultyTerminalProps) {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const canvasRef = useRef<HTMLCanvasElement>(null);\n  const wakeRef = useRef<(() => void) | null>(null);\n  const settings: Settings = {\n    scale: Math.max(0.05, scale),\n    gridX: Math.max(0.1, (gridMul?.[0] ?? 2) * 15),\n    gridY: Math.max(0.1, (gridMul?.[1] ?? 1) * 15),\n    digitSize,\n    timeScale,\n    pause,\n    scanlineIntensity,\n    glitchAmount,\n    flickerAmount,\n    noiseAmp,\n    chromaticAberration,\n    dither: typeof dither === 'boolean' ? (dither ? 1 : 0) : dither,\n    curvature,\n    tint,\n    mouseReact,\n    mouseStrength,\n    pageLoadAnimation,\n    brightness,\n    glyphs: GLYPHS[glyphs] ?? 0,\n    glow: clamp(glow, 0, 3),\n    persistence: clamp(persistence, 0, 1),\n    scramble: clamp(scramble, 0, 1),\n    mouseGlow: clamp(mouseGlow, 0, 1),\n    vignette: clamp(vignette, 0, 1),\n    clickEffect: CLICKS[clickEffect] ?? 0,\n    clickSeconds: CLICK_SECONDS[clickEffect] ?? 1,\n    variation,\n    lightMode,\n    dpr\n  };\n  const settingsRef = useRef(settings);\n  settingsRef.current = settings;\n\n  useEffect(() => {\n    const container = containerRef.current;\n    const canvas = canvasRef.current;\n    const gl = canvas?.getContext('webgl2', {\n      alpha: true,\n      premultipliedAlpha: true,\n      antialias: false,\n      depth: false,\n      stencil: false\n    });\n    if (!container || !canvas || !gl) return undefined;\n    const cellShader = link(gl, CELL_FRAGMENT);\n    const mainShader = link(gl, MAIN_FRAGMENT);\n    if (!cellShader || !mainShader) return undefined;\n    const cu = cellShader.uniforms;\n    const mu = mainShader.uniforms;\n    const vao = gl.createVertexArray();\n\n    const makeTexture = () => {\n      const texture = gl.createTexture();\n      gl.bindTexture(gl.TEXTURE_2D, texture);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n      return texture;\n    };\n    const targets = [0, 1].map(() => {\n      const rings = makeTexture();\n      const extra = makeTexture();\n      const framebuffer = gl.createFramebuffer();\n      gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, rings, 0);\n      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, extra, 0);\n      return { rings, extra, framebuffer };\n    });\n    gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\n    const reduce = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;\n    const view = { width: 1, height: 1, ratio: 1, columns: 1 };\n    const cells = { x: 0, y: 0, w: 1, h: 1, key: '' };\n    const pointer = { x: 0.5, y: 0.5, inside: false };\n    const mouse = { x: 0.5, y: 0.5 };\n    const clicks = new Float32Array(MAX_CLICKS * 4);\n    let clickIndex = 0;\n    let clock = 0;\n    let hover = 0;\n    let current = 0;\n    let fresh = true;\n    const seedOffset = settingsRef.current.variation;\n    const initialOffset = typeof seedOffset === 'number' ? seedOffset * 10 : Math.random() * 100;\n    let iTime = initialOffset * settingsRef.current.timeScale;\n    let introClock = settingsRef.current.pageLoadAnimation && !reduce ? 0 : 1;\n    let raf = 0;\n    let last = 0;\n    let visible = true;\n    let quiet = 0;\n\n    const layoutCells = (s: Settings) => {\n      const reach = 1 + Math.max(0, s.curvature) * 2;\n      const uvMin = 0.5 - 0.5 * reach;\n      const uvMax = 0.5 + 0.5 * reach;\n      const margin = 0.06 * Math.max(1, Math.abs(s.glitchAmount)) * Math.max(1, Math.abs(s.flickerAmount)) + 0.2;\n      const gridX = s.gridX * view.columns;\n      const x0 = Math.floor((uvMin * s.scale - margin) * gridX) - 2;\n      const x1 = Math.floor((uvMax * s.scale + margin) * gridX) + 2;\n      const y0 = Math.floor((uvMin * s.scale - margin) * s.gridY) - 2;\n      const y1 = Math.floor((uvMax * s.scale + margin) * s.gridY) + 2;\n      const key = `${x0}:${x1}:${y0}:${y1}`;\n      if (key === cells.key) return;\n      cells.key = key;\n      cells.x = x0;\n      cells.y = y0;\n      cells.w = Math.max(1, Math.min(4096, x1 - x0 + 1));\n      cells.h = Math.max(1, Math.min(4096, y1 - y0 + 1));\n      for (const target of targets) {\n        for (const texture of [target.rings, target.extra]) {\n          gl.bindTexture(gl.TEXTURE_2D, texture);\n          gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, cells.w, cells.h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n        }\n      }\n      fresh = true;\n    };\n\n    const stepCells = (s: Settings, decay: number) => {\n      const source = targets[current];\n      const target = targets[1 - current];\n      gl.bindFramebuffer(gl.FRAMEBUFFER, target.framebuffer);\n      gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]);\n      gl.viewport(0, 0, cells.w, cells.h);\n      gl.disable(gl.BLEND);\n      gl.useProgram(cellShader.program);\n      gl.activeTexture(gl.TEXTURE0);\n      gl.bindTexture(gl.TEXTURE_2D, source.rings);\n      gl.activeTexture(gl.TEXTURE1);\n      gl.bindTexture(gl.TEXTURE_2D, source.extra);\n      gl.uniform1i(cu.uPrev0, 0);\n      gl.uniform1i(cu.uPrev1, 1);\n      gl.uniform1f(cu.iTime, iTime);\n      gl.uniform2f(cu.uGridMul, (s.gridX * view.columns) / 15, s.gridY / 15);\n      gl.uniform1f(cu.uNoiseAmp, s.noiseAmp);\n      gl.uniform1f(cu.uScale, s.scale);\n      gl.uniform2f(cu.uMouse, mouse.x, mouse.y);\n      gl.uniform1f(cu.uMouseStrength, s.mouseStrength);\n      gl.uniform1f(cu.uUseMouse, s.mouseReact ? 1 : 0);\n      gl.uniform1f(cu.uPageLoadProgress, introClock);\n      gl.uniform1f(cu.uUsePageLoadAnimation, s.pageLoadAnimation ? 1 : 0);\n      gl.uniform2i(cu.uOrigin, cells.x, cells.y);\n      gl.uniform1f(cu.uDecay, decay);\n      gl.uniform1f(cu.uFresh, fresh ? 1 : 0);\n      gl.uniform1f(cu.uHover, hover);\n      gl.bindVertexArray(vao);\n      gl.drawArrays(gl.TRIANGLES, 0, 3);\n      gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n      current = 1 - current;\n      fresh = false;\n    };\n\n    const draw = () => {\n      const s = settingsRef.current;\n      const source = targets[current];\n      gl.viewport(0, 0, canvas.width, canvas.height);\n      gl.clearColor(0, 0, 0, 0);\n      gl.clear(gl.COLOR_BUFFER_BIT);\n      gl.useProgram(mainShader.program);\n      gl.activeTexture(gl.TEXTURE0);\n      gl.bindTexture(gl.TEXTURE_2D, source.rings);\n      gl.activeTexture(gl.TEXTURE1);\n      gl.bindTexture(gl.TEXTURE_2D, source.extra);\n      gl.uniform1i(mu.uRings0, 0);\n      gl.uniform1i(mu.uRings1, 1);\n      gl.uniform1f(mu.iTime, iTime);\n      gl.uniform3f(mu.iResolution, canvas.width, canvas.height, canvas.width / canvas.height);\n      gl.uniform1f(mu.uScale, s.scale);\n      gl.uniform2f(mu.uGridMul, (s.gridX * view.columns) / 15, s.gridY / 15);\n      gl.uniform1f(mu.uDigitSize, s.digitSize);\n      gl.uniform1f(mu.uScanlineIntensity, s.scanlineIntensity);\n      gl.uniform1f(mu.uGlitchAmount, reduce ? 0 : s.glitchAmount);\n      gl.uniform1f(mu.uFlickerAmount, reduce ? 0 : s.flickerAmount);\n      gl.uniform1f(mu.uChromaticAberration, s.chromaticAberration);\n      gl.uniform1f(mu.uDither, s.dither);\n      gl.uniform1f(mu.uCurvature, s.curvature);\n      gl.uniform3fv(mu.uTint, hexToRgb(s.tint));\n      gl.uniform1f(mu.uBrightness, s.brightness);\n      gl.uniform1f(mu.uLightMode, s.lightMode ? 1 : 0);\n      gl.uniform2i(mu.uOrigin, cells.x, cells.y);\n      gl.uniform2i(mu.uCellCount, cells.w, cells.h);\n      gl.uniform1i(mu.uGlyphs, s.glyphs);\n      gl.uniform1f(mu.uGlow, s.glow);\n      gl.uniform1f(mu.uScramble, s.scramble);\n      gl.uniform1f(mu.uMouseGlow, s.mouseGlow);\n      gl.uniform1f(mu.uVignette, s.vignette);\n      gl.uniform4fv(mu.uClicks, clicks);\n      gl.uniform1i(mu.uClickMode, s.clickEffect);\n      gl.uniform1f(mu.uClickSeconds, s.clickSeconds);\n      gl.uniform1f(mu.uClock, clock);\n      gl.bindVertexArray(vao);\n      gl.drawArrays(gl.TRIANGLES, 0, 3);\n      gl.bindVertexArray(null);\n    };\n\n    const resize = () => {\n      const s = settingsRef.current;\n      view.width = Math.max(1, container.clientWidth);\n      view.height = Math.max(1, container.clientHeight);\n      view.ratio = Math.min(s.dpr || window.devicePixelRatio || 1, 2);\n      view.columns = Math.min(1, view.width / view.height / 1.5);\n      canvas.width = Math.max(1, Math.round(view.width * view.ratio));\n      canvas.height = Math.max(1, Math.round(view.height * view.ratio));\n      layoutCells(s);\n      stepCells(s, 0);\n      draw();\n      wake();\n    };\n\n    const frame = (now: number) => {\n      raf = 0;\n      if (!visible || document.hidden) return;\n      const s = settingsRef.current;\n      const dt = last ? clamp((now - last) / 1000, 0.001, 1 / 20) : 1 / 60;\n      last = now;\n      clock += dt;\n      if (!s.pause && !reduce) iTime += dt * s.timeScale;\n      const targetX = pointer.inside ? pointer.x : 0.5;\n      const targetY = pointer.inside ? pointer.y : 0.5;\n      const follow = 1 - Math.exp(-dt / (pointer.inside ? 0.19988 : 0.6));\n      mouse.x += (targetX - mouse.x) * follow;\n      mouse.y += (targetY - mouse.y) * follow;\n      hover += ((pointer.inside ? 1 : 0) - hover) * (1 - Math.exp(-dt / 0.35));\n      for (let k = 0; k < MAX_CLICKS; k++) {\n        if (clicks[k * 4 + 3] > 0 && clock - clicks[k * 4 + 2] > s.clickSeconds) clicks[k * 4 + 3] = 0;\n      }\n      layoutCells(s);\n      const decay = s.persistence > 0 ? Math.exp(-dt / (0.08 + s.persistence * 1.4)) : 0;\n      stepCells(s, decay);\n      draw();\n      if (introClock < 1) introClock = Math.min(1, introClock + dt / INTRO_SECONDS);\n      let busy = !(s.pause || reduce) || introClock < 1;\n      busy = busy || Math.abs(targetX - mouse.x) + Math.abs(targetY - mouse.y) > 0.0001;\n      busy = busy || Math.abs((pointer.inside ? 1 : 0) - hover) > 0.001;\n      for (let k = 0; k < MAX_CLICKS; k++) busy = busy || clicks[k * 4 + 3] > 0;\n      quiet = busy ? 0 : quiet + dt;\n      if (quiet < 2.5) raf = requestAnimationFrame(frame);\n    };\n\n    function wake() {\n      quiet = 0;\n      if (!raf && visible && !document.hidden) {\n        last = 0;\n        raf = requestAnimationFrame(frame);\n      }\n    }\n\n    const locate = (event: PointerEvent) => {\n      const rect = container.getBoundingClientRect();\n      const x = (event.clientX - rect.left) / Math.max(rect.width, 1);\n      const y = 1 - (event.clientY - rect.top) / Math.max(rect.height, 1);\n      return { x, y, inside: x >= 0 && x <= 1 && y >= 0 && y <= 1 };\n    };\n\n    const onPointerMove = (event: PointerEvent) => {\n      const { x, y, inside } = locate(event);\n      const was = pointer.inside;\n      pointer.inside = inside;\n      if (inside) {\n        pointer.x = x;\n        pointer.y = y;\n      }\n      if (inside || was) wake();\n    };\n\n    const onPointerDown = (event: PointerEvent) => {\n      const s = settingsRef.current;\n      const { x, y, inside } = locate(event);\n      if (!inside || reduce || !s.clickEffect) return;\n      const o = clickIndex * 4;\n      clicks[o] = x;\n      clicks[o + 1] = y;\n      clicks[o + 2] = clock;\n      clicks[o + 3] = 1;\n      clickIndex = (clickIndex + 1) % MAX_CLICKS;\n      wake();\n    };\n\n    const onPointerLeave = () => {\n      pointer.inside = false;\n      wake();\n    };\n\n    const onVisibility = () => wake();\n\n    const resizeObserver = new ResizeObserver(resize);\n    resizeObserver.observe(container);\n    const intersection = new IntersectionObserver(entries => {\n      visible = entries.some(entry => entry.isIntersecting);\n      if (visible) wake();\n    });\n    intersection.observe(container);\n    window.addEventListener('pointermove', onPointerMove, { passive: true });\n    window.addEventListener('pointerdown', onPointerDown, { passive: true });\n    document.documentElement.addEventListener('pointerleave', onPointerLeave);\n    document.addEventListener('visibilitychange', onVisibility);\n    resize();\n    wakeRef.current = wake;\n\n    return () => {\n      cancelAnimationFrame(raf);\n      wakeRef.current = null;\n      resizeObserver.disconnect();\n      intersection.disconnect();\n      window.removeEventListener('pointermove', onPointerMove);\n      window.removeEventListener('pointerdown', onPointerDown);\n      document.documentElement.removeEventListener('pointerleave', onPointerLeave);\n      document.removeEventListener('visibilitychange', onVisibility);\n      for (const target of targets) {\n        gl.deleteTexture(target.rings);\n        gl.deleteTexture(target.extra);\n        gl.deleteFramebuffer(target.framebuffer);\n      }\n      gl.deleteVertexArray(vao);\n      gl.deleteProgram(cellShader.program);\n      gl.deleteProgram(mainShader.program);\n      gl.getExtension('WEBGL_lose_context')?.loseContext();\n    };\n  }, []);\n\n  useEffect(() => {\n    wakeRef.current?.();\n  });\n\n  return (\n    <div\n      ref={containerRef}\n      className={['relative h-full w-full overflow-hidden', className].filter(Boolean).join(' ')}\n      style={style}\n      {...rest}\n    >\n      <canvas ref={canvasRef} className=\"absolute inset-0 block h-full w-full\" aria-hidden=\"true\" />\n    </div>\n  );\n}\n"
		}
	],
	"registryDependencies": [],
	"dependencies": []
}