@remotion/studio
Version:
APIs for interacting with the Remotion Studio
1,535 lines (1,482 loc) • 1.11 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/Studio.tsx
import { useLayoutEffect as useLayoutEffect4 } from "react";
import { createPortal } from "react-dom";
import { Internals as Internals70 } from "remotion";
// src/components/Editor.tsx
import { PlayerInternals as PlayerInternals19 } from "@remotion/player";
import React187, { useCallback as useCallback149, useMemo as useMemo150, useState as useState98 } from "react";
import { Internals as Internals65 } 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, useCallback, useMemo, useState as useState2 } 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 TIMELINE_TRACK_EXPANDED_HEIGHT = 100;
var SCHEMA_FIELD_ROW_HEIGHT = 22;
var UNSUPPORTED_FIELD_ROW_HEIGHT = 22;
var SUPPORTED_SCHEMA_TYPES = new Set([
"number",
"boolean",
"rotation",
"translate"
]);
var getSchemaFields = (controls) => {
if (!controls) {
return null;
}
return Object.entries(controls.schema).map(([key, fieldSchema]) => {
const typeName = fieldSchema.type;
const supported = SUPPORTED_SCHEMA_TYPES.has(typeName);
return {
key,
description: fieldSchema.description,
typeName,
supported,
rowHeight: supported ? SCHEMA_FIELD_ROW_HEIGHT : UNSUPPORTED_FIELD_ROW_HEIGHT,
currentValue: controls.currentValue[key],
fieldSchema
};
});
};
var getExpandedTrackHeight = (controls) => {
const fields = getSchemaFields(controls);
if (!fields || fields.length === 0) {
return TIMELINE_TRACK_EXPANDED_HEIGHT;
}
const separators = Math.max(0, fields.length - 1);
return fields.reduce((sum, f) => sum + f.rowHeight, 0) + separators;
};
var TIMELINE_LAYER_HEIGHT_VIDEO = 75;
var TIMELINE_LAYER_HEIGHT_IMAGE = 50;
var TIMELINE_LAYER_HEIGHT_AUDIO = 25;
var getTimelineLayerHeight = (type) => {
if (type === "video") {
return TIMELINE_LAYER_HEIGHT_VIDEO;
}
if (type === "image") {
return TIMELINE_LAYER_HEIGHT_IMAGE;
}
return TIMELINE_LAYER_HEIGHT_AUDIO;
};
// 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/TimelineSlider.tsx
import {
createRef as createRef2,
useContext as useContext2,
useEffect,
useImperativeHandle,
useLayoutEffect as useLayoutEffect2,
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, useContext, useLayoutEffect, useState } from "react";
import { jsx as jsx2 } from "react/jsx-runtime";
var TimelineWidthContext = createContext(null);
var TimelineWidthProvider = ({ children }) => {
const size = PlayerInternals.useElementSize(sliderAreaRef, {
triggerOnWindowResize: false,
shouldApplyCssTransforms: true
});
const { zoom: zoomMap } = useContext(TimelineZoomCtx);
const [widthOverride, setWidthOverride] = useState(null);
const observedWidth = size?.width ?? null;
useLayoutEffect(() => {
const actual = sliderAreaRef.current?.clientWidth ?? null;
if (actual !== null && actual !== observedWidth) {
setWidthOverride(actual);
} else {
setWidthOverride(null);
}
}, [observedWidth, zoomMap]);
return /* @__PURE__ */ jsx2(TimelineWidthContext.Provider, {
value: widthOverride ?? observedWidth,
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 = useContext2(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 = useContext2(TimelineWidthContext);
const { zoom: zoomMap } = useContext2(TimelineZoomCtx);
const { canvasContent } = useContext2(Internals.CompositionManager);
if (timelineWidth === null) {
throw new Error("Unexpectedly did not have timeline width");
}
const zoomLevel = canvasContent?.type === "composition" ? zoomMap[canvasContent.compositionId] ?? TIMELINE_MIN_ZOOM : TIMELINE_MIN_ZOOM;
useLayoutEffect2(() => {
const el = ref.current;
const measuredWidth = sliderAreaRef.current?.clientWidth;
if (!el || measuredWidth === undefined || measuredWidth === 0) {
return;
}
el.style.transform = `translateX(${getXPositionOfItemInTimelineImperatively(timelinePosition, videoConfig.durationInFrames, measuredWidth)}px)`;
}, [
timelinePosition,
videoConfig.durationInFrames,
timelineWidth,
zoomLevel
]);
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: container2,
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 viewportClientXToScrollContentX = ({
clientX,
scrollEl
}) => {
const rect = scrollEl.getBoundingClientRect();
const clampedClientX = Math.min(Math.max(clientX, rect.left), rect.right);
return clampedClientX + scrollEl.scrollLeft - rect.left;
};
var zoomAndPreserveCursor = ({
oldZoom,
newZoom,
currentFrame: currentFrame2,
currentDurationInFrames,
anchorFrame,
anchorContentX
}) => {
const ratio = newZoom / oldZoom;
if (ratio === 1) {
return;
}
const { current } = scrollableRef;
if (!current) {
return;
}
const frameIncrement = getFrameIncrement(currentDurationInFrames);
const frameForScroll = anchorFrame ?? currentFrame2;
const prevCursorPosition = anchorContentX !== null ? Math.min(Math.max(anchorContentX, 0), current.scrollWidth) : frameIncrement * frameForScroll + TIMELINE_PADDING;
const newCursorPosition = ratio * (prevCursorPosition - TIMELINE_PADDING) + TIMELINE_PADDING;
current.scrollLeft += newCursorPosition - prevCursorPosition;
};
// src/components/ZoomPersistor.tsx
import { useContext as useContext3, 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 } = useContext3(TimelineZoomCtx);
const { canvasContent } = useContext3(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, setZoomState] = useState2(() => getZoomFromLocalStorage());
const setZoom = useCallback((compositionId, callback, options) => {
setZoomState((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;
const anchorFrame = options?.anchorFrame ?? null;
const anchorContentX = options?.anchorContentX ?? null;
zoomAndPreserveCursor({
oldZoom: prevZoomMap[compositionId] ?? TIMELINE_MIN_ZOOM,
newZoom,
currentDurationInFrames: getCurrentDuration(),
currentFrame: getCurrentFrame(),
anchorFrame,
anchorContentX
});
return { ...prevZoomMap, [compositionId]: newZoom };
});
}, []);
const value = useMemo(() => {
return {
zoom,
setZoom
};
}, [zoom, setZoom]);
return /* @__PURE__ */ jsx4(TimelineZoomCtx.Provider, {
value,
children
});
};
// src/state/z-index.tsx
import {
createContext as createContext5,
useContext as useContext5,
useEffect as useEffect4,
useMemo as useMemo5,
useRef as useRef3
} from "react";
// src/helpers/use-keybinding.ts
import { useCallback as useCallback3, useContext as useContext4, useEffect as useEffect3, useMemo as useMemo3, useState as useState3 } from "react";
// src/state/keybindings.tsx
import { createContext as createContext3, useCallback as useCallback2, useMemo as useMemo2, 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 = useCallback2((binding) => {
registered.current = [...registered.current, binding];
window.addEventListener(binding.event, binding.callback);
}, []);
const unregisterKeybinding = useCallback2((binding) => {
registered.current = registered.current.filter((r) => {
if (r.id === binding.id) {
window.removeEventListener(binding.event, binding.callback);
return false;
}
return true;
});
}, []);
const unregisterPane = useCallback2((paneId) => {
const matchedKeybindings = registered.current.filter((r) => r.registeredFromPane === paneId);
for (const matched of matchedKeybindings) {
unregisterKeybinding(matched);
}
}, [unregisterKeybinding]);
const value = useMemo2(() => {
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] = useState3(() => String(Math.random()));
const context = useContext4(KeybindingContext);
const { isHighestContext } = useZIndex();
const registerKeybinding = useCallback3((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 useMemo3(() => ({ registerKeybinding, isHighestContext }), [registerKeybinding, isHighestContext]);
};
// src/state/highest-z-index.tsx
import { createContext as createContext4, useCallback as useCallback4, useMemo as useMemo4, useState as useState4 } 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] = useState4([]);
const registerZIndex = useCallback4((newIndex) => {
setZIndexes((prev) => [...prev, newIndex]);
}, []);
const unregisterZIndex = useCallback4((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 = useMemo4(() => {
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 = useContext5(ZIndexContext);
const highestContext = useContext5(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 = useMemo5(() => {
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 = useContext5(ZIndexContext);
const highestContext = useContext5(HighestZIndexContext);
const isHighestContext = highestContext.highestIndex === context.currentIndex;
return useMemo5(() => ({
currentZIndex: context.currentIndex,
highestZIndex: highestContext.highestIndex,
isHighestContext,
tabIndex: isHighestContext ? 0 : -1
}), [context.currentIndex, highestContext.highestIndex, isHighestContext]);
};
// src/components/EditorContent.tsx
import { useContext as useContext81 } from "react";
import { Internals as Internals58 } from "remotion";
// src/helpers/is-current-selected-still.ts
import { useContext as useContext6 } 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 } = useContext6(Internals3.CompositionManager);
if (canvasContent === null) {
return false;
}
if (isStill) {
return false;
}
return canvasContent.type === "composition";
};
// src/components/InitialCompositionLoader.tsx
import { useCallback as useCallback24, useContext as useContext16, useEffect as useEffect15 } from "react";
import { Internals as Internals12 } from "remotion";
// src/helpers/mobile-layout.ts
import { useEffect as useEffect5, useRef as useRef4, useState as useState5 } from "react";
var breakpoint = 900;
function getIsMobile() {
return window.innerWidth < breakpoint;
}
var useMobileLayout = () => {
const [isMobile, setIsMobile] = useState5(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 useMemo6, useState as useState6 } 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] = useState6(() => loadExpandedFolders("compositions"));
const [assetFoldersExpanded, setAssetFoldersExpanded] = useState6(() => loadExpandedFolders("assets"));
const value = useMemo6(() => {
return {
compositionFoldersExpanded,
setCompositionFoldersExpanded,
assetFoldersExpanded,
setAssetFoldersExpanded
};
}, [assetFoldersExpanded, compositionFoldersExpanded]);
return /* @__PURE__ */ jsx8(FolderContext.Provider, {
value,
children
});
};
// src/state/sidebar.tsx
import { createContext as createContext8, useMemo as useMemo7, useState as useState7 } 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] = useState7(() => ({
left: getSavedCollapsedStateLeft(isMobileLayout),
right: getSavedCollapsedStateRight(isMobileLayout)
}));
const value = useMemo7(() => {
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 useCallback17, useContext as useContext10, useMemo as useMemo21 } from "react";
import { Internals as Internals7 } from "remotion";
// src/error-overlay/remotion-overlay/ShortcutHint.tsx
import { useMemo as useMemo8 } from "react";
import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
var cmdOrCtrlCharacter = window.navigator.platform.startsWith("Mac") ? "⌘" : "Ctrl";
var container3 = {
display: "inline-block",
marginLeft: 6,
opacity: 0.6,
verticalAlign: "middle",
fontSize: 14
};
var ShortcutHint = ({ keyToPress, cmdOrCtrl }) => {
const style = useMemo8(() => {
if (keyToPress === "↵") {
return {
display: "inline-block",
transform: `translateY(2px)`,
fontSize: 14
};
}
return {};
}, [keyToPress]);
if (areKeyboardShortcutsDisabled()) {
return null;
}
return /* @__PURE__ */ jsxs3("span", {
style: container3,
children: [
cmdOrCtrl ? `${cmdOrCtrlCharacter}` : "",
/* @__PURE__ */ jsx10("span", {
style,
children: keyToPress.toUpperCase()
})
]
});
};
// 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/helpers/sort-by-nonce-history.ts
var getSharedEpochOrder = (a, b) => {
const aMap = new Map(a);
const bMap = new Map(b);
const allEpochs = [...new Set([...aMap.keys(), ...bMap.keys()])].sort((x, y) => y - x);
for (const epoch of allEpochs) {
const aNonce = aMap.get(epoch);
const bNonce = bMap.get(epoch);
if (aNonce !== undefined && bNonce !== undefined) {
return aNonce - bNonce;
}
}
return null;
};
var sortItemsByNonceHistory = (items) => {
const n = items.length;
if (n <= 1) {
return items.slice();
}
const order = Array.from({ length: n }, () => Array(n).fill(null));
for (let i = 0;i < n; i++) {
for (let j = i + 1;j < n; j++) {
const cmp = getSharedEpochOrder(items[i].nonce, items[j].nonce);
order[i][j] = cmp;
order[j][i] = cmp !== null ? -cmp : null;
}
}
for (let k = 0;k < n; k++) {
for (let i = 0;i < n; i++) {
for (let j = 0;j < n; j++) {
if (order[i][j] === null && order[i][k] !== null && order[k][j] !== null && order[i][k] < 0 && order[k][j] < 0) {
order[i][j] = -1;
order[j][i] = 1;
}
}
}
}
const indexMap = new Map(items.map((item, i) => [item, i]));
const result = items.slice();
result.sort((a, b) => {
const ai = indexMap.get(a);
const bi = indexMap.get(b);
const transitiveOrder = order[ai][bi];
if (transitiveOrder !== null) {
return transitiveOrder;
}
return a.nonce[a.nonce.length - 1][0] - b.nonce[b.nonce.length - 1][0];
});
return result;
};
// src/state/modals.ts
import { createContext as createContext9 } from "react";
var ModalsContext = createContext9({
selectedModal: null,
setSelectedModal: () => {
return;
}
});
// src/components/CompositionSelectorItem.tsx
import { useCallback as useCallback16, useContext as useContext9, useMemo as useMemo20, useState as useState15 } from "react";
// src/icons/folder.tsx
import { jsx as jsx11 } from "react/jsx-runtime";
var CollapsedFolderIcon = ({ color, ...props }) => {
return /* @__PURE__ */ jsx11("svg", {
viewBox: "0 0 512 512",
...props,
children: /* @__PURE__ */ jsx11("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__ */ jsx11("svg", {
viewBox: "0 0 576 512",
...props,
children: /* @__PURE__ */ jsx11("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__ */ jsx11("svg", {
viewBox: "0 0 576 512",
...props,
children: /* @__PURE__ */ jsx11("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 jsx12 } from "react/jsx-runtime";
var StillIcon = ({ color, ...props }) => {
return /* @__PURE__ */ jsx12("svg", {
...props,
viewBox: "0 0 512 512",
children: /* @__PURE__ */ jsx12("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 jsx13 } from "react/jsx-runtime";
var FilmIcon = ({ color, ...props }) => {
return /* @__PURE__ */ jsx13("svg", {
...props,
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 512 512",
children: /* @__PURE__ */ jsx13("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/components/CompositionContextButton.tsx
import { useCallback as useCallback13, useContext as useContext7, useMemo as useMemo17 } from "react";
// src/helpers/client-id.tsx
import React13, { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo10, useRef as useRef6 } from "react";
import { Internals as Internals4 } from "remotion";
// src/components/Notifications/NotificationCenter.tsx
import {
createRef as createRef3,
useCallback as useCallback5,
useImperativeHandle as useImperativeHandle2,
useState as useState8
} from "react";
// src/components/Notifications/Notification.tsx
import { useEffect as useEffect6 } from "react";
import { jsx as jsx14 } 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__ */ jsx14("div", {
className: "css-reset",
style: notification,
children
});
};
// src/components/Notifications/NotificationCenter.tsx
import { jsx as jsx15 } from "react/jsx-runtime";
var container4 = {
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] = useState8([]);
const onRemove = useCallback5((id) => {
setNotifications((not) => {
return not.filter((n) => n.id !== id);
});
}, []);
const addNotification = useCallback5((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,