remotion
Version:
Make videos programmatically
2,356 lines • 68.6 kB
JavaScript
// src/bezier.ts
var NEWTON_ITERATIONS = 4;
var NEWTON_MIN_SLOPE = 0.001;
var SUBDIVISION_PRECISION = 0.0000001;
var SUBDIVISION_MAX_ITERATIONS = 10;
var kSplineTableSize = 11;
var kSampleStepSize = 1 / (kSplineTableSize - 1);
var float32ArraySupported = typeof Float32Array === "function";
function a(aA1, aA2) {
return 1 - 3 * aA2 + 3 * aA1;
}
function b(aA1, aA2) {
return 3 * aA2 - 6 * aA1;
}
function c(aA1) {
return 3 * aA1;
}
function calcBezier(aT, aA1, aA2) {
return ((a(aA1, aA2) * aT + b(aA1, aA2)) * aT + c(aA1)) * aT;
}
function getSlope(aT, aA1, aA2) {
return 3 * a(aA1, aA2) * aT * aT + 2 * b(aA1, aA2) * aT + c(aA1);
}
function binarySubdivide({
aX,
_aA,
_aB,
mX1,
mX2
}) {
let currentX;
let currentT;
let i = 0;
let aA = _aA;
let aB = _aB;
do {
currentT = aA + (aB - aA) / 2;
currentX = calcBezier(currentT, mX1, mX2) - aX;
if (currentX > 0) {
aB = currentT;
} else {
aA = currentT;
}
} while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
return currentT;
}
function newtonRaphsonIterate(aX, _aGuessT, mX1, mX2) {
let aGuessT = _aGuessT;
for (let i = 0;i < NEWTON_ITERATIONS; ++i) {
const currentSlope = getSlope(aGuessT, mX1, mX2);
if (currentSlope === 0) {
return aGuessT;
}
const currentX = calcBezier(aGuessT, mX1, mX2) - aX;
aGuessT -= currentX / currentSlope;
}
return aGuessT;
}
function bezier(mX1, mY1, mX2, mY2) {
if (!(mX1 >= 0 && mX1 <= 1 && mX2 >= 0 && mX2 <= 1)) {
throw new Error("bezier x values must be in [0, 1] range");
}
const sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
if (mX1 !== mY1 || mX2 !== mY2) {
for (let i = 0;i < kSplineTableSize; ++i) {
sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
}
}
function getTForX(aX) {
let intervalStart = 0;
let currentSample = 1;
const lastSample = kSplineTableSize - 1;
for (;currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
intervalStart += kSampleStepSize;
}
--currentSample;
const dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]);
const guessForT = intervalStart + dist * kSampleStepSize;
const initialSlope = getSlope(guessForT, mX1, mX2);
if (initialSlope >= NEWTON_MIN_SLOPE) {
return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
}
if (initialSlope === 0) {
return guessForT;
}
return binarySubdivide({
aX,
_aA: intervalStart,
_aB: intervalStart + kSampleStepSize,
mX1,
mX2
});
}
return function(x) {
const clampedX = Math.min(1, Math.max(0, x));
if (mX1 === mY1 && mX2 === mY2) {
return clampedX;
}
if (clampedX === 0) {
return 0;
}
if (clampedX === 1) {
return 1;
}
return calcBezier(getTForX(clampedX), mY1, mY2);
};
}
// src/validate-frame.ts
var validateFrame = ({
allowFloats,
durationInFrames,
frame
}) => {
if (typeof frame === "undefined") {
throw new TypeError(`Argument missing for parameter "frame"`);
}
if (typeof frame !== "number") {
throw new TypeError(`Argument passed for "frame" is not a number: ${frame}`);
}
if (!Number.isFinite(frame)) {
throw new RangeError(`Frame ${frame} is not finite`);
}
if (frame % 1 !== 0 && !allowFloats) {
throw new RangeError(`Argument for frame must be an integer, but got ${frame}`);
}
if (frame < 0 && frame < -durationInFrames) {
throw new RangeError(`Cannot use frame ${frame}: Duration of composition is ${durationInFrames}, therefore the lowest frame that can be rendered is ${-durationInFrames}`);
}
if (frame > durationInFrames - 1) {
throw new RangeError(`Cannot use frame ${frame}: Duration of composition is ${durationInFrames}, therefore the highest frame that can be rendered is ${durationInFrames - 1}`);
}
};
// src/validation/validate-fps.ts
function validateFps(fps, location, isGif) {
if (typeof fps !== "number") {
throw new Error(`"fps" must be a number, but you passed a value of type ${typeof fps} ${location}`);
}
if (!Number.isFinite(fps)) {
throw new Error(`"fps" must be a finite, but you passed ${fps} ${location}`);
}
if (isNaN(fps)) {
throw new Error(`"fps" must not be NaN, but got ${fps} ${location}`);
}
if (fps <= 0) {
throw new TypeError(`"fps" must be positive, but got ${fps} ${location}`);
}
if (isGif && fps > 50) {
throw new TypeError(`The FPS for a GIF cannot be higher than 50. Use the --every-nth-frame option to lower the FPS: https://remotion.dev/docs/render-as-gif`);
}
}
// src/validation/validation-spring-duration.ts
var validateSpringDuration = (dur) => {
if (typeof dur === "undefined") {
return;
}
if (typeof dur !== "number") {
throw new TypeError(`A "duration" of a spring must be a "number" but is "${typeof dur}"`);
}
if (Number.isNaN(dur)) {
throw new TypeError('A "duration" of a spring is NaN, which it must not be');
}
if (!Number.isFinite(dur)) {
throw new TypeError('A "duration" of a spring must be finite, but is ' + dur);
}
if (dur <= 0) {
throw new TypeError('A "duration" of a spring must be positive, but is ' + dur);
}
};
// src/spring/spring-utils.ts
var defaultSpringConfig = {
damping: 10,
mass: 1,
stiffness: 100,
overshootClamping: false
};
var advanceCache = {};
function advance({
animation,
now,
config
}) {
const { toValue, lastTimestamp, current, velocity } = animation;
const deltaTime = Math.min(now - lastTimestamp, 64);
if (config.damping <= 0) {
throw new Error("Spring damping must be greater than 0, otherwise the spring() animation will never end, causing an infinite loop.");
}
const c2 = config.damping;
const m = config.mass;
const k = config.stiffness;
const cacheKey = [
toValue,
lastTimestamp,
current,
velocity,
c2,
m,
k,
now
].join("-");
if (advanceCache[cacheKey]) {
return advanceCache[cacheKey];
}
const v0 = -velocity;
const x0 = toValue - current;
const zeta = c2 / (2 * Math.sqrt(k * m));
const omega0 = Math.sqrt(k / m);
const omega1 = omega0 * Math.sqrt(1 - zeta ** 2);
const t = deltaTime / 1000;
const sin1 = Math.sin(omega1 * t);
const cos1 = Math.cos(omega1 * t);
const underDampedEnvelope = Math.exp(-zeta * omega0 * t);
const underDampedFrag1 = underDampedEnvelope * (sin1 * ((v0 + zeta * omega0 * x0) / omega1) + x0 * cos1);
const underDampedPosition = toValue - underDampedFrag1;
const underDampedVelocity = zeta * omega0 * underDampedFrag1 - underDampedEnvelope * (cos1 * (v0 + zeta * omega0 * x0) - omega1 * x0 * sin1);
const criticallyDampedEnvelope = Math.exp(-omega0 * t);
const criticallyDampedPosition = toValue - criticallyDampedEnvelope * (x0 + (v0 + omega0 * x0) * t);
const criticallyDampedVelocity = criticallyDampedEnvelope * (v0 * (t * omega0 - 1) + t * x0 * omega0 * omega0);
const animationNode = {
toValue,
prevPosition: current,
lastTimestamp: now,
current: zeta < 1 ? underDampedPosition : criticallyDampedPosition,
velocity: zeta < 1 ? underDampedVelocity : criticallyDampedVelocity
};
advanceCache[cacheKey] = animationNode;
return animationNode;
}
var calculationCache = {};
function springCalculation({
frame,
fps,
config = {}
}) {
const from = 0;
const to = 1;
const cacheKey = [
frame,
fps,
config.damping,
config.mass,
config.overshootClamping,
config.stiffness
].join("-");
if (calculationCache[cacheKey]) {
return calculationCache[cacheKey];
}
let animation = {
lastTimestamp: 0,
current: from,
toValue: to,
velocity: 0,
prevPosition: 0
};
const frameClamped = Math.max(0, frame);
const unevenRest = frameClamped % 1;
for (let f = 0;f <= Math.floor(frameClamped); f++) {
const time = f / fps * 1000;
animation = advance({
animation,
now: time,
config: {
...defaultSpringConfig,
...config
}
});
}
if (unevenRest > 0) {
animation = advance({
animation,
now: frameClamped / fps * 1000,
config: {
...defaultSpringConfig,
...config
}
});
}
calculationCache[cacheKey] = animation;
return animation;
}
// src/spring/measure-spring.ts
var cache = new Map;
function measureSpring({
fps,
config = {},
threshold = 0.005
}) {
if (typeof threshold !== "number") {
throw new TypeError(`threshold must be a number, got ${threshold} of type ${typeof threshold}`);
}
if (threshold === 0) {
return Infinity;
}
if (threshold === 1) {
return 0;
}
if (isNaN(threshold)) {
throw new TypeError("Threshold is NaN");
}
if (!Number.isFinite(threshold)) {
throw new TypeError("Threshold is not finite");
}
if (threshold < 0) {
throw new TypeError("Threshold is below 0");
}
const cacheKey = [
fps,
config.damping,
config.mass,
config.overshootClamping,
config.stiffness,
threshold
].join("-");
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
validateFps(fps, "to the measureSpring() function", false);
let frame = 0;
let finishedFrame = 0;
const calc = () => {
return springCalculation({
fps,
frame,
config
});
};
let animation = calc();
const calcDifference = () => {
return Math.abs(animation.current - animation.toValue);
};
let difference = calcDifference();
while (difference >= threshold) {
frame++;
animation = calc();
difference = calcDifference();
}
finishedFrame = frame;
for (let i = 0;i < 20; i++) {
frame++;
animation = calc();
difference = calcDifference();
if (difference >= threshold) {
i = 0;
finishedFrame = frame + 1;
}
}
cache.set(cacheKey, finishedFrame);
return finishedFrame;
}
// src/spring/index.ts
function spring({
frame: passedFrame,
fps,
config = {},
from = 0,
to = 1,
durationInFrames: passedDurationInFrames,
durationRestThreshold,
delay = 0,
reverse = false
}) {
validateSpringDuration(passedDurationInFrames);
validateFrame({
frame: passedFrame,
durationInFrames: Infinity,
allowFloats: true
});
validateFps(fps, "to spring()", false);
const needsToCalculateNaturalDuration = reverse || typeof passedDurationInFrames !== "undefined";
const naturalDuration = needsToCalculateNaturalDuration ? measureSpring({
fps,
config,
threshold: durationRestThreshold
}) : undefined;
const naturalDurationGetter = needsToCalculateNaturalDuration ? {
get: () => naturalDuration
} : {
get: () => {
throw new Error("did not calculate natural duration, this is an error with Remotion. Please report");
}
};
const reverseProcessed = reverse ? (passedDurationInFrames ?? naturalDurationGetter.get()) - passedFrame : passedFrame;
const delayProcessed = reverseProcessed + (reverse ? delay : -delay);
const durationProcessed = passedDurationInFrames === undefined ? delayProcessed : delayProcessed / (passedDurationInFrames / naturalDurationGetter.get());
if (passedDurationInFrames && delayProcessed > passedDurationInFrames) {
return to;
}
const spr = springCalculation({
fps,
frame: durationProcessed,
config
});
const inner = config.overshootClamping ? to >= from ? Math.min(spr.current, to) : Math.max(spr.current, to) : spr.current;
const interpolated = from === 0 && to === 1 ? inner : interpolate(inner, [0, 1], [from, to]);
return interpolated;
}
// src/easing.ts
var clampUnit = (t) => Math.min(1, Math.max(0, t));
var springEasingDurationInFrames = 30;
class Easing {
static step0(n) {
return n > 0 ? 1 : 0;
}
static step1(n) {
return n >= 1 ? 1 : 0;
}
static linear(t) {
return t;
}
static ease(t) {
return Easing.bezier(0.42, 0, 1, 1)(t);
}
static quad(t) {
return t * t;
}
static cubic(t) {
return t * t * t;
}
static poly(n) {
return (t) => t ** n;
}
static sin(t) {
return 1 - Math.cos(t * Math.PI / 2);
}
static circle(t) {
const u = clampUnit(t);
return 1 - Math.sqrt(1 - u * u);
}
static exp(t) {
return 2 ** (10 * (t - 1));
}
static elastic(bounciness = 1) {
const p = bounciness * Math.PI;
return (t) => 1 - Math.cos(t * Math.PI / 2) ** 3 * Math.cos(t * p);
}
static back(s = 1.70158) {
return (t) => t * t * ((s + 1) * t - s);
}
static spring({
allowTail = false,
durationRestThreshold,
...config
} = {}) {
const easing = (t) => {
if (t <= 0) {
return 0;
}
if (!allowTail && t >= 1) {
return 1;
}
if (allowTail) {
return spring({
fps: springEasingDurationInFrames,
frame: t * measureSpring({
fps: springEasingDurationInFrames,
config,
threshold: durationRestThreshold
}),
config
});
}
return spring({
fps: springEasingDurationInFrames,
frame: t * springEasingDurationInFrames,
config,
durationInFrames: springEasingDurationInFrames,
durationRestThreshold
});
};
return Object.assign(easing, {
remotionShouldExtendRight: allowTail
});
}
static bounce(t) {
const u = clampUnit(t);
if (u < 1 / 2.75) {
return 7.5625 * u * u;
}
if (u < 2 / 2.75) {
const t2_ = u - 1.5 / 2.75;
return 7.5625 * t2_ * t2_ + 0.75;
}
if (u < 2.5 / 2.75) {
const t2_ = u - 2.25 / 2.75;
return 7.5625 * t2_ * t2_ + 0.9375;
}
const t2 = u - 2.625 / 2.75;
return 7.5625 * t2 * t2 + 0.984375;
}
static bezier(x1, y1, x2, y2) {
return bezier(x1, y1, x2, y2);
}
static in(easing) {
return easing;
}
static out(easing) {
return (t) => 1 - easing(1 - t);
}
static inOut(easing) {
return (t) => {
if (t < 0.5) {
return easing(t * 2) / 2;
}
return 1 - easing((1 - t) * 2) / 2;
};
}
}
// src/normalize-number.ts
var normalizeNumber = (value) => {
return Math.round(value * 1e6) / 1e6;
};
// src/interpolate.ts
var angleUnits = new Set(["deg", "rad", "grad", "turn"]);
var lengthUnits = new Set([
"%",
"cap",
"ch",
"cm",
"cqb",
"cqh",
"cqi",
"cqmax",
"cqmin",
"cqw",
"dvh",
"dvw",
"em",
"ex",
"ic",
"in",
"lh",
"lvh",
"lvw",
"mm",
"pc",
"pt",
"px",
"q",
"rem",
"rlh",
"svh",
"svw",
"vb",
"vh",
"vi",
"vmax",
"vmin",
"vw"
]);
var cssNumberRegex = /^([+-]?(?:\d+\.?\d*|\.\d+))([a-zA-Z%]+)?$/;
var transformOriginKeywords = new Set([
"left",
"center",
"right",
"top",
"bottom"
]);
var transformOriginKeywordOptions = (keyword) => {
if (keyword === "left") {
return [{ axis: "x", value: { value: 0, unit: "%" } }];
}
if (keyword === "right") {
return [{ axis: "x", value: { value: 100, unit: "%" } }];
}
if (keyword === "top") {
return [{ axis: "y", value: { value: 0, unit: "%" } }];
}
if (keyword === "bottom") {
return [{ axis: "y", value: { value: 100, unit: "%" } }];
}
return [
{ axis: "x", value: { value: 50, unit: "%" } },
{ axis: "y", value: { value: 50, unit: "%" } }
];
};
var transformOriginCenter = { value: 50, unit: "%" };
var stringifyNumber = (value) => {
return String(normalizeNumber(value));
};
class UnsupportedStringInterpolationValueError extends TypeError {
}
var parseStringInterpolationComponent = (component, value) => {
const match = cssNumberRegex.exec(component);
if (match === null) {
throw new UnsupportedStringInterpolationValueError(`Cannot interpolate "${value}" because "${component}" is not a supported scale, translate, or rotate value`);
}
const unit = match[2] ?? null;
const numberValue = Number(match[1]);
if (!Number.isFinite(numberValue)) {
throw new TypeError(`Cannot interpolate "${value}" because "${component}" is not finite`);
}
if (unit === null) {
return { kind: "scale", value: numberValue, unit: null };
}
if (angleUnits.has(unit)) {
return { kind: "rotate", value: numberValue, unit };
}
if (lengthUnits.has(unit)) {
return { kind: "translate", value: numberValue, unit };
}
throw new TypeError(`Cannot interpolate "${value}" because "${unit}" is not a supported translate or rotate unit`);
};
var parseTransformOriginLengthPercentage = ({
component,
value,
allowPercentage
}) => {
const match = cssNumberRegex.exec(component);
if (match === null) {
throw new TypeError(`Cannot interpolate "${value}" because "${component}" is not a supported transform-origin ${allowPercentage ? "length-percentage" : "z length"}`);
}
const unit = match[2] ?? null;
const numberValue = Number(match[1]);
if (!Number.isFinite(numberValue)) {
throw new TypeError(`Cannot interpolate "${value}" because "${component}" is not finite`);
}
if (unit === null || !lengthUnits.has(unit) || !allowPercentage && unit === "%") {
throw new TypeError(`Cannot interpolate "${value}" because "${component}" is not a supported transform-origin ${allowPercentage ? "length-percentage" : "z length"}`);
}
return { value: numberValue, unit };
};
var parseTransformOriginToken = (component, value) => {
const lower = component.toLowerCase();
if (transformOriginKeywords.has(lower)) {
return { type: "keyword", keyword: lower };
}
return {
type: "length-percentage",
parsed: parseTransformOriginLengthPercentage({
component,
value,
allowPercentage: true
})
};
};
var parseTwoTransformOriginKeywords = (first, second, value) => {
const candidates = [];
for (const firstOption of transformOriginKeywordOptions(first)) {
for (const secondOption of transformOriginKeywordOptions(second)) {
if (firstOption.axis === secondOption.axis) {
continue;
}
candidates.push(firstOption.axis === "x" ? [firstOption.value, secondOption.value] : [secondOption.value, firstOption.value]);
}
}
if (candidates.length === 0) {
throw new TypeError(`Cannot interpolate "${value}" because "${first} ${second}" is not a valid transform-origin keyword pair`);
}
return candidates[0];
};
var parseTransformOriginXY = (parts, value) => {
if (parts.length === 1) {
const token = parseTransformOriginToken(parts[0], value);
if (token.type === "length-percentage") {
return [token.parsed, transformOriginCenter];
}
if (token.keyword === "top" || token.keyword === "bottom") {
return [
transformOriginCenter,
transformOriginKeywordOptions(token.keyword)[0].value
];
}
return [
transformOriginKeywordOptions(token.keyword)[0].value,
transformOriginCenter
];
}
const first = parseTransformOriginToken(parts[0], value);
const second = parseTransformOriginToken(parts[1], value);
if (first.type === "length-percentage" && second.type === "length-percentage") {
return [first.parsed, second.parsed];
}
if (first.type === "keyword" && second.type === "keyword") {
return parseTwoTransformOriginKeywords(first.keyword, second.keyword, value);
}
const keyword = first.type === "keyword" ? first : second.type === "keyword" ? second : null;
const length = first.type === "length-percentage" ? first.parsed : second.type === "length-percentage" ? second.parsed : null;
if (keyword === null || length === null) {
throw new Error("Expected a keyword and a length-percentage value");
}
const keywordIsFirst = first.type === "keyword";
if (keyword.keyword === "left" || keyword.keyword === "right") {
if (!keywordIsFirst) {
throw new TypeError(`Cannot interpolate "${value}" because horizontal transform-origin keywords must come before a length-percentage value`);
}
return [transformOriginKeywordOptions(keyword.keyword)[0].value, length];
}
if (keyword.keyword === "top" || keyword.keyword === "bottom") {
return [length, transformOriginKeywordOptions(keyword.keyword)[0].value];
}
return keywordIsFirst ? [transformOriginCenter, length] : [length, transformOriginCenter];
};
var parseTransformOriginValue = (output, parts) => {
const [x, y] = parseTransformOriginXY(parts.slice(0, 2), output);
const z = parts[2] === undefined ? { value: 0, unit: null } : parseTransformOriginLengthPercentage({
component: parts[2],
value: output,
allowPercentage: false
});
return {
kind: "translate",
values: [x.value, y.value, z.value, 0],
units: [x.unit, y.unit, z.unit, null],
dimensions: parts[2] === undefined ? 2 : 3,
axisRotation: false
};
};
var parseAxisRotationValue = (output) => {
const parts = output.trim().split(/\s+/);
const keywordAxis = parts.length === 2 ? parts[0].toLowerCase() : null;
if (keywordAxis === "x" || keywordAxis === "y" || keywordAxis === "z") {
const keywordAngle = parseStringInterpolationComponent(parts[1], output);
if (keywordAngle.kind !== "rotate") {
return null;
}
return {
kind: "rotate",
values: keywordAxis === "x" ? [1, 0, 0, keywordAngle.value] : keywordAxis === "y" ? [0, 1, 0, keywordAngle.value] : [0, 0, 1, keywordAngle.value],
units: [null, null, null, keywordAngle.unit],
dimensions: 4,
axisRotation: true
};
}
if (parts.length !== 4) {
return null;
}
const axis = parts.slice(0, 3).map(Number);
if (!axis.every(Number.isFinite)) {
return null;
}
const vectorAngle = parseStringInterpolationComponent(parts[3], output);
if (vectorAngle.kind !== "rotate") {
return null;
}
return {
kind: "rotate",
values: [axis[0], axis[1], axis[2], vectorAngle.value],
units: [null, null, null, vectorAngle.unit],
dimensions: 4,
axisRotation: true
};
};
var parseStringInterpolationValue = (output) => {
if (typeof output === "number") {
if (!Number.isFinite(output)) {
throw new Error(`outputRange must contain only finite numbers, but got [${output}]`);
}
return {
kind: "scale",
values: [output, output, 1, 0],
units: [null, null, null, null],
dimensions: 1,
axisRotation: false
};
}
const axisRotation = parseAxisRotationValue(output);
if (axisRotation !== null) {
return axisRotation;
}
const parts = output.trim().split(/\s+/);
if (parts.length < 1 || parts.length > 3 || parts[0] === "") {
throw new TypeError(`String outputRange values must contain 1 to 3 components, but got "${output}"`);
}
if (parts.some((part) => transformOriginKeywords.has(part.toLowerCase()))) {
return parseTransformOriginValue(output, parts);
}
const parsed = parts.map((part) => parseStringInterpolationComponent(part, output));
const [{ kind }] = parsed;
for (const part of parsed) {
if (part.kind !== kind) {
throw new TypeError(`Cannot interpolate "${output}" because it mixes ${kind} and ${part.kind} values`);
}
}
if (kind === "scale") {
const x = parsed[0].value;
const y = parsed[1]?.value ?? x;
const z = parsed[2]?.value ?? 1;
return {
kind,
values: [x, y, z, 0],
units: [null, null, null, null],
dimensions: parsed.length,
axisRotation: false
};
}
return {
kind,
values: [parsed[0].value, parsed[1]?.value ?? 0, parsed[2]?.value ?? 0, 0],
units: [
parsed[0].unit,
parsed[1]?.unit ?? null,
parsed[2]?.unit ?? null,
null
],
dimensions: parsed.length,
axisRotation: false
};
};
var serializeStringInterpolationValue = ({
kind,
values,
units,
dimensions,
axisRotation
}) => {
if (axisRotation) {
return `${stringifyNumber(values[0])} ${stringifyNumber(values[1])} ${stringifyNumber(values[2])} ${stringifyNumber(values[3])}${units[3]}`;
}
if (kind === "scale") {
return values.slice(0, dimensions).map((value) => stringifyNumber(value)).join(" ");
}
return values.slice(0, dimensions).map((value, index) => `${stringifyNumber(value)}${units[index]}`).join(" ");
};
var toSignedArea = (scale) => {
if (scale === 0) {
return 0;
}
return Math.sign(scale) * scale * scale;
};
var fromSignedArea = (area) => {
if (area === 0) {
return 0;
}
return Math.sign(area) * Math.sqrt(Math.abs(area));
};
function interpolateFunction(input, inputRange, outputRange, options) {
const { extrapolateLeft, extrapolateRight, easing, output } = options;
let result = input;
const [inputMin, inputMax] = inputRange;
const [outputMin, outputMax] = outputRange;
if (result < inputMin) {
if (extrapolateLeft === "identity") {
return result;
}
if (extrapolateLeft === "clamp") {
result = inputMin;
} else if (extrapolateLeft === "wrap") {
const range = inputMax - inputMin;
result = ((result - inputMin) % range + range) % range + inputMin;
} else if (extrapolateLeft === "extend") {}
}
if (result > inputMax) {
if (extrapolateRight === "identity") {
return result;
}
if (extrapolateRight === "clamp") {
result = inputMax;
} else if (extrapolateRight === "wrap") {
const range = inputMax - inputMin;
result = ((result - inputMin) % range + range) % range + inputMin;
} else if (extrapolateRight === "extend") {}
}
if (outputMin === outputMax) {
return outputMin;
}
result = (result - inputMin) / (inputMax - inputMin);
result = easing(result);
if (output === "perceptual-scale") {
const signedAreaMin = toSignedArea(outputMin);
const signedAreaMax = toSignedArea(outputMax);
result = fromSignedArea(result * (signedAreaMax - signedAreaMin) + signedAreaMin);
} else {
result = result * (outputMax - outputMin) + outputMin;
}
return result;
}
function findRange(input, inputRange) {
let i;
for (i = 1;i < inputRange.length - 1; ++i) {
if (inputRange[i] >= input) {
break;
}
}
return i - 1;
}
var defaultEasing = (num) => num;
var resolveOutputOption = (output) => {
return output ?? "linear";
};
var shouldExtendRightForEasing = (easing) => {
return easing.remotionShouldExtendRight === true;
};
var resolveEasingForSegment = ({
easing,
segmentIndex
}) => {
if (easing === undefined) {
return defaultEasing;
}
if (typeof easing === "function") {
return easing;
}
return easing[segmentIndex];
};
var interpolateSegment = ({
input,
inputRange,
outputRange,
easing,
extrapolateLeft,
extrapolateRight,
output
}) => {
return interpolateFunction(input, inputRange, outputRange, {
easing,
extrapolateLeft,
extrapolateRight: input > inputRange[1] && extrapolateRight === "clamp" && shouldExtendRightForEasing(easing) ? "extend" : extrapolateRight,
output
});
};
var interpolateNumber = ({
input,
inputRange,
outputRange,
options
}) => {
const output = resolveOutputOption(options?.output);
if (inputRange.length === 1) {
return outputRange[0];
}
const easingOption = options?.easing;
let extrapolateLeft = "extend";
if (options?.extrapolateLeft !== undefined) {
extrapolateLeft = options.extrapolateLeft;
}
let extrapolateRight = "extend";
if (options?.extrapolateRight !== undefined) {
extrapolateRight = options.extrapolateRight;
}
const posterizedInput = options?.posterize === undefined ? input : Math.floor(input / options.posterize) * options.posterize;
const range = findRange(posterizedInput, inputRange);
const easing = resolveEasingForSegment({
easing: easingOption,
segmentIndex: range
});
let result = interpolateSegment({
input: posterizedInput,
inputRange: [inputRange[range], inputRange[range + 1]],
outputRange: [outputRange[range], outputRange[range + 1]],
easing,
extrapolateLeft,
extrapolateRight,
output
});
for (let segmentIndex = 0;segmentIndex < range; segmentIndex++) {
const previousEasing = resolveEasingForSegment({
easing: easingOption,
segmentIndex
});
if (!shouldExtendRightForEasing(previousEasing)) {
continue;
}
const previousSegmentEnd = inputRange[segmentIndex + 1];
if (posterizedInput <= previousSegmentEnd) {
continue;
}
const continuedSegmentValue = interpolateSegment({
input: posterizedInput,
inputRange: [inputRange[segmentIndex], previousSegmentEnd],
outputRange: [outputRange[segmentIndex], outputRange[segmentIndex + 1]],
easing: previousEasing,
extrapolateLeft,
extrapolateRight: "extend",
output
});
result += continuedSegmentValue - outputRange[segmentIndex + 1];
}
return result;
};
var interpolateString = ({
input,
inputRange,
outputRange,
options
}) => {
const initiallyParsedOutputRange = outputRange.map(parseStringInterpolationValue);
const hasAxisRotation = initiallyParsedOutputRange.some((parsed) => parsed.axisRotation);
const posterizedInput = options?.posterize === undefined ? input : Math.floor(input / options.posterize) * options.posterize;
const segmentIndex = inputRange.length === 1 ? 0 : findRange(posterizedInput, inputRange);
const parsedOutputRange = hasAxisRotation ? initiallyParsedOutputRange.map((parsed, index) => {
if (parsed.kind !== "rotate") {
return parsed;
}
if (parsed.axisRotation) {
return parsed;
}
if (parsed.dimensions !== 1) {
throw new TypeError("Cannot interpolate a multi-angle rotate value with an axis rotation");
}
const adjacentAxisRotation = parsed.values[0] === 0 ? index === 0 ? initiallyParsedOutputRange.find((candidate) => candidate.axisRotation) : index === initiallyParsedOutputRange.length - 1 ? [...initiallyParsedOutputRange].reverse().find((candidate) => candidate.axisRotation) : index === segmentIndex ? initiallyParsedOutputRange[index + 1] : index === segmentIndex + 1 ? initiallyParsedOutputRange[index - 1] : undefined : undefined;
const axis = adjacentAxisRotation?.axisRotation ? adjacentAxisRotation.values : [0, 0, 1];
return {
kind: "rotate",
values: [axis[0], axis[1], axis[2], parsed.values[0]],
units: [null, null, null, parsed.units[0]],
dimensions: 4,
axisRotation: true
};
}) : initiallyParsedOutputRange;
const kind = parsedOutputRange[0]?.kind;
if (kind === undefined) {
throw new Error("outputRange must have at least 1 element");
}
for (const parsed of parsedOutputRange) {
if (parsed.kind !== kind) {
throw new TypeError(`Cannot interpolate ${kind} values with ${parsed.kind} values`);
}
}
const dimensions = Math.max(...parsedOutputRange.map((parsed) => parsed.dimensions));
const units = [
null,
null,
null,
null
];
if (kind !== "scale") {
for (let axis = 0;axis < dimensions; axis++) {
if (hasAxisRotation && axis < 3) {
continue;
}
for (const parsed of parsedOutputRange) {
const unit = parsed.units[axis];
if (unit === null) {
continue;
}
if (units[axis] === null) {
units[axis] = unit;
continue;
}
if (units[axis] !== unit) {
throw new TypeError(`Cannot interpolate ${kind} values with different units on axis ${axis + 1}: ${units[axis]} and ${unit}`);
}
}
if (units[axis] === null) {
throw new TypeError(`Cannot interpolate ${kind} values because axis ${axis + 1} has no unit`);
}
}
}
const values = [0, 0, 0, 0];
for (let axis = 0;axis < dimensions; axis++) {
values[axis] = interpolateNumber({
input,
inputRange,
outputRange: parsedOutputRange.map((parsed) => parsed.values[axis]),
options
});
}
return serializeStringInterpolationValue({
kind,
values,
units,
dimensions,
axisRotation: hasAxisRotation
});
};
var interpolateDiscreteString = ({
input,
inputRange,
outputRange,
options
}) => {
if (inputRange.length === 1) {
return outputRange[0];
}
for (let segmentIndex = 0;segmentIndex < inputRange.length - 1; segmentIndex++) {
if (resolveEasingForSegment({
easing: options?.easing,
segmentIndex
}) !== Easing.step1) {
throw new TypeError("Non-numeric strings can only be interpolated using Easing.step1");
}
}
const posterizedInput = options?.posterize === undefined ? input : Math.floor(input / options.posterize) * options.posterize;
const inputMin = inputRange[0];
const inputMax = inputRange[inputRange.length - 1];
let resolvedInput = posterizedInput;
if (resolvedInput < inputMin) {
if (options?.extrapolateLeft === "identity") {
throw new TypeError('extrapolateLeft: "identity" is not supported for non-numeric strings');
}
if (options?.extrapolateLeft === "wrap") {
const wrapRange = inputMax - inputMin;
resolvedInput = ((resolvedInput - inputMin) % wrapRange + wrapRange) % wrapRange + inputMin;
} else {
return outputRange[0];
}
}
if (resolvedInput > inputMax) {
if (options?.extrapolateRight === "identity") {
throw new TypeError('extrapolateRight: "identity" is not supported for non-numeric strings');
}
if (options?.extrapolateRight === "wrap") {
const wrapRange = inputMax - inputMin;
resolvedInput = ((resolvedInput - inputMin) % wrapRange + wrapRange) % wrapRange + inputMin;
} else {
return outputRange[outputRange.length - 1];
}
}
const range = findRange(resolvedInput, inputRange);
return resolvedInput >= inputRange[range + 1] ? outputRange[range + 1] : outputRange[range];
};
var validateTupleOutputRange = (outputRange) => {
const dimensions = outputRange[0]?.length;
if (dimensions === undefined) {
throw new Error("outputRange must have at least 1 element");
}
if (dimensions === 0) {
throw new TypeError("outputRange tuples must contain at least 1 number");
}
for (const output of outputRange) {
if (output.length !== dimensions) {
throw new TypeError(`outputRange tuples must all have the same length, but got ${dimensions} and ${output.length}`);
}
for (const value of output) {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new TypeError(`outputRange tuples must contain only finite numbers, but got [${output.join(",")}]`);
}
}
}
return dimensions;
};
var interpolateTuple = ({
input,
inputRange,
outputRange,
options
}) => {
const dimensions = validateTupleOutputRange(outputRange);
return new Array(dimensions).fill(true).map((_, axis) => interpolateNumber({
input,
inputRange,
outputRange: outputRange.map((output) => output[axis]),
options
}));
};
function checkValidInputRange(arr) {
for (let i = 1;i < arr.length; ++i) {
if (!(arr[i] > arr[i - 1])) {
throw new Error(`inputRange must be strictly monotonically increasing but got [${arr.join(",")}]`);
}
}
}
function checkInfiniteRange(name, arr) {
if (arr.length < 1) {
throw new Error(name + " must have at least 1 element");
}
for (const element of arr) {
if (typeof element !== "number") {
throw new Error(`${name} must contain only numbers`);
}
if (!Number.isFinite(element)) {
throw new Error(`${name} must contain only finite numbers, but got [${arr.join(",")}]`);
}
}
}
function assertValidInterpolateEasingOption(easing, inputRangeLength) {
if (easing === undefined) {
return;
}
if (typeof easing === "function") {
return;
}
const expectedLength = inputRangeLength - 1;
if (easing.length !== expectedLength) {
throw new Error(`When easing is an array, it must have one entry per segment between keyframes (length inputRange.length - 1 = ${expectedLength}), but got length ${easing.length}`);
}
for (let i = 0;i < easing.length; i++) {
if (typeof easing[i] !== "function") {
throw new Error(`easing[${i}] must be a function`);
}
}
}
function assertValidInterpolatePosterizeOption(posterize) {
if (posterize === undefined) {
return;
}
if (typeof posterize !== "number" || !Number.isFinite(posterize) || posterize <= 0) {
throw new Error(`posterize must be a positive finite number, but got ${posterize}`);
}
}
function assertValidInterpolateOutputOption(output) {
if (output === undefined || output === "linear" || output === "perceptual-scale") {
return;
}
throw new Error(`output must be "linear" or "perceptual-scale", but got ${String(output)}`);
}
function interpolate(input, inputRange, outputRange, options) {
if (typeof input === "undefined") {
throw new Error("input can not be undefined");
}
if (typeof inputRange === "undefined") {
throw new Error("inputRange can not be undefined");
}
if (typeof outputRange === "undefined") {
throw new Error("outputRange can not be undefined");
}
if (inputRange.length !== outputRange.length) {
throw new Error("inputRange (" + inputRange.length + ") and outputRange (" + outputRange.length + ") must have the same length");
}
checkInfiniteRange("inputRange", inputRange);
checkValidInputRange(inputRange);
assertValidInterpolateEasingOption(options?.easing, inputRange.length);
assertValidInterpolatePosterizeOption(options?.posterize);
assertValidInterpolateOutputOption(options?.output);
if (typeof input !== "number") {
throw new TypeError("Cannot interpolate an input which is not a number");
}
if (!Array.isArray(outputRange)) {
throw new Error("outputRange must contain only numbers");
}
const hasStringOutput = outputRange.some((output) => typeof output === "string");
if (hasStringOutput) {
if (!outputRange.every((output) => typeof output === "string" || typeof output === "number")) {
throw new TypeError("outputRange must contain only numbers, or supported scale, translate, and rotate strings");
}
try {
return interpolateString({ input, inputRange, outputRange, options });
} catch (error) {
if (!outputRange.every((output) => typeof output === "string")) {
throw error;
}
const hasNonNumericString = outputRange.some((output) => {
try {
parseStringInterpolationValue(output);
return false;
} catch (parseError) {
return parseError instanceof UnsupportedStringInterpolationValueError;
}
});
if (!hasNonNumericString) {
throw error;
}
return interpolateDiscreteString({
input,
inputRange,
outputRange,
options
});
}
}
if (outputRange.every((output) => Array.isArray(output))) {
return interpolateTuple({ input, inputRange, outputRange, options });
}
if (!outputRange.every((output) => typeof output === "number")) {
throw new TypeError("outputRange must contain only numbers, numeric tuples, or supported scale, translate, and rotate strings");
}
checkInfiniteRange("outputRange", outputRange);
return interpolateNumber({ input, inputRange, outputRange, options });
}
// src/random.ts
function mulberry32(a2) {
let t = a2 + 1831565813;
t = Math.imul(t ^ t >>> 15, t | 1);
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
return ((t ^ t >>> 14) >>> 0) / 4294967296;
}
function hashCode(str) {
let i = 0;
let chr = 0;
let hash = 0;
for (i = 0;i < str.length; i++) {
chr = str.charCodeAt(i);
hash = (hash << 5) - hash + chr;
hash |= 0;
}
return hash;
}
var random = (seed, dummy) => {
if (dummy !== undefined) {
throw new TypeError("random() takes only one argument");
}
if (seed === null) {
return Math.random();
}
if (typeof seed === "string") {
return mulberry32(hashCode(seed));
}
if (typeof seed === "number") {
return mulberry32(seed * 10000000000);
}
throw new Error("random() argument must be a number or a string");
};
// src/delay-render-constants.ts
var DELAY_RENDER_CALLSTACK_TOKEN = "The delayRender was called:";
var DELAY_RENDER_RETRIES_LEFT = "Retries left: ";
var DELAY_RENDER_RETRY_TOKEN = "- Rendering the frame will be retried.";
var DELAY_RENDER_CLEAR_TOKEN = "handle was cleared after";
// src/find-props-to-delete.ts
var findPropsToDelete = ({
schema,
key,
value
}) => {
const fieldSchema = schema[key];
if (!fieldSchema) {
throw new Error("Key " + JSON.stringify(key) + " not found in schema");
}
if (typeof value !== "string") {
throw new Error("Value must be a string, but is " + JSON.stringify(value));
}
if (fieldSchema.type !== "enum") {
throw new Error("Key " + JSON.stringify(key) + " is not an enum");
}
const currentVariant = fieldSchema.variants[value];
if (!currentVariant) {
throw new Error("Value for " + JSON.stringify(key) + " must be one of " + Object.keys(fieldSchema.variants).map((v) => JSON.stringify(v)).join(", ") + ", got " + JSON.stringify(value));
}
const otherVariants = Object.keys(fieldSchema.variants).filter((v) => v !== value);
const otherKeys = new Set;
for (const variant of otherVariants) {
const otherVariant = fieldSchema.variants[variant];
const keys = Object.keys(otherVariant);
for (const k of keys) {
otherKeys.add(k);
}
}
return [...otherKeys];
};
// src/input-props-serialization.ts
var DATE_TOKEN = "remotion-date:";
var FILE_TOKEN = "remotion-file:";
var serializeJSONWithSpecialTypes = ({
data,
indent,
staticBase
}) => {
let customDateUsed = false;
let customFileUsed = false;
let mapUsed = false;
let setUsed = false;
try {
const serializedString = JSON.stringify(data, function(key, value) {
const item = this[key];
if (item instanceof Date) {
customDateUsed = true;
return `${DATE_TOKEN}${item.toISOString()}`;
}
if (item instanceof Map) {
mapUsed = true;
return value;
}
if (item instanceof Set) {
setUsed = true;
return value;
}
if (typeof item === "string" && staticBase !== null && staticBase !== "" && item.startsWith(staticBase)) {
customFileUsed = true;
return `${FILE_TOKEN}${item.replace(staticBase + "/", "")}`;
}
return value;
}, indent);
return { serializedString, customDateUsed, customFileUsed, mapUsed, setUsed };
} catch (err) {
throw new Error("Could not serialize the passed input props to JSON: " + err.message);
}
};
var resolveFileTokenToUrl = (value) => {
const encodedName = value.replace(FILE_TOKEN, "");
let name = encodedName;
try {
name = encodedName.split("/").map(decodeURIComponent).join("/");
} catch {}
const matchingStaticFile = window.remotion_staticFiles?.find((file) => file.name === name);
if (matchingStaticFile) {
return matchingStaticFile.src;
}
return `${window.remotion_staticBase}/${encodedName}`;
};
var deserializeJSONWithSpecialTypes = (data) => {
return JSON.parse(data, (_, value) => {
if (typeof value === "string" && value.startsWith(DATE_TOKEN)) {
return new Date(value.replace(DATE_TOKEN, ""));
}
if (typeof value === "string" && value.startsWith(FILE_TOKEN)) {
return resolveFileTokenToUrl(value);
}
return value;
});
};
// src/interactivity-schema.ts
var transformSchema = {
"style.transformOrigin": {
type: "transform-origin",
step: 1,
default: "50% 50%",
description: "Transform origin"
},
"style.translate": {
type: "translate",
step: 1,
default: "0px 0px",
description: "Offset"
},
"style.scale": {
type: "scale",
max: 100,
step: 0.01,
default: 1,
description: "Scale",
defaultKeyframeOutput: "perceptual-scale"
},
"style.rotate": {
type: "rotation-css",
step: 1,
default: "0deg",
description: "Rotation"
},
"style.opacity": {
type: "number",
min: 0,
max: 1,
step: 0.01,
default: 1,
description: "Opacity",
hiddenFromList: false
}
};
var borderSchema = {
"style.borderWidth": {
type: "number",
default: undefined,
min: 0,
step: 1,
description: "Border width",
hiddenFromList: false
},
"style.borderStyle": {
type: "enum",
default: "none",
description: "Border style",
variants: {
none: {},
hidden: {},
solid: {},
dashed: {},
dotted: {},
double: {},
groove: {},
ridge: {},
inset: {},
outset: {}
}
},
"style.borderColor": {
type: "color",
default: undefined,
description: "Border color"
}
};
var borderRadiusSchema = {
"style.borderRadius": {
type: "number",
default: 0,
min: 0,
step: 1,
description: "Border radius",
hiddenFromList: false,
keyframable: true
},
"style.borderTopLeftRadius": {
type: "number",
default: 0,
min: 0,
step: 1,
description: "Top left radius",
hiddenFromList: false
},
"style.borderTopRightRadius": {
type: "number",
default: 0,
min: 0,
step: 1,
description: "Top right radius",
hiddenFromList: false
},
"style.borderBottomRightRadius": {
type: "number",
default: 0,
min: 0,
step: 1,
description: "Bottom right radius",
hiddenFromList: false
},
"style.borderBottomLeftRadius": {
type: "number",
default: 0,
min: 0,
step: 1,
description: "Bottom left radius",
hiddenFromList: false
}
};
var backgroundSchema = {
"style.backgroundColor": {
type: "color",
default: "transparent",
description: "Color"
}
};
var svgColorSchema = {
color: {
type: "color",
default: undefined,
description: "Current color"
}
};
var svgStrokeSchema = {
...svgColorSchema,
stroke: {
type: "color",
default: "none",
description: "Stroke"
},
strokeWidth: {
type: "number",
default: 1,
description: "Stroke width",
min: 0,
step: 1,
hiddenFromList: false
}
};
var svgPaintSchema = {
fill: {
type: "color",
default: undefined,
description: "Fill"
},
...svgStrokeSchema
};
var premountSchema = {
premountFor: {
type: "number",
default: 0,
description: "Premount For",
min: 0,
step: 1,
hiddenFromList: false,
keyframable: false
},
postmountFor: {
type: "number",
default: 0,
min: 0,
step: 1,
hiddenFromList: true,
keyframable: false
}
};
var sequencePremountSchema = {
...premountSchema
};
var cropSchema = {
cropLeft: {
type: "number",
default: 0,
description: "Crop left",
min: 0,
max: 1,
step: 0.01,
hiddenFromList: false,
keyframable: true
},
cropRight: {
type: "number",
default: 0,
description: "Crop right",
min: 0,
max: 1,
step: 0.01,
hiddenFromList: false,
keyframable: true
},
cropTop: {
type: "number",
default: 0,
description: "Crop top",
min: 0,
max: 1,
step: 0.01,
hiddenFromList: false,
keyframable: true
},
cropBottom: {
type: "number",
default: 0,
description: "Crop bottom",
min: 0,
max: 1,
step: 0.01,
hiddenFromList: false,
keyframable: true
}
};
var sequenceCropSchema = cropSchema;
var sequenceStyleSchema = {
...sequenceCropSchema,
...transformSchema,
...backgroundSchema,
...borderSchema,
...borderRadiusSchema,
...sequencePremountSchema
};
var hiddenField = {
type: "boolean",
default: false,
description: "Hidden"
};
var showInTimelineField = {
type: "hidden"
};
var sequenceNameField = {
type: "hidden"
};
var durationInFramesField = {
type: "number",
default: undefined,
min: 1,
step: 1,
hiddenFromList: true
};
var fromField = {
type: "number",
default: 0,
step: 1,
hiddenFromList: true
};
var trimBeforeField = {
type: "number",
default: 0,
min: 0,
step: 1,
hiddenFromList: true
};
var freezeField = {
type: "number",
default: null,
step: 1,
hiddenFromList: true
};
var baseSchema = {
durationInFrames: durationInFramesField,
from: fromField,
trimBefore: trimBeforeField,
freeze: freezeField,
hidden: hiddenField,
name: sequenceNameField,
showInTimeline: showInTimelineField
};
var sequenceSchema = {
...baseSchema,
layout: {
type: "enum",
default: "absolute-fill",
description: "Layout",
variants: {
"absolute-fill": sequenceStyleSchema,
none: {}
}
}
};
var baseSchemaWithoutFrom = {
durationInFrames: durationInFramesField,
trimBefore: trimBeforeField,
freeze: freezeField,
hidden: hiddenField,
name: sequenceNameField,
showInTimeline: showInTimelineField
};
var sequenceSchemaWithoutFrom = {
...baseSchemaWithoutFrom,
layout: sequenceSchema.layout
};
var sequenceSchemaDefaultLayoutNone = {
...sequenceSchema,
layout: {
...sequenceSchema.layout,
default: "none"
}
};
// src/interpolate-colors.ts
var NUMBER = "[-+]?\\d*\\.?\\d+";
var PERCENTAGE = NUMBER + "%";
function call(...args) {
return "\\(\\s*(" + args.join(")\\s*,\\s*(") + ")\\s*\\)";
}
var MODERN_VALUE = "(?:none|[-+]?\\d*\\.?\\d+(?:%|deg|rad|grad|turn)?)";
function modernColorCall(name) {
return new RegExp(name + "\\(\\s*(" + MODERN_VALUE + ")\\s+(" + MODERN_VALUE + ")\\s+(" + MODERN_VALUE + ")(?:\\s*\\/\\s*(" + MODERN_VALUE + "))?\\s*\\)");
}
function getMatchers() {
const cachedMatchers = {
rgb: undefined,
rgba: undefined,
hsl: undefined,
hsla: undefined,
hex3: undefined,
hex4: undefined,
hex5: undefined,
hex6: undefined,
hex8: undefined,
oklch: undefined,
oklab: undefined,
lab: undefined,
lch: undefined,
hwb: undefined
};
if (cachedMatchers.rgb === undefined) {
cachedMatchers.rgb = new RegExp("rgb" + call(NUMBER, NUMBER, NUMBER));
cachedMatchers.rgba = new RegExp("rgba" + call(NUMBER, NUMBER, NUMBER, NUMBER));
cachedMatchers.hsl = new RegExp("hsl" + call(NUMBER, PERCENTAGE, PERCENTAGE));
cachedMatchers.hsla = new RegExp("hsla" + call(NUMBER, PERCENTAGE, PERCENTAGE, NUMBER));
cachedMatchers.hex3 = /^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/;
cachedMatchers.hex4 = /^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/;
cachedMatchers.hex6 = /^#([0-9a-fA-F]{6})$/;
cachedMatchers.hex8 = /^#([0-9a-fA-F]{8})$/;
cachedMatchers.oklch = modernColorCall("oklch");
cachedMatchers.oklab = modernColorCall("oklab");
cachedMatchers.lab = modernColorCall("lab");
cachedMatchers.lch = modernColorCall("lch");
cachedMatchers.hwb = modernColorCall("hwb");
}
return cachedMatchers;
}
function hue2rgb(p, q, t) {
if (t < 0) {
t += 1;
}
if (t > 1) {
t -= 1;
}
if (t < 1 / 6) {
return p + (q - p) * 6 * t;
}
if (t < 1 / 2) {
return q;
}
if (t < 2 / 3) {
return p + (q - p) * (2 / 3 - t) * 6;
}
return p;
}
function hslToRgb(h, s, l) {
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
const r = hue2rgb(p, q, h + 1 / 3);
const g = hue2rgb(p, q, h);
const b2 = hue2rgb(p, q, h - 1 / 3);
return Math.round(r * 255) << 24 | Math.round(g * 255) << 16 | Math.round(b2 * 255) << 8;
}
function parse255(str) {
const int = Number.parseInt(str, 10);
if (int < 0) {
return 0;
}
if (int > 255) {
return 255;
}
return int;
}
function parse360(str) {
const int = Number.parseFloat(str);
return (int % 360 + 360) % 360 / 360;
}
function parse1(str) {
const num = Number.parseFloat(str);
if (num < 0) {
return 0;
}
if (num > 1) {
return 255;
}
return Math.round(num * 255);
}
function parsePercentage(str) {
const int = Number.parseFloat(str);
if (int < 0) {
return 0;
}
if (int > 100) {
return 1;
}
return int / 100;
}
function parseModernComponent(str, percentScale) {
if (str === "none")
return 0;
if (str.endsWith("%")) {
return Number.parseFloat(str) / 100 * percentScale;
}
return Number.parseFloat(str);
}
function parseHueAngle(str) {
if (str === "none")
return 0;
if (str.endsWith("rad")) {
return Number.parseFloat(str) * 180 / Math.PI;
}
if (str.endsWith("grad"))
return Number.parseFloat(str) * 0.9;
if (str.endsWith("turn"))
return Number.parseFloat(str) * 360;
return Number.parseFloat(str);
}
function parseModernAlpha(str) {
if (str === undefined || str === "none")
return 1;
if (str.endsWith("%")) {
return Math.max(0, Math.min(1, Number.parseFloat(str) / 100));
}
return Math.max(0, Math.min(1, Number.parseFloat(str)));
}
function linearToSrgb(c2) {
if (c2 <= 0.0031308)
return 12.92 * c2;
return 1.055 * c2 ** (1 / 2.4) - 0.055;
}
function clamp01(v) {
return Math.max(0, Math.min(1, v));
}
function rgbFloatToInt(r, g, b2, alpha) {
const ri = Math.round(clamp01(r) * 255);
const gi = Math.round(clamp01(g) * 255);
const bi = Math.round(clamp01(b2) * 255);
const ai = Math.round(clamp01(alpha) * 255);
return (ri << 24 | gi << 16 | bi << 8 | ai) >>> 0;
}
function oklabToSrgb(L, a2, b2) {
const l_ = L + 0.3963377774 * a2 + 0.2158037573 * b2;
const m_ = L - 0.1055613458 * a2 - 0.0638541728 * b2;
const s_ = L - 0.0894841775 * a2 - 1.291485548 * b2;
const l = l_ * l_ * l_;
const m = m_ * m_ * m_;
const s = s_ * s_ * s_;
const rLin = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s;
const gLin = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s;
const bLin = -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s;
return [linearToSrgb(rLin), linearToSrgb(gLin), linearToSrgb(bLin)];
}
function labToSrgb(L, a2, b2) {
const epsilon = 216 / 24389;
const kappa = 24389 / 27;
const Xn = 0.95047;
const Yn = 1;
const Zn = 1.08883;
const fy = (L + 16) / 116;
const fx = a2 / 500 + fy;
const fz = fy - b2 / 200;
const fx3 = fx * fx * fx;
const fz3 = fz * fz * fz;
const xr = fx3 > epsilon ? fx3 : (116 * fx - 16) / kappa;
const yr = L > kappa * epsilon ? ((L + 16) / 116) ** 3 : L / kappa;
const zr = fz3 > epsilon ? fz3 : (116 * fz - 16) / kappa;
const X = xr * Xn;
const Y = yr * Yn;
const Z = zr * Zn;
const rLin = 3.2404542 * X - 1.5371385 * Y - 0.4985314 * Z;
const gLin = -0.969266 * X + 1.8760108 * Y + 0.041556 * Z;
const bLin = 0.0556434 * X - 0.2040259 * Y + 1.0572252 * Z;
return [linearToSrgb(rLin), linearToSrgb(gLin), linearToSrgb(bLin)];
}
function hwbToSrgb(h, w, bk) {
if (w + bk >= 1) {
const gray = w / (w + bk);
return [gray, gray, gray];
}
const q = 1;
const p = 0;
const r = hue2rgb(p, q, h + 1 / 3);
const g = hue2rgb(p, q, h);
const bl = hue2rgb(p, q, h - 1 / 3);
const factor = 1 - w - bk;
return [r * factor + w, g * factor + w, bl * factor + w];
}
var colorNames = {
transparent: 0,
aliceblue: 4042850303,
antiquewhite: 4209760255,
aqua: 16777215,
aquamarine: 2147472639,
azure: 4043309055,
beige: 4126530815,
bisque: 4293182719,
black: 255,
blanchedalmond: 4293643775,
blue: 65535,
blueviolet: 2318131967,
brown: 2771004159,
burlywood: 3736635391,
burntsienna: 3934150143,
cadetblue: 1604231423,
chartreuse: 2147418367,
chocolate: 3530104575,
coral: 4286533887,
cornflowerblue: 1687547391,
cornsilk: 4294499583,
crimson: 3692313855,
cyan: 16777215,
darkblue: 35839,
darkcyan: 9145343,
darkgoldenrod: 3095792639,
darkgray: 2846468607,
darkgreen: 6553855,
darkgrey: 2846468607,
darkkhaki: 3182914559,
darkmagenta: 2332068863,
darkolivegreen: 1433087999,
darkorange: 4287365375,
darkorchid: 2570243327,
darkred: 2332033279,
darksalmon: 3918953215,
darkseagreen: 2411499519,
darkslateblue: 1211993087,
darkslategray: 793726975,
darkslategrey: 793726975,
darkturquoise: 13554175,
darkviolet: 2483082239,
deeppink: 4279538687,
deepskyblue: 12582911,
dimgray: 1768516095,
dimgrey: 1768516095,
dodgerblue: 512819199,
firebrick: 2988581631,
floralwhite: 4294635775,
forestgreen: 579543807,
fuchsia: 4278255615,
gainsboro: 3705462015,
ghostwhite: 4177068031,
gold: 4292280575,
goldenrod: 3668254975,
gray: 2155905279,
green: 8388863,
greenyellow: 2919182335,
grey: 2155905279,
honeydew: 4043305215,
hotpink: 4285117695,
indianred: 3445382399,
indigo: 1258324735,
ivory: 4294963455,
khaki: 4041641215,
lavender: 3873897215,
lavenderblush: 4293981695,
lawngreen: 2096890111,
lemonchiffon: 4294626815,
lightblue: 2916673279,
lightcoral: 4034953471,
lightcyan: 3774873599,
lightgoldenrodyellow: 4210742015,
lightgray: 3553874943,
lightgreen: 2431553791,
lightgrey: 3553874943,
lightpink: 4290167295,
lightsalmon: 4288707327,
lightseagreen: 548580095,
lightskyblue: 2278488831,
lightslategray: 2005441023,
lightslategrey: 2005441023,
lightsteelblue: 2965692159,
lightyellow: 4294959359,
lime: 16711935,
limegreen: 852308735,
linen: 4210091775,
magenta: 4278255615,
maroon: 2147483903,
mediumaquamarine: 1724754687,
mediumblue: 52735,
mediumorchid: 3126187007,
mediumpurple: 2473647103,
mediumseagreen: 1018393087,
mediumslateblue: 2070474495,
mediumspringgreen: 16423679,
mediumturquoise: 1221709055,
mediumvioletred: 3340076543,
midnightblue: 421097727,
mintcream: 4127193855,
mistyrose: 4293190143,
moccasin: 4293178879,
navajowhite: 4292783615,
navy: 33023,
oldlace: 4260751103,
olive: 2155872511,
olivedrab: 1804477439,
orange: 4289003775,
orangered: 4282712319,
orchid: 3664828159,
palegoldenrod: 4008225535,
palegreen: 2566625535,
paleturquoise: 2951671551,
palevioletred: 3681588223,
papayawhip: 4293907967,
peachpuff: 4292524543,
peru: 3448061951,
pink: 4290825215,
plum: 3718307327,
powderblue: 2967529215,
purple: 2147516671,
rebeccapurple: 1714657791,
red: 4278190335,
rosybrown: 3163525119,
royalblue: 1097458175,
saddlebrown: 2336560127,
salmon: 4202722047,
sandybrown: 4104413439,
seagreen: 780883967,
seashell: 4294307583,
sienna: 2689740287,
silver: 3233857791,
skyblue: 2278484991,
slateblue: 1784335871,
slategray: 1887473919,
slategrey: 1887473919,
snow: 4294638335,
springgreen: 16744447,
steelblue: 1182971135,
tan: 3535047935,
teal: 8421631,
thistle: 3636451583,
tomato: 4284696575,
turquoise: 1088475391,
violet: 4001558271,
wheat: 4125012991,
white: 4294967295,
whitesmoke: 4126537215,
yellow: 4294902015,
yellowgreen: 2597139199
};
function normalizeColor(color) {
const matchers = getMatchers();
let match;
if (matchers.hex6) {
if (match = matchers.hex6.exec(color)) {
return Number.parseInt(match[1] + "ff", 16) >>> 0;
}
}
if (colorNames[color] !== undefined) {
return colorNames[color];
}
if (matchers.rgb) {
if (match = matchers.rgb.exec(color)) {
return (parse255(match[1]) << 24 | parse255(match[2]) << 16 | parse255(match[3]) << 8 | 255) >>> 0;
}
}
if (matchers.rgba) {
if (match = matchers.rgba.exec(color)) {
return (parse255(match[1]) << 24 | parse255(match[2]) << 16 | parse255(match[3]) << 8 | parse1(match[4])) >>> 0;
}
}
if (matchers.hex3) {
if (match = matchers.hex3.exec(color)) {
return Number.parseInt(match[1] + match[1] + match[2] + match[2] + match[3] + match[3] + "ff", 16) >>> 0;
}
}
if (matchers.hex8) {
if (match = matchers.hex8.exec(color)) {
return Number.parseInt(match[1], 16) >>> 0;
}
}
if (matchers.hex4) {
if (match = matchers.hex4.exec(color)) {
return Number.parseInt(match[1] + match[1] + match[2] + match[2] + match[3] + match[3] + match[4] + match[4], 16) >>> 0;
}
}
if (matchers.hsl) {
if (match = matchers.hsl.exec(color)) {
return (hslToRgb(parse360(match[1]), parsePercentage(match[2]), parsePercentage(match[3])) | 255) >>> 0;
}
}
if (matchers.hsla) {
if (match = matchers.hsla.exec(color)) {
return (hslToRgb(parse360(match[1]), parsePercentage(match[2]), parsePercentage(match[3])) | parse1(match[4])) >>> 0;
}
}
if (matchers.oklch) {
if (match = matchers.oklch.exec(color)) {
const L = parseModernComponent(match[1], 1);
const C = parseModernComponent(match[2], 0.4);
const H = parseHueAngle(match[3]);
const alpha = parseModernAlpha(match[4]);
const hRad = H * Math.PI / 180;
const [r, g, b2] = oklabToSrgb(L, C * Math.cos(hRad), C * Math.sin(hRad));
return rgbFloatToInt(r, g, b2, alpha);
}
}
if (matchers.oklab) {
if (match = matchers.oklab.exec(color)) {
const L = parseModernComponent(match[1], 1);
const a2 = parseModernComponent(match[2], 0.4);
const b2 = parseModernComponent(match[3], 0.4);
const alpha = parseModernAlpha(match[4]);
const [r, g, bl] = oklabToSrgb(L, a2, b2);
return rgbFloatToInt(r, g, bl, alpha);
}
}
if (matchers.lab) {
if (match = matchers.lab.exec(color)) {
const L = parseModernComponent(match[1], 100);
const a2 = parseModernComponent(match[2], 125);
const b2 = parseModernComponent(match[3], 125);
const alpha = parseModernAlpha(match[4]);
const [r, g, bl] = labToSrgb(L, a2, b2);
return rgbFloatToInt(r, g, bl, alpha);
}
}
if (matchers.lch) {
if (match = matchers.lch.exec(color)) {
const L = parseModernComponent(match[1], 100);
const C = parseModernComponent(match[2], 150);
const H = parseHueAngle(match[3]);
const alpha = parseModernAlpha(match[4]);
const hRad = H * Math.PI / 180;
const [r, g, bl] = labToSrgb(L, C * Math.cos(hRad), C * Math.sin(hRad));
return rgbFloatToInt(r, g, bl, alpha);
}
}
if (matchers.hwb) {
if (match = matchers.hwb.exec(color)) {
const H = parseHueAngle(match[1]);
const W = parseModernComponent(match[2], 1);
const B = parseModernComponent(match[3], 1);
const alpha = parseModernAlpha(match[4]);
const [r, g, bl] = hwbToSrgb(H / 360, W, B);
return rgbFloatToInt(r, g, bl, alpha);
}
}
throw new Error(`invalid color string ${color} provided`);
}
function processColor(color) {
const normalizedColor = normalizeColor(color);
return (normalizedColor << 24 | normalizedColor >>> 8) >>> 0;
}
// src/prores-profile.ts
var proResProfileOptions = [
"4444-xq",
"4444",
"hq",
"standard",
"light",
"proxy"
];
// src/scale-value.ts
var defaultScaleValue = [1, 1, 1];
var parseScaleString = (value) => {
const parts = value.trim().split(/\s+/);
if (parts.length < 1 || parts.length > 3 || parts[0] === "") {
return null;
}
const parsed = parts.map((part) => Number(part));
if (!parsed.every((part) => Number.isFinite(part))) {
return null;
}
const x = parsed[0];
const y = parsed[1] ?? x;
const z = parsed[2] ?? 1;
return [x, y, z];
};
var parseValidScaleValue = (value) => {
if (typeof value === "number") {
return Number.isFinite(value) ? [value, value, 1] : null;
}
if (typeof value === "string") {
return parseScaleString(value);
}
return null;
};
var parseScaleValue = (value) => {
return parseValidScaleValue(value) ?? defaultScaleValue;
};
var serializeScaleValue = ([x, y, z]) => {
const normalizedX = normalizeNumber(x);
const normalizedY = normalizeNumber(y);
const normalizedZ = normalizeNumber(z);
if (normalizedX === normalizedY && normalizedZ === 1) {
return normalizedX;
}
if (normalizedZ === 1) {
return `${normalizedX} ${normalizedY}`;
}
return `${normalizedX} ${normalizedY} ${normalizedZ}`;
};
// src/truthy.ts
function truthy(value) {
return Boolean(value);
}
// src/v5-flag.ts
var ENABLE_V5_BREAKING_CHANGES = false;
// src/codec.ts
var validCodecs = [
"h264",
"h265",
"vp8",
"vp9",
"av1",
"mp3",
"aac",
"wav",
"prores",
"h264-mkv",
"h264-ts",
"gif"
];
// src/validation/validate-default-codec.ts
function validateCodec(defaultCodec, location, name) {
if (typeof defaultCodec === "undefined") {
return;
}
if (typeof defaultCodec !== "string") {
throw new TypeError(`The "${name}" prop ${location} must be a string, but you passed a value of type ${typeof defaultCodec}.`);
}
if (!validCodecs.includes(defaultCodec)) {
throw new Error(`The "${name}" prop ${location} must be one of ${validCodecs.join(", ")}, but you passed ${defaultCodec}.`);
}
}
// src/validation/validate-default-props.ts
var validateDefaultAndInputProps = (defaultProps, name, compositionId) => {
if (!defaultProps) {
return;
}
if (typeof defaultProps !== "object") {
throw new Error(`"${name}" must be an object, but you passed a value of type ${typeof defaultProps}`);
}
if (Array.isArray(defaultProps)) {
throw new Error(`"${name}" must be an object, an array was passed ${compositionId ? `for composition "${compositionId}"` : ""}`);
}
};
// src/validation/validate-dimensions.ts
function validateDimension(amount, nameOfProp, location) {
if (typeof amount !== "number") {
throw new Error(`The "${nameOfProp}" prop ${location} must be a number, but you passed a value of type ${typeof amount}`);
}
if (isNaN(amount)) {
throw new TypeError(`The "${nameOfProp}" prop ${location} must not be NaN, but is NaN.`);
}
if (!Number.isFinite(amount)) {
throw new TypeError(`The "${nameOfProp}" prop ${location} must be finite, but is ${amount}.`);
}
if (amount % 1 !== 0) {
throw new TypeError(`The "${nameOfProp}" prop ${location} must be an integer, but is ${amount}.`);
}
if (amount <= 0) {
throw new TypeError(`The "${nameOfProp}" prop ${location} must be positive, but got ${amount}.`);
}
}
// src/validation/validate-duration-in-frames.ts
function validateDurationInFrames(durationInFrames, options) {
const { allowFloats, component } = options;
if (typeof durationInFrames === "undefined") {
throw new Error(`The "durationInFrames" prop ${component} is missing.`);
}
if (typeof durationInFrames !== "number") {
throw new Error(`The "durationInFrames" prop ${component} must be a number, but you passed a value of type ${typeof durationInFrames}`);
}
if (durationInFrames <= 0) {
throw new TypeError(`The "durationInFrames" prop ${component} must be positive, but got ${durationInFrames}.`);
}
if (!allowFloats && durationInFrames % 1 !== 0) {
throw new TypeError(`The "durationInFrames" prop ${component} must be an integer, but got ${durationInFrames}.`);
}
if (!Number.isFinite(durationInFrames)) {
throw new TypeError(`The "durationInFrames" prop ${component} must be finite, but got ${durationInFrames}.`);
}
}
// src/video/get-current-time.ts
var getExpectedMediaFrameUncorrected = ({
frame,
playbackRate,
startFrom
}) => {
return interpolate(frame, [-1, startFrom, startFrom + 1], [-1, startFrom, startFrom + playbackRate]);
};
// src/absolute-src.ts
var getAbsoluteSrc = (relativeSrc) => {
if (typeof window === "undefined") {
return relativeSrc;
}
if (relativeSrc.startsWith("http://") || relativeSrc.startsWith("https://") || relativeSrc.startsWith("file://") || relativeSrc.startsWith("blob:") || relativeSrc.startsWith("data:")) {
return relativeSrc;
}
return new URL(relativeSrc, window.origin).href;
};
// src/video/offthread-video-source.ts
var getOffthreadVideoSource = ({
src,
transparent,
currentTime,
toneMapped
}) => {
return `http://localhost:${window.remotion_proxyPort}/proxy?src=${encodeURIComponent(getAbsoluteSrc(src))}&time=${encodeURIComponent(Math.max(0, currentTime))}&transparent=${String(transparent)}&toneMapped=${String(toneMapped)}`;
};
// src/no-react.ts
var NoReactInternals = {
processColor,
truthy,
validateFps,
validateDimension,
validateDurationInFrames,
validateDefaultAndInputProps,
validateFrame,
serializeJSONWithSpecialTypes,
bundleName: "bundle.js",
bundleMapName: "bundle.js.map",
deserializeJSONWithSpecialTypes,
DELAY_RENDER_CALLSTACK_TOKEN,
DELAY_RENDER_RETRY_TOKEN,
DELAY_RENDER_CLEAR_TOKEN,
DELAY_RENDER_ATTEMPT_TOKEN: DELAY_RENDER_RETRIES_LEFT,
getOffthreadVideoSource,
getExpectedMediaFrameUncorrected,
ENABLE_V5_BREAKING_CHANGES,
MIN_NODE_VERSION: ENABLE_V5_BREAKING_CHANGES ? 22 : 16,
MIN_BUN_VERSION: ENABLE_V5_BREAKING_CHANGES ? "1.1.3" : "1.0.3",
MIN_ESLINT_VERSION: ENABLE_V5_BREAKING_CHANGES ? "8.57.0" : "7.15.0",
colorNames,
DATE_TOKEN,
FILE_TOKEN,
validateCodec,
proResProfileOptions,
findPropsToDelete,
sequenceSchema,
parseScaleValue,
serializeScaleValue
};
export {
random,
interpolate,
assertValidInterpolatePosterizeOption,
assertValidInterpolateEasingOption,
NoReactInternals
};