UNPKG

@remotion/studio

Version:

APIs for interacting with the Remotion Studio

710 lines (695 loc) 20.3 kB
var __create = Object.create; var __getProtoOf = Object.getPrototypeOf; var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __toESM = (mod, isNodeMode, target) => { target = mod != null ? __create(__getProtoOf(mod)) : {}; const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target; for (let key of __getOwnPropNames(mod)) if (!__hasOwnProp.call(to, key)) __defProp(to, key, { get: () => mod[key], enumerable: true }); return to; }; var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); // src/api/create-composition.tsx import { Composition, Still } from "remotion"; import { jsx } from "react/jsx-runtime"; var createComposition = ({ ...other }) => () => { return /* @__PURE__ */ jsx(Composition, { ...other }); }; var createStill = ({ ...other }) => () => { return /* @__PURE__ */ jsx(Still, { ...other }); }; // src/api/delete-static-file.ts import { getRemotionEnvironment } from "remotion"; // src/components/call-api.ts var callApi = (endpoint, body, signal) => { return new Promise((resolve, reject) => { fetch(endpoint, { method: "post", headers: { "content-type": "application/json" }, signal, body: JSON.stringify(body) }).then((res) => res.json()).then((data) => { if (data.success) { resolve(data.data); } else { reject(new Error(data.error)); } }).catch((err) => { reject(err); }); }); }; // src/api/delete-static-file.ts var deleteStaticFile = async (relativePath) => { if (!getRemotionEnvironment().isStudio) { throw new Error("deleteStaticFile() is only available in the Studio"); } if (window.remotion_isReadOnlyStudio) { throw new Error("deleteStaticFile() is not available in Read-Only Studio"); } if (relativePath.startsWith(window.remotion_staticBase)) { relativePath = relativePath.substring(window.remotion_staticBase.length + 1); } const res = await callApi("/api/delete-static-file", { relativePath }); return res; }; // src/components/RenderModal/SchemaEditor/scroll-to-default-props-path.ts import React from "react"; var DEFAULT_PROPS_PATH_CLASSNAME = "__remotion-default-props-editor-label"; var DEFAULT_PROPS_PATH_ACTIVE_CLASSNAME = "__remotion-default-props-editor-label-active"; var defaultPropsEditorScrollableAreaRef = React.createRef(); // src/api/focus-default-props-path.ts var focusDefaultPropsPath = ({ path, scrollBehavior }) => { const currentlyActive = document.querySelector(`.${DEFAULT_PROPS_PATH_ACTIVE_CLASSNAME}`); if (currentlyActive !== null) { currentlyActive.classList.remove(DEFAULT_PROPS_PATH_ACTIVE_CLASSNAME); } const query = document.querySelector(`.${DEFAULT_PROPS_PATH_CLASSNAME}[data-json-path="${path.join(".")}"]`); if (query === null) { return { success: false }; } query.scrollIntoView({ behavior: scrollBehavior }); query.classList.add(DEFAULT_PROPS_PATH_ACTIVE_CLASSNAME); return { success: true }; }; // src/api/get-static-files.ts var warnedServer = false; var warnedPlayer = false; var warnServerOnce = () => { if (warnedServer) { return; } warnedServer = true; console.warn("Called getStaticFiles() on the server. The API is only available in the browser. An empty array was returned."); }; var warnPlayerOnce = () => { if (warnedPlayer) { return; } warnedPlayer = true; console.warn("Called getStaticFiles() while using the Remotion Player. The API is only available while using the Remotion Studio. An empty array was returned."); }; var getStaticFiles = () => { if (typeof document === "undefined") { warnServerOnce(); return []; } if (window.remotion_isPlayer) { warnPlayerOnce(); return []; } return window.remotion_staticFiles; }; // src/api/go-to-composition.ts import { Internals } from "remotion"; var goToComposition = (compositionId) => { Internals.compositionSelectorRef.current?.selectComposition(compositionId); }; // src/api/helpers/calc-new-props.ts import { Internals as Internals2, getRemotionEnvironment as getRemotionEnvironment2 } from "remotion"; var calcNewProps = (compositionId, defaultProps) => { if (!getRemotionEnvironment2().isStudio) { throw new Error("saveDefaultProps can only be called in the Remotion Studio."); } const { compositionsRef } = Internals2; const compositionsStore = compositionsRef.current; if (!compositionsStore) { throw new Error("No compositions ref found. Are you in the Remotion Studio and are the Remotion versions aligned?"); } const compositions = compositionsStore.getCompositions(); const composition = compositions.find((c) => c.id === compositionId); if (!composition) { throw new Error(`No composition with the ID ${compositionId} found. Available compositions: ${compositions.map((c) => c.id).join(", ")}`); } const savedDefaultProps = composition.defaultProps ?? {}; const generatedDefaultProps = defaultProps({ schema: composition.schema, savedDefaultProps, unsavedDefaultProps: savedDefaultProps }); return { composition, generatedDefaultProps }; }; // src/api/pause.ts import { Internals as Internals3 } from "remotion"; var pause = () => { Internals3.timeValueRef.current?.pause(); }; // src/api/play.ts import { Internals as Internals4 } from "remotion"; var play = (e) => { Internals4.timeValueRef.current?.play(e); }; // src/api/reevaluate-composition.ts import { Internals as Internals5 } from "remotion"; var reevaluateComposition = () => { Internals5.resolveCompositionsRef.current?.reloadCurrentlySelectedComposition(); }; // src/api/restart-studio.ts import { getRemotionEnvironment as getRemotionEnvironment3 } from "remotion"; var restartStudio = () => { if (!getRemotionEnvironment3().isStudio) { throw new Error("restartStudio() is only available in the Studio"); } if (window.remotion_isReadOnlyStudio) { throw new Error("restartStudio() is not available in read-only Studio"); } return callApi("/api/restart-studio", {}); }; // src/api/save-default-props.ts import { getRemotionEnvironment as getRemotionEnvironment4 } from "remotion"; // src/components/RenderModal/SchemaEditor/zod-schema-type.ts var getZodDef = (schema) => { if (schema._def) return schema._def; if (schema._zod) return schema._zod.def; throw new Error("Invalid zod schema: missing _def and _zod"); }; var v3TypeNameMap = { ZodString: "string", ZodNumber: "number", ZodBoolean: "boolean", ZodObject: "object", ZodArray: "array", ZodEnum: "enum", ZodUnion: "union", ZodDiscriminatedUnion: "discriminatedUnion", ZodOptional: "optional", ZodNullable: "nullable", ZodDefault: "default", ZodTuple: "tuple", ZodDate: "date", ZodAny: "any", ZodUnknown: "unknown", ZodBigInt: "bigint", ZodNull: "null", ZodUndefined: "undefined", ZodEffects: "effects", ZodLiteral: "literal", ZodRecord: "record", ZodNever: "never", ZodVoid: "void", ZodNaN: "nan", ZodSymbol: "symbol", ZodIntersection: "intersection", ZodMap: "map", ZodSet: "set", ZodLazy: "lazy", ZodFunction: "function", ZodNativeEnum: "nativeEnum", ZodCatch: "catch", ZodPromise: "promise", ZodBranded: "branded", ZodPipeline: "pipeline" }; var isZodV3Schema = (schema) => { const def = getZodDef(schema); return "typeName" in def; }; var getZodSchemaType = (schema) => { const def = getZodDef(schema); if ("typeName" in def) { const { typeName } = def; return v3TypeNameMap[typeName] ?? typeName; } const { type } = def; if (type === "union" && def.discriminator !== undefined) { return "discriminatedUnion"; } return type; }; var getZodSchemaDescription = (schema) => { if (isZodV3Schema(schema)) { return getZodDef(schema).description; } return schema.description; }; var getObjectShape = (schema) => { const { shape } = getZodDef(schema); return typeof shape === "function" ? shape() : shape; }; var getArrayElement = (schema) => { const def = getZodDef(schema); return isZodV3Schema(schema) ? def.type : def.element; }; var getInnerType = (schema) => { return getZodDef(schema).innerType; }; var getEffectsInner = (schema) => { return getZodDef(schema).schema; }; var getUnionOptions = (schema) => { return getZodDef(schema).options; }; var getIntersectionSchemas = (schema) => { const def = getZodDef(schema); return { left: def.left, right: def.right }; }; var getTupleItems = (schema) => { return getZodDef(schema).items; }; var getRecordValueType = (schema) => { return getZodDef(schema).valueType; }; var getRecordKeyType = (schema) => { return getZodDef(schema).keyType; }; var getPipelineOutput = (schema) => { return getZodDef(schema).out; }; var getBrandedInner = (schema) => { return isZodV3Schema(schema) ? getZodDef(schema).type : schema; }; // src/components/RenderModal/SchemaEditor/extract-enum-json-paths.ts var extractEnumJsonPaths = ({ schema, zodRuntime, currentPath, zodTypes }) => { const description = getZodSchemaDescription(schema); if (zodTypes && description === zodTypes.ZodZypesInternals.REMOTION_MATRIX_BRAND) { return [currentPath]; } const typeName = getZodSchemaType(schema); switch (typeName) { case "object": { const shape = getObjectShape(schema); const keys = Object.keys(shape); return keys.map((key) => { return extractEnumJsonPaths({ schema: shape[key], zodRuntime, currentPath: [...currentPath, key], zodTypes }); }).flat(1); } case "array": { return extractEnumJsonPaths({ schema: getArrayElement(schema), zodRuntime, currentPath: [...currentPath, "[]"], zodTypes }); } case "union": { return getUnionOptions(schema).map((option) => { return extractEnumJsonPaths({ schema: option, zodRuntime, currentPath, zodTypes }); }).flat(1); } case "discriminatedUnion": { return getUnionOptions(schema).map((op) => { return extractEnumJsonPaths({ schema: op, zodRuntime, currentPath, zodTypes }); }).flat(1); } case "literal": { return [currentPath]; } case "effects": { return extractEnumJsonPaths({ schema: getEffectsInner(schema), zodRuntime, currentPath, zodTypes }); } case "intersection": { const { left, right } = getIntersectionSchemas(schema); const leftValue = extractEnumJsonPaths({ schema: left, zodRuntime, currentPath, zodTypes }); const rightValue = extractEnumJsonPaths({ schema: right, zodRuntime, currentPath, zodTypes }); return [...leftValue, ...rightValue]; } case "tuple": { return getTupleItems(schema).map((item, i) => extractEnumJsonPaths({ schema: item, zodRuntime, currentPath: [...currentPath, i], zodTypes })).flat(1); } case "record": { const recordPath = [...currentPath, "{}"]; const keyResults = extractEnumJsonPaths({ schema: getRecordKeyType(schema), zodRuntime, currentPath: recordPath, zodTypes }); const valueResults = extractEnumJsonPaths({ schema: getRecordValueType(schema), zodRuntime, currentPath: recordPath, zodTypes }); return [...keyResults, ...valueResults]; } case "function": { throw new Error("Cannot create a value for type function"); } case "enum": { return [currentPath]; } case "nativeEnum": { return []; } case "optional": case "nullable": case "catch": { return extractEnumJsonPaths({ schema: getInnerType(schema), zodRuntime, currentPath, zodTypes }); } case "default": { return extractEnumJsonPaths({ schema: getInnerType(schema), zodRuntime, currentPath, zodTypes }); } case "promise": { return []; } case "branded": { return extractEnumJsonPaths({ schema: getBrandedInner(schema), zodRuntime, currentPath, zodTypes }); } case "pipeline": case "pipe": { return extractEnumJsonPaths({ schema: getPipelineOutput(schema), zodRuntime, currentPath, zodTypes }); } case "string": case "number": case "bigint": case "boolean": case "nan": case "date": case "symbol": case "undefined": case "null": case "any": case "unknown": case "never": case "void": case "map": case "lazy": case "set": case "custom": { return []; } default: throw new Error("Not implemented: " + typeName); } }; // src/components/RenderQueue/actions.ts import { NoReactInternals } from "remotion/no-react"; var callUpdateDefaultPropsApi = (compositionId, defaultProps, enumPaths) => { return callApi("/api/update-default-props", { compositionId, defaultProps: NoReactInternals.serializeJSONWithSpecialTypes({ data: defaultProps, indent: undefined, staticBase: window.remotion_staticBase }).serializedString, enumPaths }); }; // src/api/save-default-props.ts var saveDefaultProps = async ({ compositionId, defaultProps }) => { if (!getRemotionEnvironment4().isStudio) { throw new Error("saveDefaultProps() is only available in the Studio"); } if (window.remotion_isReadOnlyStudio) { throw new Error("saveDefaultProps() is not available in read-only Studio"); } try { await import("zod"); } catch { throw new Error('"zod" is required to use saveDefaultProps(), but is not installed.'); } const z = await import("zod"); let zodTypes = null; try { zodTypes = await import("@remotion/zod-types"); } catch {} const { generatedDefaultProps, composition } = calcNewProps(compositionId, defaultProps); const res = await callUpdateDefaultPropsApi(compositionId, generatedDefaultProps, composition.schema ? extractEnumJsonPaths({ schema: composition.schema, zodRuntime: z, currentPath: [], zodTypes }) : []); if (res.success) { return Promise.resolve(); } const err = new Error(res.reason); err.stack = res.stack; return Promise.reject(err); }; // src/api/seek.ts import { Internals as Internals6 } from "remotion"; var seek = (frame) => { Internals6.timeValueRef.current?.seek(Math.max(0, frame)); }; // src/api/toggle.ts import { Internals as Internals7 } from "remotion"; var toggle = (e) => { Internals7.timeValueRef.current?.toggle(e); }; // src/api/update-default-props.ts var updateDefaultProps = saveDefaultProps; // src/api/visual-control.ts import { useSyncExternalStore } from "react"; import { getRemotionEnvironment as getRemotionEnvironment5 } from "remotion"; // src/visual-controls/visual-control-store.ts var version = 0; var listeners = new Set; var visualControlStore = { subscribe(listener) { listeners.add(listener); return () => { listeners.delete(listener); }; }, getSnapshot() { return version; }, emitChange() { version++; for (const listener of listeners) { listener(); } } }; // src/visual-controls/VisualControls.tsx import { createContext, createRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react"; import { useRemotionEnvironment } from "remotion"; import { jsx as jsx2 } from "react/jsx-runtime"; var VisualControlsTabActivatedContext = createContext(false); var VisualControlsContext = createContext({ handles: {} }); var visualControlRef = createRef(); var SetVisualControlsContext = createContext({ updateHandles: () => { throw new Error("updateHandles is not implemented"); }, updateValue: () => { throw new Error("updateValue is not implemented"); }, visualControl: () => { throw new Error("visualControl is not implemented"); } }); // src/api/visual-control.ts var visualControl = (key, value, schema) => { useSyncExternalStore(visualControlStore.subscribe, visualControlStore.getSnapshot, visualControlStore.getSnapshot); if (getRemotionEnvironment5().isRendering) { return value; } if (!visualControlRef.current) { return value; } return visualControlRef.current.globalVisualControl(key, value, schema); }; // src/api/watch-public-folder.ts import { getRemotionEnvironment as getRemotionEnvironment6 } from "remotion"; var WATCH_REMOTION_STATIC_FILES = "remotion_staticFilesChanged"; var watchPublicFolder = (callback) => { if (!getRemotionEnvironment6().isStudio) { console.warn("The watchPublicFolder() API is only available while using the Remotion Studio."); return { cancel: () => { return; } }; } if (window.remotion_isReadOnlyStudio) { throw new Error("watchPublicFolder() is not available in read-only Studio"); } const emitUpdate = () => { callback(getStaticFiles()); }; window.addEventListener(WATCH_REMOTION_STATIC_FILES, emitUpdate); const cancel = () => { return window.removeEventListener(WATCH_REMOTION_STATIC_FILES, emitUpdate); }; return { cancel }; }; // src/api/watch-static-file.ts import { getRemotionEnvironment as getRemotionEnvironment7 } from "remotion"; var watchStaticFile = (fileName, callback) => { if (!getRemotionEnvironment7().isStudio) { console.warn("watchStaticFile() is only available while using the Remotion Studio."); return { cancel: () => { return; } }; } if (window.remotion_isReadOnlyStudio) { console.warn("watchStaticFile() is only available in an interactive Studio."); return { cancel: () => { return; } }; } const withoutStaticBase = fileName.startsWith(window.remotion_staticBase) ? fileName.replace(window.remotion_staticBase, "") : fileName; const withoutLeadingSlash = withoutStaticBase.startsWith("/") ? withoutStaticBase.slice(1) : withoutStaticBase; let prevFileData = window.remotion_staticFiles.find((file) => file.name === withoutLeadingSlash); const { cancel } = watchPublicFolder((staticFiles) => { const newFileData = staticFiles.find((file) => file.name === withoutLeadingSlash); if (!newFileData) { if (prevFileData !== undefined) { callback(null); } prevFileData = undefined; return; } if (prevFileData === undefined || prevFileData.lastModified !== newFileData.lastModified) { callback(newFileData); prevFileData = newFileData; } }); return { cancel }; }; // src/api/write-static-file.ts var writeStaticFile = async ({ contents, filePath }) => { if (window.remotion_isReadOnlyStudio) { throw new Error("writeStaticFile() is not available in read-only Studio"); } const url = new URL(`${window.remotion_staticBase}/api/add-asset`, window.location.origin); if (filePath.includes("\\")) { return Promise.reject(new Error("File path cannot contain backslashes")); } url.search = new URLSearchParams({ filePath }).toString(); const response = await fetch(url, { method: "POST", body: contents }); if (!response.ok) { const jsonResponse = await response.json(); throw new Error(jsonResponse.error); } }; // src/index.ts var StudioInternals = { createComposition, createStill }; export { writeStaticFile, watchStaticFile, watchPublicFolder, visualControl, updateDefaultProps, toggle, seek, saveDefaultProps, restartStudio, reevaluateComposition, play, pause, goToComposition, getStaticFiles, focusDefaultPropsPath, deleteStaticFile, StudioInternals };