import React from 'react'; import { AbsoluteFill, Img, interpolate, Sequence, spring, staticFile, useCurrentFrame, useVideoConfig, } from 'remotion'; import { colors, fonts } from '../lib/theme'; import { IPhone17ProFrame, SCREEN_W, SCREEN_H } from '../components/IPhone17ProFrame'; import { IOSKeyboard } from '../components/IOSKeyboard'; // ─── Scene timing (frames @ 30fps) ─── const TITLE_DUR = 60; const PHONES_DUR = 30; const TYPING_DUR = 468; const HOLD_DUR = 60; const OUTRO_DUR = 45; const TITLE_START = 0; const PHONES_START = TITLE_DUR; // 60 const TYPING_START = PHONES_START + PHONES_DUR; // 90 const HOLD_START = TYPING_START + TYPING_DUR; // 558 const OUTRO_START = HOLD_START + HOLD_DUR; // 618 export const ZEROLAG_TOTAL_FRAMES = OUTRO_START + OUTRO_DUR; // 663 // ─── iPhone 17 Pro safe area ─── const SAFE_AREA_TOP = 59; // Below Dynamic Island const PHONE_SCALE = 1.12; // Scale up to fill more of the frame // The Codeman screenshot starts content at y=0 (the session tab). // On a real device it would sit below the safe area, so we offset it. const SCREENSHOT_Y_OFFSET = SAFE_AREA_TOP; // Terminal typing overlay position (relative to screenshot top) // Session tab is ~44px, then terminal starts. Adding safe area offset: const TERMINAL_TOP = SAFE_AREA_TOP + 52; const TERMINAL_LEFT = 14; const TERMINAL_FONT = 22; // Large for video readability // ─── Typing schedule (with typo + backspace correction) ─── const CORRECT_TEXT = 'fix the auth bug in the login flow'; const FRAME_GAP = 12; // ~400ms between keystrokes const TYPO_INDEX = 28; // After "logi", type "m" instead of "n" // Remote connection lag: 600ms-1.2s+ per char (18-36+ frames) const LAGGY_DELAYS = [ 24, 30, 36, 32, 26, 22, 34, 28, 38, 20, 30, 24, 32, 26, 36, 22, 30, 24, 32, 28, 34, 26, 30, 22, 28, 36, 24, 30, 32, 26, 34, 28, 24, 30, 26, 32, 28, 34, ]; type KeyAction = { frame: number; action: 'type' | 'backspace'; char: string; lagDelay: number }; const buildSchedule = (): KeyAction[] => { const actions: KeyAction[] = []; let idx = 0; const lag = (i: number) => LAGGY_DELAYS[i % LAGGY_DELAYS.length]; // Type correctly up to typo point: "fix the auth bug in the logi" for (let i = 0; i < TYPO_INDEX; i++) { actions.push({ frame: idx * FRAME_GAP, action: 'type', char: CORRECT_TEXT[i], lagDelay: lag(idx) }); idx++; } // Typo: type "m" instead of "n" actions.push({ frame: idx * FRAME_GAP, action: 'type', char: 'm', lagDelay: lag(idx) }); idx++; // Backspace to fix it actions.push({ frame: idx * FRAME_GAP, action: 'backspace', char: '⌫', lagDelay: lag(idx) }); idx++; // Type correct remaining: "n flow" for (let i = TYPO_INDEX; i < CORRECT_TEXT.length; i++) { actions.push({ frame: idx * FRAME_GAP, action: 'type', char: CORRECT_TEXT[i], lagDelay: lag(idx) }); idx++; } return actions; }; const TYPING_SCHEDULE = buildSchedule(); /** Replay actions in order up to current frame, computing the visible text buffer */ const computeVisibleText = (frame: number, withLag: boolean): string => { let buffer = ''; for (const a of TYPING_SCHEDULE) { const threshold = withLag ? a.frame + a.lagDelay : a.frame; if (frame < threshold) break; // TCP-ordered: stop at first unresolved if (a.action === 'backspace') buffer = buffer.slice(0, -1); else buffer += a.char; } return buffer; }; // ─── iOS Status Bar (sits in the safe area, flanking Dynamic Island) ─── const IOSStatusBar: React.FC = () => (
{/* iOS status bar in the safe area */}