{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "glitch-cut",
  "title": "Glitch Cut",
  "description": "A broadcast-glitch hard cut: the frame tears into displaced slices with channel split and corrupted blocks, and the incoming scene takes over slice by slice at the peak.",
  "dependencies": ["remotion", "@remotion/transitions"],
  "registryDependencies": ["@remocn/canvas-presentation"],
  "files": [
    {
      "path": "registry/remocn/glitch-cut/index.tsx",
      "content": "\"use client\";\n\nimport type {\n  TransitionPresentation,\n  TransitionPresentationComponentProps,\n} from \"@remotion/transitions\";\nimport type React from \"react\";\nimport { AbsoluteFill, random } from \"remotion\";\nimport {\n  makeCanvasPresentation,\n  makeSceneShader,\n} from \"@/lib/remocn/canvas-presentation\";\n\nexport type GlitchCutProps = {\n  intensity?: number;\n  slices?: number;\n  rgbSplit?: number;\n  blockNoise?: number;\n};\n\nconst DEFAULTS = {\n  intensity: 1,\n  slices: 24,\n  rgbSplit: 1,\n  blockNoise: 0.6,\n};\n\nconst STEPS = 22;\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_intensity;\nuniform float u_slices;\nuniform float u_rgb_split;\nuniform float u_block_noise;\n\nin vec2 v_uv;\nout vec4 outColor;\n\nconst float PI = 3.141592653589793;\nconst float STEPS = ${STEPS}.0;\nconst float BLOCK_COLUMNS = 16.0;\n\nfloat hash(vec2 p) {\n  return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);\n}\n\nvec4 sampleSplit(sampler2D tex, vec2 uv, float split) {\n  vec4 center = texture(tex, uv);\n  float r = texture(tex, vec2(uv.x + split, uv.y)).r;\n  float b = texture(tex, vec2(uv.x - split, uv.y)).b;\n  return vec4(r, center.g, b, center.a);\n}\n\nvoid main() {\n  float energy = pow(max(sin(u_progress * PI), 0.0), 0.7);\n  float clock = floor(u_progress * STEPS);\n\n  float row = floor(v_uv.y * u_slices);\n  float torn = step(0.45, hash(vec2(row, clock)));\n  float amount = hash(vec2(row + 17.0, clock * 1.7 + 3.0)) - 0.5;\n  float sliceShift = amount * 0.3 * u_intensity * energy * torn;\n\n  vec2 block = vec2(floor(v_uv.x * BLOCK_COLUMNS), row);\n  float corrupt = step(\n    1.0 - u_block_noise * 0.5 * energy,\n    hash(block + clock * 7.3)\n  );\n  float blockShift =\n    (hash(block + clock * 3.1 + 5.0) - 0.5) * 0.2 * u_intensity * corrupt;\n\n  vec2 uv = vec2(fract(v_uv.x + sliceShift + blockShift), v_uv.y);\n  float split = u_rgb_split * 0.018 * energy * (0.35 + abs(amount));\n\n  float cut = 0.5 + (hash(vec2(row, 91.0)) - 0.5) * 0.32;\n  float handed = step(cut, u_progress);\n\n  vec4 from = sampleSplit(u_from, uv, split);\n  vec4 to = sampleSplit(u_to, uv, split);\n  vec4 color = mix(from, to, handed);\n  vec4 leaked = mix(to, from, handed);\n\n  float leak = corrupt * step(0.55, hash(block + clock * 11.0 + 13.0));\n  color = mix(color, leaked, leak * 0.8);\n\n  color.rgb +=\n    vec3(0.05, 0.015, 0.09) *\n    energy *\n    torn *\n    hash(vec2(row, clock + 41.0)) *\n    color.a;\n\n  outColor = color;\n}`;\n\nconst shader = makeSceneShader<GlitchCutProps>(\n  FRAGMENT_SHADER,\n  ({ gl, uniform, passedProps }) => {\n    const {\n      intensity = DEFAULTS.intensity,\n      slices = DEFAULTS.slices,\n      rgbSplit = DEFAULTS.rgbSplit,\n      blockNoise = DEFAULTS.blockNoise,\n    } = passedProps;\n    gl.uniform1f(uniform(\"u_intensity\"), intensity);\n    gl.uniform1f(uniform(\"u_slices\"), slices);\n    gl.uniform1f(uniform(\"u_rgb_split\"), rgbSplit);\n    gl.uniform1f(uniform(\"u_block_noise\"), blockNoise);\n  },\n);\n\nconst bandCut = (band: number) =>\n  0.5 + (random(`glitch-cut-handoff-${band}`) - 0.5) * 0.32;\n\nconst GlitchCutFallback: React.FC<\n  TransitionPresentationComponentProps<GlitchCutProps>\n> = ({\n  children,\n  presentationProgress,\n  presentationDirection,\n  passedProps,\n}) => {\n  const {\n    intensity = DEFAULTS.intensity,\n    slices = DEFAULTS.slices,\n    rgbSplit = DEFAULTS.rgbSplit,\n  } = passedProps;\n  const entering = presentationDirection === \"entering\";\n  const p = presentationProgress;\n  const energy = Math.max(0, Math.sin(p * Math.PI)) ** 0.7;\n\n  if (energy < 0.002) {\n    const settled = entering ? (p >= 0.5 ? 1 : 0) : p < 0.5 ? 1 : 0;\n    return <AbsoluteFill style={{ opacity: settled }}>{children}</AbsoluteFill>;\n  }\n\n  const clock = Math.floor(p * STEPS);\n  const bands = Math.max(2, Math.min(6, Math.round(slices / 5)));\n  const height = 100 / bands;\n\n  return (\n    <AbsoluteFill>\n      {Array.from({ length: bands }, (_, band) => {\n        const shown = p >= bandCut(band) ? entering : !entering;\n        if (!shown) {\n          return null;\n        }\n        const shift =\n          (random(`glitch-cut-shift-${clock}-${band}`) - 0.5) *\n          52 *\n          intensity *\n          energy;\n        const tint = band % 2 === 0 ? \"rgb(255, 42, 90)\" : \"rgb(0, 224, 255)\";\n        const tintOpacity =\n          rgbSplit * 0.2 * energy * random(`glitch-cut-tint-${clock}-${band}`);\n        return (\n          <AbsoluteFill\n            key={band}\n            style={{\n              clipPath: `inset(${band * height}% 0 ${100 - (band + 1) * height}% 0)`,\n              translate: `${shift}px`,\n            }}\n          >\n            {children}\n            <AbsoluteFill\n              style={{\n                background: tint,\n                mixBlendMode: \"screen\",\n                opacity: tintOpacity,\n              }}\n            />\n          </AbsoluteFill>\n        );\n      })}\n    </AbsoluteFill>\n  );\n};\n\nconst presentation = makeCanvasPresentation<GlitchCutProps>({\n  shader,\n  fallback: GlitchCutFallback,\n});\n\nexport function glitchCut(\n  props: GlitchCutProps = {},\n): TransitionPresentation<GlitchCutProps> {\n  return presentation(props);\n}\n",
      "type": "registry:component",
      "target": "components/remocn/glitch-cut.tsx"
    }
  ],
  "type": "registry:component"
}
