{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "warp",
  "type": "registry:ui",
  "title": "Warp",
  "description": "A jump-to-light-speed hyperspace background: layered star-streaks accelerate radially from a vanishing point, their length breathing with a slow warp surge, their heads fringed by real chromatic aberration and lit by a colored bloom, over a soft central glow and a faint twinkling starfield. The pointer steers the vanishing point. Original OGL fragment shader, tone-mapped, dithered, DPR-clamped, offscreen-paused, reduced-motion-safe. Idles on its own when untouched — a ghost slowly steers the jump.",
  "dependencies": [
    "ogl"
  ],
  "registryDependencies": [
    "https://crucibleui.com/r/ogl-canvas.json",
    "https://crucibleui.com/r/use-visibility-pause.json",
    "https://crucibleui.com/r/use-reduced-motion.json"
  ],
  "files": [
    {
      "path": "registry/default/backgrounds/warp/warp.tsx",
      "type": "registry:ui",
      "target": "components/crucible/warp.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { OglCanvas } from \"@/lib/ogl-canvas\";\n\nexport interface WarpProps extends React.ComponentPropsWithoutRef<\"div\"> {\n  /**\n   * Three tones: `[core, head, bloom]`. `core` is the streak body (kept near\n   * white for the classic light-speed look), `head` the chromatic tint that\n   * blooms at the leading edge of each streak, `bloom` the central glow at the\n   * vanishing point. Defaults to a cool blue-white hyperspace palette. Pass a\n   * warm set (e.g. `[\"#fff3e6\", \"#ff9a5c\", \"#ffcf99\"]`) for a solar jump.\n   */\n  colors?: [string, string, string];\n  /** Outward-travel speed multiplier. 1 = default, 2 = twice as fast. Default 1. */\n  speed?: number;\n  /**\n   * Overall luminosity of the streaks, heads, and center bloom, 0–2. 1 =\n   * default; lower is a calmer drift, higher blazes into full jump. Default 1.\n   */\n  intensity?: number;\n  /**\n   * Streak density, 0.3–2. Scales how many stars radiate from the vanishing\n   * point. Lower is a sparse calm field, higher a dense warp tunnel. Default 1.\n   */\n  density?: number;\n  /**\n   * Idle autoplay: after ~1.5s with no real pointer, a seeded ghost pointer\n   * steers the vanishing point on a slow seeded drift so it never looks dead in thumbnails and\n   * heroes; the real pointer takes over instantly. Never runs under reduced\n   * motion. @default true\n   */\n  idle?: boolean;\n  /** Freeze the animation at its current frame. */\n  paused?: boolean;\n}\n\nconst DEFAULT_COLORS: [string, string, string] = [\n  \"#eaf1ff\", // core — blue-white streak body\n  \"#5b8cff\", // head — cool chromatic tint at the leading edge\n  \"#9db8ff\", // bloom — soft periwinkle glow at the vanishing point\n];\n\n/** Hex → [r,g,b] in 0–1. Accepts #rgb / #rrggbb. */\nfunction hexToRgb(hex: string): [number, number, number] {\n  let h = hex.replace(\"#\", \"\").trim();\n  if (h.length === 3) {\n    h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];\n  }\n  const int = parseInt(h, 16);\n  if (Number.isNaN(int) || h.length !== 6) return [0.92, 0.95, 1.0];\n  return [((int >> 16) & 255) / 255, ((int >> 8) & 255) / 255, (int & 255) / 255];\n}\n\nfunction clamp(v: number, lo: number, hi: number): number {\n  return Math.min(Math.max(v, lo), hi);\n}\n\n/**\n * Original hyperspace-warp fragment shader. Space is read in polar coordinates\n * around a vanishing point: several layers of star-streaks radiate outward,\n * each one a thin line whose LEADING EDGE accelerates toward the frame edge\n * (radius grows with the square of its phase, so stars ease out slowly then\n * blur to light-speed). Streak length breathes with a slow \"warp surge\" so the\n * field pulses between a gentle drift and a full jump. Every streak carries a\n * per-channel radial offset — a genuine chromatic aberration that fringes the\n * heads cyan/red — plus a colored head-bloom sampled from the palette. A tight\n * core bloom and wide halo sit at the vanishing point over a faint twinkling\n * backdrop. Line thickness is corrected by radius so streaks stay constant\n * width in screen space, not fat wedges. The pointer nudges the vanishing\n * point. Exponential tone curve + film-grain dither kill 8-bit banding.\n * GLSL ES 1.00 (WebGL1/2 compatible); no fract() on spatial coords (only in\n * the hash), so there are no wrap seams.\n */\nconst FRAGMENT = /* glsl */ `\nprecision highp float;\n\nvarying vec2 vUv;\n\nuniform float uTime;\nuniform vec2 uResolution;\nuniform vec2 uPointer;\nuniform float uPointerActive; // eased 0..1 pointer (or ghost) presence\n\nuniform vec3 uCore;      // streak body\nuniform vec3 uHead;      // chromatic head tint\nuniform vec3 uBloom;     // center glow\nuniform float uSpeed;\nuniform float uIntensity;\nuniform float uDensity;\n\nconst float TAU = 6.28318530718;\nconst float MAXR = 1.45;  // normalized radius a streak head travels to\n\n// ---- compact value-noise hash (fract only inside) ----\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 sq(float x) { return x * x; }\n\n// One layer's radial band for a single channel, bright at the head, fading to\n// the tail behind it.\nfloat band(float r, float head, float len) {\n  return smoothstep(head - len, head, r) * (1.0 - smoothstep(head, head + 0.03, r));\n}\n\nvoid main() {\n  vec2 uv = vUv;\n  float aspect = uResolution.x / max(uResolution.y, 1.0);\n  float t = uTime * uSpeed;\n\n  // Vanishing point: pointer nudges it a little off-center.\n  vec2 center = vec2(0.5) + (uPointer - vec2(0.5)) * 0.18 * uPointerActive;\n  vec2 p = uv - center;\n  p.x *= aspect;\n\n  float r = length(p);\n  float ang = atan(p.y, p.x) / TAU + 0.5; // 0..1\n\n  // Steady hyperspace stream with a gentle breathing surge — a calm, constant\n  // jump (Star Wars), not a dramatic drift↔lightspeed pulse. Two slow beats.\n  float surge = 0.66 + 0.14 * sin(t * 0.22) + 0.06 * sin(t * 0.09 + 1.3);\n  surge = clamp(surge, 0.0, 1.0);\n\n  vec3 acc = vec3(0.0);\n\n  for (int i = 0; i < 3; i++) {\n    float fi = float(i);\n    // Whole columns only — a fractional count leaves a seam where the angle wraps.\n    float cols = max(floor((12.0 + fi * 10.0) * clamp(uDensity, 0.3, 2.0) + 0.5), 1.0);\n    float depth = 0.55 + 0.45 * fi / 2.0; // nearer layers brighter/faster\n\n    float g = ang * cols;\n    float col = floor(g);\n    float fcol = fract(g);\n\n    float h1 = hash21(vec2(col, fi * 5.7 + 1.3));\n    float h2 = hash21(vec2(col, fi * 9.1 + 4.2));\n\n    // Angular offset of this streak inside its column → screen-space arc.\n    float ao = 0.2 + 0.6 * h2;\n    float angDist = (fcol - ao) * (TAU / cols);\n    float arc = angDist * max(r, 0.05);\n\n    // Outward travel, accelerating: radius ~ phase^2.\n    float spd = (0.24 + 0.5 * h1) * (0.55 + 0.45 * surge);\n    float phase = fract(h1 * 7.31 + t * spd);\n    float head = phase * phase * MAXR;\n    float len = (0.14 + 0.6 * phase) * (0.5 + 0.7 * surge);\n\n    // Chromatic aberration: subtle per-channel radial offset (kept restrained\n    // so the streaks read as clean light, not a rainbow smear).\n    float ca = 0.006 * (0.4 + surge) * (0.35 + phase);\n    float bR = band(r * (1.0 + ca), head, len);\n    float bG = band(r, head, len);\n    float bB = band(r * (1.0 - ca), head, len);\n    vec3 radial = vec3(bR, bG, bB);\n\n    // Constant screen-space line width (thinner in far layers).\n    float w = 0.0055 + 0.004 * (1.0 - depth);\n    float thin = exp(-(arc * arc) / (2.0 * w * w));\n\n    // Per-star brightness + twinkle.\n    float bright = (0.3 + 0.7 * h2) * depth;\n    float tw = 0.75 + 0.25 * sin(t * (1.4 + 3.0 * h1) + h2 * TAU);\n\n    // Colored bloom at the leading edge.\n    float headGlow = exp(-sq((r - head) / (len * 0.6 + 0.02)));\n\n    vec3 streak = uCore * radial + uHead * headGlow * (0.35 + 0.4 * surge);\n    acc += streak * thin * bright * tw;\n  }\n\n  // Center bloom: tight core + wide halo at the vanishing point (restrained).\n  float coreGlow = exp(-r * r / (2.0 * 0.0045));\n  float halo = exp(-r * r / (2.0 * 0.06));\n  acc += uBloom * (coreGlow * 0.7 + halo * 0.18) * (0.6 + 0.4 * surge);\n\n  // Whisper-faint twinkling backdrop so the gaps aren't dead black (sparse +\n  // dim so it reads as distant stars, not blocky noise).\n  vec2 sg = floor((p + 4.0) * 90.0);\n  float sh = hash21(sg);\n  float star = step(0.992, sh) * (0.4 + 0.6 * sin(t * 2.0 + sh * TAU));\n  acc += uCore * star * 0.05 * smoothstep(0.02, 0.25, r);\n\n  vec3 col = acc * uIntensity;\n\n  // Exponential tone curve rolls the stacked bloom to white instead of clipping\n  // (lower exposure keeps the field restrained rather than blown-out).\n  col = vec3(1.0) - exp(-col * 1.3);\n\n  // Vignette + film-grain dither.\n  vec2 vg = (uv - 0.5) * vec2(aspect, 1.0);\n  col *= 1.0 - 0.35 * dot(vg, vg);\n  float grain = hash21(gl_FragCoord.xy + fract(t) * 137.0) - 0.5;\n  col += grain * (1.6 / 255.0);\n\n  gl_FragColor = vec4(max(col, 0.0), 1.0);\n}\n`;\n\n/**\n * Warp — a jump-to-light-speed hyperspace background. Layered star-streaks\n * accelerate radially from a vanishing point, their length breathing with a\n * slow warp surge, their heads fringed by real chromatic aberration and lit by\n * a colored bloom, over a soft central glow and a faint twinkling starfield.\n * The pointer nudges the vanishing point. Built on the `ogl-canvas` harness:\n * DPR-clamped, offscreen-paused, reduced-motion-safe (freezes on a static\n * radial-streak starburst), full GL teardown. Drop it inside any `relative`\n * container; it fills and sits behind content.\n */\nexport function Warp({\n  colors = DEFAULT_COLORS,\n  speed = 1,\n  intensity = 1,\n  density = 1,\n  paused = false,\n  idle = true,\n  className,\n  ...props\n}: WarpProps) {\n  const uniforms = React.useMemo(\n    () => ({\n      uCore: { value: hexToRgb(colors[0]) },\n      uHead: { value: hexToRgb(colors[1]) },\n      uBloom: { value: hexToRgb(colors[2]) },\n      uSpeed: { value: Math.max(speed, 0) },\n      uIntensity: { value: clamp(intensity, 0, 2) },\n      uDensity: { value: clamp(density, 0.3, 2) },\n    }),\n    [colors, speed, intensity, density]\n  );\n\n  return (\n    <OglCanvas\n      aria-hidden\n      data-crucible=\"warp\"\n      className={cn(\"absolute inset-0 -z-10 bg-neutral-950\", className)}\n      fragment={FRAGMENT}\n      uniforms={uniforms}\n      dpr={2}\n      paused={paused}\n      ghost={idle}\n      reducedMotionTime={6}\n      {...props}\n    />\n  );\n}\n"
    }
  ]
}