{
  "name": "transition-light-leak",
  "type": "registry:ui",
  "description": "Film flare overlay that peaks on the cut and hides the seam under it",
  "dependencies": [
    "remotion",
    "@remotion/light-leaks"
  ],
  "registryDependencies": [
    "motion-tokens"
  ],
  "files": [
    {
      "path": "registry/bases/default/primitives/transition-light-leak.tsx",
      "type": "registry:ui",
      "content": "import { LightLeak } from \"@remotion/light-leaks\";\nimport {\n  AbsoluteFill,\n  interpolate,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\nimport { DURATION, EASING } from \"@/remotion/lib/motion-tokens\";\n\nexport type TransitionLightLeakProps = {\n  durationInFrames?: number;\n  seed?: number;\n  /** Hue rotation in degrees — warm amber by default */\n  hueShift?: number;\n  /** Peak opacity of the leak, 0→1. */\n  intensity?: number;\n  /**\n   * Where the leak peaks inside its window, 0→1. Sit it on the cut itself so\n   * the flare hides the seam rather than arriving after it.\n   */\n  peakAt?: number;\n  blendMode?: \"screen\" | \"plus-lighter\" | \"normal\";\n};\n\n/** Light leak overlay for TransitionSeries.Overlay */\nexport const TransitionLightLeak: React.FC<TransitionLightLeakProps> = ({\n  durationInFrames,\n  seed = 0,\n  hueShift = 28,\n  intensity = 1,\n  peakAt = 0.4,\n  blendMode = \"screen\",\n}) => {\n  const frame = useCurrentFrame();\n  const { durationInFrames: sequenceDuration } = useVideoConfig();\n  // An overlay is mounted for exactly its own window, so its length is\n  // knowable. Defaulting to a fixed `DURATION.fast` made the envelope finish\n  // early — or never finish — whenever the overlay was not 12 frames long.\n  const span = durationInFrames ?? sequenceDuration ?? DURATION.fast;\n  const peak = Math.min(Math.max(peakAt, 0.05), 0.95);\n\n  const envelope = interpolate(\n    frame,\n    [0, span * peak * 0.55, span * peak, span],\n    [0, intensity, intensity * 0.85, 0],\n    {\n      extrapolateLeft: \"clamp\",\n      extrapolateRight: \"clamp\",\n      easing: EASING.editorial,\n    },\n  );\n  const scale = interpolate(envelope, [0, intensity], [1.08, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n\n  return (\n    <AbsoluteFill style={{ opacity: envelope, scale, mixBlendMode: blendMode }}>\n      <LightLeak durationInFrames={span} seed={seed} hueShift={hueShift} />\n    </AbsoluteFill>\n  );\n};\n"
    }
  ],
  "atlas": {
    "lane": "cuts",
    "drive": "time",
    "tier": "advanced"
  }
}