{ "$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 \n {/* Source (\"painting\") layer — blurs out as AI thinks. */}\n \n\n {/* Generated image layer — fades in over the blurred source. */}\n \n\n {/* Dot grid overlay — pure SVG pattern with animated opacity + shimmer. */}\n \n \n \n \n \n \n \n \n\n {/* \"Generating…\" pill */}\n \n \n \n {animatedPillText}\n \n \n \n );\n}\n\n// Convert a #rrggbb to rgba(r, g, b, a). Falls back to the original string if\n// not a 6-digit hex (so users can pass a CSS color name and still get *some*\n// glow, just without alpha).\nfunction hexWithAlpha(hex: string, alpha: number): string {\n const m = /^#([0-9a-f]{6})$/i.exec(hex);\n if (!m) return hex;\n const int = parseInt(m[1], 16);\n const r = (int >> 16) & 255;\n const g = (int >> 8) & 255;\n const b = int & 255;\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n", "type": "registry:component", "target": "components/remocn/ai-generate-overlay.tsx" } ], "type": "registry:component" }