import { interpolate } from 'remotion'; import { C } from '../lib/colors'; import { springIn, softSpring, clampLerp } from '../lib/easings'; // Phase 2 (frames 159โ€“261 global โ†’ localFrame 0..102): build log streams in // line-by-line, then a server card emerges. Two pills slot into the card: // โ€ข `code` arrives from the left (the LLM side) // โ€ข `๐Ÿ”’ NOTION_API_KEY` arrives from the right (the vault side) // This visualizes the architectural moment: code and credentials are // injected at runtime from separate paths. // // Then a subtle caption appears below the card: // "your isolated container ยท only you can reach it" // // Log lines stagger ~10 frames apart starting at localFrame 4. // Server card emerges at localFrame ~58. // Slot pills fly in at localFrame ~78. const LOG_LINES = [ { label: 'Generating spec', detail: '2 tools detected' }, { label: 'Static checks', detail: 'passed' }, { label: 'Building image', detail: '17.2s' }, { label: 'Deploying', detail: 'live' }, ]; const LINE_STAGGER = 18; const LINE_START = 8; const CARD_START = 110; const SLOTS_START = 140; const CAPTION_START = 160; const SCENE_LEN = 216; // Countdown displays "< NN s" in the corner while build is running. // We tick from 58 โ†’ 12 between frames 8 โ†’ CARD_START. const COUNTDOWN_START_VAL = 58; const COUNTDOWN_END_VAL = 12; export function BuildScene({ localFrame, fps }: { localFrame: number; fps: number }) { const panelIn = springIn(localFrame, fps, 0); const panelOpacity = clampLerp(localFrame, 0, 12); // Card emerges late in phase. const cardIn = softSpring(localFrame, fps, CARD_START, 24); // Once the card is up, the log panel slides up to make room. const panelShift = interpolate(cardIn, [0, 1], [0, -160]); // Slot pills fly in after card is settled. const codeSlotIn = softSpring(localFrame, fps, SLOTS_START, 18); const secretSlotIn = softSpring(localFrame, fps, SLOTS_START + 4, 18); // Caption appears last. const captionIn = clampLerp(localFrame, CAPTION_START, CAPTION_START + 10); // Countdown: ticks from 58 โ†’ 12 as the log lines stream. const countT = clampLerp(localFrame, 8, CARD_START); const countVal = Math.round( interpolate(countT, [0, 1], [COUNTDOWN_START_VAL, COUNTDOWN_END_VAL]), ); const countShown = localFrame >= 4 && localFrame <= CARD_START + 24; return (
{/* Build log panel */}
{/* Panel header โ€” tiny status row */}
build ยท notion-search {countShown && ( {'< '}{countVal}{' s'} )} โ— running
{LOG_LINES.map((line, i) => ( ))}
{/* Server card (emerges in second half) */} {cardIn > 0.01 && ( )} {/* Isolated container caption */} {captionIn > 0.01 && (
your isolated container ยท only you can reach it
)}
); } function LogLine({ label, detail, localFrame, startFrame, fps, }: { label: string; detail: string; localFrame: number; startFrame: number; fps: number; }) { const spring = springIn(localFrame, fps, startFrame); const opacity = clampLerp(localFrame, startFrame, startFrame + 10); const x = interpolate(spring, [0, 1], [-30, 0]); // Check fills in slightly after the line slides in. const checkFill = clampLerp(localFrame, startFrame + 6, startFrame + 14); return (
{/* Checkmark circle */}
{label} {detail}
); } function ServerCard({ progress, localFrame, codeSlotIn, secretSlotIn, }: { progress: number; localFrame: number; codeSlotIn: number; secretSlotIn: number; }) { const scale = interpolate(progress, [0, 1], [0.85, 1]); const y = interpolate(progress, [0, 1], [40, 180]); // Live dot pulses once the slots have arrived. const liveOn = secretSlotIn > 0.6; const pulsePhase = (localFrame - (SLOTS_START + 18)) / 30; const livePulse = liveOn ? 0.6 + 0.4 * Math.sin(pulsePhase * Math.PI * 2) : 0; return (
{/* Header row: title + live dot */}
notion-search
{liveOn ? 'live' : 'starting'}
{/* Slot pills row */}
{/* code slot โ€” arrives from the left */} {/* secret slot โ€” arrives from the right */}
{/* Tool rows */}
); } function SlotPill({ label, kind, in: progress, fromX, }: { label: string; kind: 'code' | 'secret'; in: number; fromX: number; }) { const x = interpolate(progress, [0, 1], [fromX, 0]); const opacity = clampLerp(progress, 0.05, 0.6); const isSecret = kind === 'secret'; return (
{isSecret ? ( ) : ( {'<>'} )} {label}
); } function MiniLockIcon() { return ( ); } function ToolRow({ name, desc }: { name: string; desc: string }) { return (
{name} {desc}
); }