{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "grid-wave",
  "title": "Grid Wave",
  "description": "A wave travels across the frame raising the picture into a grid of solid blocks with lit tops and shaded sides, which set back down carrying the next scene.",
  "dependencies": ["remotion", "@remotion/transitions"],
  "registryDependencies": ["@remocn/canvas-presentation"],
  "files": [
    {
      "path": "registry/remocn/grid-wave/index.tsx",
      "content": "\"use client\";\n\nimport type {\n  TransitionPresentation,\n  TransitionPresentationComponentProps,\n} from \"@remotion/transitions\";\nimport type React from \"react\";\nimport { AbsoluteFill } from \"remotion\";\nimport {\n  makeCanvasPresentation,\n  makeSceneShader,\n} from \"@/lib/remocn/canvas-presentation\";\n\nexport type GridWaveDirection = \"ripple\" | \"left\" | \"right\" | \"up\" | \"down\";\n\nexport type GridWaveProps = {\n  tileSize?: number;\n  waveWidth?: number;\n  lift?: number;\n  gap?: number;\n  tint?: string;\n  direction?: GridWaveDirection;\n};\n\nconst DEFAULTS = {\n  tileSize: 90,\n  waveWidth: 0.16,\n  lift: 2,\n  gap: 0.12,\n  tint: \"#8fb4ff\",\n  direction: \"ripple\" as GridWaveDirection,\n};\n\nconst AXES: Record<GridWaveDirection, [number, number]> = {\n  ripple: [0, 0],\n  left: [1, 0],\n  right: [-1, 0],\n  up: [0, -1],\n  down: [0, 1],\n};\n\nfunction hexToRgb(hex: string): [number, number, number] {\n  const value = hex.replace(\"#\", \"\");\n  const full =\n    value.length === 3\n      ? value\n          .split(\"\")\n          .map((c) => c + c)\n          .join(\"\")\n      : value;\n  const int = Number.parseInt(full, 16);\n  if (Number.isNaN(int)) {\n    return [0.56, 0.71, 1];\n  }\n  return [\n    ((int >> 16) & 255) / 255,\n    ((int >> 8) & 255) / 255,\n    (int & 255) / 255,\n  ];\n}\n\nconst FRAGMENT_SHADER = `#version 300 es\nprecision highp float;\n\nuniform sampler2D u_from;\nuniform sampler2D u_to;\nuniform float u_progress;\nuniform float u_aspect;\nuniform vec2 u_grid;\nuniform float u_wave;\nuniform float u_lift;\nuniform float u_gap;\nuniform vec3 u_tint;\nuniform vec2 u_dir;\nuniform float u_radial;\n\nin vec2 v_uv;\nout vec4 outColor;\n\nfloat hash(vec2 p) {\n  return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);\n}\n\nfloat waveCoord(vec2 p) {\n  vec2 off = (p - 0.5) * vec2(u_aspect, 1.0);\n  float ring = length(off) / (0.5 * sqrt(u_aspect * u_aspect + 1.0));\n  float sweep = clamp(dot(p - 0.5, u_dir) + 0.5, 0.0, 1.0);\n  return mix(sweep, ring, u_radial);\n}\n\nvoid main() {\n  float front = u_progress * (1.0 + 6.0 * u_wave) - 3.0 * u_wave;\n\n  float selfPass =\n    smoothstep(-u_wave * 0.4, u_wave * 0.4, front - waveCoord(v_uv));\n  vec4 base = mix(texture(u_from, v_uv), texture(u_to, v_uv), selfPass);\n\n  float edge = 0.5 - u_gap * 0.5;\n  vec2 anchor = floor(v_uv * u_grid);\n\n  vec4 face = vec4(0.0);\n  float depth = -1.0;\n\n  for (int oy = -1; oy <= 1; oy++) {\n    for (int ox = -1; ox <= 1; ox++) {\n      vec2 cell = anchor + vec2(float(ox), float(oy));\n      vec2 centre = (cell + 0.5) / u_grid;\n\n      float stagger = (hash(cell) - 0.5) * u_wave * 0.4;\n      float w = front - waveCoord(centre) + stagger;\n      float band = w / u_wave;\n      float pulse = exp(-band * band);\n      if (pulse <= depth) {\n        continue;\n      }\n\n      float rise = pulse * u_lift;\n      vec2 lean = centre - 0.5;\n      lean = lean / max(length(lean), 0.0001) * rise * 0.15;\n      float grow = 1.0 + rise * 0.5;\n\n      vec2 q = (v_uv - centre) * u_grid;\n      vec2 top = (q - lean) / grow;\n      float onTop = max(abs(top.x), abs(top.y));\n      float onFoot = max(abs(q.x), abs(q.y));\n      bool lit = onTop <= edge;\n      if (!lit && onFoot > edge) {\n        continue;\n      }\n      depth = pulse;\n\n      float pass = smoothstep(-u_wave * 0.4, u_wave * 0.4, w);\n      vec2 local = lit ? top : clamp(top, -edge, edge);\n      vec2 src = centre + local / u_grid;\n      vec4 col = mix(texture(u_from, src), texture(u_to, src), pass);\n\n      if (lit) {\n        col.rgb = mix(col.rgb, u_tint, pulse * 0.12);\n        col.rgb *= 1.0 + pulse * 0.06;\n      } else {\n        col.rgb = mix(col.rgb, u_tint, pulse * 0.06);\n        col.rgb *= 1.0 - pulse * 0.24;\n      }\n\n      face = col;\n    }\n  }\n\n  outColor = face + base * (1.0 - face.a);\n}`;\n\nconst shader = makeSceneShader<GridWaveProps>(\n  FRAGMENT_SHADER,\n  ({ gl, uniform, width, height, passedProps }) => {\n    const {\n      tileSize = DEFAULTS.tileSize,\n      waveWidth = DEFAULTS.waveWidth,\n      lift = DEFAULTS.lift,\n      gap = DEFAULTS.gap,\n      tint = DEFAULTS.tint,\n      direction = DEFAULTS.direction,\n    } = passedProps;\n    const size = Math.max(tileSize, 8);\n    const axis = AXES[direction] ?? AXES[DEFAULTS.direction];\n    const rgb = hexToRgb(tint);\n    gl.uniform2f(\n      uniform(\"u_grid\"),\n      Math.max(Math.round(width / size), 2),\n      Math.max(Math.round(height / size), 2),\n    );\n    gl.uniform1f(uniform(\"u_wave\"), Math.max(waveWidth, 0.02));\n    gl.uniform1f(uniform(\"u_lift\"), Math.max(lift, 0) * 0.35);\n    gl.uniform1f(uniform(\"u_gap\"), Math.min(Math.max(gap, 0), 0.45));\n    gl.uniform3f(uniform(\"u_tint\"), rgb[0], rgb[1], rgb[2]);\n    gl.uniform2f(uniform(\"u_dir\"), axis[0], axis[1]);\n    gl.uniform1f(uniform(\"u_radial\"), direction === \"ripple\" ? 1 : 0);\n  },\n);\n\nconst GridWaveFallback: React.FC<\n  TransitionPresentationComponentProps<GridWaveProps>\n> = ({ children, presentationProgress, presentationDirection }) => {\n  const p = presentationProgress;\n\n  if (presentationDirection === \"exiting\") {\n    return <AbsoluteFill>{children}</AbsoluteFill>;\n  }\n\n  const reveal = Math.min(1, Math.max(0, (p - 0.2) / 0.6));\n\n  return (\n    <AbsoluteFill\n      style={{\n        opacity: reveal,\n        scale: `${1.04 - reveal * 0.04}`,\n      }}\n    >\n      {children}\n    </AbsoluteFill>\n  );\n};\n\nconst presentation = makeCanvasPresentation<GridWaveProps>({\n  shader,\n  fallback: GridWaveFallback,\n});\n\nexport function gridWave(\n  props: GridWaveProps = {},\n): TransitionPresentation<GridWaveProps> {\n  return presentation(props);\n}\n",
      "type": "registry:component",
      "target": "components/remocn/grid-wave.tsx"
    }
  ],
  "type": "registry:component"
}
