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 = 610;
const HOLD_DUR = 50;
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; // 700
const OUTRO_START = HOLD_START + HOLD_DUR; // 750
export const ZEROLAG_TOTAL_FRAMES = OUTRO_START + OUTRO_DUR; // 795
// ─── 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
// Claude Code header: tab bar + session info + prompt context from screenshot
const HEADER_H = 120;
// Terminal typing overlay: aligned with the ❯ prompt position in the Claude Code screenshot
const TERMINAL_TOP = 185;
const TERMINAL_LEFT = 14;
const TERMINAL_FONT = 21; // Slightly smaller to fit toolbar below
// Codeman toolbar from screenshot (bottom section showing /init, /clear, Run, etc.)
const TOOLBAR_H = 95;
// ─── Typing schedule ───
const CORRECT_TEXT =
'zerolag technology brings in a visual dom overlay to make typing instant, even if your codeman server is on the other side of the world';
const FRAME_GAP = 4; // ~133ms per keystroke (~75 WPM)
// Remote connection lag: 600ms–2.7s per char (18–80 frames @ 30fps).
// Periodic spikes simulate packet loss / retransmission bursts.
// TCP head-of-line blocking causes a single spike to freeze all subsequent chars.
const LAGGY_DELAYS = [
24, 30, 26, 32, 72, 28, 22, 34, 26, 30, 20, 28, 36, 24, 30, 22, 26, 34, 28, 20, 68, 30, 24, 32,
26, 22, 28, 34, 30, 26, 32, 24, 80, 22, 30, 26, 28, 34, 24, 30,
];
type KeyAction = { frame: number; char: string; lagDelay: number };
const buildSchedule = (): KeyAction[] => {
const actions: KeyAction[] = [];
const lag = (i: number) => LAGGY_DELAYS[i % LAGGY_DELAYS.length];
for (let i = 0; i < CORRECT_TEXT.length; i++) {
actions.push({ frame: i * FRAME_GAP, char: CORRECT_TEXT[i], lagDelay: lag(i) });
}
return actions;
};
const TYPING_SCHEDULE = buildSchedule();
/**
* Replay actions in order up to current frame, computing the visible text buffer.
* TCP-ordered: stops at first unresolved echo (head-of-line blocking).
*/
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;
buffer += a.char;
}
return buffer;
};
// ─── iOS Status Bar (sits in the safe area, flanking Dynamic Island) ───
const IOSStatusBar: React.FC = () => (
9:41
{/* Signal */}
{/* WiFi */}
{/* Battery */}
);
// ─── Typing overlay ───
const TypingOverlay: React.FC<{
typed: string;
cursorVisible: boolean;
}> = ({ typed, cursorVisible }) => {
const frame = useCurrentFrame();
const cursorOpacity = cursorVisible ? (Math.floor(frame / 18) % 2 === 0 ? 0.85 : 0.5) : 0;
const lineH = Math.round(TERMINAL_FONT * 1.4);
const charW = TERMINAL_FONT * 0.62;
return (
{'❯ '}
{typed}
);
};
// ─── Single phone: iPhone 17 Pro + Claude Code header + typing + keyboard ───
const MobileCodeman: React.FC<{
typed: string;
cursorVisible: boolean;
activeKey?: string;
pressAge?: number;
showKeyboard?: boolean;
noAnimation?: boolean;
showPromo?: boolean;
}> = ({ typed, cursorVisible, activeKey, pressAge, showKeyboard = true, noAnimation, showPromo }) => (
{/* iOS status bar in the safe area */}
{/* Claude Code header from screenshot (tabs + session info) */}
})
{/* Fade to terminal background */}
{/* Dark mask: hides screenshot text below Claude Code info (e.g. "Try edit...") */}
{/* Smooth gradient blend from screenshot into dark terminal */}
{/* Typing animation */}
{/* Codeman toolbar from screenshot (/init, /clear, /compact, Run, Run Shell, voice) */}
{/* Gradient blend at top */}
{/* Promo cards between text and toolbar (left phone only) */}
{showPromo &&
}
{/* iOS keyboard at bottom */}
{showKeyboard && (
)}
);
// ─── Label above phone (prominent header) ───
const PhoneLabel: React.FC<{
title: string;
detail: string;
dotColor: string;
detailColor: string;
}> = ({ title, detail, dotColor, detailColor }) => (
);
// ─── Promo banners (inside left phone, between text and keyboard) ───
const PromoBanners: React.FC = () => (
Codeman
github.com/Ark0N/Codeman
xterm-zerolag-input
npmjs.com/package/xterm-zerolag-input
);
// ─── Title scene ───
const TitleScene: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const titleScale = spring({ frame, fps, config: { damping: 15, stiffness: 80 } });
const titleOpacity = interpolate(frame, [0, 20], [0, 1], { extrapolateRight: 'clamp' });
const subtitleOpacity = interpolate(frame, [15, 35], [0, 1], { extrapolateRight: 'clamp' });
return (
Zerolag Input
Local echo for remote terminal sessions
);
};
// ─── Outro ───
const OutroScene: React.FC = () => {
const frame = useCurrentFrame();
const opacity = interpolate(frame, [0, 20], [0, 1], { extrapolateRight: 'clamp' });
return (
Codeman
Zero-latency mobile input
npm i xterm-zerolag-input
);
};
// ─── Typing demo scene ───
const TypingDemo: React.FC = () => {
const frame = useCurrentFrame();
const laggyTyped = computeVisibleText(frame, true);
const zerolagTyped = computeVisibleText(frame, false);
let activeKey: string | undefined;
let pressAge = 99;
for (let i = TYPING_SCHEDULE.length - 1; i >= 0; i--) {
const ev = TYPING_SCHEDULE[i];
if (frame >= ev.frame && frame < ev.frame + 5) {
activeKey = ev.char;
pressAge = frame - ev.frame;
break;
}
}
return (
);
};
// ─── Phones entrance ───
const PanelsEntrance: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const scale = spring({ frame, fps, config: { damping: 15, stiffness: 80 } });
const opacity = interpolate(frame, [0, 10], [0, 1], { extrapolateRight: 'clamp' });
return (
);
};
// ─── Main composition ───
export const ZerolagDemo: React.FC = () => {
return (
);
};