This component no longer exists. It was in remotionui’s manifest when we first indexed it and is not there now — the registry served its inventory on 2026-08-11 and this item was not on it. The install command below will fail. It is kept here because a tutorial somewhere still tells people to run it.
ai-composer-utils
remotionui/ai-composer-utilsRemovedUtility
Shared typewriter, caret, morph, and intro helpers for AI composer scenes
Install it
npx shadcn@latest add https://remotionui.com/r/ai-composer-utils.jsonSource
1import type { CSSProperties } from "react";2import { interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";34/** Frame the prompt starts typing in the chat-style composers. */5export const AI_TYPING_START = 42;6/** Frame the prompt starts typing in the terminal-style composers. */7export const AI_TYPING_START_TUI = 48;8/** Characters per second for the chat-style composers. */9export const AI_TYPING_CPS = 22;1011export function stageScale(12 width: number,13 height: number,14 refW = 1280,15 refH = 720,16): number {17 return Math.min(width / refW, height / refH);18}1920export interface TypewriterOptions {21 cps?: number;22 speed?: number;23 startFrame?: number;24}2526export interface TypewriterState {27 text: string;28 count: number;29 done: boolean;30 typing: boolean;31}3233/**34 * Character-by-character reveal. `typing` is false both before the first35 * character and after the last one, so a caret bound to `!typing` blinks while36 * idle and holds solid while text is being revealed.37 */38export function useTypewriter(39 full: string,40 options: TypewriterOptions = {},41): TypewriterState {42 const {43 cps = AI_TYPING_CPS,44 speed = 1,45 startFrame = AI_TYPING_START,46 } = options;47 const frame = useCurrentFrame();48 const { fps } = useVideoConfig();49 const local = frame * speed - startFrame;50 const over = (full.length / cps) * fps;51 const count =52 local <= 053 ? 054 : over <= 055 ? full.length56 : Math.max(0, Math.min(full.length, Math.floor((local / over) * full.length)));5758 return {59 text: full.slice(0, count),60 count,61 done: count >= full.length,62 typing: count > 0 && count < full.length,63 };64}6566/** Spring that drives the mic/waveform → send button morph. */67export function morphProgressAt(68 frame: number,69 opts: { startFrame?: number; fps: number; speed?: number },70): number {71 const { startFrame = AI_TYPING_START, fps, speed = 1 } = opts;72 const value = spring({73 fps,74 frame: frame * speed - startFrame,75 config: { damping: 14, stiffness: 200, mass: 0.6 },76 });77 return Math.max(0, Math.min(value, 1));78}7980export function introBounceIn(81 frame: number,82 fps: number,83): { translateY: number; scale: number } {84 const s = spring({85 fps,86 frame,87 config: { damping: 14, stiffness: 110, mass: 0.7 },88 });89 return {90 translateY: interpolate(s, [0, 1], [28, 0]),91 scale: interpolate(s, [0, 1], [0.97, 1]),92 };93}9495export function fadeUpAt(96 frame: number,97// … truncated
