remotion
Version:
Make videos programmatically
1,564 lines (1,507 loc) • 274 kB
JavaScript
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, {
get: all[name],
enumerable: true,
configurable: true,
set: (newValue) => all[name] = () => newValue
});
};
// src/_check-rsc.ts
import { createContext } from "react";
if (typeof createContext !== "function") {
const err = [
'Remotion requires React.createContext, but it is "undefined".',
'If you are in a React Server Component, turn it into a client component by adding "use client" at the top of the file.',
"",
"Before:",
' import {useCurrentFrame} from "remotion";',
"",
"After:",
' "use client";',
' import {useCurrentFrame} from "remotion";'
];
throw new Error(err.join(`
`));
}
// src/Clipper.tsx
var Clipper = () => {
throw new Error("<Clipper> has been removed as of Remotion v4.0.228. The native clipping APIs were experimental and subject to removal at any time. We removed them because they were sparingly used and made rendering often slower rather than faster.");
};
// src/Composition.tsx
import { Suspense, useCallback as useCallback4, useContext as useContext9, useEffect as useEffect2 } from "react";
import { createPortal } from "react-dom";
// src/CanUseRemotionHooks.tsx
import { createContext as createContext2 } from "react";
import { jsx } from "react/jsx-runtime";
var CanUseRemotionHooks = createContext2(false);
var CanUseRemotionHooksProvider = ({ children }) => {
return /* @__PURE__ */ jsx(CanUseRemotionHooks.Provider, {
value: true,
children
});
};
// src/composition-render-error-context.ts
import { createContext as createContext3 } from "react";
var CompositionRenderErrorContext = createContext3({
setError: () => {},
clearError: () => {}
});
// src/CompositionErrorBoundary.tsx
import React2 from "react";
class CompositionErrorBoundary extends React2.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error) {
this.props.onError(error);
}
componentDidUpdate(_prevProps) {
if (!this.state.hasError) {
this.props.onClear();
}
}
render() {
if (this.state.hasError) {
return null;
}
return this.props.children;
}
}
// src/CompositionManagerContext.tsx
import { createContext as createContext4 } from "react";
var CompositionManager = createContext4({
compositions: [],
folders: [],
currentCompositionMetadata: null,
canvasContent: null
});
var CompositionSetters = createContext4({
registerComposition: () => {
return;
},
unregisterComposition: () => {
return;
},
registerFolder: () => {
return;
},
unregisterFolder: () => {
return;
},
setCanvasContent: () => {
return;
},
onlyRenderComposition: null
});
// src/Folder.tsx
import { createContext as createContext6, useContext as useContext2, useEffect, useMemo as useMemo2 } from "react";
// src/nonce.ts
import { createContext as createContext5, useCallback, useContext, useMemo, useRef } from "react";
var NonceContext = createContext5({
getNonce: () => 0
});
var fastRefreshNonce = 0;
try {
if (typeof __webpack_module__ !== "undefined") {
if (__webpack_module__.hot) {
__webpack_module__.hot.addStatusHandler((status) => {
if (status === "idle") {
fastRefreshNonce++;
}
});
}
}
} catch {}
var useNonce = () => {
const context = useContext(NonceContext);
const nonce = context.getNonce();
const nonceRef = useRef(nonce);
nonceRef.current = nonce;
const history = useRef([[fastRefreshNonce, nonce]]);
const get = useCallback(() => {
if (fastRefreshNonce !== history.current[history.current.length - 1][0]) {
history.current = [
...history.current,
[fastRefreshNonce, nonceRef.current]
];
}
return history.current;
}, [history]);
return useMemo(() => {
return { get };
}, [get]);
};
// src/truthy.ts
function truthy(value) {
return Boolean(value);
}
// src/validation/validate-folder-name.ts
var getRegex = () => /^([a-zA-Z0-9-\u4E00-\u9FFF])+$/g;
var isFolderNameValid = (name) => name.match(getRegex());
var validateFolderName = (name) => {
if (name === undefined || name === null) {
throw new TypeError("You must pass a name to a <Folder />.");
}
if (typeof name !== "string") {
throw new TypeError(`The "name" you pass into <Folder /> must be a string. Got: ${typeof name}`);
}
if (!isFolderNameValid(name)) {
throw new Error(`Folder name can only contain a-z, A-Z, 0-9 and -. You passed ${name}`);
}
};
var invalidFolderNameErrorMessage = `Folder name must match ${String(getRegex())}`;
// src/Folder.tsx
import { jsx as jsx2 } from "react/jsx-runtime";
var FolderContext = createContext6({
folderName: null,
parentName: null
});
var Folder = ({ name, children }) => {
const parent = useContext2(FolderContext);
const { registerFolder, unregisterFolder } = useContext2(CompositionSetters);
const nonce = useNonce();
validateFolderName(name);
const parentNameArr = [parent.parentName, parent.folderName].filter(truthy);
const parentName = parentNameArr.length === 0 ? null : parentNameArr.join("/");
const value = useMemo2(() => {
return {
folderName: name,
parentName
};
}, [name, parentName]);
useEffect(() => {
registerFolder(name, parentName, nonce.get());
return () => {
unregisterFolder(name, parentName);
};
}, [
name,
parent.folderName,
parentName,
registerFolder,
unregisterFolder,
nonce
]);
return /* @__PURE__ */ jsx2(FolderContext.Provider, {
value,
children
});
};
// src/get-remotion-environment.ts
function getNodeEnvString() {
return ["NOD", "E_EN", "V"].join("");
}
var getEnvString = () => {
return ["e", "nv"].join("");
};
var getRemotionEnvironment = () => {
const isPlayer = typeof window !== "undefined" && window.remotion_isPlayer;
const isRendering = typeof window !== "undefined" && typeof window.process !== "undefined" && typeof window.process.env !== "undefined" && (window.process[getEnvString()][getNodeEnvString()] === "test" || window.process[getEnvString()][getNodeEnvString()] === "production" && typeof window !== "undefined" && typeof window.remotion_puppeteerTimeout !== "undefined");
const isStudio = typeof window !== "undefined" && window.remotion_isStudio;
const isReadOnlyStudio = typeof window !== "undefined" && window.remotion_isReadOnlyStudio;
return {
isStudio,
isRendering,
isPlayer,
isReadOnlyStudio,
isClientSideRendering: false
};
};
// 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 && 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 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 `${window.remotion_staticBase}/${value.replace(FILE_TOKEN, "")}`;
}
return value;
});
};
var serializeThenDeserialize = (props) => {
return deserializeJSONWithSpecialTypes(serializeJSONWithSpecialTypes({
data: props,
indent: 2,
staticBase: window.remotion_staticBase
}).serializedString);
};
var serializeThenDeserializeInStudio = (props) => {
if (getRemotionEnvironment().isStudio) {
return serializeThenDeserialize(props);
}
return props;
};
// src/is-player.tsx
import { createContext as createContext7, useContext as useContext3 } from "react";
import { jsx as jsx3 } from "react/jsx-runtime";
var IsPlayerContext = createContext7(false);
var IsPlayerContextProvider = ({
children
}) => {
return /* @__PURE__ */ jsx3(IsPlayerContext.Provider, {
value: true,
children
});
};
var useIsPlayer = () => {
return useContext3(IsPlayerContext);
};
// src/AbsoluteFill.tsx
import { forwardRef, useMemo as useMemo3 } from "react";
import { jsx as jsx4 } from "react/jsx-runtime";
var hasTailwindClassName = ({
className,
classPrefix,
type
}) => {
if (!className) {
return false;
}
if (type === "exact") {
const split = className.split(" ");
return classPrefix.some((token) => {
return split.some((part) => {
return part.trim() === token || part.trim().endsWith(`:${token}`) || part.trim().endsWith(`!${token}`);
});
});
}
return classPrefix.some((prefix) => {
return className.startsWith(prefix) || className.includes(` ${prefix}`) || className.includes(`!${prefix}`) || className.includes(`:${prefix}`);
});
};
var AbsoluteFillRefForwarding = (props, ref) => {
const { style, ...other } = props;
const actualStyle = useMemo3(() => {
return {
position: "absolute",
top: hasTailwindClassName({
className: other.className,
classPrefix: ["top-", "inset-"],
type: "prefix"
}) ? undefined : 0,
left: hasTailwindClassName({
className: other.className,
classPrefix: ["left-", "inset-"],
type: "prefix"
}) ? undefined : 0,
right: hasTailwindClassName({
className: other.className,
classPrefix: ["right-", "inset-"],
type: "prefix"
}) ? undefined : 0,
bottom: hasTailwindClassName({
className: other.className,
classPrefix: ["bottom-", "inset-"],
type: "prefix"
}) ? undefined : 0,
width: hasTailwindClassName({
className: other.className,
classPrefix: ["w-"],
type: "prefix"
}) ? undefined : "100%",
height: hasTailwindClassName({
className: other.className,
classPrefix: ["h-"],
type: "prefix"
}) ? undefined : "100%",
display: hasTailwindClassName({
className: other.className,
classPrefix: [
"block",
"inline-block",
"inline",
"flex",
"inline-flex",
"flow-root",
"grid",
"inline-grid",
"contents",
"list-item",
"hidden"
],
type: "exact"
}) ? undefined : "flex",
flexDirection: hasTailwindClassName({
className: other.className,
classPrefix: [
"flex-row",
"flex-col",
"flex-row-reverse",
"flex-col-reverse"
],
type: "exact"
}) ? undefined : "column",
...style
};
}, [other.className, style]);
return /* @__PURE__ */ jsx4("div", {
ref,
style: actualStyle,
...other
});
};
var AbsoluteFill = forwardRef(AbsoluteFillRefForwarding);
// src/loading-indicator.tsx
import { jsx as jsx5, jsxs } from "react/jsx-runtime";
var rotate = {
transform: `rotate(90deg)`
};
var ICON_SIZE = 40;
var label = {
color: "white",
fontSize: 14,
fontFamily: "sans-serif"
};
var container = {
justifyContent: "center",
alignItems: "center"
};
var Loading = () => {
return /* @__PURE__ */ jsxs(AbsoluteFill, {
style: container,
id: "remotion-comp-loading",
children: [
/* @__PURE__ */ jsx5("style", {
type: "text/css",
children: `
@keyframes anim {
from {
opacity: 0
}
to {
opacity: 1
}
}
#remotion-comp-loading {
animation: anim 2s;
animation-fill-mode: forwards;
}
`
}),
/* @__PURE__ */ jsx5("svg", {
width: ICON_SIZE,
height: ICON_SIZE,
viewBox: "-100 -100 400 400",
style: rotate,
children: /* @__PURE__ */ jsx5("path", {
fill: "#555",
stroke: "#555",
strokeWidth: "100",
strokeLinejoin: "round",
d: "M 2 172 a 196 100 0 0 0 195 5 A 196 240 0 0 0 100 2.259 A 196 240 0 0 0 2 172 z"
})
}),
/* @__PURE__ */ jsxs("p", {
style: label,
children: [
"Resolving ",
"<Suspense>",
"..."
]
})
]
});
};
// src/portal-node.ts
var _portalNode = null;
var portalNode = () => {
if (!_portalNode) {
if (typeof document === "undefined") {
throw new Error("Tried to call an API that only works in the browser from outside the browser");
}
_portalNode = document.createElement("div");
_portalNode.style.position = "absolute";
_portalNode.style.top = "0px";
_portalNode.style.left = "0px";
_portalNode.style.right = "0px";
_portalNode.style.bottom = "0px";
_portalNode.style.width = "100%";
_portalNode.style.height = "100%";
_portalNode.style.display = "flex";
_portalNode.style.flexDirection = "column";
const containerNode = document.createElement("div");
containerNode.style.position = "fixed";
containerNode.style.top = -999999 + "px";
containerNode.appendChild(_portalNode);
document.body.appendChild(containerNode);
}
return _portalNode;
};
// src/ResolveCompositionConfig.tsx
import { createContext as createContext9, createRef, useContext as useContext5, useMemo as useMemo5 } from "react";
// src/input-props-override.ts
var getKey = () => {
return `remotion_inputPropsOverride` + window.location.origin;
};
var getInputPropsOverride = () => {
if (typeof localStorage === "undefined")
return null;
const override = localStorage.getItem(getKey());
if (!override)
return null;
return JSON.parse(override);
};
var setInputPropsOverride = (override) => {
if (typeof localStorage === "undefined")
return;
if (override === null) {
localStorage.removeItem(getKey());
return;
}
localStorage.setItem(getKey(), JSON.stringify(override));
};
// src/config/input-props.ts
var didWarnSSRImport = false;
var warnOnceSSRImport = () => {
if (didWarnSSRImport) {
return;
}
didWarnSSRImport = true;
console.warn("Called `getInputProps()` on the server. This function is not available server-side and has returned an empty object.");
console.warn("To hide this warning, don't call this function on the server:");
console.warn(" typeof window === 'undefined' ? {} : getInputProps()");
};
var getInputProps = () => {
if (typeof window === "undefined") {
warnOnceSSRImport();
return {};
}
if (getRemotionEnvironment().isPlayer) {
throw new Error("You cannot call `getInputProps()` from a <Player>. Instead, the props are available as React props from component that you passed as `component` prop.");
}
const override = getInputPropsOverride();
if (override) {
return override;
}
if (typeof window === "undefined" || typeof window.remotion_inputProps === "undefined") {
throw new Error("Cannot call `getInputProps()` - window.remotion_inputProps is not set. This API is only available if you are in the Studio, or while you are rendering server-side.");
}
const param = window.remotion_inputProps;
if (!param) {
return {};
}
const parsed = deserializeJSONWithSpecialTypes(param);
return parsed;
};
// src/EditorProps.tsx
import React4, { createContext as createContext8, useCallback as useCallback2, useMemo as useMemo4 } from "react";
import { jsx as jsx6 } from "react/jsx-runtime";
var EditorPropsContext = createContext8({
props: {},
updateProps: () => {
throw new Error("Not implemented");
}
});
var timeValueRef = React4.createRef();
var EditorPropsProvider = ({ children }) => {
const [props, setProps] = React4.useState({});
const updateProps = useCallback2(({
defaultProps,
id,
newProps
}) => {
setProps((prev) => {
return {
...prev,
[id]: typeof newProps === "function" ? newProps(prev[id] ?? defaultProps) : newProps
};
});
}, []);
const ctx = useMemo4(() => {
return { props, updateProps };
}, [props, updateProps]);
return /* @__PURE__ */ jsx6(EditorPropsContext.Provider, {
value: ctx,
children
});
};
// src/use-remotion-environment.ts
import { useContext as useContext4, useState } from "react";
// src/remotion-environment-context.ts
import React5 from "react";
var RemotionEnvironmentContext = React5.createContext(null);
// src/use-remotion-environment.ts
var useRemotionEnvironment = () => {
const context = useContext4(RemotionEnvironmentContext);
const [env] = useState(() => getRemotionEnvironment());
return context ?? env;
};
// 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/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/ResolveCompositionConfig.tsx
var ResolveCompositionContext = createContext9(null);
var resolveCompositionsRef = createRef();
var needsResolution = (composition) => {
return Boolean(composition.calculateMetadata);
};
var useResolvedVideoConfig = (preferredCompositionId) => {
const context = useContext5(ResolveCompositionContext);
const { props: allEditorProps } = useContext5(EditorPropsContext);
const { compositions, canvasContent, currentCompositionMetadata } = useContext5(CompositionManager);
const currentComposition = canvasContent?.type === "composition" ? canvasContent.compositionId : null;
const compositionId = preferredCompositionId ?? currentComposition;
const composition = compositions.find((c) => c.id === compositionId);
const selectedEditorProps = useMemo5(() => {
return composition ? allEditorProps[composition.id] ?? {} : {};
}, [allEditorProps, composition]);
const env = useRemotionEnvironment();
return useMemo5(() => {
if (!composition) {
return null;
}
if (currentCompositionMetadata) {
return {
type: "success",
result: {
...currentCompositionMetadata,
id: composition.id,
defaultProps: composition.defaultProps ?? {}
}
};
}
if (!needsResolution(composition)) {
validateDurationInFrames(composition.durationInFrames, {
allowFloats: false,
component: `in <Composition id="${composition.id}">`
});
validateFps(composition.fps, `in <Composition id="${composition.id}">`, false);
validateDimension(composition.width, "width", `in <Composition id="${composition.id}">`);
validateDimension(composition.height, "height", `in <Composition id="${composition.id}">`);
return {
type: "success",
result: {
width: composition.width,
height: composition.height,
fps: composition.fps,
id: composition.id,
durationInFrames: composition.durationInFrames,
defaultProps: composition.defaultProps ?? {},
props: {
...composition.defaultProps ?? {},
...selectedEditorProps ?? {},
...typeof window === "undefined" || env.isPlayer || !window.remotion_inputProps ? {} : getInputProps() ?? {}
},
defaultCodec: null,
defaultOutName: null,
defaultVideoImageFormat: null,
defaultPixelFormat: null,
defaultProResProfile: null
}
};
}
if (!context) {
return null;
}
if (!context[composition.id]) {
return null;
}
return context[composition.id];
}, [
composition,
context,
currentCompositionMetadata,
selectedEditorProps,
env.isPlayer
]);
};
// src/use-delay-render.tsx
import { createContext as createContext11, useCallback as useCallback3, useContext as useContext7 } from "react";
// src/cancel-render.ts
var getErrorStackWithMessage = (error) => {
const stack = error.stack ?? "";
return stack.startsWith("Error:") ? stack : `${error.message}
${stack}`;
};
var isErrorLike = (err) => {
if (err instanceof Error) {
return true;
}
if (err === null) {
return false;
}
if (typeof err !== "object") {
return false;
}
if (!("stack" in err)) {
return false;
}
if (typeof err.stack !== "string") {
return false;
}
if (!("message" in err)) {
return false;
}
if (typeof err.message !== "string") {
return false;
}
return true;
};
function cancelRenderInternal(scope, err) {
let error;
if (isErrorLike(err)) {
error = err;
if (!error.stack) {
error.stack = new Error(error.message).stack;
}
} else if (typeof err === "string") {
error = Error(err);
} else {
error = Error("Rendering was cancelled");
}
if (scope) {
scope.remotion_cancelledError = getErrorStackWithMessage(error);
}
throw error;
}
function cancelRender(err) {
return cancelRenderInternal(typeof window !== "undefined" ? window : undefined, err);
}
// src/log.ts
var logLevels = ["trace", "verbose", "info", "warn", "error"];
var getNumberForLogLevel = (level) => {
return logLevels.indexOf(level);
};
var isEqualOrBelowLogLevel = (currentLevel, level) => {
return getNumberForLogLevel(currentLevel) <= getNumberForLogLevel(level);
};
var transformArgs = ({
args,
logLevel,
tag
}) => {
const arr = [...args];
if (getRemotionEnvironment().isRendering && !getRemotionEnvironment().isClientSideRendering) {
arr.unshift(Symbol.for(`__remotion_level_${logLevel}`));
}
if (tag && getRemotionEnvironment().isRendering && !getRemotionEnvironment().isClientSideRendering) {
arr.unshift(Symbol.for(`__remotion_tag_${tag}`));
}
return arr;
};
var verbose = (options, ...args) => {
if (isEqualOrBelowLogLevel(options.logLevel, "verbose")) {
return console.debug(...transformArgs({ args, logLevel: "verbose", tag: options.tag }));
}
};
var trace = (options, ...args) => {
if (isEqualOrBelowLogLevel(options.logLevel, "trace")) {
return console.debug(...transformArgs({ args, logLevel: "trace", tag: options.tag }));
}
};
var info = (options, ...args) => {
if (isEqualOrBelowLogLevel(options.logLevel, "info")) {
return console.log(...transformArgs({ args, logLevel: "info", tag: options.tag }));
}
};
var warn = (options, ...args) => {
if (isEqualOrBelowLogLevel(options.logLevel, "warn")) {
return console.warn(...transformArgs({ args, logLevel: "warn", tag: options.tag }));
}
};
var error = (options, ...args) => {
return console.error(...transformArgs({ args, logLevel: "error", tag: options.tag }));
};
var Log = {
trace,
verbose,
info,
warn,
error
};
// src/delay-render.ts
if (typeof window !== "undefined") {
window.remotion_renderReady = false;
if (!window.remotion_delayRenderTimeouts) {
window.remotion_delayRenderTimeouts = {};
}
window.remotion_delayRenderHandles = [];
}
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";
var defaultTimeout = 30000;
var delayRenderInternal = ({
scope,
environment,
label: label2,
options
}) => {
if (typeof label2 !== "string" && label2 !== null) {
throw new Error("The label parameter of delayRender() must be a string or undefined, got: " + JSON.stringify(label2));
}
const handle = Math.random();
scope.remotion_delayRenderHandles.push(handle);
const called = Error().stack?.replace(/^Error/g, "") ?? "";
if (environment.isRendering) {
const timeoutToUse = (options?.timeoutInMilliseconds ?? scope.remotion_puppeteerTimeout ?? defaultTimeout) - 2000;
const retriesLeft = (options?.retries ?? 0) - (scope.remotion_attempt - 1);
scope.remotion_delayRenderTimeouts[handle] = {
label: label2 ?? null,
startTime: Date.now(),
timeout: setTimeout(() => {
const message = [
`A delayRender()`,
label2 ? `"${label2}"` : null,
`was called but not cleared after ${timeoutToUse}ms. See https://remotion.dev/docs/timeout for help.`,
retriesLeft > 0 ? DELAY_RENDER_RETRIES_LEFT + retriesLeft : null,
retriesLeft > 0 ? DELAY_RENDER_RETRY_TOKEN : null,
DELAY_RENDER_CALLSTACK_TOKEN,
called
].filter(truthy).join(" ");
if (environment.isClientSideRendering) {
scope.remotion_cancelledError = getErrorStackWithMessage(Error(message));
} else {
cancelRenderInternal(scope, Error(message));
}
}, timeoutToUse)
};
}
scope.remotion_renderReady = false;
return handle;
};
var delayRender = (label2, options) => {
if (typeof window === "undefined") {
return Math.random();
}
return delayRenderInternal({
scope: window,
environment: getRemotionEnvironment(),
label: label2 ?? null,
options: options ?? {}
});
};
var continueRenderInternal = ({
scope,
handle,
environment,
logLevel
}) => {
if (typeof handle === "undefined") {
throw new TypeError("The continueRender() method must be called with a parameter that is the return value of delayRender(). No value was passed.");
}
if (typeof handle !== "number") {
throw new TypeError("The parameter passed into continueRender() must be the return value of delayRender() which is a number. Got: " + JSON.stringify(handle));
}
scope.remotion_delayRenderHandles = scope.remotion_delayRenderHandles.filter((h) => {
if (h === handle) {
if (environment.isRendering && scope !== undefined) {
if (!scope.remotion_delayRenderTimeouts[handle]) {
return false;
}
const { label: label2, startTime, timeout } = scope.remotion_delayRenderTimeouts[handle];
clearTimeout(timeout);
const message = [
label2 ? `"${label2}"` : "A handle",
DELAY_RENDER_CLEAR_TOKEN,
`${Date.now() - startTime}ms`
].filter(truthy).join(" ");
Log.verbose({ logLevel, tag: "delayRender()" }, message);
delete scope.remotion_delayRenderTimeouts[handle];
}
return false;
}
return true;
});
if (scope.remotion_delayRenderHandles.length === 0) {
scope.remotion_renderReady = true;
}
};
var continueRender = (handle) => {
if (typeof window === "undefined") {
return;
}
continueRenderInternal({
scope: window,
handle,
environment: getRemotionEnvironment(),
logLevel: window.remotion_logLevel ?? "info"
});
};
// src/log-level-context.tsx
import { createContext as createContext10 } from "react";
import * as React6 from "react";
var LogLevelContext = createContext10({
logLevel: "info",
mountTime: 0
});
var useLogLevel = () => {
const { logLevel } = React6.useContext(LogLevelContext);
if (logLevel === null) {
throw new Error("useLogLevel must be used within a LogLevelProvider");
}
return logLevel;
};
var useMountTime = () => {
const { mountTime } = React6.useContext(LogLevelContext);
if (mountTime === null) {
throw new Error("useMountTime must be used within a LogLevelProvider");
}
return mountTime;
};
// src/use-delay-render.tsx
var DelayRenderContextType = createContext11(null);
var useDelayRender = () => {
const environment = useRemotionEnvironment();
const scope = useContext7(DelayRenderContextType) ?? (typeof window !== "undefined" ? window : undefined);
const logLevel = useLogLevel();
const delayRender2 = useCallback3((label2, options) => {
if (!scope) {
return Math.random();
}
return delayRenderInternal({
scope,
environment,
label: label2 ?? null,
options: options ?? {}
});
}, [environment, scope]);
const continueRender2 = useCallback3((handle) => {
if (!scope) {
return;
}
continueRenderInternal({
scope,
handle,
environment,
logLevel
});
}, [environment, logLevel, scope]);
const cancelRender2 = useCallback3((err) => {
return cancelRenderInternal(scope ?? (typeof window !== "undefined" ? window : undefined), err);
}, [scope]);
return { delayRender: delayRender2, continueRender: continueRender2, cancelRender: cancelRender2 };
};
// src/use-lazy-component.ts
import React7, { useMemo as useMemo6, useRef as useRef2 } from "react";
var useLazyComponent = ({
compProps,
componentName,
noSuspense
}) => {
const componentRef = useRef2(null);
if ("component" in compProps) {
componentRef.current = compProps.component;
}
const lazy = useMemo6(() => {
if ("component" in compProps) {
if (typeof document === "undefined" || noSuspense) {
return compProps.component;
}
if (typeof compProps.component === "undefined") {
throw new Error(`A value of \`undefined\` was passed to the \`component\` prop. Check the value you are passing to the <${componentName}/> component.`);
}
const Wrapper = (props) => {
const Comp = componentRef.current;
return React7.createElement(Comp, props);
};
return Wrapper;
}
if ("lazyComponent" in compProps && typeof compProps.lazyComponent !== "undefined") {
if (typeof compProps.lazyComponent === "undefined") {
throw new Error(`A value of \`undefined\` was passed to the \`lazyComponent\` prop. Check the value you are passing to the <${componentName}/> component.`);
}
return React7.lazy(compProps.lazyComponent);
}
throw new Error("You must pass either 'component' or 'lazyComponent'");
}, [compProps.lazyComponent]);
return lazy;
};
// src/use-video.ts
import { useContext as useContext8, useMemo as useMemo7 } from "react";
var useVideo = () => {
const { canvasContent, compositions, currentCompositionMetadata } = useContext8(CompositionManager);
const selected = compositions.find((c) => {
return canvasContent?.type === "composition" && c.id === canvasContent.compositionId;
});
const resolved = useResolvedVideoConfig(selected?.id ?? null);
return useMemo7(() => {
if (!resolved) {
return null;
}
if (resolved.type === "error") {
return null;
}
if (resolved.type === "loading") {
return null;
}
if (!selected) {
return null;
}
return {
...resolved.result,
defaultProps: selected.defaultProps ?? {},
id: selected.id,
...currentCompositionMetadata ?? {},
component: selected.component
};
}, [currentCompositionMetadata, resolved, selected]);
};
// src/validation/validate-composition-id.ts
var getRegex2 = () => /^([a-zA-Z0-9-\u4E00-\u9FFF])+$/g;
var isCompositionIdValid = (id) => id.match(getRegex2());
var validateCompositionId = (id) => {
if (!isCompositionIdValid(id)) {
throw new Error(`Composition id can only contain a-z, A-Z, 0-9, CJK characters and -. You passed ${id}`);
}
};
var invalidCompositionErrorMessage = `Composition ID must match ${String(getRegex2())}`;
// 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/Composition.tsx
import { jsx as jsx7 } from "react/jsx-runtime";
var Fallback = () => {
const { continueRender: continueRender2, delayRender: delayRender2 } = useDelayRender();
useEffect2(() => {
const fallback = delayRender2("Waiting for Root component to unsuspend");
return () => continueRender2(fallback);
}, [continueRender2, delayRender2]);
return null;
};
var InnerComposition = ({
width,
height,
fps,
durationInFrames,
id,
defaultProps,
schema,
...compProps
}) => {
const compManager = useContext9(CompositionSetters);
const { registerComposition, unregisterComposition } = compManager;
const video = useVideo();
const lazy = useLazyComponent({
compProps,
componentName: "Composition",
noSuspense: false
});
const nonce = useNonce();
const isPlayer = useIsPlayer();
const environment = useRemotionEnvironment();
const canUseComposition = useContext9(CanUseRemotionHooks);
if (typeof window !== "undefined") {
window.remotion_seenCompositionIds = Array.from(new Set([...window.remotion_seenCompositionIds ?? [], id]));
}
if (canUseComposition) {
if (isPlayer) {
throw new Error("<Composition> was mounted inside the `component` that was passed to the <Player>. See https://remotion.dev/docs/wrong-composition-mount for help.");
}
throw new Error("<Composition> mounted inside another composition. See https://remotion.dev/docs/wrong-composition-mount for help.");
}
const { folderName, parentName } = useContext9(FolderContext);
const stack = compProps.stack ?? null;
useEffect2(() => {
if (!id) {
throw new Error("No id for composition passed.");
}
validateCompositionId(id);
validateDefaultAndInputProps(defaultProps, "defaultProps", id);
registerComposition({
durationInFrames: durationInFrames ?? undefined,
fps: fps ?? undefined,
height: height ?? undefined,
width: width ?? undefined,
id,
folderName,
component: lazy,
defaultProps: serializeThenDeserializeInStudio(defaultProps ?? {}),
nonce: nonce.get(),
parentFolderName: parentName,
schema: schema ?? null,
calculateMetadata: compProps.calculateMetadata ?? null,
stack
});
return () => {
unregisterComposition(id);
};
}, [
durationInFrames,
fps,
height,
lazy,
id,
folderName,
defaultProps,
width,
nonce,
parentName,
schema,
compProps.calculateMetadata,
stack,
registerComposition,
unregisterComposition
]);
const resolved = useResolvedVideoConfig(id);
const { setError, clearError } = useContext9(CompositionRenderErrorContext);
const onError = useCallback4((error2) => {
setError(error2);
}, [setError]);
const onClear = useCallback4(() => {
clearError();
}, [clearError]);
if (environment.isStudio && video && video.component === lazy && video.id === id) {
const Comp = lazy;
if (resolved === null || resolved.type !== "success" && resolved.type !== "success-and-refreshing") {
return null;
}
return createPortal(/* @__PURE__ */ jsx7(CanUseRemotionHooksProvider, {
children: /* @__PURE__ */ jsx7(CompositionErrorBoundary, {
onError,
onClear,
children: /* @__PURE__ */ jsx7(Suspense, {
fallback: /* @__PURE__ */ jsx7(Loading, {}),
children: /* @__PURE__ */ jsx7(Comp, {
...resolved.result.props ?? {}
})
})
})
}), portalNode());
}
if (environment.isRendering && video && video.component === lazy && video.id === id) {
const Comp = lazy;
if (resolved === null || resolved.type !== "success" && resolved.type !== "success-and-refreshing") {
return null;
}
return createPortal(/* @__PURE__ */ jsx7(CanUseRemotionHooksProvider, {
children: /* @__PURE__ */ jsx7(Suspense, {
fallback: /* @__PURE__ */ jsx7(Fallback, {}),
children: /* @__PURE__ */ jsx7(Comp, {
...resolved.result.props ?? {}
})
})
}), portalNode());
}
return null;
};
var Composition = (props) => {
const { onlyRenderComposition } = useContext9(CompositionSetters);
if (onlyRenderComposition && onlyRenderComposition !== props.id) {
return null;
}
return /* @__PURE__ */ jsx7(InnerComposition, {
...props
});
};
// src/enable-sequence-stack-traces.ts
var componentsToAddStacksTo = [];
var getComponentsToAddStacksTo = () => componentsToAddStacksTo;
var addSequenceStackTraces = (component) => {
componentsToAddStacksTo.push(component);
};
// src/version.ts
var VERSION = "4.0.445";
// src/multiple-versions-warning.ts
var checkMultipleRemotionVersions = () => {
if (typeof globalThis === "undefined") {
return;
}
const set = () => {
globalThis.remotion_imported = VERSION;
if (typeof window !== "undefined") {
window.remotion_imported = VERSION;
}
};
const alreadyImported = globalThis.remotion_imported || typeof window !== "undefined" && window.remotion_imported;
if (alreadyImported) {
if (alreadyImported === VERSION) {
return;
}
if (typeof alreadyImported === "string" && alreadyImported.includes("webcodecs")) {
set();
return;
}
throw new TypeError(`\uD83D\uDEA8 Multiple versions of Remotion detected: ${[
VERSION,
typeof alreadyImported === "string" ? alreadyImported : "an older version"
].filter(truthy).join(" and ")}. This will cause things to break in an unexpected way.
Check that all your Remotion packages are on the same version. If your dependencies depend on Remotion, make them peer dependencies. You can also run \`npx remotion versions\` from your terminal to see which versions are mismatching.`);
}
set();
};
// src/Null.tsx
var Null = () => {
throw new Error("<Null> has been removed as of Remotion v4.0.228. The native clipping APIs were experimental and subject to removal at any time. We removed them because they were sparingly used and made rendering often slower rather than faster.");
};
// src/Sequence.tsx
import {
forwardRef as forwardRef2,
useContext as useContext15,
useEffect as useEffect3,
useMemo as useMemo13,
useState as useState4
} from "react";
// src/freeze.tsx
import { useContext as useContext14, useMemo as useMemo11 } from "react";
// src/SequenceContext.tsx
import { createContext as createContext12 } from "react";
var SequenceContext = createContext12(null);
// src/timeline-position-state.ts
var exports_timeline_position_state = {};
__export(exports_timeline_position_state, {
useTimelineSetFrame: () => useTimelineSetFrame,
useTimelinePosition: () => useTimelinePosition,
useTimelineContext: () => useTimelineContext,
usePlayingState: () => usePlayingState,
useAbsoluteTimelinePosition: () => useAbsoluteTimelinePosition,
persistCurrentFrame: () => persistCurrentFrame,
getInitialFrameState: () => getInitialFrameState,
getFrameForComposition: () => getFrameForComposition
});
import { useContext as useContext10, useMemo as useMemo9 } from "react";
// src/TimelineContext.tsx
import {
createContext as createContext13,
useLayoutEffect,
useMemo as useMemo8,
useRef as useRef3,
useState as useState2
} from "react";
// src/random.ts
function mulberry32(a) {
let t = a + 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/TimelineContext.tsx
import { jsx as jsx8 } from "react/jsx-runtime";
var SetTimelineContext = createContext13({
setFrame: () => {
throw new Error("default");
},
setPlaying: () => {
throw new Error("default");
}
});
var TimelineContext = createContext13(null);
var AbsoluteTimeContext = createContext13(null);
var TimelineContextProvider = ({ children, frameState }) => {
const [playing, setPlaying] = useState2(false);
const imperativePlaying = useRef3(false);
const [playbackRate, setPlaybackRate] = useState2(1);
const audioAndVideoTags = useRef3([]);
const [remotionRootId] = useState2(() => String(random(null)));
const [_frame, setFrame] = useState2(() => getInitialFrameState());
const frame = frameState ?? _frame;
const { delayRender: delayRender2, continueRender: continueRender2 } = useDelayRender();
if (typeof window !== "undefined") {
useLayoutEffect(() => {
window.remotion_setFrame = (f, composition, attempt) => {
window.remotion_attempt = attempt;
const id = delayRender2(`Setting the current frame to ${f}`);
let asyncUpdate = true;
setFrame((s) => {
const currentFrame = s[composition] ?? window.remotion_initialFrame;
if (currentFrame === f) {
asyncUpdate = false;
return s;
}
return {
...s,
[composition]: f
};
});
if (asyncUpdate) {
requestAnimationFrame(() => continueRender2(id));
} else {
continueRender2(id);
}
};
window.remotion_isPlayer = false;
}, [continueRender2, delayRender2]);
}
const timelineContextValue = useMemo8(() => {
return {
frame,
playing,
imperativePlaying,
rootId: remotionRootId,
playbackRate,
setPlaybackRate,
audioAndVideoTags
};
}, [frame, playbackRate, playing, remotionRootId]);
const setTimelineContextValue = useMemo8(() => {
return {
setFrame,
setPlaying
};
}, []);
return /* @__PURE__ */ jsx8(AbsoluteTimeContext.Provider, {
value: timelineContextValue,
children: /* @__PURE__ */ jsx8(TimelineContext.Provider, {
value: timelineContextValue,
children: /* @__PURE__ */ jsx8(SetTimelineContext.Provider, {
value: setTimelineContextValue,
children
})
})
});
};
// src/timeline-position-state.ts
var makeKey = () => {
return `remotion.time-all`;
};
var persistCurrentFrame = (time) => {
localStorage.setItem(makeKey(), JSON.stringify(time));
};
var getInitialFrameState = () => {
const item = localStorage.getItem(makeKey()) ?? "{}";
const obj = JSON.parse(item);
return obj;
};
var getFrameForComposition = (composition) => {
const item = localStorage.getItem(makeKey()) ?? "{}";
const obj = JSON.parse(item);
if (obj[composition] !== undefined) {
return Number(obj[composition]);
}
if (typeof window === "undefined") {
return 0;
}
return window.remotion_initialFrame ?? 0;
};
var useTimelinePositionFromContext = (state) => {
const videoConfig = useVideo();
const env = useRemotionEnvironment();
if (!videoConfig) {
return typeof window === "undefined" ? 0 : window.remotion_initialFrame ?? 0;
}
const unclamped = state.frame[videoConfig.id] ?? (env.isPlayer ? 0 : getFrameForComposition(videoConfig.id));
return Math.min(videoConfig.durationInFrames - 1, unclamped);
};
var useTimelineContext = () => {
const state = useContext10(TimelineContext);
if (state === null) {
throw new Error("TimelineContext is not available. This hook must be used inside a <Player> or the Remotion Studio.");
}
return state;
};
var useTimelinePosition = () => {
const state = useTimelineContext();
return useTimelinePositionFromContext(state);
};
var useAbsoluteTimelinePosition = () => {
const state = useContext10(AbsoluteTimeContext);
if (state === null) {
throw new Error("AbsoluteTimeContext is not available. This hook must be used inside a <Player> or the Remotion Studio.");
}
return useTimelinePositionFromContext(state);
};
var useTimelineSetFrame = () => {
const { setFrame } = useContext10(SetTimelineContext);
return setFrame;
};
var usePlayingState = () => {
const { playing, imperativePlaying } = useTimelineContext();
const { setPlaying } = useContext10(SetTimelineContext);
return useMemo9(() => [playing, setPlaying, imperativePlaying], [imperativePlaying, playing, setPlaying]);
};
// src/use-current-frame.ts
import { useContext as useContext11 } from "react";
var useCurrentFrame = () => {
const canUseRemotionHooks = useContext11(CanUseRemotionHooks);
const env = useRemotionEnvironment();
if (!canUseRemotionHooks) {
if (env.isPlayer) {
throw new Error(`useCurrentFrame can only be called inside a component that was passed to <Player>. See: https://www.remotion.dev/docs/player/examples`);
}
throw new Error(`useCurrentFrame() can only be called inside a component that was registered as a composition. See https://www.remotion.dev/docs/the-fundamentals#defining-compositions`);
}
const frame = useTimelinePosition();
const context = useContext11(SequenceContext);
const contextOffset = context ? context.cumulatedFrom + context.relativeFrom : 0;
return frame - contextOffset;
};
// src/use-video-config.ts
import { useContext as useContext13 } from "react";
// src/use-unsafe-video-config.ts
import { useContext as useContext12, useMemo as useMemo10 } from "react";
var useUnsafeVideoConfig = () => {
const context = useContext12(SequenceContext);
const ctxWidth = context?.width ?? null;
const ctxHeight = context?.height ?? null;
const ctxDuration = context?.durationInFrames ?? null;
const video = useVideo();
return useMemo10(() => {
if (!video) {
return null;
}
const {
id,
durationInFrames,
fps,
height,
width,
defaultProps,
props,
defaultCodec,
defaultOutName,
defaultVideoImageFormat,
defaultPixelFormat,
defaultProResProfile
} = video;
return {
id,
width: ctxWidth ?? width,
height: ctxHeight ?? height,
fps,
durationInFrames: ctxDuration ?? durationInFrames,
defaultProps,
props,
defaultCodec,
defaultOutName,
defaultVideoImageFormat,
defaultPixelFormat,
defaultProResProfile
};
}, [ctxDuration, ctxHeight, ctxWidth, video]);
};
// src/use-video-config.ts
var useVideoConfig = () => {
const videoConfig = useUnsafeVideoConfig();
const context = useContext13(CanUseRemotionHooks);
const isPlayer = useIsPlayer();
if (!videoConfig) {
if (typeof window !== "undefined" && window.remotion_isPlayer || isPlayer) {
throw new Error([
"No video config found. Likely reasons:",
"- You are probably calling useVideoConfig() from outside the component passed to <Player />. See https://www.remotion.dev/docs/player/examples for how to set up the Player correctly.",
"- You have multiple versions of Remotion installed which causes the React context to get lost."
].join("-"));
}
throw new Error("No video config found. You are probably calling useVideoConfig() from a component which has not been registered as a <Composition />. See https://www.remotion.dev/docs/the-fundamentals#defining-compositions for more information.");
}
if (!context) {
throw new Error("Called useVide