{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "ai-generate-overlay", "title": "AI Generate Overlay", "description": "Source image blurs under a shimmering dot grid and a pulsing 'Generating…' pill before a new image fades in.", "dependencies": [ "remotion" ], "files": [ { "path": "registry/remocn/ai-generate-overlay/index.tsx", "content": "\"use client\";\n\nimport { interpolate, spring, useCurrentFrame, useVideoConfig } from \"remotion\";\n\nexport interface AIGenerateOverlayProps {\n maxBlur?: number;\n blurStartFrame?: number;\n blurPeakFrame?: number;\n revealStartFrame?: number;\n pillText?: string;\n accent?: string;\n background?: string;\n sourceImageBg?: string;\n generatedImageBg?: string;\n dotColor?: string;\n dotSize?: number;\n dotSpacing?: number;\n speed?: number;\n className?: string;\n}\n\nconst FONT_FAMILY =\n \"var(--font-geist-sans), -apple-system, BlinkMacSystemFont, sans-serif\";\n\nconst DEFAULT_SOURCE_BG =\n \"linear-gradient(135deg, #6b3a1a 0%, #d4a574 50%, #8b4513 100%)\";\n\nconst DEFAULT_GENERATED_BG =\n \"radial-gradient(ellipse at center, #d4a574 0%, #8b4513 22%, #2a1a10 42%), linear-gradient(180deg, #1a1410 0%, #0a0806 50%, #1a1410 100%)\";\n\nexport function AIGenerateOverlay({\n maxBlur = 20,\n blurStartFrame = 20,\n blurPeakFrame = 40,\n revealStartFrame = 110,\n pillText = \"Generating…\",\n accent = \"#a78bfa\",\n background = \"#050505\",\n sourceImageBg = DEFAULT_SOURCE_BG,\n generatedImageBg = DEFAULT_GENERATED_BG,\n dotColor = \"#ffffff\",\n dotSize = 1.2,\n dotSpacing = 20,\n speed = 1,\n className,\n}: AIGenerateOverlayProps) {\n const rawFrame = useCurrentFrame();\n const frame = rawFrame * speed;\n const { fps, durationInFrames } = useVideoConfig();\n\n // Phase 1 → 2: blur ramps up over [blurStartFrame, blurPeakFrame]\n const blur = interpolate(\n frame,\n [blurStartFrame, blurPeakFrame],\n [0, maxBlur],\n { extrapolateLeft: \"clamp\", extrapolateRight: \"clamp\" },\n );\n\n // Dot grid opacity follows blur, plus a subtle sine shimmer.\n const gridBaseOpacity = interpolate(\n frame,\n [blurStartFrame, blurPeakFrame],\n [0, 0.4],\n { extrapolateLeft: \"clamp\", extrapolateRight: \"clamp\" },\n );\n const gridShimmer = Math.sin(frame / 6) * 0.05;\n const gridOpacity = Math.max(0, gridBaseOpacity + gridShimmer);\n\n // Generated image fade-in from revealStartFrame.\n const reveal = spring({\n frame: frame - revealStartFrame,\n fps,\n config: { mass: 1, damping: 22, stiffness: 90 },\n });\n const generatedOpacity = interpolate(reveal, [0, 1], [0, 1], {\n extrapolateLeft: \"clamp\",\n extrapolateRight: \"clamp\",\n });\n\n // Pill is visible during phases 2-3, fades out as the new image fades in.\n const pillIn = interpolate(frame, [blurStartFrame, blurPeakFrame], [0, 1], {\n extrapolateLeft: \"clamp\",\n extrapolateRight: \"clamp\",\n });\n const pillOut = interpolate(generatedOpacity, [0, 0.6], [1, 0], {\n extrapolateLeft: \"clamp\",\n extrapolateRight: \"clamp\",\n });\n const pillOpacity = pillIn * pillOut;\n const pillScale = 1 + Math.sin(frame / 8) * 0.02;\n const pillGlow = 0.35 + Math.sin(frame / 8) * 0.15;\n\n // Detached dot count animation for the pill text — three trailing dots.\n const dotPhase = Math.floor(frame / 10) % 4;\n const animatedPillText =\n pillText.replace(/[…\\.]+$/, \"\") + \".\".repeat(dotPhase);\n\n // Typed unused-suppression: keep durationInFrames referenced for IDEs.\n void durationInFrames;\n\n return (\n