/** * Root — Remotion entry point. * * Registers 5 compositions: * ShipFeed — 7-slide ship announcement, 1080×1080 * ShipReels — 7-slide ship announcement, 1080×1920 * AdFeed — 5-7 slide ad video, 1080×1080 * AdReels — 5-7 slide ad video, 1080×1920 * DemoFeed — marketing demo video with custom scenes, 1080×1080 * * Ad compositions use calculateMetadata for dynamic duration — Claude may * generate 5-7 slides, so duration is computed from props at render time. * * This file is bundled at runtime by @remotion/bundler (webpack). * It must call registerRoot() as a side effect. */ import React from 'react'; import { Composition, registerRoot } from 'remotion'; import { ShipVideo } from './ShipVideo'; import type { ShipVideoProps } from './ShipVideo'; import { AdVideo } from './AdVideo'; import type { AdVideoProps } from './AdVideo'; import { DemoVideo } from './DemoVideo'; import type { DemoVideoProps, DemoSlideData } from './DemoVideo'; import type { SlideData } from './Slide'; // Remotion v4 Composition requires both type args. // Since we don't use Zod schemas, cast component props to satisfy the generic constraint. type AnyComp = React.ComponentType>; const ShipComp = ShipVideo as unknown as AnyComp; const AdComp = AdVideo as unknown as AnyComp; const DemoComp = DemoVideo as unknown as AnyComp; // ─── Frame counts ──────────────────────────────────────────────────────────── const FRAMES_PER_SLIDE = 90; // 3s at 30fps const SHIP_SLIDE_COUNT = 7; const AD_SLIDE_COUNT_DEFAULT = 5; // ─── Default props (used in Remotion Studio preview) ───────────────────────── const placeholderSlide: SlideData = { type: 'cover', title: 'Preview', subtitle: 'Just Shipped', }; const defaultShipSlides: SlideData[] = Array(SHIP_SLIDE_COUNT).fill(placeholderSlide); const defaultAdSlides: SlideData[] = Array(AD_SLIDE_COUNT_DEFAULT).fill(placeholderSlide); const defaultDemoSlides: DemoSlideData[] = [ { type: 'cover', title: 'Demo Preview', subtitle: 'While You Slept' }, { type: 'terminal', title: 'Processing', content: 'Loading...' }, { type: 'key_point', title: 'Key Point', content: 'Demo content' }, { type: 'notification', title: 'Alert', content: 'Demo notification' }, { type: 'kanban', title: 'Board', items: ['Col 1', 'Col 2', 'Col 3', 'Col 4'] }, { type: 'pipeline', title: 'Pipeline', content: 'Step 1 → Step 2 → Step 3' }, { type: 'cta', title: 'Get Started', cta_text: 'npm install vibebusiness' }, ]; // ─── Root component ────────────────────────────────────────────────────────── export const RemotionRoot: React.FC = () => { return ( <> ({ durationInFrames: (props as unknown as AdVideoProps).slides.length * FRAMES_PER_SLIDE, props, })} /> ({ durationInFrames: (props as unknown as AdVideoProps).slides.length * FRAMES_PER_SLIDE, props, })} /> ); }; registerRoot(RemotionRoot);