@remotion/studio
Version:
APIs for interacting with the Remotion Studio
1,557 lines (1,501 loc) • 1.38 MB
JavaScript
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/previewEntry.tsx
import ReactDOM10 from "react-dom/client";
import { Internals as Internals68 } from "remotion";
import { NoReactInternals as NoReactInternals16 } from "remotion/no-react";
// src/Studio.tsx
import { useLayoutEffect as useLayoutEffect2 } from "react";
import { createPortal } from "react-dom";
import { Internals as Internals67 } from "remotion";
// src/components/Editor.tsx
import { PlayerInternals as PlayerInternals19 } from "@remotion/player";
import React176, { useCallback as useCallback132, useEffect as useEffect78, useMemo as useMemo131 } from "react";
import { Internals as Internals63 } from "remotion";
// src/helpers/colors.ts
var BACKGROUND = "rgb(31,36,40)";
var BACKGROUND__TRANSPARENT = "rgba(31,36,40, 0)";
var INPUT_BACKGROUND = "#2f363d";
var BORDER_COLOR = "#000";
var LIGHT_COLOR = "#ddd";
var SELECTED_BACKGROUND = "hsla(0, 0%, 100%, 0.15)";
var LIGHT_TEXT = "#A6A7A9";
var RULER_COLOR = "#808080";
var VERY_LIGHT_TEXT = "rgba(255, 255, 255, 0.3)";
var SELECTED_HOVER_BACKGROUND = "hsla(0, 0%, 100%, 0.25)";
var CLEAR_HOVER = "rgba(255, 255, 255, 0.06)";
var INPUT_BORDER_COLOR_UNHOVERED = "rgba(0, 0, 0, 0.6)";
var INPUT_BORDER_COLOR_HOVERED = "rgba(255, 255, 255, 0.05)";
var TIMELINE_BACKGROUND = "#111";
var FAIL_COLOR = "#ff3232";
var TEXT_COLOR = "#fff";
var WARNING_COLOR = "#f1c40f";
var BLUE = "#0b84f3";
var BLUE_DISABLED = "#284f73";
var LIGHT_TRANSPARENT = "rgba(255, 255, 255, 0.7)";
var UNSELECTED_GUIDE = "#7e1219";
var SELECTED_GUIDE = "#d22d3a";
var LINE_COLOR = "#363A3E";
var TIMELINE_TRACK_SEPARATOR = "rgba(0, 0, 0, 0.3)";
var getBackgroundFromHoverState = ({
selected,
hovered
}) => {
if (selected) {
if (hovered) {
return SELECTED_HOVER_BACKGROUND;
}
return SELECTED_BACKGROUND;
}
if (hovered) {
return CLEAR_HOVER;
}
return "transparent";
};
// src/helpers/noop.ts
var noop = () => {
return;
};
// src/state/canvas-ref.ts
import { createRef } from "react";
var canvasRef = createRef();
var drawRef = createRef();
// src/state/timeline-zoom.tsx
import { createContext as createContext2, useMemo as useMemo2, useState } from "react";
// src/components/Timeline/imperative-state.ts
var currentFrame = 0;
var currentZoom = 1;
var currentDuration = 1;
var currentFps = 1;
var getCurrentZoom = () => {
return currentZoom;
};
var setCurrentZoom = (z) => {
currentZoom = z;
};
var getCurrentFrame = () => {
return currentFrame;
};
var setCurrentFrame = (f) => {
currentFrame = f;
};
var getCurrentDuration = () => {
return currentDuration;
};
var setCurrentDuration = (d) => {
currentDuration = d;
};
var getCurrentFps = () => {
return currentFps;
};
var setCurrentFps = (d) => {
currentFps = d;
};
// src/components/Timeline/timeline-scroll-logic.ts
import { interpolate } from "remotion";
// src/helpers/timeline-layout.ts
var TIMELINE_PADDING = 16;
var TIMELINE_BORDER = 1;
var TIMELINE_ITEM_BORDER_BOTTOM = 1;
var getTimelineLayerHeight = (type) => {
if (type === "video") {
return 50;
}
return 25;
};
// src/components/Timeline/TimelineSlider.tsx
import {
createRef as createRef2,
useContext,
useEffect,
useImperativeHandle,
useMemo,
useRef
} from "react";
import { Internals, useVideoConfig } from "remotion";
// src/helpers/get-left-of-timeline-slider.ts
var getXPositionOfItemInTimelineImperatively = (frame, duration, width) => {
const proportion = frame / (duration - 1);
return proportion * (width - TIMELINE_PADDING * 2) + TIMELINE_PADDING;
};
// src/components/Timeline/TimelineSliderHandle.tsx
import { jsx } from "react/jsx-runtime";
var container = {
width: 20,
height: 20,
position: "fixed",
marginLeft: -8
};
var TimelineSliderHandle = () => {
return /* @__PURE__ */ jsx("div", {
style: container,
children: /* @__PURE__ */ jsx("svg", {
width: 17,
viewBox: "0 0 159 212",
children: /* @__PURE__ */ jsx("path", {
d: "M17.0234375,1.07763419 L143.355469,1.07763419 C151.63974,1.07763419 158.355469,7.79336295 158.355469,16.0776342 L158.355469,69.390507 C158.355469,73.7938677 156.420655,77.9748242 153.064021,80.8248415 L89.3980057,134.881757 C83.7986799,139.635978 75.5802263,139.635978 69.9809005,134.881757 L6.66764807,81.1243622 C3.0872392,78.0843437 1.0234375,73.6246568 1.0234375,68.9277387 L1.0234375,17.0776342 C1.0234375,8.2410782 8.1868815,1.07763419 17.0234375,1.07763419 Z",
fill: "#f02c00"
})
})
});
};
// src/components/Timeline/TimelineWidthProvider.tsx
import { PlayerInternals } from "@remotion/player";
import { createContext } from "react";
// src/components/Timeline/timeline-refs.ts
import React from "react";
var sliderAreaRef = React.createRef();
var scrollableRef = React.createRef();
var timelineVerticalScroll = React.createRef();
// src/components/Timeline/TimelineWidthProvider.tsx
import { jsx as jsx2 } from "react/jsx-runtime";
var TimelineWidthContext = createContext(null);
var TimelineWidthProvider = ({ children }) => {
const size = PlayerInternals.useElementSize(sliderAreaRef, {
triggerOnWindowResize: false,
shouldApplyCssTransforms: true
});
return /* @__PURE__ */ jsx2(TimelineWidthContext.Provider, {
value: size?.width ?? null,
children
});
};
// src/components/Timeline/TimelineSlider.tsx
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
var container2 = {
position: "absolute",
bottom: 0,
top: 0,
pointerEvents: "none"
};
var line = {
height: "100vh",
width: 1,
position: "fixed",
backgroundColor: "#f02c00"
};
var redrawTimelineSliderFast = createRef2();
var TimelineSlider = () => {
const videoConfig = Internals.useUnsafeVideoConfig();
const timelineWidth = useContext(TimelineWidthContext);
if (videoConfig === null || timelineWidth === null) {
return null;
}
return /* @__PURE__ */ jsx3(Inner, {});
};
var Inner = () => {
const videoConfig = useVideoConfig();
const timelinePosition = Internals.Timeline.useTimelinePosition();
const ref = useRef(null);
const timelineWidth = useContext(TimelineWidthContext);
if (timelineWidth === null) {
throw new Error("Unexpectedly did not have timeline width");
}
const style = useMemo(() => {
const left = getXPositionOfItemInTimelineImperatively(timelinePosition, videoConfig.durationInFrames, timelineWidth);
return {
...container2,
transform: `translateX(${left}px)`
};
}, [timelinePosition, videoConfig.durationInFrames, timelineWidth]);
useImperativeHandle(redrawTimelineSliderFast, () => {
return {
draw: (frame, width) => {
const { current } = ref;
if (!current) {
throw new Error("unexpectedly did not have ref to timelineslider");
}
current.style.transform = `translateX(${getXPositionOfItemInTimelineImperatively(frame, getCurrentDuration(), width ?? sliderAreaRef.current?.clientWidth ?? 0)}px)`;
}
};
}, []);
useEffect(() => {
const currentRef = ref.current;
if (!currentRef) {
return;
}
const { current } = timelineVerticalScroll;
if (!current) {
return;
}
const onScroll = () => {
currentRef.style.top = current.scrollTop + "px";
};
current.addEventListener("scroll", onScroll);
return () => {
current.removeEventListener("scroll", onScroll);
};
}, []);
return /* @__PURE__ */ jsxs("div", {
ref,
style,
children: [
/* @__PURE__ */ jsx3("div", {
style: line
}),
/* @__PURE__ */ jsx3(TimelineSliderHandle, {})
]
});
};
// src/components/Timeline/timeline-scroll-logic.ts
var canScrollTimelineIntoDirection = () => {
const current = scrollableRef.current;
const { scrollWidth, scrollLeft, clientWidth } = current;
const canScrollRight = scrollWidth - scrollLeft - clientWidth > TIMELINE_PADDING;
const canScrollLeft = scrollLeft > TIMELINE_PADDING;
return { canScrollRight, canScrollLeft };
};
var SCROLL_INCREMENT = 200;
var calculateFrameWhileScrollingRight = ({
durationInFrames,
width,
scrollLeft
}) => {
return getFrameFromX({
clientX: scrollLeft,
durationInFrames,
width,
extrapolate: "clamp"
}) + Math.ceil((scrollableRef.current?.clientWidth - TIMELINE_PADDING) / getFrameIncrement(durationInFrames));
};
var getFrameWhileScrollingLeft = ({
durationInFrames,
width
}) => {
const nextFrame = getFrameFromX({
clientX: scrollableRef.current?.scrollLeft - SCROLL_INCREMENT,
durationInFrames,
width,
extrapolate: "clamp"
});
const currentFrame2 = getFrameFromX({
clientX: scrollableRef.current?.scrollLeft,
durationInFrames,
width,
extrapolate: "clamp"
});
return Math.max(0, Math.min(currentFrame2 - 1, nextFrame));
};
var isCursorInViewport = ({
frame,
durationInFrames
}) => {
const width = scrollableRef.current?.scrollWidth ?? 0;
const scrollLeft = scrollableRef.current?.scrollLeft ?? 0;
const scrollPosOnRightEdge = getScrollPositionForCursorOnRightEdge({
nextFrame: frame,
durationInFrames
});
const scrollPosOnLeftEdge = getScrollPositionForCursorOnLeftEdge({
nextFrame: frame,
durationInFrames
});
const currentFrameRight = calculateFrameWhileScrollingRight({
durationInFrames,
scrollLeft,
width
});
return !(scrollPosOnRightEdge >= getScrollPositionForCursorOnRightEdge({
nextFrame: currentFrameRight,
durationInFrames
}) || scrollPosOnLeftEdge < scrollLeft);
};
var ensureFrameIsInViewport = ({
direction,
durationInFrames,
frame
}) => {
redrawTimelineSliderFast.current?.draw(frame);
const width = scrollableRef.current?.scrollWidth ?? 0;
const scrollLeft = scrollableRef.current?.scrollLeft ?? 0;
if (direction === "fit-left") {
const currentFrameLeft = getFrameFromX({
clientX: scrollLeft,
durationInFrames,
width,
extrapolate: "clamp"
});
const scrollPos = getScrollPositionForCursorOnLeftEdge({
nextFrame: frame,
durationInFrames
});
const needsToScrollLeft = scrollPos <= getScrollPositionForCursorOnLeftEdge({
nextFrame: currentFrameLeft,
durationInFrames
});
if (needsToScrollLeft) {
scrollToTimelineXOffset(scrollPos);
}
}
if (direction === "fit-right") {
const currentFrameRight = calculateFrameWhileScrollingRight({
durationInFrames,
scrollLeft,
width
});
const scrollPos = getScrollPositionForCursorOnRightEdge({
nextFrame: frame,
durationInFrames
});
const needsToScrollRight = scrollPos >= getScrollPositionForCursorOnRightEdge({
nextFrame: currentFrameRight,
durationInFrames
});
if (needsToScrollRight) {
scrollToTimelineXOffset(scrollPos);
}
}
if (direction === "page-right" || direction === "page-left") {
if (!isCursorInViewport({ frame, durationInFrames })) {
scrollToTimelineXOffset(direction === "page-left" ? getScrollPositionForCursorOnRightEdge({
nextFrame: frame,
durationInFrames
}) : getScrollPositionForCursorOnLeftEdge({
nextFrame: frame,
durationInFrames
}));
}
}
if (direction === "center") {
const scrollPosOnRightEdge = getScrollPositionForCursorOnRightEdge({
nextFrame: frame,
durationInFrames
});
const scrollPosOnLeftEdge = getScrollPositionForCursorOnLeftEdge({
nextFrame: frame,
durationInFrames
});
scrollToTimelineXOffset((scrollPosOnLeftEdge + scrollPosOnRightEdge) / 2);
}
};
var scrollToTimelineXOffset = (scrollPos) => {
scrollableRef.current?.scroll({
left: scrollPos
});
};
var getScrollPositionForCursorOnLeftEdge = ({
nextFrame,
durationInFrames
}) => {
const frameIncrement = getFrameIncrement(durationInFrames);
const scrollPos = frameIncrement * nextFrame;
return scrollPos;
};
var getScrollPositionForCursorOnRightEdge = ({
nextFrame,
durationInFrames
}) => {
const frameIncrement = getFrameIncrement(durationInFrames);
const framesRemaining = durationInFrames - 1 - nextFrame;
const fromRight = framesRemaining * frameIncrement + TIMELINE_PADDING;
const scrollPos = scrollableRef.current?.scrollWidth - fromRight - scrollableRef.current?.clientWidth + TIMELINE_PADDING + 4;
return scrollPos;
};
var getFrameIncrement = (durationInFrames) => {
const width = scrollableRef.current?.scrollWidth ?? 0;
return getFrameIncrementFromWidth(durationInFrames, width);
};
var getFrameIncrementFromWidth = (durationInFrames, width) => {
return (width - TIMELINE_PADDING * 2) / (durationInFrames - 1);
};
var getFrameWhileScrollingRight = ({
durationInFrames,
width
}) => {
const nextFrame = calculateFrameWhileScrollingRight({
durationInFrames,
width,
scrollLeft: scrollableRef.current?.scrollLeft + SCROLL_INCREMENT
});
const currentFrame2 = calculateFrameWhileScrollingRight({
durationInFrames,
width,
scrollLeft: scrollableRef.current?.scrollLeft
});
return Math.min(durationInFrames - 1, Math.max(nextFrame, currentFrame2 + 1));
};
var getFrameFromX = ({
clientX,
durationInFrames,
width,
extrapolate
}) => {
const pos = clientX - TIMELINE_PADDING;
const frame = Math.round(interpolate(pos, [0, width - TIMELINE_PADDING * 2], [0, durationInFrames - 1], {
extrapolateLeft: extrapolate,
extrapolateRight: extrapolate
}));
return frame;
};
var zoomAndPreserveCursor = ({
oldZoom,
newZoom,
currentFrame: currentFrame2,
currentDurationInFrames
}) => {
const ratio = newZoom / oldZoom;
if (ratio === 1) {
return;
}
const { current } = scrollableRef;
if (!current) {
return;
}
const frameIncrement = getFrameIncrement(currentDurationInFrames);
const prevCursorPosition = frameIncrement * currentFrame2 + TIMELINE_PADDING;
const newCursorPosition = ratio * (prevCursorPosition - TIMELINE_PADDING) + TIMELINE_PADDING;
current.scrollLeft += newCursorPosition - prevCursorPosition;
redrawTimelineSliderFast.current?.draw(currentFrame2, (scrollableRef.current?.clientWidth ?? 0) * ratio);
};
// src/components/ZoomPersistor.tsx
import { useContext as useContext2, useEffect as useEffect2 } from "react";
import { Internals as Internals2 } from "remotion";
// src/helpers/url-state.ts
var getUrlHandlingType = () => {
if (window.remotion_isReadOnlyStudio) {
return "query-string";
}
return "spa";
};
var pushUrl = (url) => {
if (getUrlHandlingType() === "query-string") {
window.history.pushState({}, "Studio", `${window.location.pathname}?${url}`);
} else {
window.history.pushState({}, "Studio", url);
}
};
var clearUrl = () => {
window.location.href = window.location.pathname;
};
var reloadUrl = () => {
window.location.reload();
};
var getRoute = () => {
if (getUrlHandlingType() === "query-string") {
return window.location.search.substring(1);
}
return window.location.pathname;
};
// src/components/load-canvas-content-from-url.ts
var deriveCanvasContentFromUrl = () => {
const route = getRoute();
const substrings = route.split("/").filter(Boolean);
const lastPart = substrings[substrings.length - 1];
if (substrings[0] === "assets") {
return {
type: "asset",
asset: decodeURIComponent(route.substring("/assets/".length))
};
}
if (substrings[0] === "outputs") {
return {
type: "output",
path: decodeURIComponent(route.substring("/outputs/".length))
};
}
if (lastPart) {
return {
type: "composition",
compositionId: decodeURIComponent(lastPart)
};
}
return null;
};
// src/components/ZoomPersistor.tsx
var makeKey = () => {
return `remotion.zoom-map`;
};
var persistCurrentZoom = (zoom) => {
localStorage.setItem(makeKey(), JSON.stringify(zoom));
};
var getZoomFromLocalStorage = () => {
const zoom = localStorage.getItem(makeKey());
return zoom ? JSON.parse(zoom) : {};
};
var ZoomPersistor = () => {
const [playing] = Internals2.Timeline.usePlayingState();
const { zoom } = useContext2(TimelineZoomCtx);
const { canvasContent } = useContext2(Internals2.CompositionManager);
const urlState = deriveCanvasContentFromUrl();
const isActive = urlState && urlState.type === "composition" && canvasContent && canvasContent.type === "composition" && urlState.compositionId === canvasContent.compositionId;
useEffect2(() => {
if (!isActive) {
return;
}
persistCurrentZoom(zoom);
}, [zoom, isActive, playing, urlState]);
return null;
};
// src/state/timeline-zoom.tsx
import { jsx as jsx4 } from "react/jsx-runtime";
var TIMELINE_MIN_ZOOM = 1;
var TIMELINE_MAX_ZOOM = 5;
var TimelineZoomCtx = createContext2({
zoom: {},
setZoom: () => {
throw new Error("has no context");
}
});
var TimelineZoomContext = ({ children }) => {
const [zoom, setZoom] = useState(() => getZoomFromLocalStorage());
const value = useMemo2(() => {
return {
zoom,
setZoom: (compositionId, callback) => {
setZoom((prevZoomMap) => {
const newZoomWithFloatingPointErrors = Math.min(TIMELINE_MAX_ZOOM, Math.max(TIMELINE_MIN_ZOOM, callback(prevZoomMap[compositionId] ?? TIMELINE_MIN_ZOOM)));
const newZoom = Math.round(newZoomWithFloatingPointErrors * 10) / 10;
zoomAndPreserveCursor({
oldZoom: prevZoomMap[compositionId] ?? TIMELINE_MIN_ZOOM,
newZoom,
currentDurationInFrames: getCurrentDuration(),
currentFrame: getCurrentFrame()
});
return { ...prevZoomMap, [compositionId]: newZoom };
});
}
};
}, [zoom]);
return /* @__PURE__ */ jsx4(TimelineZoomCtx.Provider, {
value,
children
});
};
// src/state/z-index.tsx
import {
createContext as createContext5,
useContext as useContext4,
useEffect as useEffect4,
useMemo as useMemo6,
useRef as useRef3
} from "react";
// src/helpers/use-keybinding.ts
import { useCallback as useCallback2, useContext as useContext3, useEffect as useEffect3, useMemo as useMemo4, useState as useState2 } from "react";
// src/state/keybindings.tsx
import { createContext as createContext3, useCallback, useMemo as useMemo3, useRef as useRef2 } from "react";
import { jsx as jsx5 } from "react/jsx-runtime";
var KeybindingContext = createContext3({
registerKeybinding: () => {
throw new Error("Has no keybinding context");
},
unregisterKeybinding: () => {
return;
},
unregisterPane: () => {
return;
}
});
var KeybindingContextProvider = ({ children }) => {
const registered = useRef2([]);
const registerKeybinding = useCallback((binding) => {
registered.current = [...registered.current, binding];
window.addEventListener(binding.event, binding.callback);
}, []);
const unregisterKeybinding = useCallback((binding) => {
registered.current = registered.current.filter((r) => {
if (r.id === binding.id) {
window.removeEventListener(binding.event, binding.callback);
return false;
}
return true;
});
}, []);
const unregisterPane = useCallback((paneId) => {
const matchedKeybindings = registered.current.filter((r) => r.registeredFromPane === paneId);
for (const matched of matchedKeybindings) {
unregisterKeybinding(matched);
}
}, [unregisterKeybinding]);
const value = useMemo3(() => {
return {
registerKeybinding,
unregisterKeybinding,
unregisterPane
};
}, [registerKeybinding, unregisterKeybinding, unregisterPane]);
return /* @__PURE__ */ jsx5(KeybindingContext.Provider, {
value,
children
});
};
// src/helpers/use-keybinding.ts
if (!process.env.KEYBOARD_SHORTCUTS_ENABLED) {
console.warn("Keyboard shortcuts disabled either due to: a) --disable-keyboard-shortcuts being passed b) Config.setKeyboardShortcutsEnabled(false) being set or c) a Remotion version mismatch.");
}
var areKeyboardShortcutsDisabled = () => {
return !process.env.KEYBOARD_SHORTCUTS_ENABLED;
};
var useKeybinding = () => {
const [paneId] = useState2(() => String(Math.random()));
const context = useContext3(KeybindingContext);
const { isHighestContext } = useZIndex();
const registerKeybinding = useCallback2((options) => {
if (!process.env.KEYBOARD_SHORTCUTS_ENABLED) {
return {
unregister: () => {
return;
}
};
}
if (!isHighestContext && !options.keepRegisteredWhenNotHighestContext) {
return {
unregister: () => {
return;
}
};
}
const listener = (e) => {
const commandKey = window.navigator.platform.startsWith("Mac") ? e.metaKey : e.ctrlKey;
if (!e.key) {
return;
}
if (e.key.toLowerCase() === options.key.toLowerCase() && options.commandCtrlKey === commandKey) {
if (!options.triggerIfInputFieldFocused) {
const { activeElement } = document;
if (activeElement instanceof HTMLInputElement) {
return;
}
if (activeElement instanceof HTMLTextAreaElement) {
return;
}
}
options.callback(e);
if (options.preventDefault) {
e.preventDefault();
}
}
};
const toRegister = {
registeredFromPane: paneId,
event: options.event,
key: options.key,
callback: listener,
id: String(Math.random())
};
context.registerKeybinding(toRegister);
return {
unregister: () => context.unregisterKeybinding(toRegister)
};
}, [context, isHighestContext, paneId]);
useEffect3(() => {
return () => {
context.unregisterPane(paneId);
};
}, [context, paneId]);
return useMemo4(() => ({ registerKeybinding, isHighestContext }), [registerKeybinding, isHighestContext]);
};
// src/state/highest-z-index.tsx
import { createContext as createContext4, useCallback as useCallback3, useMemo as useMemo5, useState as useState3 } from "react";
import { jsx as jsx6 } from "react/jsx-runtime";
var HighestZIndexContext = createContext4({
highestIndex: 0,
registerZIndex: () => {
return;
},
unregisterZIndex: () => {
return;
}
});
var HighestZIndexProvider = ({ children }) => {
const [zIndexes, setZIndexes] = useState3([]);
const registerZIndex = useCallback3((newIndex) => {
setZIndexes((prev) => [...prev, newIndex]);
}, []);
const unregisterZIndex = useCallback3((newIndex) => {
setZIndexes((prev) => {
const index = prev.indexOf(newIndex);
if (index === -1) {
throw new Error("did not find z-index " + newIndex);
}
return prev.filter((_n, i) => i !== index);
});
}, []);
const highestIndex = Math.max(...zIndexes);
const value = useMemo5(() => {
return {
highestIndex,
registerZIndex,
unregisterZIndex
};
}, [registerZIndex, unregisterZIndex, highestIndex]);
return /* @__PURE__ */ jsx6(HighestZIndexContext.Provider, {
value,
children
});
};
// src/state/input-dragger-click-lock.ts
var clickLock = false;
var getClickLock = () => clickLock;
var setClickLock = (lock) => {
clickLock = lock;
};
// src/state/z-index.tsx
import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
var ZIndexContext = createContext5({
currentIndex: 0
});
var margin = {
margin: "auto"
};
var EscapeHook = ({ onEscape }) => {
const keybindings = useKeybinding();
useEffect4(() => {
const escape = keybindings.registerKeybinding({
event: "keydown",
key: "Escape",
callback: onEscape,
commandCtrlKey: false,
preventDefault: true,
triggerIfInputFieldFocused: true,
keepRegisteredWhenNotHighestContext: false
});
return () => {
escape.unregister();
};
}, [keybindings, onEscape]);
return null;
};
var HigherZIndex = ({ children, onEscape, onOutsideClick, disabled }) => {
const context = useContext4(ZIndexContext);
const highestContext = useContext4(HighestZIndexContext);
const containerRef = useRef3(null);
const currentIndex = disabled ? context.currentIndex : context.currentIndex + 1;
useEffect4(() => {
if (disabled) {
return;
}
highestContext.registerZIndex(currentIndex);
return () => highestContext.unregisterZIndex(currentIndex);
}, [currentIndex, highestContext, disabled]);
useEffect4(() => {
if (disabled) {
return;
}
let onUp = null;
const listener = (downEvent) => {
const outsideClick = !containerRef.current?.contains(downEvent.target);
if (!outsideClick) {
return;
}
onUp = (upEvent) => {
if (highestContext.highestIndex === currentIndex && !getClickLock() && document.contains(upEvent.target)) {
upEvent.stopPropagation();
onOutsideClick(upEvent.target);
}
};
window.addEventListener("pointerup", onUp, { once: true });
};
requestAnimationFrame(() => {
window.addEventListener("pointerdown", listener);
});
return () => {
if (onUp) {
window.removeEventListener("pointerup", onUp, { once: true });
}
onUp = null;
return window.removeEventListener("pointerdown", listener);
};
}, [currentIndex, disabled, highestContext.highestIndex, onOutsideClick]);
const value = useMemo6(() => {
return {
currentIndex
};
}, [currentIndex]);
return /* @__PURE__ */ jsxs2(ZIndexContext.Provider, {
value,
children: [
disabled ? null : /* @__PURE__ */ jsx7(EscapeHook, {
onEscape
}),
/* @__PURE__ */ jsx7("div", {
ref: containerRef,
style: margin,
children
})
]
});
};
var useZIndex = () => {
const context = useContext4(ZIndexContext);
const highestContext = useContext4(HighestZIndexContext);
const isHighestContext = highestContext.highestIndex === context.currentIndex;
return useMemo6(() => ({
currentZIndex: context.currentIndex,
highestZIndex: highestContext.highestIndex,
isHighestContext,
tabIndex: isHighestContext ? 0 : -1
}), [context.currentIndex, highestContext.highestIndex, isHighestContext]);
};
// src/components/EditorContent.tsx
import { useContext as useContext68 } from "react";
import { Internals as Internals56 } from "remotion";
// src/helpers/is-current-selected-still.ts
import { useContext as useContext5 } from "react";
import { Internals as Internals3 } from "remotion";
// src/helpers/is-composition-still.ts
var isCompositionStill = (comp) => {
if (!comp) {
return false;
}
return comp.durationInFrames === 1;
};
// src/helpers/is-current-selected-still.ts
var useIsStill = () => {
const resolved = Internals3.useResolvedVideoConfig(null);
if (!resolved || resolved.type !== "success") {
return false;
}
return isCompositionStill(resolved.result);
};
var useIsVideoComposition = () => {
const isStill = useIsStill();
const { canvasContent } = useContext5(Internals3.CompositionManager);
if (canvasContent === null) {
return false;
}
if (isStill) {
return false;
}
return canvasContent.type === "composition";
};
// src/components/InitialCompositionLoader.tsx
import { useCallback as useCallback22, useContext as useContext13, useEffect as useEffect13 } from "react";
import { Internals as Internals10 } from "remotion";
// 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/helpers/mobile-layout.ts
import { useEffect as useEffect5, useRef as useRef4, useState as useState4 } from "react";
var breakpoint = 900;
function getIsMobile() {
return window.innerWidth < breakpoint;
}
var useMobileLayout = () => {
const [isMobile, setIsMobile] = useState4(getIsMobile());
const isMobileRef = useRef4(isMobile);
useEffect5(() => {
function handleResize() {
if (getIsMobile() !== isMobileRef.current) {
setIsMobile(getIsMobile());
}
isMobileRef.current = getIsMobile();
}
window.addEventListener("resize", handleResize);
return () => {
return window.removeEventListener("resize", handleResize);
};
}, []);
return isMobile;
};
// src/state/folders.tsx
import { createContext as createContext7, useMemo as useMemo7, useState as useState5 } from "react";
// src/helpers/persist-open-folders.tsx
import { createContext as createContext6 } from "react";
var openFolderKey = ({
folderName,
parentName
}) => {
return [parentName ?? "no-parent", folderName].join("/");
};
var localStorageKey = (type) => type === "compositions" ? "remotion.expandedFolders" : "remotion.expandedAssetFolders";
var persistExpandedFolders = (type, state) => {
window.localStorage.setItem(localStorageKey(type), JSON.stringify(state));
};
var loadExpandedFolders = (type) => {
const item = window.localStorage.getItem(localStorageKey(type));
if (item === null) {
return {};
}
return JSON.parse(item);
};
var ExpandedFoldersContext = createContext6({
toggleFolder: () => {},
foldersExpanded: {},
setFoldersExpanded: () => {}
});
// src/state/folders.tsx
import { jsx as jsx8 } from "react/jsx-runtime";
var FolderContext = createContext7({
compositionFoldersExpanded: {},
setCompositionFoldersExpanded: () => {
throw new Error("default state");
},
assetFoldersExpanded: {},
setAssetFoldersExpanded: () => {
throw new Error("default state");
}
});
var FolderContextProvider = ({ children }) => {
const [compositionFoldersExpanded, setCompositionFoldersExpanded] = useState5(() => loadExpandedFolders("compositions"));
const [assetFoldersExpanded, setAssetFoldersExpanded] = useState5(() => loadExpandedFolders("assets"));
const value = useMemo7(() => {
return {
compositionFoldersExpanded,
setCompositionFoldersExpanded,
assetFoldersExpanded,
setAssetFoldersExpanded
};
}, [assetFoldersExpanded, compositionFoldersExpanded]);
return /* @__PURE__ */ jsx8(FolderContext.Provider, {
value,
children
});
};
// src/state/sidebar.tsx
import { createContext as createContext8, useMemo as useMemo8, useState as useState6 } from "react";
import { jsx as jsx9 } from "react/jsx-runtime";
var storageKey = (sidebar) => {
if (sidebar === "right") {
return "remotion.sidebarRightCollapsing";
}
return "remotion.sidebarCollapsing";
};
var getSavedCollapsedStateLeft = (isMobileLayout = false) => {
const state = window.localStorage.getItem(storageKey("left"));
if (isMobileLayout) {
return "collapsed";
}
if (state === "collapsed") {
return "collapsed";
}
if (state === "expanded") {
return "expanded";
}
return "responsive";
};
var getSavedCollapsedStateRight = (isMobileLayout = false) => {
const state = window.localStorage.getItem(storageKey("right"));
if (isMobileLayout) {
return "collapsed";
}
if (state === "expanded") {
return "expanded";
}
return "collapsed";
};
var saveCollapsedState = (type, sidebar) => {
window.localStorage.setItem(storageKey(sidebar), type);
};
var SidebarContext = createContext8({
sidebarCollapsedStateLeft: "collapsed",
setSidebarCollapsedState: () => {
throw new Error("sidebar collapsed state");
},
sidebarCollapsedStateRight: "collapsed"
});
var SidebarContextProvider = ({ children }) => {
const isMobileLayout = useMobileLayout();
const [sidebarCollapsedState, setSidebarCollapsedState] = useState6(() => ({
left: getSavedCollapsedStateLeft(isMobileLayout),
right: getSavedCollapsedStateRight(isMobileLayout)
}));
const value = useMemo8(() => {
return {
sidebarCollapsedStateLeft: sidebarCollapsedState.left,
sidebarCollapsedStateRight: sidebarCollapsedState.right,
setSidebarCollapsedState: (options) => {
const { left, right } = options;
setSidebarCollapsedState((f) => {
const copied = { ...f };
if (left) {
const updatedLeft = typeof left === "function" ? left(f.left) : left;
saveCollapsedState(updatedLeft, "left");
copied.left = updatedLeft;
}
if (right) {
const updatedRight = typeof right === "function" ? right(f.right) : right;
saveCollapsedState(updatedRight, "right");
copied.right = updatedRight;
}
return copied;
});
}
};
}, [sidebarCollapsedState]);
return /* @__PURE__ */ jsx9(SidebarContext.Provider, {
value,
children
});
};
// src/components/CompositionSelector.tsx
import { useCallback as useCallback15, useContext as useContext9, useMemo as useMemo21 } from "react";
import { Internals as Internals7 } from "remotion";
// src/helpers/create-folder-tree.ts
var buildAssetFolderStructure = (files, parentFolderName, foldersExpanded) => {
const notInFolder = files.filter((f) => !f.name.includes("/"));
const inFolder = files.filter((f) => f.name.includes("/"));
const groupedByFolder = {};
for (const item of inFolder) {
const folderName = item.name.split("/")[0];
if (!groupedByFolder[folderName]) {
groupedByFolder[folderName] = [];
}
groupedByFolder[folderName].push(item);
}
return {
files: notInFolder,
folders: Object.keys(groupedByFolder).map((folderName) => {
const filesInFolder = groupedByFolder[folderName];
const filesWithoutFolderName = filesInFolder.map((f) => {
return {
...f,
name: f.name.substring(folderName.length + 1)
};
});
const key = [parentFolderName, folderName].filter(Boolean).join("/");
const isExpanded = foldersExpanded[key] ?? false;
return {
name: folderName,
items: buildAssetFolderStructure(filesWithoutFolderName, [parentFolderName, folderName].filter(Boolean).join("/"), foldersExpanded),
expanded: isExpanded
};
})
};
};
var splitParentIntoNameAndParent = (name) => {
if (name === null) {
return {
name: null,
parent: null
};
}
const splitted = name.split("/");
const lastName = splitted[splitted.length - 1];
const parentParentArray = splitted.slice(0, splitted.length - 1);
const parentParent = parentParentArray.length === 0 ? null : parentParentArray.join("/");
return {
name: lastName,
parent: parentParent
};
};
var doesFolderExist = (items, folderName, parentName) => {
for (const item of items) {
if (item.type === "folder") {
if (item.folderName === folderName && item.parentName === parentName) {
return item.items;
}
const found = doesFolderExist(item.items, folderName, parentName);
if (found !== false) {
return found;
}
}
}
return false;
};
var findItemListToPush = (items, folderName, parentName) => {
if (folderName === null) {
return items;
}
const folder = doesFolderExist(items, folderName, parentName);
if (!folder) {
console.log({ items, folderName, parentName });
throw new Error("did not find folder " + folderName);
}
return folder;
};
var createFolderIfDoesNotExist = (items, availableFolders, folderItem, foldersExpanded) => {
if (doesFolderExist(items, folderItem.name, folderItem.parent)) {
return;
}
const splitted = splitParentIntoNameAndParent(folderItem.parent);
if (folderItem.parent) {
const parent = availableFolders.find((f) => f.name === splitted.name && f.parent === splitted.parent);
if (!parent) {
throw new Error("unexpectedly did not have parent");
}
createFolderIfDoesNotExist(items, availableFolders, parent, foldersExpanded);
}
const itemList = findItemListToPush(items, splitted.name, splitted.parent);
if (!itemList) {
throw new Error("why did folder not exist? " + folderItem.name);
}
itemList.push({
type: "folder",
folderName: folderItem.name,
items: [],
key: folderItem.name,
expanded: foldersExpanded[openFolderKey({
folderName: folderItem.name,
parentName: folderItem.parent
})] ?? false,
parentName: folderItem.parent
});
};
var createFolderTree = (comps, folders, foldersExpanded) => {
const items = [];
const uniqueFolderKeys = [];
for (const folder of folders) {
const folderKey = openFolderKey({
folderName: folder.name,
parentName: folder.parent
});
if (uniqueFolderKeys.includes(folderKey)) {
if (folder.parent) {
throw new Error(`Multiple folders with the name ${folder.name} inside the folder ${folder.parent} exist. Folder names must be unique.`);
}
throw new Error("Multiple folders with the name " + folder.name + " exist. Folder names must be unique.");
}
uniqueFolderKeys.push(folderKey);
createFolderIfDoesNotExist(items, folders, folder, foldersExpanded);
}
for (const item of comps) {
const toPush = {
type: "composition",
composition: item,
key: item.id
};
const list = findItemListToPush(items, item.folderName, item.parentFolderName);
list.push(toPush);
}
return items;
};
// src/components/CompositionSelectorItem.tsx
import { useCallback as useCallback14, useContext as useContext8, useMemo as useMemo20, useState as useState14 } from "react";
// src/icons/folder.tsx
import { jsx as jsx10 } from "react/jsx-runtime";
var CollapsedFolderIcon = ({ color, ...props }) => {
return /* @__PURE__ */ jsx10("svg", {
viewBox: "0 0 512 512",
...props,
children: /* @__PURE__ */ jsx10("path", {
fill: color,
d: "M447.1 96H272L226.7 50.75C214.7 38.74 198.5 32 181.5 32H63.1c-35.35 0-64 28.65-64 64v320c0 35.35 28.65 64 64 64h384c35.35 0 64-28.65 64-64V160C511.1 124.7 483.3 96 447.1 96zM480 416c0 17.64-14.36 32-32 32H64c-17.64 0-32-14.36-32-32V96c0-17.64 14.36-32 32-32h117.5c8.549 0 16.58 3.328 22.63 9.375L258.7 128H448c17.64 0 32 14.36 32 32V416z"
})
});
};
var ExpandedFolderIcon = ({ color, ...props }) => {
return /* @__PURE__ */ jsx10("svg", {
viewBox: "0 0 576 512",
...props,
children: /* @__PURE__ */ jsx10("path", {
fill: color,
d: "M566.6 211.6C557.5 199.1 543.4 192 527.1 192H134.2C114.3 192 96.2 204.5 89.23 223.1L32 375.8V96c0-17.64 14.36-32 32-32h117.5c8.549 0 16.58 3.328 22.63 9.375L258.7 128H448c17.64 0 32 14.36 32 32h32c0-35.35-28.65-64-64-64H272L226.7 50.75C214.7 38.74 198.5 32 181.5 32H64C28.65 32 0 60.65 0 96v320c0 35.35 28.65 64 64 64h403.1c21.11 0 39.53-13.53 45.81-33.69l60-192C578.4 239.6 575.8 224 566.6 211.6zM543.2 244.8l-60 192C481.1 443.5 475 448 467.1 448H64c-3.322 0-6.357-.9551-9.373-1.898c-2.184-1.17-4.109-2.832-5.596-4.977c-3.031-4.375-3.703-9.75-1.828-14.73l72-192C121.5 228.2 127.5 224 134.2 224h393.8c5.141 0 9.844 2.375 12.89 6.516C543.9 234.7 544.8 239.9 543.2 244.8z"
})
});
};
var ExpandedFolderIconSolid = ({ color, ...props }) => {
return /* @__PURE__ */ jsx10("svg", {
viewBox: "0 0 576 512",
...props,
children: /* @__PURE__ */ jsx10("path", {
fill: color,
d: "M384 480h48c11.4 0 21.9-6 27.6-15.9l112-192c5.8-9.9 5.8-22.1 .1-32.1S555.5 224 544 224H144c-11.4 0-21.9 6-27.6 15.9L48 357.1V96c0-8.8 7.2-16 16-16H181.5c4.2 0 8.3 1.7 11.3 4.7l26.5 26.5c21 21 49.5 32.8 79.2 32.8H416c8.8 0 16 7.2 16 16v32h48V160c0-35.3-28.7-64-64-64H298.5c-17 0-33.3-6.7-45.3-18.7L226.7 50.7c-12-12-28.3-18.7-45.3-18.7H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H87.7 384z"
})
});
};
// src/icons/still.tsx
import { jsx as jsx11 } from "react/jsx-runtime";
var StillIcon = ({ color, ...props }) => {
return /* @__PURE__ */ jsx11("svg", {
...props,
viewBox: "0 0 512 512",
children: /* @__PURE__ */ jsx11("path", {
fill: color,
d: "M144 288C144 226.1 194.1 176 256 176C317.9 176 368 226.1 368 288C368 349.9 317.9 400 256 400C194.1 400 144 349.9 144 288zM256 208C211.8 208 176 243.8 176 288C176 332.2 211.8 368 256 368C300.2 368 336 332.2 336 288C336 243.8 300.2 208 256 208zM362.9 64.82L373.3 96H448C483.3 96 512 124.7 512 160V416C512 451.3 483.3 480 448 480H64C28.65 480 0 451.3 0 416V160C0 124.7 28.65 96 64 96H138.7L149.1 64.82C155.6 45.22 173.9 32 194.6 32H317.4C338.1 32 356.4 45.22 362.9 64.82H362.9zM64 128C46.33 128 32 142.3 32 160V416C32 433.7 46.33 448 64 448H448C465.7 448 480 433.7 480 416V160C480 142.3 465.7 128 448 128H350.3L332.6 74.94C330.4 68.41 324.3 64 317.4 64H194.6C187.7 64 181.6 68.41 179.4 74.94L161.7 128H64z"
})
});
};
// src/icons/video.tsx
import { jsx as jsx12 } from "react/jsx-runtime";
var FilmIcon = ({ color, ...props }) => {
return /* @__PURE__ */ jsx12("svg", {
...props,
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 512 512",
children: /* @__PURE__ */ jsx12("path", {
fill: color,
d: "M448 32H64C28.65 32 0 60.65 0 96v320c0 35.35 28.65 64 64 64h384c35.35 0 64-28.65 64-64V96C512 60.65 483.3 32 448 32zM384 64v176H128V64H384zM32 96c0-17.64 14.36-32 32-32h32v80H32V96zM32 176h64v64H32V176zM32 272h64v64H32V272zM64 448c-17.64 0-32-14.36-32-32v-48h64V448H64zM128 448V272h256V448H128zM480 416c0 17.64-14.36 32-32 32h-32v-80h64V416zM480 336h-64v-64h64V336zM480 240h-64v-64h64V240zM480 144h-64V64h32c17.64 0 32 14.36 32 32V144z"
})
});
};
// src/state/modals.ts
import { createContext as createContext9 } from "react";
var ModalsContext = createContext9({
selectedModal: null,
setSelectedModal: () => {
return;
}
});
// src/components/CompositionContextButton.tsx
import { useCallback as useCallback11, useContext as useContext6, useMemo as useMemo17 } from "react";
// src/helpers/client-id.tsx
import React12, { useCallback as useCallback5, useEffect as useEffect7, useMemo as useMemo10, useRef as useRef5 } from "react";
import { Internals as Internals4 } from "remotion";
// src/components/Notifications/NotificationCenter.tsx
import {
createRef as createRef3,
useCallback as useCallback4,
useImperativeHandle as useImperativeHandle2,
useState as useState7
} from "react";
// src/components/Notifications/Notification.tsx
import { useEffect as useEffect6 } from "react";
import { jsx as jsx13 } from "react/jsx-runtime";
var notification = {
backgroundColor: "#111111",
color: "white",
fontFamily: "Arial, Helvetica, sans-serif",
display: "inline-flex",
padding: "8px 14px",
borderRadius: 4,
fontSize: 15,
border: "0.25px solid rgba(255, 255, 255, 0.1)",
boxShadow: "0 2px 3px rgba(0, 0, 0, 1)",
marginTop: 3,
marginBottom: 3,
alignItems: "center"
};
var Notification = ({ children, id, duration, created, onRemove }) => {
useEffect6(() => {
if (duration === null) {
return;
}
const timeout = setTimeout(() => {
onRemove(id);
}, duration - (Date.now() - created));
return () => {
clearTimeout(timeout);
};
}, [created, duration, id, onRemove]);
return /* @__PURE__ */ jsx13("div", {
className: "css-reset",
style: notification,
children
});
};
// src/components/Notifications/NotificationCenter.tsx
import { jsx as jsx14 } from "react/jsx-runtime";
var container3 = {
position: "absolute",
justifyContent: "center",
alignItems: "center",
display: "flex",
width: "100%",
flexDirection: "column",
paddingTop: 20,
pointerEvents: "none",
backgroundColor: "transparent"
};
var notificationCenter = createRef3();
var showNotification = (content, durationInMs) => {
return notificationCenter.current.addNotification({
content,
duration: durationInMs,
created: Date.now(),
id: String(Math.random()).replace("0.", "")
});
};
var NotificationCenter = () => {
const [notifications, setNotifications] = useState7([]);
const onRemove = useCallback4((id) => {
setNotifications((not) => {
return not.filter((n) => n.id !== id);
});
}, []);
const addNotification = useCallback4((notification2) => {
setNotifications((previousNotifications) => {
return [...previousNotifications, notification2];
});
return {
replaceContent: (newContent, durationInMs) => {
setNotifications((oldNotifications) => {
return oldNotifications.map((notificationToMap) => {
if (notificationToMap.id === notification2.id) {
return {
...notificationToMap,
duration: durationInMs,
content: newContent,
created: Date.now()
};
}
return notificationToMap;
});
});
}
};
}, []);
useImperativeHandle2(notificationCenter, () => {
return {
addNotification
};
}, [addNotification]);
return /* @__PURE__ */ jsx14("div", {
style: container3,
children: notifications.map((n) => {
return /* @__PURE__ */ jsx14(Notification, {
created: n.created,
duration: n.duration,
id: n.id,
onRemove,
children: n.content
}, n.id);
})
});
};
// src/components/PlayBeepSound.tsx
var beeped = {};
var playBeepSound = async (renderId) => {
if (beeped[renderId]) {
return;
}
beeped[renderId] = true;
const beepAudio = new Audio("/beep.wav");
try {
await beepAudio.play();
} catch (error) {
console.error("Error playing beep sound:", error);
throw error;
}
};
var PlayBeepSound_default = playBeepSound;
// src/components/RenderQueue/context.tsx
import React11, { createRef as createRef4, useImperativeHandle as useImperativeHandle3, useMemo as useMemo9, useState as useState8 } from "react";
import { jsx as jsx15 } from "react/jsx-runtime";
var RenderQueueContext = React11.createContext({
jobs: []
});
var renderJobsRef = createRef4();
var RenderQueueContextProvider = ({ children }) => {
const [jobs, setJobs] = useState8(window.remotion_initialRenderQueue ?? []);
const value = useMemo9(() => {
return {
jobs
};
}, [jobs]);
useImperativeHandle3(renderJobsRef, () => {
return {
updateRenderJobs: (newJobs) => {
setJobs(newJobs);
}
};
}, []);
return /* @__PURE__ */ jsx15(RenderQueueContext.Provider, {
value,
children
});
};
// src/helpers/client-id.tsx
import { jsx as jsx16 } from "react/jsx-runtime";
var StudioServerConnectionCtx = React12.createContext({
previewServerState: {
type: "init"
},
subscribeToEvent: () => {
throw new Error("Context not initalized");
}
});
var PreviewServerConnection = ({ children, readOnlyStudio }) => {
const listeners = useRef5([]);
const subscribeToEvent = useCallback5((type, listener) => {
listeners.current.push({ type, listener });
return () => {
listeners.current = listeners.current.filter((l) => l.type !== type || l.listener !== listener);
};
}, []);
const openEventSource = useCallback5(() => {
const source = new EventSource("/events");
source.addEventListener("message", (event) => {
const newEvent = JSON.parse(event.data);
if (newEvent.type === "new-input-props" || newEvent.type === "new-env-variables") {
reloadUrl();
}
if (newEvent.type === "init") {
setState({
type: "connected",
clientId: newEvent.clientId
});
}
if (newEvent.type === "render-queue-updated") {
renderJobsRef.current?.updateRenderJobs(newEvent.queue);
for (const job of newEvent.queue) {
if (job.status === "done" && job.beepOnFinish) {
PlayBeepSound_default(job.id);
}
}
}
if (newEvent.type === "render-job-failed") {
showNotification(`Rendering "${newEvent.compositionId}" failed`, 2000);
}
if (newEvent.type === "new-public-folder") {
const payload = {
files: newEvent.files
};
window.remotion_staticFiles = newEvent.files;
window.remotion_publicFolderExists = newEvent.folderExists;
window.dispatchEvent(new CustomEvent(Internals4.WATCH_REMOTION_STATIC_FILES, {
detail: payload
}));
}
listeners.current.forEach((l) => {
if (l.type === newEvent.type) {
l.listener(newEvent);
}
});
});
source.addEventListener("open", () => {
source.addEventListener("error", () => {
setState({ type: "disconnected" });
source?.close();
setTimeout(() => {
openEventSource();
}, 1000);
}, { once: true });
});
const close = () => {
source.close();
};
return {
close
};
}, []);
useEffect7(() => {
if (readOnlyStudio) {
return;
}
co