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 { useContext as useContext90, useLayoutEffect as useLayoutEffect2 } from "react"; import { createPortal } from "react-dom"; import { Internals as Internals66 } from "remotion"; // src/components/Editor.tsx import { PlayerInternals as PlayerInternals19 } from "@remotion/player"; import React177, { useCallback as useCallback135, useEffect as useEffect84, useMemo as useMemo136 } from "react"; import { Internals as Internals61 } 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 useContext70 } from "react"; import { Internals as Internals54 } 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 useCallback23, useContext as useContext13, useEffect as useEffect14 } 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 useCallback16, 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 useCallback15, 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 useCallback12, useContext as useContext6, useMemo as useMemo17 } from "react"; // src/helpers/client-id.tsx import React12, { useCallback as useCallback6, 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 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, useCallback as useCallback5, useEffect as useEffect7, useImperativeHandle as useImperativeHandle3, useMemo as useMemo9, useRef as useRef5, useState as useState8 } from "react"; // src/components/RenderQueue/client-render-queue.ts var compositionRegistry = new Map; var registerCompositionForJob = (jobId, compositionRef) => { compositionRegistry.set(jobId, compositionRef); }; var getCompositionForJob = (jobId) => { return compositionRegistry.get(jobId); }; var cleanupCompositionForJob = (jobId) => { compositionRegistry.delete(jobId); }; var generateJobId = () => { return `client-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`; }; var clientJobAbortControllers = new Map; var getAbortController = (jobId) => { let controller = clientJobAbortControllers.get(jobId); if (!controller) { controller = new AbortController; clientJobAbortControllers.set(jobId, controller); } return controller; }; var deleteAbortController = (jobId) => { clientJobAbortControllers.delete(jobId); }; var cancelAbortController = (jobId) => { const controller = clientJobAbortControllers.get(jobId); if (controller) { controller.abort(); } }; // src/components/RenderQueue/context.tsx import { jsx as jsx15 } from "react/jsx-runtime"; var isClientRenderJob = (job) => { return job.type === "client-still" || job.type === "client-video"; }; var noopString = () => ""; var noop2 = () => { return; }; var RenderQueueContext = React11.createContext({ jobs: [], serverJobs: [], clientJobs: [], addClientStillJob: noopString, addClientVideoJob: noopString, updateClientJobProgress: noop2, markClientJobDone: noop2, markClientJobFailed: noop2, markClientJobCancelled: noop2, removeClientJob: noop2, cancelClientJob: noop2, setProcessJobCallback: noop2, getAbortController: () => new AbortController, getCompositionForJob: () => { return; } }); var renderJobsRef = createRef4(); var RenderQueueContextProvider = ({ children }) => { const [serverJobs, setServerJobs] = useState8(window.remotion_initialRenderQueue ?? []); const [clientJobs, setClientJobs] = useState8([]); const [currentlyProcessing, setCurrentlyProcessing] = useState8(null); const processJobCallbackRef = useRef5(null); useEffect7(() => { if (currentlyProcessing) { return; } const nextJob = clientJobs.find((job) => job.status === "idle"); if (!nextJob || !processJobCallbackRef.current) { return; } setCurrentlyProcessing(nextJob.id); setClientJobs((prev) => prev.map((job) => job.id === nextJob.id ? { ...job, status: "running", progress: { renderedFrames: 0, encodedFrames: 0, totalFrames: 0 } } : job)); processJobCallbackRef.current(nextJob); }, [clientJobs, currentlyProcessing]); const addClientStillJob = useCallback5((params, compositionRef) => { const id = generateJobId(); registerCompositionForJob(id, compositionRef); const newJob = { ...params, id, startedAt: Date.now(), status: "idle" }; setClientJobs((prev) => [...prev, newJob]); return id; }, []); const addClientVideoJob = useCallback5((params, compositionRef) => { const id = generateJobId(); registerCompositionForJob(id, compositionRef); const newJob = { ...params, id, startedAt: Date.now(), status: "idle" }; setClientJobs((prev) => [...prev, newJob]); return id; }, []); const updateClientJobProgress = useCallback5((jobId, progress) => { setClientJobs((prev) => prev.map((job) => job.id === jobId ? { ...job, status: "running", progress } : job)); }, []); const markClientJobDone = useCallback5((jobId, getBlob, metadata) => { deleteAbortController(jobId); cleanupCompositionForJob(jobId); setClientJobs((prev) => prev.map((job) => job.id === jobId ? { ...job, status: "done", getBlob, metadata } : job)); setCurrentlyProcessing(null); }, []); const markClientJobFailed = useCallback5((jobId, error) => { deleteAbortController(jobId); cleanupCompositionForJob(jobId); setClientJobs((prev) => prev.map((job) => job.id === jobId ? { ...job, status: "failed", error: { message: error.message, stack: error.stack } } : job)); setCurrentlyProcessing(null); }, []); const markClientJobCancelled = useCallback5((jobId) => { deleteAbortController(jobId); cleanupCompositionForJob(jobId); setClientJobs((prev) => prev.map((job) => job.id === jobId ? { ...job, status: "cancelled" } : job)); setCurrentlyProcessing(null); }, []); const removeClientJob = useCallback5((jobId) => { setClientJobs((prev) => { const jobToRemove = prev.find((j) => j.id === jobId); if (jobToRemove?.status === "running") { return prev; } deleteAbortController(jobId); cleanupCompositionForJob(jobId); return prev.filter((job) => job.id !== jobId); }); }, []); const cancelClientJob = useCallback5((jobId) => { cancelAbortController(jobId); }, []); const setProcessJobCallback = useCallback5((callback) => { processJobCallbackRef.current = callback; }, []); useImperativeHandle3(renderJobsRef, () => ({ updateRenderJobs: (newJobs) => { setServerJobs(newJobs); } }), []); const value = useMemo9(() => { return { jobs: [...serverJobs, ...clientJobs], serverJobs, clientJobs, addClientStillJob, addClientVideoJob, updateClientJobProgress, markClientJobDone, markClientJobFailed, markClientJobCancelled, removeClientJob, cancelClientJob, setProcessJobCallback, getAbortController, getCompositionForJob }; }, [ serverJobs, clientJobs, addClientStillJob, addClientVideoJob, updateClientJobProgress, markClientJobDone, markClientJobFailed, markClientJobCancelled, removeClientJob, cancelClientJob, setProcessJobCallback ]); 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 = useRef6([]); const subscribeToEvent = useCallback6((type, listener) => { listeners.current.push({ type, listener }); return () => { listeners.current = listeners.current.filter((l) => l.type !== type || l.listener !== listener); }; }, []); const openEventSource = useCallback6(() => { 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 }; }, []); useEffect8(() => { if (readOnlyStudio) { return; } const { close } = openEventSource(); return () => { close(); }; }, [openEventSource, readOnlyStudio]); const [state, setState] = React12.useState({ type: "init" }); const context = useMemo10(() => { return { previewServerState: state, subscribeToEvent }; }, [state, subscribeToEvent]); return /* @__PURE__ */ jsx16(StudioServerConnectionCtx.Provider, { value: context, children }); }; // src/icons/ellipsis.tsx import { jsx as jsx17 } from "react/jsx-runtime"; var EllipsisIcon = (props) => { return /* @__PURE__ */ jsx17("svg", { ...props.svgProps, viewBox: "0 0 448 512", children: /* @__PURE__ */ jsx17("path", { fill: props.fill, d: "M8 256a56 56 0 1 1 112 0A56 56 0 1 1 8 256zm160 0a56 56 0 1 1 112 0 56 56 0 1 1 -112 0zm216-56a56 56 0 1 1 0 112 56 56 0 1 1 0-112z" }) }); }; // src/components/InlineDropdown.tsx import { PlayerInternals as PlayerInternals3 } from "@remotion/player"; import { useCallback as useCallback11, useMemo as useMemo16, useRef as useRef9, useState as useState12 } from "react"; import ReactDOM2 from "react-dom"; // src/components/InlineAction.tsx import { useCallback as useCallback7, useMemo as useMemo11, useState as useState9 } from "react"; import { jsx as jsx18 } from "react/jsx-runtime"; var InlineAction = ({ renderAction, onClick, disabled, title }) => { const { tabIndex } = useZIndex(); const [hovered, setHovered] = useState9(false); const onPointerEnter = useCallback7(() => { setHovered(true); }, []); const onPointerLeave = useCallback7(() => { setHovered(false); }, []); const style = useMemo11(() => { return { border: "none", background: disabled ? "transparent" : getBackgroundFromHoverState({ hovered, selected: false }), height: 24, width: 24, display: "inline-flex", justifyContent: "center", alignItems: "center", borderRadius: 3, pointerEvents: disabled ? "none" : "auto" }; }, [disabled, hovered]); return /* @__PURE__ */ jsx18("button", { type: "button", onPointerEnter, onPointerLeave, onClick, style, tabIndex, title, children: renderAction(hovered ? "white" : LIGHT_TEXT) }); }; // src/components/Menu/portals.ts var portals = [ document.getElementById("menuportal-0"), document.getElementById("menuportal-1"), document.getElementById("menuportal-2"), document.getElementById("menuportal-3"), document.getElementById("menuportal-4"), document.getElementById("menuportal-5") ]; var getPortal = (i) => { return portals[i]; }; // src/components/Menu/styles.ts var MENU_VERTICAL_PADDING = 4; var SUBMENU_LEFT_INSET = -8; var MAX_MENU_WIDTH = 400; var MAX_MOBILE_MENU_WIDTH = 300; var menuContainer = { backgroundColor: BACKGROUND, position: "fixed", color: "white", userSelect: "none", WebkitUserSelect: "none" }; var SHADOW_TOWARDS_BOTTOM = "0 2px 8px rgba(0, 0, 0, 0.5)"; var SHADOW_TOWARDS_TOP = "0 -2px 8px rgba(0, 0, 0, 0.5)"; var menuContainerTowardsBottom = { ...menuContainer, boxShadow: SHADOW_TOWARDS_BOTTOM }; var menuContainerTowardsTop = { ...menuContainer, boxShadow: SHADOW_TOWARDS_TOP }; var fullScreenOverlay = { position: "fixed", top: 0, left: 0, right: 0, bottom: 0 }; var outerPortal = { position: "fixed" }; var inlineCodeSnippet = { fontSize: 14, color: BLUE, fontFamily: "monospace" }; // src/components/NewComposition/MenuContent.tsx import { useCallback as useCallback10, useEffect as useEffect10, useMemo as useMemo15, useRef as useRef8, useState as useState11 } from "react"; // src/components/Menu/MenuDivider.tsx import { jsx as jsx19 } from "react/jsx-runtime"; var menuDivider = { marginTop: 4, marginBottom: 4, height: 1, backgroundColor: INPUT_BORDER_COLOR_HOVERED }; var MenuDivider = () => { return /* @__PURE__ */ jsx19("div", { style: menuDivider }); }; // src/components/Menu/MenuSubItem.tsx import { PlayerInternals as PlayerInternals2 } from "@remotion/player"; import { useCallback as useCallback9, useEffect as useEffect9, useMemo as useMemo14, useRef as useRef7, useState as useState10 } from "react"; import ReactDOM from "react-dom"; // src/icons/caret.tsx import { useMemo as useMemo12 } from "react"; import { jsx as jsx20 } from "react/jsx-runtime"; var caret = { height: 12 }; var caretDown = { width: 10 }; var angleDown = { width: 10 }; var CaretRight = () => /* @__PURE__ */ jsx20("svg", { viewBox: "0 0 192 512", style: caret, children: /* @__PURE__ */ jsx20("path", { fill: "currentColor", d: "M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z" }) }); var CaretDown = () => { return /* @__PURE__ */ jsx20("svg", { viewBox: "0 0 320 512", style: caretDown, children: /* @__PURE__ */ jsx20("path", { fill: "currentColor", d: "M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z" }) }); }; var AngleDown = ({ down }) => { const style = useMemo12(() => { return { ...angleDown, transform: down ? "rotate(180deg)" : "rotate(0deg)", marginTop: 1 }; }, [down]); return /* @__PURE__ */ jsx20("svg", { style, viewBox: "0 0 448 512", children: /* @__PURE__ */ jsx20("path", { fill: LIGHT_TEXT, d: "M201.4 342.6c12.5 12.5 32.8 12.5 45.3 0l160-160c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 274.7 86.6 137.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l160 160z" }) }); }; // src/components/layout.tsx import { useMemo as useMemo13 } from "react"; import { jsx as jsx21 } from "react/jsx-runtime"; var SPACING_UNIT = 8; var Spacing = ({ x = 0, y = 0, block = false }) => { const style = useMemo13(() => { return { display: block ? "block" : "inline-block", width: x * SPACING_UNIT, height: y * SPACING_UNIT, flexShrink: 0 }; }, [block, x, y]); return /* @__PURE__ */ jsx21("div", { style }); }; var flexCss = { flex: 1 }; var Flex = ({ children }) => /* @__PURE__ */ jsx21("div", { style: flexCss, children }); var Row = ({ children, justify, className, align, flex, style = {}, ...other }) => { const finalStyle = useMemo13(() => { return { ...style, display: "flex", flexDirection: "row", justifyContent: justify ?? "flex-start", alignItems: align ?? "flex-start", flex: flex ?? undefined }; }, [align, flex, justify, style]); return /* @__PURE__ */ jsx21("div", { className, style: finalStyle, ...other, children }); }; var Column = ({ children, justify, className, align, style = {} }) => { const finalStyle = useMemo13(() => { return { ...style, display: "flex", flexDirection: "column", justifyContent: justify ?? "flex-start", alignItems: align ?? "flex-start" }; }, [align, justify, style]); return /* @__PURE__ */ jsx21("div", { className, style: finalStyle, children }); }; // src/components/Menu/SubMenu.tsx import { useCallback as useCallback8 } from "react"; import { jsx as jsx22 } from "react/jsx-runtime"; var SubMenuComponent = ({ portalStyle, subMenuActivated, subMenu, onQuitFullMenu, onQuitSubMenu }) => { const mobileLayout = useMobileLayout(); const onOutsideClick = useCallback8((e) => { if (portals.find((p) => p.contains(e)) || mobileLayout) { onQuitSubMenu(); } else { onQuitFullMenu(); } }, [mobileLayout, onQuitFullMenu, onQuitSubMenu]); return /* @__PURE__ */ jsx22(HigherZIndex, { onEscape: onQuitFullMenu, onOutsideClick, children: /* @__PURE__ */ jsx22("div", { style: portalStyle, className: "css-reset", children: /* @__PURE__ */ jsx22(MenuContent, { onNextMenu: noop, onPreviousMenu: onQuitSubMenu, values: subMenu.items, onHide: onQuitFullMenu, leaveLeftSpace: subMenu.leaveLeftSpace, preselectIndex: subMenuActivated === "without-mouse" && typeof subMenu.preselectIndex === "number" ? subMenu.preselectIndex : false, topItemCanBeUnselected: false, fixedHeight: null }) }) }); }; // src/components/Menu/is-menu-item.tsx var MENU_INITIATOR_CLASSNAME = "__remotion-studio-menu-initiator"; var MENU_ITEM_CLASSNAME = "__remotion-studio-menu-item"; var HORIZONTAL_SCROLLBAR_CLASSNAME = "__remotion-horizontal-scrollbar"; var VERTICAL_SCROLLBAR_CLASSNAME = "__remotion-vertical-scrollbar"; var isMenuItem = (el) => { return Boolean(el.classList.contains(MENU_ITEM_CLASSNAME) || el.closest(`.${MENU_ITEM_CLASSNAME}`) || el.classList.contains(MENU_INITIATOR_CLASSNAME) || el.closest(`.${MENU_INITIATOR_CLASSNAME}`)); }; // src/components/Menu/MenuSubItem.tsx import { jsx as jsx23, jsxs as jsxs3, Fragment } from "react/jsx-runtime"; var container4 = { paddingTop: 8, paddingBottom: 8, paddingLeft: 12, paddingRight: 8, cursor: "default" }; var labelStyle = { fontSize: 13, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", flex: 1 }; var keyHintCss = { flexDirection: "row", color: LIGHT_TEXT, fontSize: 13 }; var leftSpace = { width: 24, marginLeft: -6, display: "inline-flex", justifyContent: "center", alignItems: "center" }; var MenuSubItem = ({ label, leaveLeftSpace, leftItem, onActionChosen, id, selected, onItemSelected, keyHint, subMenu, onQuitMenu, subMenuActivated, setSubMenuActivated, disabled }) => { const [hovered, setHovered] = useState10(false); const ref = useRef7(null); const size = PlayerInternals2.useElementSize(ref, { triggerOnWindowResize: true, shouldApplyCssTransforms: true }); const mobileLayout = useMobileLayout(); const { currentZIndex } = useZIndex(); const style = useMemo14(() => { return { ...container4, backgroundColor: selected && !disabled ? CLEAR_HOVER : "transparent", opacity: disabled ? 0.5 : 1, cursor: disabled ? "not-allowed" : "default" }; }, [selected, disabled]); const onPointerUp = useCallback9((e) => { if (disabled) { return; } if (subMenu) { setSubMenuActivated("with-mouse"); setHovered(true); return; } onActionChosen(id, e); }, [disabled, id, onActionChosen, setSubMenuActivated, subMenu]); const onPointerEnter = useCallback9(() => { if (disabled) { return; } onItemSelected(id); setHovered(true); }, [disabled, id, onItemSelected]); const onPointerLeave = useCallback9(() => { setHovered(false); }, []); const onQuitSubmenu = useCallback9(() => { setSubMenuActivated(false); }, [setSubMenuActivated]); const portalStyle = useMemo14(() => { if (!selected || !size || !subMenu || !subMenuActivated) { return null; } const left = size.left + size.width + SUBMENU_LEFT_INSET; return { ...menuContainerTowardsBottom, left: mobileLayout ? left * 0.7 : left, top: size.top - MENU_VERTICAL_PADDING }; }, [mobileLayout, selected, size, subMenu, subMenuActivated]); useEffect9(() => { if (!hovered || !subMenu) { return; } const hi = setTimeout(() => { setSubMenuActivated("with-mouse"); }, 100); return () => clearTimeout(hi); }, [hovered, selected, setSubMenuActivated, subMenu]); useEffect9(() => { if (selected) { ref.current?.scrollIntoView({ block: "nearest" }); } }, [selected]); return /* @__PURE__ */ jsx23("div", { ref, onPointerEnter, onPointerLeave, style, onPointerUp, role: "button", className: MENU_ITEM_CLASSNAME, children: /* @__PURE__ */ jsxs3(Row, { align: "center", children: [ leaveLeftSpace ? /* @__PURE__ */ jsxs3(Fragment, { children: [ /* @__PURE__ */ jsx23("div", { style: leftSpace, children: leftItem }), /* @__PURE__ */ jsx23(Spacing, { x: 1 }) ] }) : null, /* @__PURE__ */ jsx23("div", { style: labelStyle, ...{ title: typeof label === "string" ? label : undefined }, children: label }), " ", /* @__PURE__ */ jsx23(Spacing, { x: 2 }), subMenu ? /* @__PURE__ */ jsx23(CaretRight, {}) : null, keyHint && !areKeyboardShortcutsDisabled() ? /* @__PURE__ */ jsx23("span", { style: keyHintCss, children: keyHint }) : null, portalStyle && subMenu ? ReactDOM.createPortal(/* @__PURE__ */ jsx23(SubMenuComponent, { onQuitFullMenu: onQuitMenu, subMenu, onQuitSubMenu: onQuitSubmenu, portalStyle, subMenuActivated }), getPortal(currentZIndex)) : null ] }) }); }; // src/components/NewComposition/MenuContent.tsx import { jsx as jsx24 } from "react/jsx-runtime"; var BORDER_SIZE = 1; var container5 = { paddingTop: MENU_VERTICAL_PADDING, paddingBottom: MENU_VERTICAL_PADDING, border: `${BORDER_SIZE}px solid ${INPUT_BORDER_COLOR_UNHOVERED}`, marginLeft: 0 - BORDER_SIZE, overflowY: "auto", overflowX: "hidden", minWidth: 200, maxWidth: MAX_MENU_WIDTH }; var MenuContent = ({ onHide, values, preselectIndex, onNextMenu, onPreviousMenu, leaveLeftSpace, topItemCanBeUnselected, fixedHeight }) => { const keybindings = useKeybinding(); const containerRef = useRef8(null); const isMobileLayout = useMobileLayout(); const [subMenuActivated, setSubMenuActivated] = useState11(false); if (values[0].type === "divider") { throw new Error("first value cant be divide"); } const [selectedItem, setSelectedItem] = useState11(typeof preselectIndex === "number" && preselectIndex >= 0 ? values[preselectIndex].id : null); const onEscape = useCallback10(() => { onHide(); }, [onHide]); const onItemSelected = useCallback10((id) => { setSelectedItem(id); }, []); const isItemSelectable = useCallback10((v) => { return v.type !== "divider" && !v.disabled; }, []); const onArrowUp = useCallback10(() => { setSelectedItem((prevItem) => { if (prevItem === null) { return null; } const index = values.findIndex((val) => val.id === prevItem); if (topItemCanBeUnselected && index === 0 || prevItem === null) { return null; } const previousItems = values.filter((v, i) => i < index && isItemSelectable(v)); if (previousItems.length > 0) { return previousItems[previousItems.length - 1].id; } const firstSelectable = values.find((v) => isItemSelectable(v)); if (firstSelectable) { return firstSelectable.id; } throw new Error("could not find previous item"); }); }, [topItemCanBeUnselected, values, isItemSelectable]); const onArrowDown = useCallback10(() => { setSelectedItem((prevItem) => { const index = values.findIndex((val) => val.id === prevItem); const nextItem = values.find((v, i) => i > index && isItemSelectable(v)); if (nextItem) { return nextItem.id; } const lastSelectable = values.slice().reverse().find((v) => isItemSelectable(v)); if (lastSelectable) { return lastSelectable.id; } throw new Error("could not find next item"); }); }, [values, isItemSelectable]); const onEnter = useCallback10(() => { if (selectedItem === null) { return onHide(); } const item = values.find((i) => i.id === selectedItem); if (!item) { throw new Error("cannot find item"); } if (item.type === "divider") { throw new Error("cannot find divider"); } if (item.disabled) { return; } if (item.subMenu) { return setSubMenuActivated("without-mouse"); } onHide(); item.onClick(item.id, null); }, [onHide, selectedItem, values]); const onArrowRight = useCallback10(() => { if (selectedItem === null) { return onNextMenu(); } const item = values.find((i) => i.id === selectedItem); if (!item) { throw new Error("cannot find item"); } if (item.type === "divider") { throw new Error("cannot find divider"); } if (!item.subMenu) { return onNextMenu(); } setSubMenuActivated("without-mouse"); }, [onNextMenu, selectedItem, values]); const containerWithHeight = useMemo15(() => { const containerStyles = { ...container5 }; if (fixedHeight === null) { containerStyles.maxHeight = 600; } else { containerStyles.maxHeight = fixedHeight; } if (isMobileLayout) { containerStyles.maxWidth = MAX_MOBILE_MENU_WIDTH; } return containerStyles; }, [fixedHeight, isMobileLayout]); useEffect10(() => { const escapeBinding = keybindings.registerKeybinding({ event: "keydown", key: "Escape", callback: onEscape, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const rightBinding = keybindings.registerKeybinding({ event: "keydown", key: "ArrowRight", commandCtrlKey: false, callback: onArrowRight, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const leftBinding = keybindings.registerKeybinding({ event: "keydown", commandCtrlKey: false, key: "ArrowLeft", callback: onPreviousMenu, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const downBinding = keybindings.registerKeybinding({ event: "keydown", key: "ArrowDown", commandCtrlKey: false, callback: onArrowDown, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const upBinding = keybindings.registerKeybinding({ event: "keydown", key: "ArrowUp", callback: onArrowUp, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const enterBinding = keybindings.registerKeybinding({ event: "keydown", key: "Enter", callback: onEnter, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const spaceBinding = keybindings.registerKeybinding({ event: "keyup", key: " ", callback: onEnter, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); return () => { escapeBinding.unregister(); leftBinding.unregister(); rightBinding.unregister(); downBinding.unregister(); upBinding.unregister(); enterBinding.unregister(); spaceBinding.unregister(); }; }, [ keybindings, onEscape, onNextMenu, onPreviousMenu, onArrowDown, onArrowUp, onEnter, onArrowRight ]); useEffect10(() => { if (!subMenuActivated) { return; } if (selectedItem === null) { return setSubMenuActivated(false); } const item = values.find((i) => i.id === selectedItem); if (!item) { return; } if (item.type === "divider") { throw new Error("should not select divider"); } if (!item.subMenu && subMenuActivated) { setSubMenuActivated(false); } }, [selectedItem, subMenuActivated, values]); useEffect10(() => { const { current } = containerRef; if (!current) { return; } const onPointerLeave = () => { if (subMenuActivated) { return; } setSelectedItem(null); }; current.addEventListener("pointerleave", onPointerLeave); return () => current.removeEventListener("pointerleave", onPointerLeave); }, [onHide, subMenuActivated]); return /* @__PURE__ */ jsx24("div", { ref: containerRef, style: containerWithHeight, className: VERTICAL_SCROLLBAR_CLASSNAME, children: values.map((item) => { if (item.type === "divider") { return /* @__PURE__ */ jsx24(MenuDivider, {}, item.id); } const onClick = (id, e) => { item.onClick(id, e); if (item.subMenu) { return null; } onHide(); }; return /* @__PURE__ */ jsx24(MenuSubItem, { selected: item.id === selectedItem, onActionChosen: onClick, onItemSelected, label: item.label, id: item.id, keyHint: item.keyHint, leaveLeftSpace, leftItem: item.leftItem, subMenu: item.subMenu, onQuitMenu: onHide, onNextMenu, subMenuActivated, setSubMenuActivated, disabled: item.disabled }, item.id); }) }); }; // src/components/InlineDropdown.tsx import { jsx as jsx25, jsxs as jsxs4, Fragment as Fragment2 } from "react/jsx-runtime"; var InlineDropdown = ({ values, ...props }) => { const ref = useRef9(null); const [opened, setOpened] = useState12({ type: "not-open" }); const { currentZIndex } = useZIndex(); const size = PlayerInternals3.useElementSize(ref, { triggerOnWindowResize: true, shouldApplyCssTransforms: true }); const isMobileLayout = useMobileLayout(); const onClick = useCallback11((e) => { e.preventDefault(); e.stopPropagation(); setOpened({ type: "open", left: e.clientX, top: e.clientY }); }, []); const spaceToBottom = useMemo16(() => { if (size && opened.type === "open") { return size.windowSize.height - opened.top; } return 0; }, [opened, size]); const spaceToTop = useMemo16(() => { if (size && opened.type === "open") { return opened.top; } return 0; }, [opened, size]); const portalStyle = useMemo16(() => { if (opened.type === "not-open") { return; } if (!size) { return; } const spaceToRight = size.windowSize.width - size.left; const spaceToLeft = size.left + size.width; const minSpaceRequired = isMobileLayout ? MAX_MOBILE_MENU_WIDTH : MAX_MENU_WIDTH; const verticalLayout = spaceToTop > spaceToBottom ? "bottom" : "top"; const canOpenOnLeft = spaceToLeft >= minSpaceRequired; const canOpenOnRight = spaceToRight >= minSpaceRequired; const horizontalLayout = canOpenOnRight ? "left" : "right"; return { ...menuContainerTowardsTop, ...verticalLayout === "top" ? { top: opened.top } : { bottom: size.windowSize.height - opened.top }, ...horizontalLayout === "left" ? { left: opened.left } : { right: canOpenOnLeft ? size.windowSize.width - opened.left : 0 } }; }, [opened, size, isMobileLayout, spaceToTop, spaceToBottom]); const onHide = useCallback11(() => { setOpened({ type: "not-open" }); }, []); return /* @__PURE__ */ jsxs4(Fragment2, { children: [ /* @__PURE__ */ jsx25("div", { ref, children: /* @__PURE__ */ jsx25(InlineAction, { onClick, ...props }) }), portalStyle ? ReactDOM2.createPortal(/* @__PURE__ */ jsx25("div", { style: fullScreenOverlay, children: /* @__PURE__ */ jsx25("div", { style: outerPortal, className: "css-reset", children: /* @__PURE__ */ jsx25(HigherZIndex, { onOutsideClick: onHide, onEscape: onHide, children: /* @__PURE__ */ jsx25("div", { style: portalStyle, children: /* @__PURE__ */ jsx25(MenuContent, { onNextMenu: noop, onPreviousMenu: noop, values, onHide, leaveLeftSpace: true, preselectIndex: false, topItemCanBeUnselected: false, fixedHeight: null }) }) }) }) }), getPortal(currentZIndex)) : null ] }); }; // src/components/CompositionContextButton.tsx import { jsx as jsx26 } from "react/jsx-runtime"; var CompositionContextButton = ({ visible, values }) => { const iconStyle = useMemo17(() => { return { style: { height: 12 } }; }, []); const connectionStatus = useContext6(StudioServerConnectionCtx).previewServerState.type; const renderAction = useCallback12((color) => { return /* @__PURE__ */ jsx26(EllipsisIcon, { fill: color, svgProps: iconStyle }); }, [iconStyle]); if (!visible || connectionStatus !== "connected") { return null; } return /* @__PURE__ */ jsx26(InlineDropdown, { renderAction, values }); }; // src/components/ContextMenu.tsx import { PlayerInternals as PlayerInternals4 } from "@remotion/player"; import { useCallback as useCallback13, useEffect as useEffect11, useMemo as useMemo18, useRef as useRef10, useState as useState13 } from "react"; import ReactDOM3 from "react-dom"; import { jsx as jsx27, jsxs as jsxs5, Fragment as Fragment3 } from "react/jsx-runtime"; var ContextMenu = ({ children, values }) => { const ref = useRef10(null); const [opened, setOpened] = useState13({ type: "not-open" }); const { currentZIndex } = useZIndex(); const style = useMemo18(() => { return {}; }, []); const size = PlayerInternals4.useElementSize(ref, { triggerOnWindowResize: true, shouldApplyCssTransforms: true }); const isMobileLayout = useMobileLayout(); useEffect11(() => { const { current } = ref; if (!current) { return; } const onClick = (e) => { e.preventDefault(); e.stopPropagation(); setOpened({ type: "open", left: e.clientX, top: e.clientY }); return false; }; current.addEventListener("contextmenu", onClick); return () => { current.removeEventListener("contextmenu", onClick); }; }, [size]); const spaceToBottom = useMemo18(() => { if (size && opened.type === "open") { return size.windowSize.height - opened.top; } return 0; }, [opened, size]); const spaceToTop = useMemo18(() => { if (size && opened.type === "open") { return opened.top; } return 0; }, [opened, size]); const portalStyle = useMemo18(() => { if (opened.type === "not-open") { return; } if (!size) { return; } const spaceToRight = size.windowSize.width - size.left; const spaceToLeft = size.left + size.width; const minSpaceRequired = isMobileLayout ? MAX_MOBILE_MENU_WIDTH : MAX_MENU_WIDTH; const verticalLayout = spaceToTop > spaceToBottom ? "bottom" : "top"; const canOpenOnLeft = spaceToLeft >= minSpaceRequired; const canOpenOnRight = spaceToRight >= minSpaceRequired; const horizontalLayout = canOpenOnRight ? "left" : "right"; return { ...menuContainerTowardsTop, ...verticalLayout === "top" ? { top: opened.top } : { bottom: size.windowSize.height - opened.top }, ...horizontalLayout === "left" ? { left: opened.left } : { right: canOpenOnLeft ? size.windowSize.width - opened.left : 0 } }; }, [opened, size, isMobileLayout, spaceToTop, spaceToBottom]); const onHide = useCallback13(() => { setOpened({ type: "not-open" }); }, []); return /* @__PURE__ */ jsxs5(Fragment3, { children: [ /* @__PURE__ */ jsx27("div", { ref, onContextMenu: () => false, style, children }), portalStyle ? ReactDOM3.createPortal(/* @__PURE__ */ jsx27("div", { style: fullScreenOverlay, children: /* @__PURE__ */ jsx27("div", { style: outerPortal, className: "css-reset", children: /* @__PURE__ */ jsx27(HigherZIndex, { onOutsideClick: onHide, onEscape: onHide, children: /* @__PURE__ */ jsx27("div", { style: portalStyle, children: /* @__PURE__ */ jsx27(MenuContent, { onNextMenu: noop, onPreviousMenu: noop, values, onHide, leaveLeftSpace: true, preselectIndex: false, topItemCanBeUnselected: false, fixedHeight: null }) }) }) }) }), getPortal(currentZIndex)) : null ] }); }; // src/components/SidebarRenderButton.tsx import { useCallback as useCallback14, useContext as useContext7, useMemo as useMemo19 } from "react"; import { Internals as Internals5 } from "remotion"; // src/icons/render.tsx import { jsx as jsx28 } from "react/jsx-runtime"; var ThinRenderIcon = (props) => { return /* @__PURE__ */ jsx28("svg", { ...props.svgProps, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", children: /* @__PURE__ */ jsx28("path", { fill: props.fill, d: "M188.9 372l-50.4-50.4c18.6-42.6 61.7-137.7 95.1-187C304.6 30.1 409 24.6 475.7 36.3c11.7 66.7 6.2 171.1-98.4 242c-49.4 33.5-145.5 75.6-188.4 93.7zm-79.9-62.8c-5.2 11.9-2.5 25.7 6.7 34.9l50.7 50.7c9.1 9.1 22.7 11.9 34.5 6.9c6.5-2.7 14.3-6 23-9.8L224 496c0 5.5 2.9 10.7 7.6 13.6s10.6 3.2 15.6 .7l101.5-50.7c21.7-10.8 35.4-33 35.4-57.2V312.1c4-2.5 7.7-4.9 11.3-7.3C516.1 222.9 520.1 100.9 506.7 28.1c-2.1-11.6-11.2-20.6-22.8-22.8C411.1-8.1 289.1-4.1 207.2 116.7c-2.4 3.6-4.9 7.3-7.3 11.3l-90.2 0c-24.2 0-46.4 13.7-57.2 35.4L1.7 264.8c-2.5 5-2.2 10.8 .7 15.6s8.1 7.6 13.6 7.6H118.5c-3.6 8-6.8 15.2-9.4 21.2zM256 470.1l0-92.5c30.3-13.7 65.4-30.3 96-47v71.7c0 12.1-6.8 23.2-17.7 28.6L256 470.1zM109.7 160h71.5c-16.9 30.7-34 65.8-48.1 96H41.9L81 177.7c5.4-10.8 16.5-17.7 28.6-17.7zM392 144a24 24 0 1 1 -48 0 24 24 0 1 1 48 0zM368 88a56 56 0 1 0 0 112 56 56 0 1 0 0-112z" }) }); }; // src/components/SidebarRenderButton.tsx import { jsx as jsx29 } from "react/jsx-runtime"; var SidebarRenderButton = ({ composition, visible }) => { const { setSelectedModal } = useContext7(ModalsContext); const { setSidebarCollapsedState } = useContext7(SidebarContext); const isMobileLayout = useMobileLayout(); const iconStyle = useMemo19(() => { return { style: { height: 12 } }; }, []); const connectionStatus = useContext7(StudioServerConnectionCtx).previewServerState.type; const { props } = useContext7(Internals5.EditorPropsContext); const onClick = useCallback14((e) => { const defaults = window.remotion_renderDefaults; if (!defaults) { throw new Error("expected defaults"); } e.stopPropagation(); setSelectedModal({ type: "server-render", compositionId: composition.id, initialFrame: 0, initialVideoImageFormat: defaults.videoImageFormat, initialStillImageFormat: defaults.stillImageFormat, initialJpegQuality: defaults.jpegQuality, initialScale: defaults.scale, initialLogLevel: defaults.logLevel, initialConcurrency: defaults.concurrency, maxConcurrency: defaults.maxConcurrency, minConcurrency: defaults.minConcurrency, initialMuted: defaults.muted, initialEnforceAudioTrack: defaults.enforceAudioTrack, initialProResProfile: defaults.proResProfile, initialx264Preset: defaults.x264Preset, initialPixelFormat: null, initialAudioBitrate: defaults.audioBitrate, initialVideoBitrate: defaults.videoBitrate, initialEveryNthFrame: defaults.everyNthFrame, initialNumberOfGifLoops: defaults.numberOfGifLoops, initialDelayRenderTimeout: defaults.delayRenderTimeout, defaultConfigurationAudioCodec: defaults.audioCodec, initialEnvVariables: window.process.env, initialDisableWebSecurity: defaults.disableWebSecurity, initialOpenGlRenderer: defaults.openGlRenderer, initialHeadless: defaults.headless, initialOffthreadVideoCacheSizeInBytes: defaults.offthreadVideoCacheSizeInBytes, initialOffthreadVideoThreads: defaults.offthreadVideoThreads, initialIgnoreCertificateErrors: defaults.ignoreCertificateErrors, defaultProps: props[composition.id] ?? composition.defaultProps, inFrameMark: null, outFrameMark: null, initialColorSpace: defaults.colorSpace, initialMultiProcessOnLinux: defaults.multiProcessOnLinux, defaultConfigurationVideoCodec: defaults.codec, initialEncodingBufferSize: defaults.encodingBufferSize, initialEncodingMaxRate: defaults.encodingMaxRate, initialUserAgent: defaults.userAgent, initialBeep: defaults.beepOnFinish, initialRepro: defaults.repro, initialForSeamlessAacConcatenation: defaults.forSeamlessAacConcatenation, renderTypeOfLastRender: null, defaulMetadata: defaults.metadata, initialHardwareAcceleration: defaults.hardwareAcceleration, initialChromeMode: defaults.chromeMode, initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes, renderDefaults: defaults, initialDarkMode: defaults.darkMode }); if (isMobileLayout) { setSidebarCollapsedState({ left: "collapsed", right: "collapsed" }); } }, [ composition.defaultProps, composition.id, isMobileLayout, props, setSelectedModal, setSidebarCollapsedState ]); const renderAction = useCallback14((color) => { return /* @__PURE__ */ jsx29(ThinRenderIcon, { fill: color, svgProps: iconStyle }); }, [iconStyle]); if (!visible || connectionStatus !== "connected") { return null; } return /* @__PURE__ */ jsx29(InlineAction, { renderAction, onClick }); }; // src/components/CompositionSelectorItem.tsx import { jsx as jsx30, jsxs as jsxs6, Fragment as Fragment4 } from "react/jsx-runtime"; var COMPOSITION_ITEM_HEIGHT = 32; var itemStyle = { paddingRight: 10, paddingTop: 6, paddingBottom: 6, fontSize: 13, display: "flex", textDecoration: "none", cursor: "default", alignItems: "center", marginBottom: 1, appearance: "none", border: "none", width: "100%", textAlign: "left", backgroundColor: BACKGROUND, height: COMPOSITION_ITEM_HEIGHT }; var labelStyle2 = { textAlign: "left", textDecoration: "none", fontSize: 13, flex: 1, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }; var iconStyle = { width: 18, height: 18, flexShrink: 0 }; var CompositionSelectorItem = ({ item, level, currentComposition, tabIndex, selectComposition, toggleFolder }) => { const selected = useMemo20(() => { if (item.type === "composition") { return currentComposition === item.composition.id; } return false; }, [item, currentComposition]); const [hovered, setHovered] = useState14(false); const onPointerEnter = useCallback15(() => { setHovered(true); }, []); const onPointerLeave = useCallback15(() => { setHovered(false); }, []); const style = useMemo20(() => { return { ...itemStyle, backgroundColor: getBackgroundFromHoverState({ hovered, selected }), paddingLeft: 12 + level * 8 }; }, [hovered, level, selected]); const label = useMemo20(() => { return { ...labelStyle2, color: selected || hovered ? "white" : LIGHT_TEXT }; }, [hovered, selected]); const onClick = useCallback15((evt) => { evt.preventDefault(); if (item.type === "composition") { selectComposition(item.composition, true); } else { toggleFolder(item.folderName, item.parentName); } }, [item, selectComposition, toggleFolder]); const onKeyPress = useCallback15((evt) => { if (evt.key === "Enter") { onClick(evt); } }, [onClick]); const { setSelectedModal } = useContext8(ModalsContext); const contextMenu = useMemo20(() => { if (item.type === "composition") { return [ { id: "duplicate", keyHint: null, label: `Duplicate...`, leftItem: null, onClick: () => { setSelectedModal({ type: "duplicate-comp", compositionId: item.composition.id, compositionType: item.composition.durationInFrames === 1 ? "still" : "composition" }); }, quickSwitcherLabel: null, subMenu: null, type: "item", value: "duplicate" }, { id: "rename", keyHint: null, label: `Rename...`, leftItem: null, onClick: () => { setSelectedModal({ type: "rename-comp", compositionId: item.composition.id }); }, quickSwitcherLabel: null, subMenu: null, type: "item", value: "rename" }, { id: "delete", keyHint: null, label: `Delete...`, leftItem: null, onClick: () => { setSelectedModal({ type: "delete-comp", compositionId: item.composition.id }); }, quickSwitcherLabel: null, subMenu: null, type: "item", value: "delete" }, { type: "divider", id: "copy-id-divider" }, { id: "copy-id", keyHint: null, label: `Copy ID`, leftItem: null, onClick: () => { navigator.clipboard.writeText(item.composition.id).catch((err) => { showNotification(`Could not copy to clipboard: ${err.message}`, 1000); }).then(() => { showNotification("Copied to clipboard", 1000); }); }, quickSwitcherLabel: null, subMenu: null, type: "item", value: "remove" } ]; } return []; }, [item, setSelectedModal]); if (item.type === "folder") { return /* @__PURE__ */ jsxs6(Fragment4, { children: [ /* @__PURE__ */ jsxs6("button", { style, onPointerEnter, onPointerLeave, tabIndex, onClick, type: "button", title: item.folderName, children: [ item.expanded ? /* @__PURE__ */ jsx30(ExpandedFolderIcon, { style: iconStyle, color: hovered || selected ? "white" : LIGHT_TEXT }) : /* @__PURE__ */ jsx30(CollapsedFolderIcon, { color: hovered || selected ? "white" : LIGHT_TEXT, style: iconStyle }), /* @__PURE__ */ jsx30(Spacing, { x: 1 }), /* @__PURE__ */ jsx30("div", { style: label, children: item.folderName }) ] }), item.expanded ? item.items.map((childItem) => { return /* @__PURE__ */ jsx30(CompositionSelectorItem, { currentComposition, selectComposition, item: childItem, tabIndex, level: level + 1, toggleFolder }, childItem.key + childItem.type); }) : null ] }); } return /* @__PURE__ */ jsx30(ContextMenu, { values: contextMenu, children: /* @__PURE__ */ jsx30(Row, { align: "center", children: /* @__PURE__ */ jsxs6("a", { style, onPointerEnter, onPointerLeave, tabIndex, onClick, onKeyPress, type: "button", title: item.composition.id, className: "__remotion-composition", "data-compname": item.composition.id, children: [ isCompositionStill(item.composition) ? /* @__PURE__ */ jsx30(StillIcon, { color: hovered || selected ? "white" : LIGHT_TEXT, style: iconStyle }) : /* @__PURE__ */ jsx30(FilmIcon, { color: hovered || selected ? "white" : LIGHT_TEXT, style: iconStyle }), /* @__PURE__ */ jsx30(Spacing, { x: 1 }), /* @__PURE__ */ jsx30("div", { style: label, children: item.composition.id }), /* @__PURE__ */ jsx30(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx30(CompositionContextButton, { values: contextMenu, visible: hovered }), /* @__PURE__ */ jsx30(SidebarRenderButton, { visible: hovered, composition: item.composition }) ] }) }) }); }; // src/components/CurrentComposition.tsx import { Internals as Internals6 } from "remotion"; // src/state/render-frame.ts var renderFrame = (frame, fps) => { const hours = Math.floor(frame / fps / 3600); const remainingMinutes = frame - hours * fps * 3600; const minutes = Math.floor(remainingMinutes / 60 / fps); const remainingSec = frame - hours * fps * 3600 - minutes * fps * 60; const seconds = Math.floor(remainingSec / fps); const frameAfterSec = Math.round(frame % fps); const hoursStr = String(hours); const minutesStr = String(minutes).padStart(2, "0"); const secondsStr = String(seconds).padStart(2, "0"); const frameStr = String(frameAfterSec).padStart(2, "0"); if (hours > 0) { return `${hoursStr}:${minutesStr}:${secondsStr}.${frameStr}`; } return `${minutesStr}:${secondsStr}.${frameStr}`; }; // src/components/CurrentComposition.tsx import { jsx as jsx31, jsxs as jsxs7 } from "react/jsx-runtime"; var CURRENT_COMPOSITION_HEIGHT = 80; var container6 = { height: CURRENT_COMPOSITION_HEIGHT, display: "block", borderBottom: `1px solid ${BORDER_COLOR}`, padding: 12, color: "white", backgroundColor: BACKGROUND }; var title = { fontWeight: "bold", fontSize: 12, whiteSpace: "nowrap", lineHeight: "18px", backgroundColor: BACKGROUND }; var subtitle = { fontSize: 12, opacity: 0.8, whiteSpace: "nowrap", lineHeight: "18px", backgroundColor: BACKGROUND }; var row = { display: "flex", flexDirection: "row", lineHeight: "18px", backgroundColor: BACKGROUND }; var CurrentComposition = () => { const video = Internals6.useVideo(); if (!video) { return /* @__PURE__ */ jsx31("div", { style: container6 }); } return /* @__PURE__ */ jsx31("div", { style: container6, children: /* @__PURE__ */ jsx31("div", { style: row, children: /* @__PURE__ */ jsxs7("div", { children: [ /* @__PURE__ */ jsx31("div", { style: title, children: video.id }), /* @__PURE__ */ jsxs7("div", { style: subtitle, children: [ video.width, "x", video.height, isCompositionStill(video) ? null : `, ${video.fps} FPS` ] }), isCompositionStill(video) ? /* @__PURE__ */ jsx31("div", { style: subtitle, children: "Still" }) : /* @__PURE__ */ jsxs7("div", { style: subtitle, children: [ "Duration ", renderFrame(video.durationInFrames, video.fps) ] }) ] }) }) }); }; // src/components/CompositionSelector.tsx import { jsx as jsx32, jsxs as jsxs8 } from "react/jsx-runtime"; var useCompositionNavigation = () => { const { compositions, canvasContent } = useContext9(Internals7.CompositionManager); const selectComposition = useSelectComposition(); const navigateToNextComposition = useCallback16(() => { if (!canvasContent || canvasContent.type !== "composition" || compositions.length <= 1) { return; } const currentIndex = compositions.findIndex((c) => c.id === canvasContent.compositionId); if (currentIndex === -1) { return; } const nextIndex = (currentIndex + 1) % compositions.length; const nextComposition = compositions[nextIndex]; selectComposition(nextComposition, true); }, [canvasContent, compositions, selectComposition]); const navigateToPreviousComposition = useCallback16(() => { if (!canvasContent || canvasContent.type !== "composition" || compositions.length <= 1) { return; } const currentIndex = compositions.findIndex((c) => c.id === canvasContent.compositionId); if (currentIndex === -1) { return; } const previousIndex = (currentIndex - 1 + compositions.length) % compositions.length; const previousComposition = compositions[previousIndex]; selectComposition(previousComposition, true); }, [canvasContent, compositions, selectComposition]); return useMemo21(() => ({ navigateToNextComposition, navigateToPreviousComposition }), [navigateToNextComposition, navigateToPreviousComposition]); }; var container7 = { display: "flex", flexDirection: "column", flex: 1, overflow: "hidden", backgroundColor: BACKGROUND }; var getKeysToExpand = (initialFolderName, parentFolderName, initial = []) => { initial.push(openFolderKey({ folderName: initialFolderName, parentName: parentFolderName })); const { name, parent } = splitParentIntoNameAndParent(parentFolderName); if (!name) { return initial; } return getKeysToExpand(name, parent, initial); }; var CompositionSelector = () => { const { compositions, canvasContent, folders } = useContext9(Internals7.CompositionManager); const { foldersExpanded } = useContext9(ExpandedFoldersContext); const { tabIndex } = useZIndex(); const selectComposition = useSelectComposition(); const items = useMemo21(() => { return createFolderTree(compositions, folders, foldersExpanded); }, [compositions, folders, foldersExpanded]); const showCurrentComposition = canvasContent && canvasContent.type === "composition"; const list = useMemo21(() => { return { height: showCurrentComposition ? `calc(100% - ${CURRENT_COMPOSITION_HEIGHT}px)` : "100%", overflowY: "auto" }; }, [showCurrentComposition]); const toggleFolder = useCallback16((folderName, parentName) => { Internals7.compositionSelectorRef.current?.toggleFolder(folderName, parentName); }, []); return /* @__PURE__ */ jsxs8("div", { style: container7, children: [ showCurrentComposition ? /* @__PURE__ */ jsx32(CurrentComposition, {}) : null, /* @__PURE__ */ jsx32("div", { className: "__remotion-vertical-scrollbar", style: list, children: items.map((c) => { return /* @__PURE__ */ jsx32(CompositionSelectorItem, { level: 0, currentComposition: showCurrentComposition ? canvasContent.compositionId : null, selectComposition, toggleFolder, tabIndex, item: c }, c.key + c.type); }) }) ] }); }; // src/components/ExplorerPanel.tsx import { createRef as createRef5, useCallback as useCallback22, useImperativeHandle as useImperativeHandle5, useState as useState19 } from "react"; // src/components/AssetSelector.tsx import React25, { useCallback as useCallback19, useContext as useContext11, useEffect as useEffect13, useMemo as useMemo24, useState as useState16 } from "react"; // 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/helpers/use-asset-drag-events.ts import { useCallback as useCallback17, useEffect as useEffect12, useMemo as useMemo22, useRef as useRef11 } from "react"; import { NoReactInternals } from "remotion/no-react"; function useAssetDragEvents({ name, parentFolder, dropLocation, setDropLocation }) { const dragDepthRef = useRef11(0); const combinedParents = useMemo22(() => { return [parentFolder, name].filter(NoReactInternals.truthy).join("/"); }, [name, parentFolder]); const isDropDiv = useMemo22(() => { return dropLocation === combinedParents; }, [combinedParents, dropLocation]); const onDragEnter = useCallback17(() => { if (dragDepthRef.current === 0) { setDropLocation((currentDropLocation) => currentDropLocation?.includes(combinedParents) ? currentDropLocation : combinedParents); } dragDepthRef.current++; }, [combinedParents, dragDepthRef, setDropLocation]); const onDragLeave = useCallback17(() => { dragDepthRef.current--; if (dragDepthRef.current === 0) { setDropLocation((currentPath) => currentPath === combinedParents ? parentFolder : currentPath); } }, [combinedParents, dragDepthRef, parentFolder, setDropLocation]); useEffect12(() => { if (dropLocation === null) { dragDepthRef.current = 0; } }, [dropLocation]); return { isDropDiv, onDragEnter, onDragLeave }; } var use_asset_drag_events_default = useAssetDragEvents; // src/components/AssetSelectorItem.tsx import React24, { useCallback as useCallback18, useContext as useContext10, useMemo as useMemo23, useRef as useRef12, useState as useState15 } from "react"; import { Internals as Internals8 } from "remotion"; import { NoReactInternals as NoReactInternals3 } from "remotion/no-react"; // src/helpers/copy-text.ts var copyText = (cmd) => { const permissionName = "clipboard-write"; return new Promise((resolve, reject) => { navigator.permissions.query({ name: permissionName }).then((result) => { if (result.state === "granted" || result.state === "prompt") { navigator.clipboard.writeText(cmd); resolve(); } else { reject(new Error("Permission to copy not granted")); } }).catch((err) => { reject(err); }); }); }; // src/icons/clipboard.tsx import { jsx as jsx33 } from "react/jsx-runtime"; var ClipboardIcon = ({ color, ...props }) => /* @__PURE__ */ jsx33("svg", { viewBox: "0 0 384 512", ...props, children: /* @__PURE__ */ jsx33("path", { fill: color, d: "M336 64h-80c0-35.3-28.7-64-64-64s-64 28.7-64 64H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48zM192 40c13.3 0 24 10.7 24 24s-10.7 24-24 24-24-10.7-24-24 10.7-24 24-24zm144 418c0 3.3-2.7 6-6 6H54c-3.3 0-6-2.7-6-6V118c0-3.3 2.7-6 6-6h42v36c0 6.6 5.4 12 12 12h168c6.6 0 12-5.4 12-12v-36h42c3.3 0 6 2.7 6 6z" }) }); // src/icons/file.tsx import { jsx as jsx34 } from "react/jsx-runtime"; var FileIcon = ({ color, ...props }) => /* @__PURE__ */ jsx34("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 384 512", ...props, children: /* @__PURE__ */ jsx34("path", { fill: color ?? "currentColor", d: "M0 64C0 28.65 28.65 0 64 0h156.1c12.7 0 25 5.057 34 14.06L369.9 129.9c9 9 14.1 21.3 14.1 34V448c0 35.3-28.7 64-64 64H64c-35.35 0-64-28.7-64-64V64zm352 128H240c-26.5 0-48-21.5-48-48V32H64c-17.67 0-32 14.33-32 32v384c0 17.7 14.33 32 32 32h256c17.7 0 32-14.3 32-32V192zm-4.7-39.4L231.4 36.69c-2-2.07-4.6-3.51-7.4-4.21V144c0 8.8 7.2 16 16 16h111.5c-.7-2.8-2.1-5.4-4.2-7.4z" }) }); // src/components/RenderQueue/actions.ts import { NoReactInternals as NoReactInternals2 } from "remotion/no-react"; // 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/components/RenderQueue/actions.ts var addStillRenderJob = ({ compositionId, outName, imageFormat, jpegQuality, frame, scale, logLevel, chromiumOptions, delayRenderTimeout, envVariables, inputProps, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, multiProcessOnLinux, beepOnFinish, metadata, chromeMode, mediaCacheSizeInBytes }) => { return callApi("/api/render", { compositionId, type: "still", outName, imageFormat, jpegQuality, frame, scale, logLevel, chromiumOptions, delayRenderTimeout, envVariables, serializedInputPropsWithCustomSchema: NoReactInternals2.serializeJSONWithSpecialTypes({ data: inputProps, staticBase: window.remotion_staticBase, indent: undefined }).serializedString, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, multiProcessOnLinux, beepOnFinish, metadata, chromeMode, mediaCacheSizeInBytes }); }; var addSequenceRenderJob = ({ compositionId, outName, imageFormat, startFrame, endFrame, scale, logLevel, chromiumOptions, delayRenderTimeout, envVariables, inputProps, concurrency, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, jpegQuality, disallowParallelEncoding, multiProcessOnLinux, beepOnFinish, repro, metadata, chromeMode, mediaCacheSizeInBytes }) => { return callApi("/api/render", { compositionId, type: "sequence", outName, imageFormat, jpegQuality, scale, startFrame, endFrame, logLevel, chromiumOptions, delayRenderTimeout, envVariables, concurrency, serializedInputPropsWithCustomSchema: NoReactInternals2.serializeJSONWithSpecialTypes({ data: inputProps, staticBase: window.remotion_staticBase, indent: undefined }).serializedString, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, disallowParallelEncoding, multiProcessOnLinux, beepOnFinish, repro, metadata, chromeMode, mediaCacheSizeInBytes }); }; var addVideoRenderJob = ({ compositionId, outName, imageFormat, jpegQuality, scale, logLevel, codec, concurrency, crf, startFrame, endFrame, muted, enforceAudioTrack, proResProfile, x264Preset, pixelFormat, audioBitrate, videoBitrate, everyNthFrame, numberOfGifLoops, delayRenderTimeout, audioCodec, disallowParallelEncoding, chromiumOptions, envVariables, inputProps, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, colorSpace, multiProcessOnLinux, encodingMaxRate, encodingBufferSize, beepOnFinish, repro, forSeamlessAacConcatenation, separateAudioTo, metadata, hardwareAcceleration, chromeMode, mediaCacheSizeInBytes }) => { return callApi("/api/render", { compositionId, type: "video", outName, imageFormat, jpegQuality, scale, logLevel, codec, concurrency, crf, endFrame, startFrame, muted, enforceAudioTrack, proResProfile, x264Preset, pixelFormat, audioBitrate, videoBitrate, everyNthFrame, numberOfGifLoops, delayRenderTimeout, audioCodec, disallowParallelEncoding, chromiumOptions, envVariables, serializedInputPropsWithCustomSchema: NoReactInternals2.serializeJSONWithSpecialTypes({ data: inputProps, staticBase: window.remotion_staticBase, indent: undefined }).serializedString, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, colorSpace, multiProcessOnLinux, encodingBufferSize, encodingMaxRate, beepOnFinish, repro, forSeamlessAacConcatenation, separateAudioTo, metadata, hardwareAcceleration, chromeMode, mediaCacheSizeInBytes }); }; var unsubscribeFromFileExistenceWatcher = ({ file, clientId }) => { return callApi("/api/unsubscribe-from-file-existence", { file, clientId }); }; var subscribeToFileExistenceWatcher = async ({ file, clientId }) => { const { exists } = await callApi("/api/subscribe-to-file-existence", { file, clientId }); return exists; }; var openInFileExplorer = ({ directory }) => { const body = { directory }; return callApi("/api/open-in-file-explorer", body); }; var applyCodemod = ({ codemod, dryRun, signal }) => { const body = { codemod, dryRun }; return callApi("/api/apply-codemod", body, signal); }; var removeRenderJob = (job) => { return callApi("/api/remove-render", { jobId: job.id }); }; var cancelRenderJob = (job) => { return callApi("/api/cancel", { jobId: job.id }); }; var updateAvailable = (signal) => { return callApi("/api/update-available", {}, signal); }; var getProjectInfo = (signal) => { return callApi("/api/project-info", {}, signal); }; var callUpdateDefaultPropsApi = (compositionId, defaultProps, enumPaths) => { return callApi("/api/update-default-props", { compositionId, defaultProps: NoReactInternals2.serializeJSONWithSpecialTypes({ data: defaultProps, indent: undefined, staticBase: window.remotion_staticBase }).serializedString, enumPaths }); }; var canUpdateDefaultProps = (compositionId, readOnlyStudio) => { if (readOnlyStudio) { return Promise.resolve({ canUpdate: false, reason: "Read-only studio" }); } return callApi("/api/can-update-default-props", { compositionId }); }; var applyVisualControlChange = ({ fileName, changes }) => { return callApi("/api/apply-visual-control-change", { fileName, changes }); }; // src/components/AssetSelectorItem.tsx import { jsx as jsx35, jsxs as jsxs9, Fragment as Fragment5 } from "react/jsx-runtime"; var ASSET_ITEM_HEIGHT = 32; var iconStyle2 = { width: 18, height: 18, flexShrink: 0 }; var itemStyle2 = { paddingRight: 10, paddingTop: 6, paddingBottom: 6, fontSize: 13, display: "flex", textDecoration: "none", cursor: "default", alignItems: "center", marginBottom: 1, appearance: "none", border: "none", width: "100%", textAlign: "left", backgroundColor: BACKGROUND, height: ASSET_ITEM_HEIGHT, userSelect: "none", WebkitUserSelect: "none" }; var labelStyle3 = { textAlign: "left", textDecoration: "none", fontSize: 13, flex: "1 1 0%", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }; var revealIconStyle = { height: 12, color: "currentColor" }; var AssetFolderItem = ({ tabIndex, item, level, parentFolder, toggleFolder, dropLocation, setDropLocation }) => { const [hovered, setHovered] = useState15(false); const openFolderTimerRef = useRef12(null); const { isDropDiv, onDragEnter, onDragLeave } = use_asset_drag_events_default({ name: item.name, parentFolder, dropLocation, setDropLocation }); const onPointerEnter = useCallback18(() => { setHovered(true); }, []); const onPointerLeave = useCallback18(() => { setHovered(false); }, []); const folderStyle = useMemo23(() => { return { ...itemStyle2, paddingLeft: 4 + level * 8, backgroundColor: hovered ? CLEAR_HOVER : "transparent" }; }, [hovered, level]); const label = useMemo23(() => { return { ...labelStyle3, color: hovered ? "white" : LIGHT_TEXT }; }, [hovered]); const onClick = useCallback18(() => { toggleFolder(item.name, parentFolder); }, [item.name, parentFolder, toggleFolder]); const Icon = item.expanded ? ExpandedFolderIcon : CollapsedFolderIcon; return /* @__PURE__ */ jsxs9("div", { onDragEnter, onDragLeave, style: { backgroundColor: isDropDiv ? CLEAR_HOVER : BACKGROUND }, children: [ /* @__PURE__ */ jsx35("div", { style: folderStyle, onPointerEnter, onPointerLeave, tabIndex, title: item.name, onClick, onDragEnter: () => { if (!item.expanded) { openFolderTimerRef.current = window.setTimeout(() => { toggleFolder(item.name, parentFolder); }, 1000); } }, onDragLeave: () => { if (openFolderTimerRef.current) { clearTimeout(openFolderTimerRef.current); } }, children: /* @__PURE__ */ jsxs9(Row, { children: [ /* @__PURE__ */ jsx35(Icon, { style: iconStyle2, color: hovered ? "white" : LIGHT_TEXT }), /* @__PURE__ */ jsx35(Spacing, { x: 1 }), /* @__PURE__ */ jsx35("div", { style: label, children: item.name }) ] }) }), item.expanded ? /* @__PURE__ */ jsx35(AssetFolderTree, { item: item.items, name: item.name, level, parentFolder, tabIndex, toggleFolder, dropLocation, setDropLocation }, item.name) : null ] }); }; var AssetFolderTree = ({ item, level, name, parentFolder, toggleFolder, tabIndex, dropLocation, setDropLocation }) => { const combinedParents = useMemo23(() => { return [parentFolder, name].filter(NoReactInternals3.truthy).join("/"); }, [name, parentFolder]); return /* @__PURE__ */ jsxs9("div", { children: [ item.folders.map((folder) => { return /* @__PURE__ */ jsx35(AssetFolderItem, { item: folder, tabIndex, level: level + 1, parentFolder: combinedParents, toggleFolder, dropLocation, setDropLocation }, folder.name); }), item.files.map((file) => { return /* @__PURE__ */ jsx35(AssetSelectorItem, { item: file, tabIndex, level, parentFolder: combinedParents }, file.src); }) ] }); }; var AssetSelectorItem = ({ item, tabIndex, level, parentFolder }) => { const isMobileLayout = useMobileLayout(); const [hovered, setHovered] = useState15(false); const { setSidebarCollapsedState } = useContext10(SidebarContext); const onPointerEnter = useCallback18(() => { setHovered(true); }, []); const { setCanvasContent } = useContext10(Internals8.CompositionSetters); const { canvasContent } = useContext10(Internals8.CompositionManager); const selected = useMemo23(() => { if (canvasContent && canvasContent.type === "asset") { const nameWOParent = canvasContent.asset.split("/").pop(); return nameWOParent === item.name; } return false; }, [canvasContent, item.name]); const onPointerLeave = useCallback18(() => { setHovered(false); }, []); const onClick = useCallback18(() => { const relativePath = parentFolder ? parentFolder + "/" + item.name : item.name; setCanvasContent({ type: "asset", asset: relativePath }); pushUrl(`/assets/${relativePath}`); if (isMobileLayout) { setSidebarCollapsedState({ left: "collapsed", right: "collapsed" }); } }, [ isMobileLayout, item.name, parentFolder, setCanvasContent, setSidebarCollapsedState ]); const style = useMemo23(() => { return { ...itemStyle2, color: hovered || selected ? "white" : LIGHT_TEXT, backgroundColor: hovered ? selected ? SELECTED_BACKGROUND : CLEAR_HOVER : selected ? SELECTED_BACKGROUND : "transparent", paddingLeft: 12 + level * 8 }; }, [hovered, level, selected]); const label = useMemo23(() => { return { ...labelStyle3, color: hovered || selected ? "white" : LIGHT_TEXT }; }, [hovered, selected]); const renderFileExplorerAction = useCallback18((color) => { return /* @__PURE__ */ jsx35(ExpandedFolderIcon, { style: revealIconStyle, color }); }, []); const renderCopyAction = useCallback18((color) => { return /* @__PURE__ */ jsx35(ClipboardIcon, { style: revealIconStyle, color }); }, []); const revealInExplorer = React24.useCallback((e) => { e.stopPropagation(); openInFileExplorer({ directory: window.remotion_publicFolderExists + "/" + parentFolder + "/" + item.name }).catch((err) => { showNotification(`Could not open file: ${err.message}`, 2000); }); }, [item.name, parentFolder]); const copyToClipboard = useCallback18((e) => { e.stopPropagation(); const content = `staticFile("${[parentFolder, item.name].join("/")}")`; copyText(content).then(() => { showNotification(`Copied '${content}' to clipboard`, 1000); }).catch((err) => { showNotification(`Could not copy: ${err.message}`, 2000); }); }, [item.name, parentFolder]); return /* @__PURE__ */ jsx35(Row, { align: "center", children: /* @__PURE__ */ jsxs9("div", { style, onPointerEnter, onPointerLeave, onClick, tabIndex, title: item.name, children: [ /* @__PURE__ */ jsx35(FileIcon, { style: iconStyle2, color: LIGHT_TEXT }), /* @__PURE__ */ jsx35(Spacing, { x: 1 }), /* @__PURE__ */ jsx35("div", { style: label, children: item.name }), hovered ? /* @__PURE__ */ jsxs9(Fragment5, { children: [ /* @__PURE__ */ jsx35(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx35(InlineAction, { title: "Copy staticFile() name", renderAction: renderCopyAction, onClick: copyToClipboard }), /* @__PURE__ */ jsx35(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx35(InlineAction, { title: "Open in Explorer", renderAction: renderFileExplorerAction, onClick: revealInExplorer }) ] }) : null ] }) }); }; // src/components/AssetSelector.tsx import { jsx as jsx36, jsxs as jsxs10 } from "react/jsx-runtime"; var container8 = { display: "flex", flexDirection: "column", flex: 1, overflow: "hidden", backgroundColor: BACKGROUND }; var emptyState = { display: "flex", flex: 1, justifyContent: "center", alignItems: "center", textAlign: "center", padding: "0 12px" }; var label = { color: LIGHT_TEXT, lineHeight: 1.5, fontSize: 14 }; var list = { height: "100%", overflowY: "auto" }; var AssetSelector = ({ readOnlyStudio }) => { const { tabIndex } = useZIndex(); const { assetFoldersExpanded, setAssetFoldersExpanded } = useContext11(FolderContext); const [dropLocation, setDropLocation] = useState16(null); const { subscribeToEvent } = useContext11(StudioServerConnectionCtx); const connectionStatus = useContext11(StudioServerConnectionCtx).previewServerState.type; const shouldAllowUpload = connectionStatus === "connected" && !readOnlyStudio; const [{ publicFolderExists, staticFiles }, setState] = React25.useState(() => { return { staticFiles: getStaticFiles(), publicFolderExists: window.remotion_publicFolderExists }; }); const assetTree = useMemo24(() => { return buildAssetFolderStructure(staticFiles, null, assetFoldersExpanded); }, [assetFoldersExpanded, staticFiles]); useEffect13(() => { const onUpdate = () => { setState({ staticFiles: getStaticFiles(), publicFolderExists: window.remotion_publicFolderExists }); }; const unsub = subscribeToEvent("new-public-folder", onUpdate); return () => { unsub(); }; }, [subscribeToEvent]); const toggleFolder = useCallback19((folderName, parentName) => { setAssetFoldersExpanded((p) => { const key = [parentName, folderName].filter(Boolean).join("/"); const prev = p[key] ?? false; const foldersExpandedState = { ...p, [key]: !prev }; persistExpandedFolders("assets", foldersExpandedState); return foldersExpandedState; }); }, [setAssetFoldersExpanded]); const { isDropDiv, onDragEnter, onDragLeave } = use_asset_drag_events_default({ name: null, parentFolder: null, dropLocation, setDropLocation }); const onDragOver = useCallback19((e) => { e.preventDefault(); }, []); const onDrop = useCallback19(async (e) => { try { e.preventDefault(); e.stopPropagation(); const { files } = e.dataTransfer; const assetPath = dropLocation ?? null; const makePath = (file) => { return [assetPath, file.name].filter(Boolean).join("/"); }; for (const file of files) { const body = await file.arrayBuffer(); await writeStaticFile({ contents: body, filePath: makePath(file) }); } if (files.length === 1) { showNotification(`Created ${makePath(files[0])}`, 3000); } else { showNotification(`Added ${files.length} files to ${assetPath}`, 3000); } } catch (error) { showNotification(`Error during upload: ${error}`, 3000); } finally { setDropLocation(null); } }, [dropLocation]); return /* @__PURE__ */ jsx36("div", { style: container8, onDragOver: shouldAllowUpload ? onDragOver : undefined, onDrop: shouldAllowUpload ? onDrop : undefined, children: staticFiles.length === 0 ? publicFolderExists ? /* @__PURE__ */ jsx36("div", { style: emptyState, children: /* @__PURE__ */ jsxs10("div", { style: label, children: [ "To add assets, place a file in the", " ", /* @__PURE__ */ jsx36("code", { style: inlineCodeSnippet, children: "public" }), " folder of your project or drag and drop a file here." ] }) }) : /* @__PURE__ */ jsx36("div", { style: emptyState, children: /* @__PURE__ */ jsxs10("div", { style: label, children: [ "To add assets, create a folder called", " ", /* @__PURE__ */ jsx36("code", { style: inlineCodeSnippet, children: "public" }), " in the root of your project and place a file in it." ] }) }) : /* @__PURE__ */ jsx36("div", { className: "__remotion-vertical-scrollbar", style: { ...list, backgroundColor: isDropDiv ? CLEAR_HOVER : BACKGROUND }, onDragEnter, onDragLeave, children: /* @__PURE__ */ jsx36(AssetFolderTree, { item: assetTree, level: 0, parentFolder: null, name: null, tabIndex, toggleFolder, dropLocation, setDropLocation }) }) }); }; // src/components/CompSelectorRef.tsx import { useCallback as useCallback20, useContext as useContext12, useImperativeHandle as useImperativeHandle4, useMemo as useMemo25, useState as useState17 } from "react"; import { Internals as Internals9 } from "remotion"; import { jsx as jsx37 } from "react/jsx-runtime"; var CompSelectorRef = ({ children }) => { const { compositions } = useContext12(Internals9.CompositionManager); const [foldersExpanded, setFoldersExpanded] = useState17(loadExpandedFolders("compositions")); const selectComposition = useSelectComposition(); const toggleFolder = useCallback20((folderName, parentName) => { setFoldersExpanded((p) => { const key = openFolderKey({ folderName, parentName }); const prev = p[key] ?? false; const foldersExpandedState = { ...p, [key]: !prev }; persistExpandedFolders("compositions", foldersExpandedState); return foldersExpandedState; }); }, []); useImperativeHandle4(Internals9.compositionSelectorRef, () => { return { expandComposition: (compName) => { const compositionToExpand = compositions.find((c) => c.id === compName); if (!compositionToExpand) { return; } const { folderName, parentFolderName } = compositionToExpand; if (folderName === null) { return; } setFoldersExpanded((previousState) => { const foldersExpandedState = { ...previousState }; const currentFolder = folderName; const currentParentName = parentFolderName; const key = openFolderKey({ folderName: currentFolder, parentName: currentParentName }); const splitted = key.split("/"); for (let i = 0;i < splitted.length - 1; i++) { const allExceptLast = i === 0 ? openFolderKey({ folderName: splitted.filter((s) => s !== "no-parent")[0], parentName: null }) : splitted.slice(0, i + 1).join("/"); foldersExpandedState[allExceptLast] = true; } persistExpandedFolders("compositions", foldersExpandedState); return foldersExpandedState; }); }, selectComposition: (compName) => { const comp = compositions.find((c) => c.id === compName); if (!comp) { throw new Error(`Composition ${compName} not found`); } selectComposition(comp, true); }, toggleFolder: (folderName, parentName) => { toggleFolder(folderName, parentName); } }; }, [compositions, selectComposition, toggleFolder]); const contextValue = useMemo25(() => { return { foldersExpanded, setFoldersExpanded, toggleFolder }; }, [foldersExpanded, setFoldersExpanded, toggleFolder]); return /* @__PURE__ */ jsx37(ExpandedFoldersContext.Provider, { value: contextValue, children }); }; // src/components/Tabs/index.tsx import { useCallback as useCallback21, useMemo as useMemo26, useState as useState18 } from "react"; import { jsx as jsx38 } from "react/jsx-runtime"; var tabsContainer = { display: "flex", flexDirection: "row" }; var Tabs = ({ children, style }) => { const definiteStyle = useMemo26(() => { return { ...tabsContainer, ...style }; }, [style]); return /* @__PURE__ */ jsx38("div", { style: definiteStyle, children }); }; var selectorButton = { border: "none", flex: 1, padding: 4, height: 40, paddingLeft: 16, display: "flex", flexDirection: "row", fontSize: 14, color: "inherit", alignItems: "center", cursor: "default" }; var Tab = ({ children, onClick, style, selected }) => { const [hovered, setHovered] = useState18(false); const { tabIndex } = useZIndex(); const onPointerEnter = useCallback21(() => { setHovered(true); }, []); const onPointerLeave = useCallback21(() => { setHovered(false); }, []); const definiteStyle = useMemo26(() => ({ ...selectorButton, backgroundColor: selected ? BACKGROUND : hovered ? CLEAR_HOVER : INPUT_BACKGROUND, color: selected ? "white" : LIGHT_TEXT, borderTop: selected ? "2px solid " + BLUE : "2px solid transparent", boxShadow: selected ? "none" : undefined, ...style }), [hovered, selected, style]); return /* @__PURE__ */ jsx38("div", { style: definiteStyle, role: "button", onClick, tabIndex, onPointerLeave, onPointerEnter, children }); }; // src/components/ExplorerPanel.tsx import { jsx as jsx39, jsxs as jsxs11 } from "react/jsx-runtime"; var container9 = { height: "100%", width: "100%", maxWidth: "100%", display: "flex", flexDirection: "column", flex: 1 }; var localStorageKey2 = "remotion.sidebarPanel"; var getSelectedPanel = () => { const panel = localStorage.getItem(localStorageKey2); if (panel === "assets") { return "assets"; } return "compositions"; }; var tabsContainer2 = { backgroundColor: BACKGROUND }; var persistSelectedOptionsSidebarPanel = (panel) => { localStorage.setItem(localStorageKey2, panel); }; var explorerSidebarTabs = createRef5(); var ExplorerPanel = ({ readOnlyStudio }) => { const [panel, setPanel] = useState19(() => getSelectedPanel()); const onCompositionsSelected = useCallback22(() => { setPanel("compositions"); persistSelectedOptionsSidebarPanel("compositions"); }, []); const onAssetsSelected = useCallback22(() => { setPanel("assets"); persistSelectedOptionsSidebarPanel("assets"); }, []); useImperativeHandle5(explorerSidebarTabs, () => { return { selectAssetsPanel: () => { setPanel("assets"); persistSelectedOptionsSidebarPanel("assets"); }, selectCompositionPanel: () => { setPanel("compositions"); persistSelectedOptionsSidebarPanel("compositions"); } }; }, []); return /* @__PURE__ */ jsx39(CompSelectorRef, { children: /* @__PURE__ */ jsxs11("div", { style: container9, className: "css-reset", children: [ /* @__PURE__ */ jsx39("div", { style: tabsContainer2, children: /* @__PURE__ */ jsxs11(Tabs, { children: [ /* @__PURE__ */ jsx39(Tab, { selected: panel === "compositions", onClick: onCompositionsSelected, children: "Compositions" }), /* @__PURE__ */ jsx39(Tab, { selected: panel === "assets", onClick: onAssetsSelected, children: "Assets" }) ] }) }), panel === "compositions" ? /* @__PURE__ */ jsx39(CompositionSelector, {}) : /* @__PURE__ */ jsx39(AssetSelector, { readOnlyStudio }) ] }) }); }; // src/components/InitialCompositionLoader.tsx var useSelectAsset = () => { const { setCanvasContent } = useContext13(Internals10.CompositionSetters); const { setAssetFoldersExpanded } = useContext13(FolderContext); return (asset) => { setCanvasContent({ type: "asset", asset }); explorerSidebarTabs.current?.selectAssetsPanel(); setAssetFoldersExpanded((ex) => { const split = asset.split("/"); const keysToExpand = split.map((_, i) => { return split.slice(0, i).join("/"); }); const newState = { ...ex }; for (const key of keysToExpand) { newState[key] = true; } return newState; }); }; }; var useSelectComposition = () => { const { setCompositionFoldersExpanded } = useContext13(FolderContext); const { setCanvasContent } = useContext13(Internals10.CompositionSetters); const isMobileLayout = useMobileLayout(); const { setSidebarCollapsedState } = useContext13(SidebarContext); return useCallback23((c, push) => { if (push) { pushUrl(`/${c.id}`); } explorerSidebarTabs.current?.selectCompositionPanel(); setCanvasContent({ type: "composition", compositionId: c.id }); const { folderName, parentFolderName } = c; if (folderName !== null) { setCompositionFoldersExpanded((ex) => { const keysToExpand = getKeysToExpand(folderName, parentFolderName); const newState = { ...ex }; for (const key of keysToExpand) { newState[key] = true; } return newState; }); if (isMobileLayout) { setSidebarCollapsedState({ left: "collapsed", right: "collapsed" }); } } }, [ isMobileLayout, setCanvasContent, setCompositionFoldersExpanded, setSidebarCollapsedState ]); }; var InitialCompositionLoader = () => { const { compositions, canvasContent } = useContext13(Internals10.CompositionManager); const { setCanvasContent } = useContext13(Internals10.CompositionSetters); const selectComposition = useSelectComposition(); const selectAsset = useSelectAsset(); useEffect14(() => { if (canvasContent) { return; } const canvasContentFromUrl = deriveCanvasContentFromUrl(); if (canvasContentFromUrl && canvasContentFromUrl.type === "composition") { const exists = compositions.find((c) => c.id === canvasContentFromUrl.compositionId); if (exists) { selectComposition(exists, false); } return; } if (canvasContentFromUrl && canvasContentFromUrl.type === "asset") { selectAsset(canvasContentFromUrl.asset); return; } if (canvasContentFromUrl && canvasContentFromUrl.type === "output") { setCanvasContent(canvasContentFromUrl); return; } if (compositions.length > 0) { selectComposition(compositions[0], true); } }, [ compositions, canvasContent, selectComposition, setCanvasContent, selectAsset ]); useEffect14(() => { const onchange = () => { const newCanvas = deriveCanvasContentFromUrl(); if (newCanvas && newCanvas.type === "composition") { const newComp = getRoute().substring(1); const exists = compositions.find((c) => c.id === newComp); if (exists) { selectComposition(exists, false); } return; } if (newCanvas && newCanvas.type === "asset") { const staticFiles = getStaticFiles(); const exists = staticFiles.find((file) => { return file.name === newCanvas.asset; }); if (exists) { setCanvasContent(newCanvas); } return; } setCanvasContent(newCanvas); }; window.addEventListener("popstate", onchange); return () => window.removeEventListener("popstate", onchange); }, [compositions, selectComposition, setCanvasContent]); return null; }; // src/components/MenuToolbar.tsx import { useCallback as useCallback93, useMemo as useMemo98, useState as useState64 } from "react"; // src/helpers/use-menu-structure.tsx import { useContext as useContext20, useMemo as useMemo37 } from "react"; import { Internals as Internals15 } from "remotion"; import { NoReactInternals as NoReactInternals5 } from "remotion/no-react"; // src/api/restart-studio.ts import { getRemotionEnvironment } from "remotion"; var restartStudio = () => { if (!getRemotionEnvironment().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/components/AskAiModal.tsx import { createRef as createRef6, useCallback as useCallback25, useEffect as useEffect15, useImperativeHandle as useImperativeHandle6, useRef as useRef13, useState as useState20 } from "react"; import { AbsoluteFill } from "remotion"; // src/components/ModalContainer.tsx import { jsx as jsx40 } from "react/jsx-runtime"; var padding = 20; var getMaxModalWidth = (width) => { return `min(calc(100vw - ${padding * 2}px), calc(${width}px - ${padding * 2}px))`; }; var getMaxModalHeight = (height) => { return `min(calc(100vh - ${padding * 2}px), calc(${height}px - ${padding * 2}px))`; }; var backgroundOverlay = { backgroundColor: "rgba(255, 255, 255, 0.2)", backdropFilter: `blur(1px)`, position: "fixed", height: "100%", width: "100%", display: "flex", padding }; var panel = { backgroundColor: BACKGROUND, boxShadow: "0 0 4px black", color: "white", margin: "auto" }; var ModalContainer = ({ children, onEscape, onOutsideClick, noZIndex }) => { return /* @__PURE__ */ jsx40("div", { className: "css-reset", style: backgroundOverlay, role: "dialog", "aria-modal": "true", children: /* @__PURE__ */ jsx40(HigherZIndex, { disabled: noZIndex, onOutsideClick, onEscape, children: /* @__PURE__ */ jsx40("div", { style: panel, children }) }) }); }; // src/components/ModalHeader.tsx import { useCallback as useCallback24, useContext as useContext14 } from "react"; // src/components/NewComposition/CancelButton.tsx import { jsx as jsx41 } from "react/jsx-runtime"; var style = { appearance: "none", border: "none", backgroundColor: "transparent", color: "white", cursor: "pointer", display: "inline-flex", justifyContent: "center", alignItems: "center" }; var CancelButton = ({ onPress, ...props }) => { const { tabIndex } = useZIndex(); return /* @__PURE__ */ jsx41("button", { tabIndex, style, type: "button", onClick: onPress, children: /* @__PURE__ */ jsx41("svg", { viewBox: "0 0 320 512", ...props, children: /* @__PURE__ */ jsx41("path", { fill: "currentColor", d: "M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z" }) }) }); }; // src/components/ModalHeader.tsx import { jsx as jsx42, jsxs as jsxs12 } from "react/jsx-runtime"; var container10 = { display: "flex", flexDirection: "row", alignItems: "center", padding: "12px 16px", width: "100%", borderBottom: "1px solid black" }; var titleStyle = { fontSize: 14, color: "white" }; var icon = { height: 20, width: 20 }; var ModalHeader = ({ title: title2, onClose }) => { const { setSelectedModal } = useContext14(ModalsContext); const onPress = useCallback24(() => { setSelectedModal(null); }, [setSelectedModal]); return /* @__PURE__ */ jsxs12("div", { style: container10, children: [ /* @__PURE__ */ jsx42("div", { style: titleStyle, children: title2 }), /* @__PURE__ */ jsx42(Flex, {}), /* @__PURE__ */ jsx42(CancelButton, { style: icon, onPress: onClose ?? onPress }) ] }); }; // src/components/AskAiModal.tsx import { jsx as jsx43, jsxs as jsxs13 } from "react/jsx-runtime"; var container11 = { height: "calc(100vh - 100px)", width: "calc(100vw - 160px)", maxWidth: 800, maxHeight: 900, display: "block" }; var askAiModalRef = createRef6(); var AskAiModal = () => { const [state, setState] = useState20("never-opened"); const iframe = useRef13(null); useImperativeHandle6(askAiModalRef, () => ({ toggle: () => { setState((s) => { if (s === "visible") { iframe.current?.blur(); iframe.current?.contentWindow?.blur(); } return s === "visible" ? "hidden" : "visible"; }); } }), []); useEffect15(() => { const onMessage = (event) => { try { const json = typeof event.data === "string" ? JSON.parse(event.data) : event.data; if (json.type === "cmd-i") { askAiModalRef.current?.toggle(); } } catch {} }; window.addEventListener("message", onMessage); return () => { window.removeEventListener("message", onMessage); }; }, []); const onQuit = useCallback25(() => { setState("hidden"); }, [setState]); useEffect15(() => { if (!iframe.current) { return; } if (state === "visible") { iframe.current.contentWindow?.postMessage({ type: "focus" }, "*"); } }, [state]); if (state === "never-opened") { return null; } return /* @__PURE__ */ jsx43(AbsoluteFill, { style: { display: state === "visible" ? "block" : "none" }, children: /* @__PURE__ */ jsxs13(ModalContainer, { noZIndex: state === "hidden", onOutsideClick: onQuit, onEscape: onQuit, children: [ /* @__PURE__ */ jsx43(ModalHeader, { title: "Ask AI", onClose: onQuit }), /* @__PURE__ */ jsx43("iframe", { ref: iframe, frameBorder: 0, style: container11, src: "https://www.remotion.dev/ai-embed", allow: "clipboard-read; clipboard-write" }) ] }) }); }; // src/components/SizeSelector.tsx import { useContext as useContext18, useMemo as useMemo33 } from "react"; import { Internals as Internals12 } from "remotion"; // src/icons/Checkmark.tsx import { jsx as jsx44 } from "react/jsx-runtime"; var style2 = { width: 14, height: 14 }; var Checkmark = () => /* @__PURE__ */ jsx44("svg", { focusable: "false", role: "img", viewBox: "0 0 512 512", style: style2, children: /* @__PURE__ */ jsx44("path", { fill: "currentColor", d: "M435.848 83.466L172.804 346.51l-96.652-96.652c-4.686-4.686-12.284-4.686-16.971 0l-28.284 28.284c-4.686 4.686-4.686 12.284 0 16.971l133.421 133.421c4.686 4.686 12.284 4.686 16.971 0l299.813-299.813c4.686-4.686 4.686-12.284 0-16.971l-28.284-28.284c-4.686-4.686-12.284-4.686-16.97 0z" }) }); // src/components/ControlButton.tsx import { useMemo as useMemo27 } from "react"; import { jsx as jsx45 } from "react/jsx-runtime"; var CONTROL_BUTTON_PADDING = 6; var ControlButton = (props) => { const style3 = useMemo27(() => { return { opacity: props.disabled ? 0.5 : 1, display: "inline-flex", background: "none", border: "none", padding: CONTROL_BUTTON_PADDING }; }, [props.disabled]); const { tabIndex } = useZIndex(); return /* @__PURE__ */ jsx45("button", { type: "button", tabIndex, ...props, style: style3 }); }; // src/components/NewComposition/ComboBox.tsx import { PlayerInternals as PlayerInternals5 } from "@remotion/player"; import { useCallback as useCallback26, useEffect as useEffect16, useMemo as useMemo28, useRef as useRef14, useState as useState21 } from "react"; import ReactDOM4 from "react-dom"; import { jsx as jsx46, jsxs as jsxs14, Fragment as Fragment6 } from "react/jsx-runtime"; var container12 = { padding: "8px 10px", display: "inline-block", backgroundColor: INPUT_BACKGROUND, borderWidth: 1, borderStyle: "solid", maxWidth: "100%" }; var label2 = { flex: 1, overflow: "hidden", textOverflow: "ellipsis", fontSize: 14, textAlign: "left" }; var Combobox = ({ values, selectedId, style: customStyle, title: title2 }) => { const [hovered, setIsHovered] = useState21(false); const [opened, setOpened] = useState21(false); const ref = useRef14(null); const { tabIndex, currentZIndex } = useZIndex(); const size = PlayerInternals5.useElementSize(ref, { triggerOnWindowResize: true, shouldApplyCssTransforms: true }); const refresh = size?.refresh; const onHide = useCallback26(() => { setOpened(false); }, []); useEffect16(() => { const { current } = ref; if (!current) { return; } const onMouseEnter = () => setIsHovered(true); const onMouseLeave = () => setIsHovered(false); const onPointerDown = () => { return setOpened((o) => { if (!o) { refresh?.(); } return !o; }); }; const onClick = (e) => { e.stopPropagation(); const isKeyboardInitiated = e.detail === 0; if (!isKeyboardInitiated) { return; } return setOpened((o) => { if (!o) { refresh?.(); window.addEventListener("pointerup", (evt) => { if (!isMenuItem(evt.target)) { setOpened(false); } }, { once: true }); } return !o; }); }; current.addEventListener("mouseenter", onMouseEnter); current.addEventListener("mouseleave", onMouseLeave); current.addEventListener("pointerdown", onPointerDown); current.addEventListener("click", onClick); return () => { current.removeEventListener("mouseenter", onMouseEnter); current.removeEventListener("mouseleave", onMouseLeave); current.removeEventListener("pointerdown", onPointerDown); current.removeEventListener("click", onClick); }; }, [refresh]); const spaceToBottom = useMemo28(() => { const margin2 = 10; if (size && opened) { return size.windowSize.height - (size.top + size.height) - margin2; } return 0; }, [opened, size]); const spaceToTop = useMemo28(() => { const margin2 = 10; if (size && opened) { return size.top - margin2; } return 0; }, [opened, size]); const derivedMaxHeight = useMemo28(() => { return spaceToTop > spaceToBottom ? spaceToTop : spaceToBottom; }, [spaceToBottom, spaceToTop]); const isMobileLayout = useMobileLayout(); const portalStyle = useMemo28(() => { if (!opened || !size) { return null; } const spaceToRight = size.windowSize.width - size.left; const spaceToLeft = size.left + size.width; const minSpaceRequired = isMobileLayout ? MAX_MOBILE_MENU_WIDTH : MAX_MENU_WIDTH; const verticalLayout = spaceToTop > spaceToBottom ? "bottom" : "top"; const canOpenOnLeft = spaceToLeft >= minSpaceRequired; const canOpenOnRight = spaceToRight >= minSpaceRequired; const horizontalLayout = canOpenOnRight ? "left" : "right"; return { ...verticalLayout === "top" ? { ...menuContainerTowardsBottom, top: size.top + size.height } : { ...menuContainerTowardsTop, bottom: size.windowSize.height - size.top }, ...horizontalLayout === "left" ? { left: size.left } : canOpenOnLeft ? { right: size.windowSize.width - size.left - size.width } : { left: 0 } }; }, [isMobileLayout, opened, size, spaceToBottom, spaceToTop]); const selected = values.find((v) => v.id === selectedId); const style3 = useMemo28(() => { return { ...container12, ...customStyle ?? {}, userSelect: "none", WebkitUserSelect: "none", color: "white", display: "inline-flex", flexDirection: "row", alignItems: "center", borderColor: opened ? SELECTED_BACKGROUND : hovered ? INPUT_BORDER_COLOR_HOVERED : INPUT_BORDER_COLOR_UNHOVERED }; }, [customStyle, hovered, opened]); return /* @__PURE__ */ jsxs14(Fragment6, { children: [ /* @__PURE__ */ jsxs14("button", { ref, title: title2, tabIndex, type: "button", style: style3, className: MENU_INITIATOR_CLASSNAME, children: [ selected ? /* @__PURE__ */ jsx46("div", { title: typeof selected.label === "string" ? selected.label : undefined, style: label2, children: selected?.label }) : null, /* @__PURE__ */ jsx46(Spacing, { x: 1 }), " ", /* @__PURE__ */ jsx46(CaretDown, {}) ] }), portalStyle ? ReactDOM4.createPortal(/* @__PURE__ */ jsx46("div", { style: fullScreenOverlay, children: /* @__PURE__ */ jsx46("div", { style: outerPortal, className: "css-reset", children: /* @__PURE__ */ jsx46(HigherZIndex, { onOutsideClick: onHide, onEscape: onHide, children: /* @__PURE__ */ jsx46("div", { style: portalStyle, children: /* @__PURE__ */ jsx46(MenuContent, { onNextMenu: noop, onPreviousMenu: noop, values, onHide, leaveLeftSpace: true, preselectIndex: values.findIndex((v) => selected && v.id === selected.id), topItemCanBeUnselected: false, fixedHeight: derivedMaxHeight }) }) }) }) }), getPortal(currentZIndex)) : null ] }); }; // src/components/Preview.tsx import { PlayerInternals as PlayerInternals6 } from "@remotion/player"; import { useContext as useContext17, useEffect as useEffect22, useMemo as useMemo32, useRef as useRef17 } from "react"; import { Internals as Internals11 } from "remotion"; // src/helpers/checkerboard-background.ts var getCheckerboardBackgroundSize = (size) => `${size}px ${size}px`; var getCheckerboardBackgroundPos = (size) => `0 0, ${size / 2}px 0, ${size / 2}px -${size / 2}px, 0px ${size / 2}px`; var checkerboardBackgroundColor = (checkerboard) => { if (checkerboard) { return "white"; } return "black"; }; var checkerboardBackgroundImage = (checkerboard) => { if (checkerboard) { return ` linear-gradient( 45deg, rgba(0, 0, 0, 0.1) 25%, transparent 25% ), linear-gradient(135deg, rgba(0, 0, 0, 0.1) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgba(0, 0, 0, 0.1) 75%), linear-gradient(135deg, transparent 75%, rgba(0, 0, 0, 0.1) 75%) `; } return; }; // src/state/checkerboard.ts import { createContext as createContext10 } from "react"; var persistCheckerboardOption = (option) => { localStorage.setItem("option", String(option)); }; var loadCheckerboardOption = () => { const item = localStorage.getItem("option"); return item !== "false"; }; var CheckerboardContext = createContext10({ checkerboard: loadCheckerboardOption(), setCheckerboard: () => { return; } }); // src/components/RenderPreview.tsx import { useContext as useContext15, useEffect as useEffect21, useState as useState26 } from "react"; // src/helpers/get-asset-metadata.ts import { getVideoMetadata } from "@remotion/media-utils"; import { staticFile } from "remotion"; var remotion_outputsBase = window.remotion_staticBase.replace("static", "outputs"); var getSrcFromCanvasContent = (canvasContent) => { if (canvasContent.type === "asset") { return staticFile(canvasContent.asset); } return remotion_outputsBase + canvasContent.path; }; var getAssetMetadata = async (canvasContent, addTime) => { if (canvasContent.type === "output-blob") { return { type: "found", size: canvasContent.sizeInBytes, dimensions: { width: canvasContent.width, height: canvasContent.height }, fetchedAt: Date.now() }; } if (canvasContent.type === "composition") { throw new Error("cannot get dimensions for composition"); } const src = getSrcFromCanvasContent(canvasContent); const file = await fetch(src, { method: "HEAD" }); if (file.status === 404) { return { type: "not-found" }; } if (file.status !== 200) { throw new Error(`Expected status code 200 or 404 for file, got ${file.status}`); } const size = file.headers.get("content-length"); if (!size) { throw new Error("Unexpected error: content-length is null"); } const fetchedAt = Date.now(); const srcWithTime = addTime ? `${src}?date=${fetchedAt}` : src; const fileType = getPreviewFileType(src); if (fileType === "video") { const resolution = await getVideoMetadata(srcWithTime); return { type: "found", size: Number(size), dimensions: { width: resolution.width, height: resolution.height }, fetchedAt }; } if (fileType === "image") { const resolution = await new Promise((resolve, reject) => { const img = new Image; img.onload = () => { resolve({ type: "found", size: Number(size), dimensions: { width: img.width, height: img.height }, fetchedAt }); }; img.onerror = () => { reject(new Error("Failed to load image")); }; img.src = srcWithTime; }); return resolution; } return { type: "found", dimensions: "none", size: Number(size), fetchedAt }; }; // src/components/FilePreview.tsx import { formatBytes } from "@remotion/studio-shared"; // src/components/JSONViewer.tsx import { useEffect as useEffect19, useState as useState24 } from "react"; // src/components/NewComposition/RemTextarea.tsx import { forwardRef as forwardRef2, useEffect as useEffect18, useImperativeHandle as useImperativeHandle8, useMemo as useMemo30, useRef as useRef16, useState as useState23 } from "react"; // src/components/NewComposition/RemInput.tsx import { forwardRef, useEffect as useEffect17, useImperativeHandle as useImperativeHandle7, useMemo as useMemo29, useRef as useRef15, useState as useState22 } from "react"; import { jsx as jsx47 } from "react/jsx-runtime"; var INPUT_HORIZONTAL_PADDING = 8; var aligner = { marginRight: -INPUT_HORIZONTAL_PADDING }; var RightAlignInput = ({ children }) => { return /* @__PURE__ */ jsx47("div", { style: aligner, children }); }; var inputBaseStyle = { padding: `${INPUT_HORIZONTAL_PADDING}px 10px`, color: "white", borderStyle: "solid", borderWidth: 1, fontSize: 14 }; var getInputBorderColor = ({ status, isFocused, isHovered }) => status === "warning" ? WARNING_COLOR : status === "error" ? FAIL_COLOR : isFocused ? SELECTED_BACKGROUND : isHovered ? INPUT_BORDER_COLOR_HOVERED : INPUT_BORDER_COLOR_UNHOVERED; var RemInputForwardRef = ({ status, rightAlign, ...props }, ref) => { const [isFocused, setIsFocused] = useState22(false); const [isHovered, setIsHovered] = useState22(false); const inputRef = useRef15(null); const { tabIndex } = useZIndex(); const style3 = useMemo29(() => { return { backgroundColor: INPUT_BACKGROUND, ...inputBaseStyle, width: "100%", borderColor: getInputBorderColor({ isFocused, isHovered, status }), textAlign: rightAlign ? "right" : "left", ...props.style ?? {} }; }, [isFocused, isHovered, rightAlign, props.style, status]); useImperativeHandle7(ref, () => { return inputRef.current; }, []); useEffect17(() => { if (!inputRef.current) { return; } const { current } = inputRef; const onFocus = () => setIsFocused(true); const onBlur = () => setIsFocused(false); const onMouseEnter = () => setIsHovered(true); const onMouseLeave = () => setIsHovered(false); current.addEventListener("focus", onFocus); current.addEventListener("blur", onBlur); current.addEventListener("mouseenter", onMouseEnter); current.addEventListener("mouseleave", onMouseLeave); return () => { current.removeEventListener("focus", onFocus); current.removeEventListener("blur", onBlur); current.removeEventListener("mouseenter", onMouseEnter); current.removeEventListener("mouseleave", onMouseLeave); }; }, [inputRef]); return /* @__PURE__ */ jsx47("input", { ref: inputRef, tabIndex, ...props, style: style3 }); }; var RemotionInput = forwardRef(RemInputForwardRef); // src/components/NewComposition/RemTextarea.tsx import { jsx as jsx48 } from "react/jsx-runtime"; var inputBaseStyle2 = { padding: `${INPUT_HORIZONTAL_PADDING}px 10px`, color: "white", borderStyle: "solid", borderWidth: 1, fontSize: 14, resize: "none", overflowX: "hidden" }; var RemTextareaFRFunction = ({ status, ...props }, ref) => { const [isFocused, setIsFocused] = useState23(false); const [isHovered, setIsHovered] = useState23(false); const inputRef = useRef16(null); const { tabIndex } = useZIndex(); useImperativeHandle8(ref, () => { return inputRef.current; }, []); const style3 = useMemo30(() => { return { backgroundColor: INPUT_BACKGROUND, ...inputBaseStyle2, width: "100%", borderColor: getInputBorderColor({ isFocused, isHovered, status }), ...props.style ?? {} }; }, [isFocused, isHovered, props.style, status]); useEffect18(() => { if (!inputRef.current) { return; } const { current } = inputRef; const onFocus = () => setIsFocused(true); const onBlur = () => setIsFocused(false); const onMouseEnter = () => setIsHovered(true); const onMouseLeave = () => setIsHovered(false); const onKeyDown = (e) => { if (!inputRef.current) { return; } if (inputRef.current !== document.activeElement) { return; } if (e.code === "Tab") { e.preventDefault(); document.execCommand("insertText", false, " ".repeat(2)); } if (e.code === "Enter") { e.preventDefault(); const { selectionStart, selectionEnd, value } = inputRef.current; if (selectionStart !== selectionEnd) { return; } let prevNewline = selectionStart; for (let i = selectionStart - 1;i >= 0; i--) { if (value[i] === ` `) { break; } prevNewline = i; } const currentLine = value.substring(prevNewline, selectionStart); const trimmed = currentLine.trim(); const difference = currentLine.length - trimmed.length; document.execCommand("insertText", false, ` ` + " ".repeat(difference)); } }; current.addEventListener("focus", onFocus); current.addEventListener("blur", onBlur); current.addEventListener("mouseenter", onMouseEnter); current.addEventListener("mouseleave", onMouseLeave); current.addEventListener("keydown", onKeyDown); return () => { current.removeEventListener("focus", onFocus); current.removeEventListener("blur", onBlur); current.removeEventListener("mouseenter", onMouseEnter); current.removeEventListener("mouseleave", onMouseLeave); current.removeEventListener("keydown", onKeyDown); }; }, [inputRef]); return /* @__PURE__ */ jsx48("textarea", { ref: inputRef, tabIndex, ...props, className: VERTICAL_SCROLLBAR_CLASSNAME, style: style3 }); }; var RemTextarea = forwardRef2(RemTextareaFRFunction); // src/components/JSONViewer.tsx import { jsx as jsx49 } from "react/jsx-runtime"; var jsonStyle = { marginTop: 14, marginBottom: 14, fontFamily: "monospace", flex: 1 }; var JSONViewer = ({ src }) => { const [json, setJson] = useState24(null); useEffect19(() => { fetch(src).then((res) => res.json()).then((jsonRes) => { setJson(JSON.stringify(jsonRes, null, 2)); }); }, [src]); return /* @__PURE__ */ jsx49(RemTextarea, { value: json ?? undefined, status: "ok", onChange: () => { return null; }, style: jsonStyle }); }; // src/components/TextViewer.tsx import { useEffect as useEffect20, useState as useState25 } from "react"; import { jsxs as jsxs15 } from "react/jsx-runtime"; var textStyle = { margin: 14, fontFamily: "monospace", flex: 1, color: "white", whiteSpace: "pre-wrap" }; var TextViewer = ({ src }) => { const [txt, setTxt] = useState25(""); useEffect20(() => { fetch(src).then(async (res) => { if (!res.ok || !res.body) { return; } const text = await res.text(); setTxt(text); }); }, [src]); return /* @__PURE__ */ jsxs15("div", { style: textStyle, children: [ txt, " " ] }); }; // src/components/FilePreview.tsx import { jsx as jsx50, jsxs as jsxs16, Fragment as Fragment7 } from "react/jsx-runtime"; var msgStyle = { fontSize: 13, color: "white", fontFamily: "sans-serif", display: "flex", justifyContent: "center" }; var FilePreview = ({ fileType, src, currentAsset, assetMetadata }) => { if (!assetMetadata) { throw new Error("expected to have assetMetadata"); } if (assetMetadata.type === "not-found") { throw new Error('expected to have assetMetadata, got "not-found"'); } if (fileType === "audio") { return /* @__PURE__ */ jsx50("audio", { src, controls: true }); } if (fileType === "video") { return /* @__PURE__ */ jsx50("video", { src, controls: true }); } if (fileType === "image") { return /* @__PURE__ */ jsx50("img", { src }); } if (fileType === "json") { return /* @__PURE__ */ jsx50(JSONViewer, { src }); } if (fileType === "txt") { return /* @__PURE__ */ jsx50(TextViewer, { src }); } return /* @__PURE__ */ jsxs16(Fragment7, { children: [ /* @__PURE__ */ jsx50("div", { style: msgStyle, children: currentAsset }), /* @__PURE__ */ jsx50(Spacing, { y: 0.5 }), /* @__PURE__ */ jsxs16("div", { style: msgStyle, children: [ "Size: ", formatBytes(assetMetadata.size), " " ] }) ] }); }; // src/components/RenderPreview.tsx import { jsx as jsx51, jsxs as jsxs17 } from "react/jsx-runtime"; var msgStyle2 = { fontSize: 13, color: "white", fontFamily: "sans-serif", display: "flex", justifyContent: "center" }; var errMsgStyle = { ...msgStyle2, color: LIGHT_TEXT }; var RenderPreview = ({ path, assetMetadata, getBlob }) => { const fileType = getPreviewFileType(path); const connectionStatus = useContext15(StudioServerConnectionCtx).previewServerState.type; const [blobUrl, setBlobUrl] = useState26(null); const [blobError, setBlobError] = useState26(null); useEffect21(() => { if (!getBlob) { setBlobUrl(null); setBlobError(null); return; } let cancelled = false; let blobUrlToRevoke = null; setBlobError(null); getBlob().then((blob) => { const url = URL.createObjectURL(blob); if (cancelled) { URL.revokeObjectURL(url); return; } blobUrlToRevoke = url; setBlobUrl(url); }).catch((err) => { if (cancelled) { return; } setBlobError(err instanceof Error ? err : new Error(String(err))); }); return () => { cancelled = true; if (blobUrlToRevoke) { URL.revokeObjectURL(blobUrlToRevoke); } }; }, [getBlob]); const src = blobUrl ?? remotion_outputsBase + path; if (connectionStatus === "disconnected") { return /* @__PURE__ */ jsx51("div", { style: errMsgStyle, children: "Studio server disconnected" }); } if (getBlob && blobError) { return /* @__PURE__ */ jsxs17("div", { style: errMsgStyle, children: [ "Failed to load preview: ", blobError.message ] }); } if (getBlob && !blobUrl) { return /* @__PURE__ */ jsx51("div", { style: msgStyle2, children: "Loading preview..." }); } return /* @__PURE__ */ jsx51(FilePreview, { assetMetadata, currentAsset: path, fileType, src }); }; // src/components/Spinner.tsx import { useMemo as useMemo31 } from "react"; import { jsx as jsx52, jsxs as jsxs18, Fragment as Fragment8 } from "react/jsx-runtime"; var viewBox = 100; var lines = 8; var className = "__remotion_spinner_line"; var remotionSpinnerAnimation = "__remotion_spinner_animation"; var translated = "M 44 0 L 50 0 a 6 6 0 0 1 6 6 L 56 26 a 6 6 0 0 1 -6 6 L 50 32 a 6 6 0 0 1 -6 -6 L 44 6 a 6 6 0 0 1 6 -6 Z"; var Spinner = ({ size, duration }) => { const style3 = useMemo31(() => { return { width: size, height: size }; }, [size]); return /* @__PURE__ */ jsxs18(Fragment8, { children: [ /* @__PURE__ */ jsx52("style", { type: "text/css", children: ` @keyframes ${remotionSpinnerAnimation} { 0% { opacity: 1; } 100% { opacity: 0.15; } } .${className} { animation: ${remotionSpinnerAnimation} ${duration}s linear infinite; } ` }), /* @__PURE__ */ jsx52("svg", { style: style3, viewBox: `0 0 ${viewBox} ${viewBox}`, children: new Array(lines).fill(true).map((_, index) => { return /* @__PURE__ */ jsx52("path", { className, style: { rotate: `${index * Math.PI * 2 / lines}rad`, transformOrigin: "center center", animationDelay: `${index * (duration / lines) - duration}s` }, d: translated, fill: LIGHT_TEXT }, index); }) }) ] }); }; // src/components/StaticFilePreview.tsx import { useContext as useContext16 } from "react"; import { staticFile as staticFile2 } from "remotion"; import { jsx as jsx53, jsxs as jsxs19 } from "react/jsx-runtime"; var msgStyle3 = { fontSize: 13, color: "white", fontFamily: "sans-serif", display: "flex", justifyContent: "center" }; var errMsgStyle2 = { ...msgStyle3, color: LIGHT_TEXT }; var StaticFilePreview = ({ currentAsset, assetMetadata }) => { const fileType = getPreviewFileType(currentAsset); const staticFileSrc = staticFile2(currentAsset); const staticFiles = getStaticFiles(); const connectionStatus = useContext16(StudioServerConnectionCtx).previewServerState.type; const exists = staticFiles.find((file) => file.name === currentAsset); if (connectionStatus === "disconnected") { return /* @__PURE__ */ jsx53("div", { style: errMsgStyle2, children: "Studio server disconnected" }); } if (!exists) { return /* @__PURE__ */ jsxs19("div", { style: errMsgStyle2, children: [ currentAsset, " does not exist in your public folder." ] }); } if (!currentAsset) { return null; } return /* @__PURE__ */ jsx53(FilePreview, { currentAsset, fileType, src: `${staticFileSrc}?date=${assetMetadata && assetMetadata.type === "found" ? assetMetadata.fetchedAt : 0}`, assetMetadata }); }; // src/components/Preview.tsx import { jsx as jsx54 } from "react/jsx-runtime"; var centeredContainer = { display: "flex", flex: 1, justifyContent: "center", alignItems: "center" }; var label3 = { fontFamily: "sans-serif", fontSize: 14, color: LIGHT_TEXT }; var getPreviewFileType = (fileName) => { if (!fileName) { return "other"; } const audioExtensions = ["mp3", "wav", "ogg", "aac"]; const videoExtensions = ["mp4", "avi", "mkv", "mov", "webm"]; const imageExtensions = ["jpg", "jpeg", "png", "gif", "bmp"]; const fileExtension = fileName.split(".").pop()?.toLowerCase(); if (fileExtension === undefined) { throw new Error("File extension is undefined"); } if (audioExtensions.includes(fileExtension)) { return "audio"; } if (videoExtensions.includes(fileExtension)) { return "video"; } if (imageExtensions.includes(fileExtension)) { return "image"; } if (fileExtension === "json") { return "json"; } if (fileExtension === "txt") { return "txt"; } return "other"; }; var checkerboardSize = 49; var containerStyle = (options) => { return { transform: `scale(${options.scale})`, marginLeft: options.xCorrection, marginTop: options.yCorrection, width: options.width, height: options.height, display: "flex", position: "absolute", backgroundColor: checkerboardBackgroundColor(options.checkerboard), backgroundImage: checkerboardBackgroundImage(options.checkerboard), backgroundSize: getCheckerboardBackgroundSize(checkerboardSize), backgroundPosition: getCheckerboardBackgroundPos(checkerboardSize) }; }; var VideoPreview = ({ canvasSize, contentDimensions, canvasContent, assetMetadata }) => { if (assetMetadata && assetMetadata.type === "not-found") { return /* @__PURE__ */ jsx54("div", { style: centeredContainer, children: /* @__PURE__ */ jsx54("div", { style: label3, children: "File does not exist" }) }); } if (contentDimensions === null) { return /* @__PURE__ */ jsx54("div", { style: centeredContainer, children: /* @__PURE__ */ jsx54(Spinner, { duration: 0.5, size: 24 }) }); } return /* @__PURE__ */ jsx54(CompWhenItHasDimensions, { contentDimensions, canvasSize, canvasContent, assetMetadata }); }; var CompWhenItHasDimensions = ({ contentDimensions, canvasSize, canvasContent, assetMetadata }) => { const { size: previewSize } = useContext17(Internals11.PreviewSizeContext); const { centerX, centerY, yCorrection, xCorrection, scale } = useMemo32(() => { if (contentDimensions === "none") { return { centerX: 0, centerY: 0, yCorrection: 0, xCorrection: 0, scale: 1 }; } return PlayerInternals6.calculateCanvasTransformation({ canvasSize, compositionHeight: contentDimensions.height, compositionWidth: contentDimensions.width, previewSize: previewSize.size }); }, [canvasSize, contentDimensions, previewSize.size]); const outer = useMemo32(() => { return { width: contentDimensions === "none" ? "100%" : contentDimensions.width * scale, height: contentDimensions === "none" ? "100%" : contentDimensions.height * scale, display: "flex", flexDirection: "column", position: "absolute", left: centerX - previewSize.translation.x, top: centerY - previewSize.translation.y, overflow: "hidden", justifyContent: canvasContent.type === "asset" ? "center" : "flex-start", alignItems: canvasContent.type === "asset" && getPreviewFileType(canvasContent.asset) === "audio" ? "center" : "normal" }; }, [ contentDimensions, scale, centerX, previewSize.translation.x, previewSize.translation.y, centerY, canvasContent ]); if (canvasContent.type === "asset") { return /* @__PURE__ */ jsx54("div", { style: outer, children: /* @__PURE__ */ jsx54(StaticFilePreview, { assetMetadata, currentAsset: canvasContent.asset }) }); } if (canvasContent.type === "output") { return /* @__PURE__ */ jsx54("div", { style: outer, children: /* @__PURE__ */ jsx54(RenderPreview, { path: canvasContent.path, assetMetadata }) }); } if (canvasContent.type === "output-blob") { return /* @__PURE__ */ jsx54("div", { style: outer, children: /* @__PURE__ */ jsx54(RenderPreview, { path: canvasContent.displayName, assetMetadata, getBlob: canvasContent.getBlob }) }); } return /* @__PURE__ */ jsx54("div", { style: outer, children: /* @__PURE__ */ jsx54(PortalContainer, { contentDimensions, scale, xCorrection, yCorrection }) }); }; var PortalContainer = ({ scale, xCorrection, yCorrection, contentDimensions }) => { const { checkerboard } = useContext17(CheckerboardContext); const style3 = useMemo32(() => { return containerStyle({ checkerboard, scale, xCorrection, yCorrection, width: contentDimensions.width, height: contentDimensions.height }); }, [ checkerboard, contentDimensions.height, contentDimensions.width, scale, xCorrection, yCorrection ]); useEffect22(() => { const { current } = portalContainer; current?.appendChild(Internals11.portalNode()); return () => { current?.removeChild(Internals11.portalNode()); }; }, []); const portalContainer = useRef17(null); return /* @__PURE__ */ jsx54("div", { ref: portalContainer, style: style3 }); }; // src/components/SizeSelector.tsx import { jsx as jsx55 } from "react/jsx-runtime"; var commonPreviewSizes = [ { size: "auto", translation: { x: 0, y: 0 } }, { size: 0.25, translation: { x: 0, y: 0 } }, { size: 0.5, translation: { x: 0, y: 0 } }, { size: 1, translation: { x: 0, y: 0 } } ]; var getPreviewSizeLabel = (previewSize) => { if (previewSize.size === "auto") { return "Fit"; } return `${(previewSize.size * 100).toFixed(0)}%`; }; var accessibilityLabel = "Preview Size"; var comboStyle = { width: 80 }; var getUniqueSizes = (size) => { const customPreviewSizes = [size, ...commonPreviewSizes]; const uniqueSizes = []; customPreviewSizes.forEach((p) => { if (!uniqueSizes.find((s) => s.size === p.size)) { uniqueSizes.push(p); } }); return uniqueSizes.sort((a, b) => { if (a.size === "auto") { return -1; } if (b.size === "auto") { return 1; } return a.size - b.size; }); }; var zoomableFileTypes = ["video", "image"]; var SizeSelector = () => { const { size, setSize } = useContext18(Internals12.PreviewSizeContext); const { canvasContent } = useContext18(Internals12.CompositionManager); const style3 = useMemo33(() => { return { padding: CONTROL_BUTTON_PADDING }; }, []); const zoomable = useMemo33(() => { if (!canvasContent) { return null; } if (canvasContent.type === "composition") { return true; } if (canvasContent.type === "asset" && zoomableFileTypes.includes(getPreviewFileType(canvasContent.asset))) { return true; } if (canvasContent.type === "output" && zoomableFileTypes.includes(getPreviewFileType(canvasContent.path))) { return true; } return false; }, [canvasContent]); const items = useMemo33(() => { return getUniqueSizes(size).map((newSize) => { return { id: String(newSize.size), label: getPreviewSizeLabel(newSize), onClick: () => { return setSize(() => { return newSize; }); }, type: "item", value: newSize.size, keyHint: newSize.size === "auto" ? "0" : null, leftItem: String(size.size) === String(newSize.size) ? /* @__PURE__ */ jsx55(Checkmark, {}) : null, subMenu: null, quickSwitcherLabel: null }; }); }, [setSize, size]); if (!zoomable) { return null; } return /* @__PURE__ */ jsx55("div", { style: style3, "aria-label": accessibilityLabel, children: /* @__PURE__ */ jsx55(Combobox, { title: accessibilityLabel, style: comboStyle, selectedId: String(size.size), values: items }) }); }; // src/components/TimelineInOutToggle.tsx import { PlayerInternals as PlayerInternals7 } from "@remotion/player"; import { createRef as createRef7, useCallback as useCallback27, useEffect as useEffect23, useImperativeHandle as useImperativeHandle9 } from "react"; import { Internals as Internals14 } from "remotion"; import { NoReactInternals as NoReactInternals4 } from "remotion/no-react"; // src/icons/timelineInOutPointer.tsx import { jsx as jsx56 } from "react/jsx-runtime"; var TimelineInPointer = (props) => { return /* @__PURE__ */ jsx56("svg", { viewBox: "0 0 256 256", fill: "none", ...props, children: /* @__PURE__ */ jsx56("path", { d: "M158 25H99V230.5H158", stroke: props.color, strokeWidth: "42", strokeLinecap: "round", strokeLinejoin: "round" }) }); }; var TimelineOutPointer = (props) => { return /* @__PURE__ */ jsx56("svg", { viewBox: "0 0 256 256", fill: "none", ...props, children: /* @__PURE__ */ jsx56("path", { d: "M98 25H157V230.5H98", stroke: props.color, strokeWidth: "42", strokeLinecap: "round", strokeLinejoin: "round" }) }); }; // src/state/in-out.ts import { createContext as createContext11, useContext as useContext19, useMemo as useMemo34 } from "react"; import { Internals as Internals13 } from "remotion"; var TimelineInOutContext = createContext11({}); var SetTimelineInOutContext = createContext11({ setInAndOutFrames: () => { throw new Error("default"); } }); var useTimelineInOutFramePosition = () => { const videoConfig = Internals13.useUnsafeVideoConfig(); const state = useContext19(TimelineInOutContext); const id = videoConfig?.id; const durationInFrames = videoConfig?.durationInFrames; return useMemo34(() => { if (!id || !durationInFrames) { return { inFrame: null, outFrame: null }; } const maxFrame = durationInFrames - 1; const actualInFrame = state[id]?.inFrame ?? null; const actualOutFrame = state[id]?.outFrame ?? null; return { inFrame: actualInFrame === null ? null : actualInFrame >= maxFrame ? null : actualInFrame, outFrame: actualOutFrame === null ? null : actualOutFrame >= maxFrame ? null : actualOutFrame }; }, [durationInFrames, id, state]); }; var useTimelineSetInOutFramePosition = () => { const { setInAndOutFrames } = useContext19(SetTimelineInOutContext); return { setInAndOutFrames }; }; // src/components/TimelineInOutToggle.tsx import { jsx as jsx57, jsxs as jsxs20, Fragment as Fragment9 } from "react/jsx-runtime"; var getTooltipText = (pointType, key) => [ `Mark ${pointType}`, areKeyboardShortcutsDisabled() ? null : `(${key})`, "- right click to clear" ].filter(NoReactInternals4.truthy).join(" "); var style3 = { width: 16, height: 16 }; var inOutHandles = createRef7(); var defaultInOutValue = { inFrame: null, outFrame: null }; var TimelineInOutPointToggle = () => { const { inFrame, outFrame } = useTimelineInOutFramePosition(); const { setInAndOutFrames } = useTimelineSetInOutFramePosition(); const videoConfig = Internals14.useUnsafeVideoConfig(); const keybindings = useKeybinding(); const { getCurrentFrame: getCurrentFrame2, isFirstFrame, isLastFrame } = PlayerInternals7.usePlayer(); const onInOutClear = useCallback27((composition) => { setInAndOutFrames((prev) => { return { ...prev, [composition]: { inFrame: null, outFrame: null } }; }); }, [setInAndOutFrames]); const onInMark = useCallback27((e) => { if (!videoConfig) { return null; } if (e?.shiftKey) { setInAndOutFrames((prev) => { return { ...prev, [videoConfig.id]: { ...prev[videoConfig.id] ?? defaultInOutValue, inFrame: null } }; }); return null; } setInAndOutFrames((prev) => { const prevOut = prev[videoConfig.id]?.outFrame; const biggestPossible = prevOut === undefined || prevOut === null ? Infinity : prevOut - 1; const selected = Math.min(getCurrentFrame2(), biggestPossible); if (selected === 0) { return { ...prev, [videoConfig.id]: { ...prev[videoConfig.id] ?? defaultInOutValue, inFrame: null } }; } const prevIn = prev[videoConfig.id]?.inFrame; if (prevIn !== null && prevIn !== undefined) { if (prevIn === selected) { return { ...prev, [videoConfig.id]: { ...prev[videoConfig.id] ?? defaultInOutValue, inFrame: null } }; } } return { ...prev, [videoConfig.id]: { ...prev[videoConfig.id] ?? defaultInOutValue, inFrame: selected } }; }); }, [getCurrentFrame2, setInAndOutFrames, videoConfig]); const clearInMark = useCallback27((e) => { if (!videoConfig) { return null; } e.preventDefault(); setInAndOutFrames((f) => { return { ...f, [videoConfig.id]: { ...f[videoConfig.id] ?? defaultInOutValue, inFrame: null } }; }); }, [setInAndOutFrames, videoConfig]); const clearOutMark = useCallback27((e) => { if (!videoConfig) { return null; } e?.preventDefault(); setInAndOutFrames((f) => { return { ...f, [videoConfig.id]: { ...f[videoConfig.id] ?? defaultInOutValue, outFrame: null } }; }); }, [setInAndOutFrames, videoConfig]); const onOutMark = useCallback27((e) => { if (!videoConfig) { return null; } if (e?.shiftKey) { setInAndOutFrames((f) => { return { ...f, [videoConfig.id]: { ...f[videoConfig.id] ?? defaultInOutValue, outFrame: null } }; }); return; } setInAndOutFrames((prev) => { const prevInFrame = prev[videoConfig.id]?.inFrame; const smallestPossible = prevInFrame === null || prevInFrame === undefined ? -Infinity : prevInFrame + 1; const selected = Math.max(getCurrentFrame2(), smallestPossible); if (selected === videoConfig.durationInFrames - 1) { return { ...prev, [videoConfig.id]: { ...prev[videoConfig.id] ?? defaultInOutValue, outFrame: null } }; } const prevOut = prev[videoConfig.id]?.outFrame; if (prevOut !== null && prevOut !== undefined) { if (prevOut === selected) { return { ...prev, [videoConfig.id]: { ...prev[videoConfig.id] ?? defaultInOutValue, outFrame: null } }; } } return { ...prev, [videoConfig.id]: { ...prev[videoConfig.id] ?? defaultInOutValue, outFrame: selected } }; }); }, [getCurrentFrame2, setInAndOutFrames, videoConfig]); const confId = videoConfig?.id; useEffect23(() => { if (!confId) { return; } const iKey = keybindings.registerKeybinding({ event: "keypress", key: "i", callback: (e) => { onInMark(e); }, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const oKey = keybindings.registerKeybinding({ event: "keypress", key: "o", callback: (e) => { onOutMark(e); }, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const xKey = keybindings.registerKeybinding({ event: "keypress", key: "x", callback: () => { onInOutClear(confId); }, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); return () => { oKey.unregister(); iKey.unregister(); xKey.unregister(); }; }, [confId, keybindings, onInMark, onInOutClear, onOutMark]); useImperativeHandle9(inOutHandles, () => { return { clearMarks: () => { if (!confId) { return; } onInOutClear(confId); }, inMarkClick: onInMark, outMarkClick: onOutMark }; }, [confId, onInMark, onInOutClear, onOutMark]); return /* @__PURE__ */ jsxs20(Fragment9, { children: [ /* @__PURE__ */ jsx57(ControlButton, { title: getTooltipText("In", "I"), "aria-label": getTooltipText("In", "I"), onClick: onInMark, onContextMenu: clearInMark, disabled: !videoConfig || isFirstFrame, children: /* @__PURE__ */ jsx57(TimelineInPointer, { color: inFrame === null ? "white" : BLUE, style: style3 }) }), /* @__PURE__ */ jsx57(ControlButton, { title: getTooltipText("Out", "O"), "aria-label": getTooltipText("Out", "O"), onClick: onOutMark, onContextMenu: clearOutMark, disabled: !videoConfig || isLastFrame, children: /* @__PURE__ */ jsx57(TimelineOutPointer, { color: outFrame === null ? "white" : BLUE, style: style3 }) }) ] }); }; // src/error-overlay/remotion-overlay/ShortcutHint.tsx import { useMemo as useMemo35 } from "react"; import { jsx as jsx58, jsxs as jsxs21 } from "react/jsx-runtime"; var cmdOrCtrlCharacter = window.navigator.platform.startsWith("Mac") ? "⌘" : "Ctrl"; var container13 = { display: "inline-block", marginLeft: 6, opacity: 0.6, verticalAlign: "middle", fontSize: 14 }; var ShortcutHint = ({ keyToPress, cmdOrCtrl }) => { const style4 = useMemo35(() => { if (keyToPress === "↵") { return { display: "inline-block", transform: `translateY(2px)`, fontSize: 14 }; } return {}; }, [keyToPress]); if (areKeyboardShortcutsDisabled()) { return null; } return /* @__PURE__ */ jsxs21("span", { style: container13, children: [ cmdOrCtrl ? `${cmdOrCtrlCharacter}` : "", /* @__PURE__ */ jsx58("span", { style: style4, children: keyToPress.toUpperCase() }) ] }); }; // src/state/editor-guides.ts import { createContext as createContext12 } from "react"; var persistEditorShowGuidesOption = (option) => { localStorage.setItem("remotion.editorShowGuides", String(option)); }; var loadEditorShowGuidesOption = () => { const item = localStorage.getItem("remotion.editorShowGuides"); return item === "true"; }; var persistGuidesList = (guides) => { localStorage.setItem("remotion.guidesList", JSON.stringify(guides)); }; var loadGuidesList = () => { const item = localStorage.getItem("remotion.guidesList"); return item ? JSON.parse(item) : []; }; var EditorShowGuidesContext = createContext12({ editorShowGuides: false, setEditorShowGuides: () => { return; }, guidesList: [], setGuidesList: () => { return; }, selectedGuideId: null, setSelectedGuideId: () => { return; }, shouldCreateGuideRef: { current: false }, shouldDeleteGuideRef: { current: false }, hoveredGuideId: null, setHoveredGuideId: () => { return; } }); // src/state/editor-rulers.ts import { createContext as createContext13 } from "react"; var persistEditorShowRulersOption = (option) => { localStorage.setItem("remotion.editorShowRulers", String(option)); }; var loadEditorShowRulersOption = () => { const item = localStorage.getItem("remotion.editorShowRulers"); return item === "true"; }; var EditorShowRulersContext = createContext13({ editorShowRulers: loadEditorShowRulersOption(), setEditorShowRulers: () => { return; } }); var RULER_WIDTH = 20; var MINIMUM_VISIBLE_CANVAS_SIZE = 50; var PREDEFINED_RULER_SCALE_GAPS = [ 1, 2, 5, 10, 20, 50, 100, 250, 500, 1000, 2000, 5000 ]; var MAXIMUM_PREDEFINED_RULER_SCALE_GAP = 5000; var MINIMUM_RULER_MARKING_GAP_PX = 50; // src/state/editor-zoom-gestures.ts import { createContext as createContext14 } from "react"; var persistEditorZoomGesturesOption = (option) => { localStorage.setItem("remotion.editorZoomGestures", String(option)); }; var loadEditorZoomGesturesOption = () => { const item = localStorage.getItem("remotion.editorZoomGestures"); return item !== "false"; }; var EditorZoomGesturesContext = createContext14({ editorZoomGestures: loadEditorZoomGesturesOption(), setEditorZoomGestures: () => { return; } }); // src/helpers/check-fullscreen-support.ts var checkFullscreenSupport = () => { return document.fullscreenEnabled || document.webkitFullscreenEnabled; }; // src/helpers/get-git-menu-item.ts var getGitSourceName = (gitSource) => { if (gitSource.type === "github") { return "GitHub"; } throw new Error("Unknown git source type"); }; var getGitSourceBranchUrl = (gitSource) => { if (gitSource.type === "github") { return `https://github.com/${gitSource.org}/${gitSource.name}/tree/${gitSource.ref}${gitSource.relativeFromGitRoot ? `/${gitSource.relativeFromGitRoot}` : ""}`; } throw new Error("Unknown git source type"); }; var getGitRefUrl = (gitSource, originalLocation) => { if (gitSource.type === "github") { return `https://github.com/${gitSource.org}/${gitSource.name}/tree/${gitSource.ref}/${gitSource.relativeFromGitRoot ? `${gitSource.relativeFromGitRoot}/` : ""}${originalLocation.source}#L${originalLocation.line}`; } throw new Error("Unknown git source type"); }; var getGitMenuItem = () => { if (!window.remotion_gitSource) { return null; } return { id: "open-git-source", value: "open-git-source", label: `Open ${getGitSourceName(window.remotion_gitSource)} Repo`, onClick: () => { window.open(getGitSourceBranchUrl(window.remotion_gitSource), "_blank"); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: `Open ${getGitSourceName(window.remotion_gitSource)} repo` }; }; // src/helpers/open-in-editor.ts var openInEditor = (stack) => { const { originalFileName, originalLineNumber, originalColumnNumber, originalFunctionName, originalScriptCode } = stack; return fetch(`/api/open-in-editor`, { method: "post", headers: { "content-type": "application/json" }, body: JSON.stringify({ stack: { originalFileName, originalLineNumber, originalColumnNumber, originalFunctionName, originalScriptCode } }) }); }; var openOriginalPositionInEditor = async (originalPosition) => { await openInEditor({ originalColumnNumber: originalPosition.column, originalFileName: originalPosition.source, originalFunctionName: null, originalLineNumber: originalPosition.line, originalScriptCode: null }); }; // src/components/Notifications/ColorDot.tsx import { useMemo as useMemo36 } from "react"; import { jsx as jsx59 } from "react/jsx-runtime"; var container14 = { height: 16, width: 16, backgroundColor: "red", border: "1px solid rgba(255, 255, 255, 0.2)", borderRadius: 8 }; var ColorDot = ({ color }) => { const style4 = useMemo36(() => { return { ...container14, backgroundColor: color }; }, [color]); return /* @__PURE__ */ jsx59("div", { style: style4 }); }; // src/helpers/pick-color.tsx import { jsx as jsx60, jsxs as jsxs22, Fragment as Fragment10 } from "react/jsx-runtime"; var pickColor = () => { const open = new EyeDropper().open(); open.then((color) => { copyText(color.sRGBHex).then(() => { showNotification(/* @__PURE__ */ jsxs22(Fragment10, { children: [ /* @__PURE__ */ jsx60(ColorDot, { color: color.sRGBHex }), " ", /* @__PURE__ */ jsx60(Spacing, { x: 1 }), " Copied", " ", color.sRGBHex ] }), 2000); }).catch((err) => { showNotification(`Could not copy: ${err.message}`, 2000); }); }).catch((err) => { if (err.message.includes("canceled")) { return; } showNotification(`Could not pick color.`, 2000); }); }; // src/helpers/show-browser-rendering.ts var SHOW_BROWSER_RENDERING = Boolean(process.env.EXPERIMENTAL_CLIENT_SIDE_RENDERING_ENABLED); // src/helpers/use-menu-structure.tsx import { jsx as jsx61 } from "react/jsx-runtime"; var openExternal = (link) => { window.open(link, "_blank"); }; var rotate = { transform: `rotate(90deg)` }; var ICON_SIZE = 16; var getFileMenu = ({ readOnlyStudio, closeMenu, previewServerState, setSelectedModal }) => { const items = [ window.remotion_isReadOnlyStudio ? { id: "input-props-override", value: "input-props-override", label: "Set input props...", onClick: () => { closeMenu(); setSelectedModal({ type: "input-props-override" }); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "Override input props" } : null, readOnlyStudio ? null : { id: "render", value: "render", label: "Render...", onClick: () => { closeMenu(); if (previewServerState !== "connected") { showNotification("Restart the studio to render", 2000); return; } const renderButton = document.getElementById("render-modal-button-server"); renderButton.click(); }, type: "item", keyHint: "R", leftItem: null, subMenu: null, quickSwitcherLabel: "Render..." }, SHOW_BROWSER_RENDERING && !readOnlyStudio ? { id: "render-on-web", value: "render-on-web", label: "Render on web...", onClick: () => { closeMenu(); const renderButton = document.getElementById("render-modal-button-client"); renderButton.click(); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "Render on web..." } : null, window.remotion_editorName && !readOnlyStudio ? { type: "divider", id: "open-in-editor-divider" } : null, window.remotion_editorName && !readOnlyStudio ? { id: "open-in-editor", value: "open-in-editor", label: `Open in ${window.remotion_editorName}`, onClick: async () => { await openInEditor({ originalFileName: `${window.remotion_cwd}`, originalLineNumber: 1, originalColumnNumber: 1, originalFunctionName: null, originalScriptCode: null }).then((res) => res.json()).then(({ success }) => { if (!success) { showNotification(`Could not open ${window.remotion_editorName}`, 2000); } }).catch((err) => { console.error(err); showNotification(`Could not open ${window.remotion_editorName}`, 2000); }); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "Open in editor..." } : null, getGitMenuItem() ].filter(NoReactInternals5.truthy); if (items.length === 0) { return null; } return { id: "file", label: "File", leaveLeftPadding: false, items, quickSwitcherLabel: null }; }; var useMenuStructure = (closeMenu, readOnlyStudio) => { const { setSelectedModal } = useContext20(ModalsContext); const { checkerboard, setCheckerboard } = useContext20(CheckerboardContext); const { editorZoomGestures, setEditorZoomGestures } = useContext20(EditorZoomGesturesContext); const { editorShowRulers, setEditorShowRulers } = useContext20(EditorShowRulersContext); const { editorShowGuides, setEditorShowGuides } = useContext20(EditorShowGuidesContext); const { size, setSize } = useContext20(Internals15.PreviewSizeContext); const { type } = useContext20(StudioServerConnectionCtx).previewServerState; const { setSidebarCollapsedState, sidebarCollapsedStateLeft, sidebarCollapsedStateRight } = useContext20(SidebarContext); const sizes = useMemo37(() => getUniqueSizes(size), [size]); const isFullscreenSupported = checkFullscreenSupport(); const { remotion_packageManager } = window; const sizePreselectIndex = sizes.findIndex((s) => String(size.size) === String(s.size)); const mobileLayout = useMobileLayout(); const structure = useMemo37(() => { let struct = [ { id: "remotion", label: /* @__PURE__ */ jsx61(Row, { align: "center", justify: "center", children: /* @__PURE__ */ jsx61("svg", { width: ICON_SIZE, height: ICON_SIZE, viewBox: "-100 -100 400 400", style: rotate, children: /* @__PURE__ */ jsx61("path", { fill: "#fff", stroke: "#fff", strokeWidth: "100", strokeLinejoin: "round", d: "M 2 172 a 196 100 0 0 0 195 5 A 196 240 0 0 0 100 2.259 A 196 240 0 0 0 2 172 z" }) }) }), leaveLeftPadding: false, items: [ { id: "about", value: "about", label: "About Remotion", onClick: () => { closeMenu(); openExternal("https://remotion.dev"); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "Help: About Remotion" }, { id: "changelog", value: "changelog", label: "Changelog", onClick: () => { closeMenu(); openExternal("https://github.com/remotion-dev/remotion/releases"); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "Help: Changelog" }, { id: "license", value: "license", label: "License", onClick: () => { closeMenu(); openExternal("https://github.com/remotion-dev/remotion/blob/main/LICENSE.md"); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "Help: License" }, { id: "acknowledgements", value: "acknowledgements", label: "Acknowledgements", onClick: () => { closeMenu(); openExternal("https://remotion.dev/acknowledgements"); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "Help: Acknowledgements" }, { type: "divider", id: "timeline-divider-1" }, { id: "restart-studio", value: "restart-studio", label: "Restart Studio Server", onClick: () => { closeMenu(); restartStudio(); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "Restart Studio Server" } ], quickSwitcherLabel: null }, getFileMenu({ readOnlyStudio, closeMenu, previewServerState: type, setSelectedModal }), { id: "view", label: "View", leaveLeftPadding: true, items: [ { id: "preview-size", keyHint: null, label: "Preview size", onClick: () => { return; }, type: "item", value: "preview-size", leftItem: null, subMenu: { leaveLeftSpace: true, preselectIndex: sizePreselectIndex, items: sizes.map((newSize) => ({ id: String(newSize.size), keyHint: newSize.size === 1 ? "0" : null, label: getPreviewSizeLabel(newSize), leftItem: String(newSize.size) === String(size.size) ? /* @__PURE__ */ jsx61(Checkmark, {}) : null, onClick: () => { closeMenu(); setSize(() => newSize); }, subMenu: null, type: "item", value: newSize.size, quickSwitcherLabel: null })), quickSwitcherLabel: null }, quickSwitcherLabel: null }, { id: "editor-zoom-gestures", keyHint: null, label: "Zoom and Pan Gestures", onClick: () => { closeMenu(); setEditorZoomGestures((c) => !c); }, type: "item", value: "editor-zoom-gestures", leftItem: editorZoomGestures ? /* @__PURE__ */ jsx61(Checkmark, {}) : null, subMenu: null, quickSwitcherLabel: editorZoomGestures ? "Disable Zoom and Pan Gestures" : "Enable Zoom and Pan Gestures" }, { id: "show-rulers", keyHint: null, label: "Show Rulers", onClick: () => { closeMenu(); setEditorShowRulers((c) => !c); }, type: "item", value: "show-ruler", leftItem: editorShowRulers ? /* @__PURE__ */ jsx61(Checkmark, {}) : null, subMenu: null, quickSwitcherLabel: editorShowRulers ? "Hide Rulers" : "Show Rulers" }, { id: "show-guides", keyHint: null, label: "Show Guides", onClick: () => { closeMenu(); setEditorShowGuides((c) => !c); }, type: "item", value: "show-guides", leftItem: editorShowGuides ? /* @__PURE__ */ jsx61(Checkmark, {}) : null, subMenu: null, quickSwitcherLabel: editorShowGuides ? "Hide Guides" : "Show Guides" }, { id: "timeline-divider-1", type: "divider" }, { id: "left-sidebar", label: "Left Sidebar", keyHint: null, type: "item", value: "preview-size", leftItem: null, quickSwitcherLabel: null, subMenu: { leaveLeftSpace: true, preselectIndex: 0, items: [ { id: "left-sidebar-responsive", keyHint: null, label: "Responsive", leftItem: sidebarCollapsedStateLeft === "responsive" ? /* @__PURE__ */ jsx61(Checkmark, {}) : null, onClick: () => { closeMenu(); setSidebarCollapsedState({ left: "responsive", right: null }); }, subMenu: null, type: "item", value: "responsive", quickSwitcherLabel: null }, { id: "left-sidebar-expanded", keyHint: null, label: "Expanded", leftItem: sidebarCollapsedStateLeft === "expanded" ? /* @__PURE__ */ jsx61(Checkmark, {}) : null, onClick: () => { closeMenu(); setSidebarCollapsedState({ left: "expanded", right: null }); }, subMenu: null, type: "item", value: "expanded", quickSwitcherLabel: "Expand" }, { id: "left-sidebar-collapsed", keyHint: null, label: "Collapsed", leftItem: sidebarCollapsedStateLeft === "collapsed" ? /* @__PURE__ */ jsx61(Checkmark, {}) : null, onClick: () => { closeMenu(); setSidebarCollapsedState({ left: "collapsed", right: null }); }, subMenu: null, type: "item", value: "collapsed", quickSwitcherLabel: "Collapse" } ] }, onClick: () => { return; } }, { id: "right-sidebar", label: "Right Sidebar", keyHint: null, type: "item", value: "preview-size", leftItem: null, quickSwitcherLabel: null, subMenu: { leaveLeftSpace: true, preselectIndex: 0, items: [ { id: "sidebar-expanded", keyHint: null, label: "Expanded", leftItem: sidebarCollapsedStateRight === "expanded" ? /* @__PURE__ */ jsx61(Checkmark, {}) : null, onClick: () => { closeMenu(); setSidebarCollapsedState({ left: null, right: "expanded" }); }, subMenu: null, type: "item", value: "expanded", quickSwitcherLabel: "Expand" }, { id: "right-sidebar-collapsed", keyHint: null, label: "Collapsed", leftItem: sidebarCollapsedStateRight === "collapsed" ? /* @__PURE__ */ jsx61(Checkmark, {}) : null, onClick: () => { closeMenu(); setSidebarCollapsedState({ left: null, right: "collapsed" }); }, subMenu: null, type: "item", value: "collapsed", quickSwitcherLabel: "Collapse" } ] }, onClick: () => { return; } }, { id: "timeline-divider-2", type: "divider" }, { id: "checkerboard", keyHint: "T", label: "Transparency as checkerboard", onClick: () => { closeMenu(); setCheckerboard((c) => !c); }, type: "item", value: "checkerboard", leftItem: checkerboard ? /* @__PURE__ */ jsx61(Checkmark, {}) : null, subMenu: null, quickSwitcherLabel: checkerboard ? "Disable Checkerboard Transparency" : "Enable Checkerboard Transparency" }, { id: "timeline-divider-3", type: "divider" }, { id: "quick-switcher", keyHint: `${cmdOrCtrlCharacter}+K`, label: "Quick Switcher", onClick: () => { closeMenu(); setSelectedModal({ type: "quick-switcher", mode: "compositions", invocationTimestamp: Date.now() }); }, type: "item", value: "quick-switcher", leftItem: null, subMenu: null, quickSwitcherLabel: "Switch composition" }, { id: "in-out-divider-5", type: "divider" }, { id: "in-mark", keyHint: "I", label: "In Mark", leftItem: null, onClick: () => { closeMenu(); inOutHandles.current?.inMarkClick(null); }, subMenu: null, type: "item", value: "in-mark", quickSwitcherLabel: "Timeline: Set In Mark" }, { id: "out-mark", keyHint: "O", label: "Out Mark", leftItem: null, onClick: () => { closeMenu(); inOutHandles.current?.outMarkClick(null); }, subMenu: null, type: "item", value: "out-mark", quickSwitcherLabel: "Timeline: Set Out Mark" }, { id: "x-mark", keyHint: "X", label: "Clear In/Out Marks", leftItem: null, onClick: () => { closeMenu(); inOutHandles.current?.clearMarks(); }, subMenu: null, type: "item", value: "clear-marks", quickSwitcherLabel: "Timeline: Clear In and Out Mark" }, { id: "goto-time", keyHint: "G", label: "Go to frame", leftItem: null, onClick: () => { closeMenu(); Internals15.timeValueRef.current?.goToFrame(); }, subMenu: null, type: "item", value: "clear-marks", quickSwitcherLabel: "Timeline: Go to frame" }, { id: "fullscreen-divider", type: "divider" }, isFullscreenSupported ? { id: "fullscreen", keyHint: null, label: "Fullscreen", leftItem: null, onClick: () => { closeMenu(); drawRef.current?.requestFullscreen(); }, subMenu: null, type: "item", value: "fullscreen", quickSwitcherLabel: "Go Fullscreen" } : null ].filter(Internals15.truthy) }, { id: "tools", label: "Tools", leaveLeftPadding: false, items: [ process.env.ASK_AI_ENABLED ? { id: "ask-ai", value: "ask-ai", label: "Ask AI", onClick: () => { closeMenu(); askAiModalRef.current?.toggle(); }, leftItem: null, keyHint: `${cmdOrCtrlCharacter}+I`, subMenu: null, type: "item", quickSwitcherLabel: "Ask AI" } : null, "EyeDropper" in window ? { id: "color-picker", value: "color-picker", label: "Color Picker", onClick: () => { closeMenu(); pickColor(); }, leftItem: null, keyHint: null, subMenu: null, type: "item", quickSwitcherLabel: "Show Color Picker" } : null, { id: "spring-editor", value: "spring-editor", label: "Timing Editor", onClick: () => { closeMenu(); window.open("https://www.remotion.dev/timing-editor", "_blank"); }, leftItem: null, keyHint: null, subMenu: null, type: "item", quickSwitcherLabel: "Open spring() Editor" } ].filter(Internals15.truthy), quickSwitcherLabel: null }, readOnlyStudio || remotion_packageManager === "unknown" ? null : { id: "install", label: "Packages", leaveLeftPadding: false, items: [ { id: "install-packages", value: "install-packages", label: "Install...", onClick: () => { closeMenu(); setSelectedModal({ type: "install-packages", packageManager: remotion_packageManager }); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: `Install packages` } ] }, { id: "help", label: "Help", leaveLeftPadding: false, items: [ { id: "shortcuts", value: "shortcuts", label: areKeyboardShortcutsDisabled() ? "Shortcuts (disabled)" : "Shortcuts", onClick: () => { closeMenu(); setSelectedModal({ type: "quick-switcher", mode: "docs", invocationTimestamp: Date.now() }); }, keyHint: "?", leftItem: null, subMenu: null, type: "item", quickSwitcherLabel: areKeyboardShortcutsDisabled() ? "Show all Keyboard Shortcuts (disabled)" : "Show all Keyboard Shortcuts" }, { id: "docs", value: "docs", label: "Docs", onClick: () => { closeMenu(); openExternal("https://remotion.dev/docs"); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "Visit Documentation" }, { id: "file-issue", value: "file-issue", label: "File an issue", onClick: () => { closeMenu(); openExternal("https://github.com/remotion-dev/remotion/issues/new/choose"); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "File GitHub issue" }, { id: "discord", value: "discord", label: "Join Discord community", onClick: () => { closeMenu(); openExternal("https://discord.com/invite/6VzzNDwUwV"); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: null }, { id: "help-divider-6", type: "divider" }, { id: "insta", value: "insta", label: "Instagram", onClick: () => { closeMenu(); openExternal("https://instagram.com/remotion"); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "Follow Remotion on Instagram" }, { id: "x", value: "x", label: "X", onClick: () => { closeMenu(); openExternal("https://x.com/remotion"); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "Follow Remotion on X" }, { id: "youtube", value: "youtube", label: "YouTube", onClick: () => { closeMenu(); openExternal("https://www.youtube.com/@remotion_dev"); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "Watch Remotion on YouTube" }, { id: "linkedin", value: "linkedin", label: "LinkedIn", onClick: () => { closeMenu(); openExternal("https://www.linkedin.com/company/remotion-dev/"); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "Follow Remotion on LinkedIn" }, { id: "tiktok", value: "tiktok", label: "TikTok", onClick: () => { closeMenu(); openExternal("https://www.tiktok.com/@remotion"); }, type: "item", keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: "Follow Remotion on TikTok" } ] } ].filter(Internals15.truthy); if (mobileLayout) { struct = [ { ...struct[0], items: [ ...struct.slice(1).map((s) => { return { ...s, keyHint: null, onClick: () => { return; }, type: "item", value: s.id, leftItem: null, subMenu: { items: s.items, leaveLeftSpace: true, preselectIndex: 0 }, quickSwitcherLabel: null }; }), ...struct[0].items ] } ]; } return struct; }, [ readOnlyStudio, closeMenu, type, sizePreselectIndex, sizes, editorZoomGestures, editorShowRulers, editorShowGuides, sidebarCollapsedStateLeft, sidebarCollapsedStateRight, checkerboard, isFullscreenSupported, remotion_packageManager, mobileLayout, size.size, setSize, setEditorZoomGestures, setEditorShowRulers, setEditorShowGuides, setSidebarCollapsedState, setCheckerboard, setSelectedModal ]); return structure; }; var getItemLabel = (item) => { if (item.quickSwitcherLabel !== null) { return item.quickSwitcherLabel; } if (typeof item.label === "string") { return item.label; } return item.label?.toString(); }; var itemToSearchResult = (item, setSelectedModal, prefixes) => { if (item.subMenu) { return item.subMenu.items.map((subItem) => { if (subItem.type === "divider") { return null; } return itemToSearchResult(subItem, setSelectedModal, [ ...prefixes, getItemLabel(item) ]); }).flat(1).filter(NoReactInternals5.truthy); } return [ { type: "menu-item", id: item.id, onSelected: () => { setSelectedModal(null); item.onClick(item.id, null); }, title: [...prefixes, getItemLabel(item)].join(": ") } ]; }; var makeSearchResults = (actions, setSelectedModal) => { const items = actions.map((menu) => { return menu.items.map((item) => { if (item.type === "divider") { return null; } return itemToSearchResult(item, setSelectedModal, []); }); }).flat(Infinity).filter(NoReactInternals5.truthy); return items; }; // src/components/Menu/MenuItem.tsx import { PlayerInternals as PlayerInternals8 } from "@remotion/player"; import { useCallback as useCallback28, useMemo as useMemo38, useRef as useRef18, useState as useState27 } from "react"; import ReactDOM5 from "react-dom"; import { jsx as jsx62, jsxs as jsxs23, Fragment as Fragment11 } from "react/jsx-runtime"; var container15 = { fontSize: 13, color: "white", paddingLeft: 10, paddingRight: 10, cursor: "default", paddingTop: 8, paddingBottom: 8, userSelect: "none", WebkitUserSelect: "none", border: "none" }; var MenuItem = ({ label: itemName, selected, id, onItemSelected, onItemHovered, onItemQuit, onPreviousMenu, onNextMenu, menu }) => { const [hovered, setHovered] = useState27(false); const ref = useRef18(null); const size = PlayerInternals8.useElementSize(ref, { triggerOnWindowResize: true, shouldApplyCssTransforms: true }); const { tabIndex, currentZIndex } = useZIndex(); const containerStyle2 = useMemo38(() => { return { ...container15, backgroundColor: getBackgroundFromHoverState({ hovered, selected }) }; }, [hovered, selected]); const portalStyle = useMemo38(() => { if (!selected || !size) { return null; } return { ...menuContainerTowardsBottom, left: size.left, top: size.top + size.height }; }, [selected, size]); const onPointerEnter = useCallback28(() => { onItemHovered(id); setHovered(true); }, [id, onItemHovered]); const onPointerLeave = useCallback28(() => { setHovered(false); }, []); const onPointerDown = useCallback28((e) => { if (e.button !== 0) { return; } onItemSelected(id); window.addEventListener("pointerup", (evt) => { if (!isMenuItem(evt.target)) { onItemQuit(); } }, { once: true }); }, [id, onItemQuit, onItemSelected]); const onClick = useCallback28((e) => { e.stopPropagation(); const isKeyboardInitiated = e.detail === 0; if (!isKeyboardInitiated) { return; } onItemSelected((p) => { return p === null ? id : null; }); }, [id, onItemSelected]); const outerStyle = useMemo38(() => { return { ...outerPortal, top: (size?.top ?? 0) + (size?.height ?? 0) }; }, [size]); return /* @__PURE__ */ jsxs23(Fragment11, { children: [ /* @__PURE__ */ jsx62("button", { ref, role: "button", tabIndex, onPointerEnter, onPointerLeave, onPointerDown, onClick, style: containerStyle2, type: "button", className: MENU_INITIATOR_CLASSNAME, children: itemName }), portalStyle ? ReactDOM5.createPortal(/* @__PURE__ */ jsx62("div", { className: "css-reset", style: outerStyle, children: /* @__PURE__ */ jsx62(HigherZIndex, { onEscape: onItemQuit, onOutsideClick: onItemQuit, children: /* @__PURE__ */ jsx62("div", { style: portalStyle, children: /* @__PURE__ */ jsx62(MenuContent, { onNextMenu: onPreviousMenu, onPreviousMenu: onNextMenu, values: menu.items, onHide: onItemQuit, leaveLeftSpace: menu.leaveLeftPadding, preselectIndex: false, topItemCanBeUnselected: true, fixedHeight: null }) }) }) }), getPortal(currentZIndex)) : null ] }); }; // src/components/MenuBuildIndicator.tsx import { useContext as useContext21, useEffect as useEffect24, useState as useState29 } from "react"; // src/components/OpenEditorButton.tsx import { useCallback as useCallback29, useMemo as useMemo39, useState as useState28 } from "react"; import { jsx as jsx63 } from "react/jsx-runtime"; var svgStyle = { width: 11, height: 11 }; var buttonStyle = { border: "none", height: "20px", display: "flex", paddingInline: "6px", justifyContent: "center", alignItems: "center" }; var OpenEditorButton = ({ type }) => { const [hovered, setHovered] = useState28(false); const svgFillColor = useMemo39(() => { return hovered ? "white" : LIGHT_TEXT; }, [hovered]); const handleClick = useCallback29(async () => { if (type === "editor") { await openInEditor({ originalFileName: `${window.remotion_cwd}`, originalLineNumber: 1, originalColumnNumber: 1, originalFunctionName: null, originalScriptCode: null }).then((res) => res.json()).then(({ success }) => { if (!success) { showNotification(`Could not open ${window.remotion_editorName}`, 2000); } }).catch((err) => { console.error(err); showNotification(`Could not open ${window.remotion_editorName}`, 2000); }); } if (type === "git") { if (!window.remotion_gitSource) { throw new Error("No git source"); } window.open(getGitSourceBranchUrl(window.remotion_gitSource), "_blank"); } }, [type]); const buttonTooltip = type === "git" ? `Open ${getGitSourceName(window.remotion_gitSource)} Repo` : `Open in ${window.remotion_editorName}`; const openInEditorSvg = /* @__PURE__ */ jsx63("svg", { viewBox: "0 0 512 512", style: svgStyle, children: /* @__PURE__ */ jsx63("path", { fill: svgFillColor, d: "M320 0c-17.7 0-32 14.3-32 32s14.3 32 32 32h82.7L201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L448 109.3V192c0 17.7 14.3 32 32 32s32-14.3 32-32V32c0-17.7-14.3-32-32-32H320zM80 32C35.8 32 0 67.8 0 112V432c0 44.2 35.8 80 80 80H400c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32V432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z" }) }); const onPointerEnter = useCallback29(() => { setHovered(true); }, []); const onPointerLeave = useCallback29(() => { setHovered(false); }, []); return /* @__PURE__ */ jsx63("button", { title: buttonTooltip, type: "button", onPointerEnter, onPointerLeave, style: buttonStyle, onClick: handleClick, children: openInEditorSvg }); }; // src/components/MenuBuildIndicator.tsx import { jsx as jsx64, jsxs as jsxs24 } from "react/jsx-runtime"; var cwd = { fontSize: 13, opacity: 0.8, display: "flex", alignItems: "center", justifyContent: "center" }; var spinnerSize = 14; var spinner = { position: "relative", width: spinnerSize, marginTop: 4 }; var noSpinner = { position: "relative", width: spinnerSize }; var MenuBuildIndicator = () => { const [isBuilding, setIsBuilding] = useState29(false); const ctx = useContext21(StudioServerConnectionCtx).previewServerState; const showButton = window.remotion_editorName && ctx.type === "connected"; useEffect24(() => { window.remotion_isBuilding = () => { setIsBuilding(true); }; window.remotion_finishedBuilding = () => { setIsBuilding(false); }; return () => { window.remotion_isBuilding = undefined; window.remotion_finishedBuilding = undefined; }; }, []); return /* @__PURE__ */ jsxs24("div", { style: cwd, title: window.remotion_cwd, children: [ showButton ? /* @__PURE__ */ jsx64(Spacing, { x: 2 }) : null, isBuilding ? /* @__PURE__ */ jsx64("div", { style: spinner, children: /* @__PURE__ */ jsx64(Spinner, { duration: 0.5, size: spinnerSize }) }) : /* @__PURE__ */ jsx64("div", { style: noSpinner }), showButton ? /* @__PURE__ */ jsx64(Spacing, { x: 0.5 }) : null, window.remotion_projectName, showButton ? /* @__PURE__ */ jsx64(Spacing, { x: 0.25 }) : null, showButton ? /* @__PURE__ */ jsx64(OpenEditorButton, { type: "editor" }) : window.remotion_gitSource ? /* @__PURE__ */ jsx64(OpenEditorButton, { type: "git" }) : null ] }); }; // src/components/SidebarCollapserControls.tsx import { useCallback as useCallback91, useContext as useContext59, useEffect as useEffect61 } from "react"; // src/components/TopPanel.tsx import { useCallback as useCallback90, useContext as useContext58, useEffect as useEffect60, useMemo as useMemo96 } from "react"; // src/helpers/use-breakpoint.ts import { useEffect as useEffect25, useState as useState30 } from "react"; function useBreakpoint(breakpoint2) { const [compactUI, setCompactUI] = useState30(window.innerWidth < breakpoint2); useEffect25(() => { function handleResize() { setCompactUI(window.innerWidth < breakpoint2); } window.addEventListener("resize", handleResize); handleResize(); return () => window.removeEventListener("resize", handleResize); }, [breakpoint2]); return compactUI; } // src/components/CanvasIfSizeIsAvailable.tsx import { useContext as useContext30, useMemo as useMemo51 } from "react"; import { Internals as Internals25 } from "remotion"; // src/components/CanvasOrLoading.tsx import { useContext as useContext29, useEffect as useEffect36 } from "react"; import { Internals as Internals24 } from "remotion"; // src/error-overlay/remotion-overlay/ErrorLoader.tsx import { useEffect as useEffect31, useState as useState34 } from "react"; // src/error-overlay/react-overlay/utils/parser.ts import { getLocationFromBuildError } from "@remotion/studio-shared"; // src/error-overlay/react-overlay/effects/resolve-file-source.ts var resolveFileSource = async (location, contextLines) => { const res = await fetch(`/api/file-source?f=${encodeURIComponent(location.fileName)}`); const text = await res.text(); const lines2 = text.split(` `).map((l, i) => { const oneIndexedLineNumber = i + 1; return [oneIndexedLineNumber, l]; }).filter(([oneIndexedLineNumber]) => { return Math.abs(oneIndexedLineNumber - location.lineNumber) <= contextLines; }); const scriptCode = lines2.map(([num, line2]) => { return { content: line2, highlight: location.lineNumber === num, lineNumber: num }; }); return { originalColumnNumber: location.columnNumber, originalFunctionName: null, originalFileName: location.fileName, originalLineNumber: location.lineNumber, originalScriptCode: scriptCode }; }; // src/error-overlay/react-overlay/utils/make-stack-frame.ts var makeStackFrame = ({ functionName, fileName, lineNumber, columnNumber }) => { if (functionName && functionName.indexOf("Object.") === 0) { functionName = functionName.slice("Object.".length); } if (functionName === "friendlySyntaxErrorLabel" || functionName === "exports.__esModule" || functionName === "" || !functionName) { functionName = null; } return { columnNumber, fileName, functionName, lineNumber }; }; // src/error-overlay/react-overlay/utils/parser.ts var regexExtractLocation = /\(?(.+?)(?::(\d+))?(?::(\d+))?\)?$/; function extractLocation(token) { const execed = regexExtractLocation.exec(token); if (!execed) { throw new Error("Could not match in extractLocation"); } return execed.slice(1).map((v) => { const p = Number(v); if (!isNaN(p)) { return p; } return v; }); } var regexValidFrame_Chrome = /^\s*(at|in)\s.+(:\d+)/; var regexValidFrame_FireFox = /(^|@)\S+:\d+|.+line\s+\d+\s+>\s+(eval|Function).+/; function parseStack(stack) { const frames = stack.filter((e) => regexValidFrame_Chrome.test(e) || regexValidFrame_FireFox.test(e)).map((e) => { if (regexValidFrame_FireFox.test(e)) { let isEval = false; if (/ > (eval|Function)/.test(e)) { e = e.replace(/ line (\d+)(?: > eval line \d+)* > (eval|Function):\d+:\d+/g, ":$1"); isEval = true; } const _data = e.split(/[@]/g); const _last = _data.pop(); if (!_last) { throw new Error("could not get last"); } const [_fileName, _lineNumber, _columnNumber] = extractLocation(_last); return makeStackFrame({ functionName: _data.join("@") || (isEval ? "eval" : null), fileName: _fileName, lineNumber: _lineNumber, columnNumber: _columnNumber }); } if (e.indexOf("(eval ") !== -1) { e = e.replace(/(\(eval at [^()]*)|(\),.*$)/g, ""); } if (e.indexOf("(at ") !== -1) { e = e.replace(/\(at /, "("); } const data = e.trim().split(/\s+/g).slice(1); const last = data.pop(); if (!last) { throw new Error("could not get last"); } const [fileName, lineNumber, columnNumber] = extractLocation(last); return makeStackFrame({ functionName: data.join(" ") || null, fileName, lineNumber, columnNumber }); }); return frames; } var parseError = async (error, contextLines) => { if (error === null) { throw new Error("You cannot pass a null object."); } if (typeof error === "string") { return parseStack(error.split(` `)).map((frame) => { return { type: "transpiled", frame }; }); } if (Array.isArray(error)) { return parseStack(error).map((frame) => { return { type: "transpiled", frame }; }); } const errorLocation = getLocationFromBuildError(error); if (errorLocation) { return [ { type: "symbolicated", frame: await resolveFileSource(errorLocation, contextLines) } ]; } if (typeof error.stack === "string") { return parseStack(error.stack.split(` `)).map((frame) => { return { type: "transpiled", frame }; }); } return []; }; // src/error-overlay/react-overlay/utils/unmapper.ts import { Internals as Internals16 } from "remotion"; // src/error-overlay/react-overlay/utils/get-lines-around.ts function getLinesAround(line2, count, lines2) { const result = []; for (let index = Math.max(0, line2 - 1 - count);index <= Math.min(lines2.length - 1, line2 - 1 + count); ++index) { result.push({ lineNumber: index + 1, content: lines2[index], highlight: index === line2 - 1 }); } return result; } // src/error-overlay/react-overlay/utils/get-source-map.ts import { SourceMapConsumer } from "source-map"; var getOriginalPosition = (source_map, line2, column) => { const result = source_map.originalPositionFor({ line: line2, column }); return { line: result.line, column: result.column, source: result.source }; }; function extractSourceMapUrl(fileContents) { const regex = /\/\/[#@] ?sourceMappingURL=([^\s'"]+)\s*$/gm; let match = null; for (;; ) { const next = regex.exec(fileContents); if (next == null) { break; } match = next; } if (!match?.[1]) { return null; } return match[1].toString(); } async function getSourceMap(fileUri, fileContents) { const sm = extractSourceMapUrl(fileContents); if (sm === null) { return null; } if (sm.indexOf("data:") === 0) { const base64 = /^data:application\/json;([\w=:"-]+;)*base64,/; const match2 = sm.match(base64); if (!match2) { throw new Error("Sorry, non-base64 inline source-map encoding is not supported."); } const converted = window.atob(sm.substring(match2[0].length)); return new SourceMapConsumer(JSON.parse(converted)); } const index = fileUri.lastIndexOf("/"); const url = fileUri.substring(0, index + 1) + sm; const obj = await fetch(url).then((res) => res.json()); return new SourceMapConsumer(obj); } // src/error-overlay/react-overlay/utils/unmapper.ts var getFileContents = async (fileName) => { const res = await fetch(fileName); const fileContents = await res.text(); return fileContents; }; var unmap = async (frames, contextLines) => { const transpiled = frames.filter((s) => s.type === "transpiled").map((s) => s.frame); const uniqueFileNames = [ ...new Set(transpiled.map((f) => f.fileName).filter(Internals16.truthy)) ]; const maps = await Promise.all(uniqueFileNames.map(async (fileName) => { const fileContents = await getFileContents(fileName); return getSourceMap(fileName, fileContents); })); const mapValues = {}; for (let i = 0;i < uniqueFileNames.length; i++) { mapValues[uniqueFileNames[i]] = maps[i]; } return frames.map((frame) => { if (frame.type === "symbolicated") { return frame.frame; } const map = mapValues[frame.frame.fileName]; if (!map) { return null; } const pos = getOriginalPosition(map, frame.frame.lineNumber, frame.frame.columnNumber); const { functionName } = frame.frame; let hasSource = null; hasSource = pos.source ? map.sourceContentFor(pos.source, false) : null; const scriptCode = hasSource && pos.line ? getLinesAround(pos.line, contextLines, hasSource.split(` `)) : null; return { originalColumnNumber: pos.column, originalFileName: pos.source, originalFunctionName: functionName, originalLineNumber: pos.line, originalScriptCode: scriptCode }; }).filter(Internals16.truthy); }; // src/error-overlay/react-overlay/utils/get-stack-frames.ts var getStackFrames = async (error, contextSize) => { const parsedFrames = await parseError(error, contextSize); const enhancedFrames = await unmap(parsedFrames, contextSize); if (enhancedFrames.map((f) => f.originalFileName).filter((f_1) => f_1 !== null && f_1 !== undefined).length === 0) { return null; } return enhancedFrames; }; // src/error-overlay/react-overlay/listen-to-runtime-errors.ts var CONTEXT_SIZE = 3; var getErrorRecord = async (error) => { const stackFrames = await getStackFrames(error, CONTEXT_SIZE); if (stackFrames === null || stackFrames === undefined) { return null; } return { error, contextSize: CONTEXT_SIZE, stackFrames }; }; // src/error-overlay/remotion-overlay/ErrorDisplay.tsx import { getLocationFromBuildError as getLocationFromBuildError2 } from "@remotion/studio-shared"; import { useMemo as useMemo44 } from "react"; // src/error-overlay/remotion-overlay/AskOnDiscord.tsx import { useCallback as useCallback30, useEffect as useEffect26 } from "react"; // src/components/Button.tsx import { forwardRef as forwardRef3, useMemo as useMemo40 } from "react"; import { jsx as jsx65 } from "react/jsx-runtime"; var button = { border: `1px solid ${INPUT_BORDER_COLOR_UNHOVERED}`, borderRadius: 4, backgroundColor: INPUT_BACKGROUND, appearance: "none", fontFamily: "inherit", fontSize: 14, color: "white", flexDirection: "row" }; var ButtonRefForwardFunction = ({ children, onClick, title: title2, disabled, style: style4, id, autoFocus, buttonContainerStyle }, ref) => { const combined = useMemo40(() => { return { ...button, ...style4 ?? {} }; }, [style4]); const buttonContainer = useMemo40(() => { return { padding: 10, cursor: disabled ? "inherit" : "pointer", fontSize: 14, opacity: disabled ? 0.7 : 1, ...buttonContainerStyle ?? {} }; }, [buttonContainerStyle, disabled]); return /* @__PURE__ */ jsx65("button", { ref, id, style: combined, type: "button", disabled, onClick, autoFocus, title: title2, children: /* @__PURE__ */ jsx65("div", { className: "css-reset", style: buttonContainer, children }) }); }; var Button = forwardRef3(ButtonRefForwardFunction); // src/error-overlay/remotion-overlay/AskOnDiscord.tsx import { jsx as jsx66, jsxs as jsxs25 } from "react/jsx-runtime"; var DISCORD_LINK = "https://remotion.dev/discord"; var AskOnDiscord = ({ canHaveKeyboardShortcuts }) => { const openInBrowser = useCallback30(() => { window.open(DISCORD_LINK, "_blank"); }, []); const { registerKeybinding } = useKeybinding(); useEffect26(() => { if (!canHaveKeyboardShortcuts) { return; } const onEditor = () => { openInBrowser(); }; const { unregister } = registerKeybinding({ event: "keydown", key: "d", callback: onEditor, commandCtrlKey: true, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); return () => unregister(); }, [canHaveKeyboardShortcuts, openInBrowser, registerKeybinding]); return /* @__PURE__ */ jsxs25(Button, { onClick: openInBrowser, children: [ "Ask on Discord", " ", canHaveKeyboardShortcuts ? /* @__PURE__ */ jsx66(ShortcutHint, { keyToPress: "d", cmdOrCtrl: true }) : null ] }); }; // src/error-overlay/remotion-overlay/CalculateMetadataErrorExplainer.tsx import { jsx as jsx67, jsxs as jsxs26 } from "react/jsx-runtime"; var CalculateMetadataErrorExplainer = () => { return /* @__PURE__ */ jsxs26("div", { style: style4, children: [ "This error occured while calling", " ", /* @__PURE__ */ jsx67("code", { style: inlineCodeSnippet, children: "calculateMetadata()" }), "." ] }); }; var style4 = { borderRadius: 3, color: "white", padding: 12, backgroundColor: BORDER_COLOR, fontSize: 14, fontFamily: "sans-serif" }; // src/error-overlay/remotion-overlay/CompositionIdsDropdown.tsx import { useEffect as useEffect27, useMemo as useMemo41, useRef as useRef19, useState as useState31 } from "react"; // src/error-overlay/remotion-overlay/CompositionIdListItem.tsx import React45 from "react"; import { jsx as jsx68 } from "react/jsx-runtime"; var listItemStyle = { padding: 8, cursor: "pointer", borderRadius: 4, lineHeight: 1.4, color: TEXT_COLOR, fontFamily: "inherit", fontSize: 14 }; var listItemActiveStyle = { backgroundColor: SELECTED_BACKGROUND }; var listItemHoverStyle = { backgroundColor: CLEAR_HOVER }; var CompositionIdListItem = ({ id, isActive, onSelect }) => { const [hover, setHover] = React45.useState(false); return /* @__PURE__ */ jsx68("div", { role: "button", onPointerEnter: () => setHover(true), onPointerLeave: () => setHover(false), onClick: () => onSelect(id), style: { ...listItemStyle, ...hover ? listItemHoverStyle : {}, ...isActive ? listItemActiveStyle : {} }, title: id, children: id }); }; // src/error-overlay/remotion-overlay/carets.tsx import { jsx as jsx69 } from "react/jsx-runtime"; var CaretRight2 = ({ size }) => { return /* @__PURE__ */ jsx69("svg", { style: { height: size ?? 20 }, "aria-hidden": "true", focusable: "false", role: "img", viewBox: "0 0 192 512", children: /* @__PURE__ */ jsx69("path", { fill: "currentColor", d: "M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z" }) }); }; var CaretDown2 = ({ invert, size }) => { return /* @__PURE__ */ jsx69("svg", { "aria-hidden": "true", focusable: "false", role: "img", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 320 512", style: { height: size ?? 20, transform: invert ? `rotate(180deg)` : "" }, children: /* @__PURE__ */ jsx69("path", { fill: "currentColor", d: "M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z" }) }); }; // src/error-overlay/remotion-overlay/CompositionIdsDropdown.tsx import { jsx as jsx70, jsxs as jsxs27 } from "react/jsx-runtime"; var containerStyle2 = { display: "inline-block", position: "relative" }; var dropdownStyle = { position: "absolute", top: "110%", left: 0, width: 320, maxHeight: 300, overflowY: "auto", backgroundColor: INPUT_BACKGROUND, border: `1px solid ${INPUT_BORDER_COLOR_UNHOVERED}`, borderRadius: 8, padding: 8, boxShadow: "0 6px 24px rgba(0,0,0,0.35)", zIndex: 1000, fontFamily: "inherit", fontSize: 14 }; var searchStyle = { width: "100%", padding: "6px 8px", borderRadius: 6, border: `1px solid ${INPUT_BORDER_COLOR_UNHOVERED}`, background: INPUT_BACKGROUND, color: TEXT_COLOR, marginBottom: 8, outline: "none", fontFamily: "inherit", fontSize: 14 }; var CompositionIdsDropdown = ({ compositionIds, currentId }) => { const [open, setOpen] = useState31(false); const [query, setQuery] = useState31(""); const containerRef = useRef19(null); const filtered = useMemo41(() => { const q = query.trim().toLowerCase(); if (!q) { return compositionIds; } return compositionIds.filter((id) => id.toLowerCase().includes(q)); }, [compositionIds, query]); const onSelect = (id) => { const isQuery = window.remotion_isReadOnlyStudio; if (isQuery) { window.location.href = `${window.location.pathname}?/${id}`; } else { window.location.href = `/${id}`; } }; useEffect27(() => { if (!open) { return; } const onClickAway = (e) => { if (!containerRef.current) { return; } if (!containerRef.current.contains(e.target)) { setOpen(false); } }; const onKey = (e) => { if (e.key === "Escape") { setOpen(false); } }; document.addEventListener("mousedown", onClickAway); document.addEventListener("touchstart", onClickAway, { passive: true }); document.addEventListener("keydown", onKey); return () => { document.removeEventListener("mousedown", onClickAway); document.removeEventListener("touchstart", onClickAway); document.removeEventListener("keydown", onKey); }; }, [open, containerRef]); return /* @__PURE__ */ jsxs27("div", { ref: containerRef, style: containerStyle2, children: [ /* @__PURE__ */ jsxs27(Button, { onClick: () => setOpen((p) => !p), buttonContainerStyle: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, minWidth: 180 }, children: [ /* @__PURE__ */ jsx70("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", fontSize: "14px", lineHeight: "24px" }, children: currentId ?? "Select composition" }), /* @__PURE__ */ jsx70(CaretDown2, { size: 20, invert: open }) ] }), open ? /* @__PURE__ */ jsxs27("div", { style: dropdownStyle, children: [ /* @__PURE__ */ jsx70("input", { value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Search compositions...", style: searchStyle, "aria-label": "Search compositions" }), /* @__PURE__ */ jsx70("div", { children: filtered.length === 0 ? /* @__PURE__ */ jsx70("div", { style: { opacity: 0.7, padding: 8, textAlign: "center" }, children: "No compositions found" }) : filtered.map((id) => /* @__PURE__ */ jsx70(CompositionIdListItem, { id, isActive: id === currentId, onSelect }, id)) }) ] }) : null ] }); }; // src/error-overlay/react-overlay/index.ts import { Internals as Internals17 } from "remotion"; var didUnmountReactApp = () => { return !Internals17.getPreviewDomElement()?.hasChildNodes(); }; // src/error-overlay/remotion-overlay/DismissButton.tsx import { useCallback as useCallback31 } from "react"; import { jsx as jsx71 } from "react/jsx-runtime"; var size = { height: 20, width: 20 }; var style5 = { appearance: "none", WebkitAppearance: "none", backgroundColor: "transparent", border: "none", cursor: "pointer" }; var DismissButton = () => { const dismiss = useCallback31(() => { clearUrl(); }, []); return /* @__PURE__ */ jsx71("button", { type: "button", style: style5, onClick: dismiss, children: /* @__PURE__ */ jsx71("svg", { focusable: "false", role: "img", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 352 512", style: size, children: /* @__PURE__ */ jsx71("path", { fill: "white", d: "M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" }) }) }); }; // src/error-overlay/remotion-overlay/ErrorMessage.tsx import { PlayerInternals as PlayerInternals9 } from "@remotion/player"; import { useCallback as useCallback32, useMemo as useMemo42, useRef as useRef20, useState as useState32 } from "react"; import { jsx as jsx72, jsxs as jsxs28 } from "react/jsx-runtime"; var fontSize = 24; var lineHeight = 1.5; var maxLines = 2; var buttonSize = 32; var maskImage = "linear-gradient(to bottom, white 60%, transparent)"; var container16 = { position: "relative", marginBottom: 15 }; var messageContainer = { overflow: "hidden" }; var textContainer = { fontSize, lineHeight }; var moreLine = { width: "100%", display: "flex", justifyContent: "center", position: "absolute", border: `1px solid ${INPUT_BORDER_COLOR_HOVERED}`, height: 0, marginTop: 4 }; var moreButton = { height: buttonSize, width: buttonSize, borderRadius: buttonSize / 2, backgroundColor: INPUT_BACKGROUND, border: `1px solid ${INPUT_BORDER_COLOR_UNHOVERED}`, marginTop: -buttonSize / 2, display: "flex", justifyContent: "center", alignItems: "center", cursor: "pointer", color: "white" }; var ErrorMessage = ({ message }) => { const [expanded, setExpanded] = useState32(false); const ref = useRef20(null); const size2 = PlayerInternals9.useElementSize(ref, { shouldApplyCssTransforms: false, triggerOnWindowResize: true }); const errorLines = size2 ? size2.height / (lineHeight * fontSize) : null; const style6 = useMemo42(() => { const isExpanded = expanded || errorLines !== null && errorLines <= maxLines; return { ...messageContainer, maxHeight: isExpanded ? undefined : fontSize * lineHeight * maxLines, maskImage: isExpanded ? undefined : maskImage, WebkitMaskImage: isExpanded ? undefined : maskImage }; }, [errorLines, expanded]); const toggle = useCallback32(() => { setExpanded((e) => !e); }, []); return /* @__PURE__ */ jsxs28("div", { style: container16, children: [ /* @__PURE__ */ jsx72("div", { style: style6, children: /* @__PURE__ */ jsx72("div", { ref, style: textContainer, children: message }) }), errorLines !== null && errorLines > maxLines ? /* @__PURE__ */ jsx72("div", { style: moreLine, children: /* @__PURE__ */ jsx72("button", { type: "button", onClick: toggle, style: moreButton, children: /* @__PURE__ */ jsx72(CaretDown2, { invert: expanded }) }) }) : null ] }); }; // src/error-overlay/remotion-overlay/Symbolicating.tsx import { jsx as jsx73, jsxs as jsxs29 } from "react/jsx-runtime"; var Symbolicating = (props) => { return /* @__PURE__ */ jsxs29("svg", { id: "loading", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 32 32", width: "16", height: "16", fill: "white", ...props, children: [ /* @__PURE__ */ jsx73("path", { opacity: ".1", d: "M14 0 H18 V8 H14 z", transform: "rotate(0 16 16)", children: /* @__PURE__ */ jsx73("animate", { attributeName: "opacity", from: "1", to: ".1", dur: "1s", repeatCount: "indefinite", begin: "0" }) }), /* @__PURE__ */ jsx73("path", { opacity: ".1", d: "M14 0 H18 V8 H14 z", transform: "rotate(45 16 16)", children: /* @__PURE__ */ jsx73("animate", { attributeName: "opacity", from: "1", to: ".1", dur: "1s", repeatCount: "indefinite", begin: "0.125s" }) }), /* @__PURE__ */ jsx73("path", { opacity: ".1", d: "M14 0 H18 V8 H14 z", transform: "rotate(90 16 16)", children: /* @__PURE__ */ jsx73("animate", { attributeName: "opacity", from: "1", to: ".1", dur: "1s", repeatCount: "indefinite", begin: "0.25s" }) }), /* @__PURE__ */ jsx73("path", { opacity: ".1", d: "M14 0 H18 V8 H14 z", transform: "rotate(135 16 16)", children: /* @__PURE__ */ jsx73("animate", { attributeName: "opacity", from: "1", to: ".1", dur: "1s", repeatCount: "indefinite", begin: "0.375s" }) }), /* @__PURE__ */ jsx73("path", { opacity: ".1", d: "M14 0 H18 V8 H14 z", transform: "rotate(180 16 16)", children: /* @__PURE__ */ jsx73("animate", { attributeName: "opacity", from: "1", to: ".1", dur: "1s", repeatCount: "indefinite", begin: "0.5s" }) }), /* @__PURE__ */ jsx73("path", { opacity: ".1", d: "M14 0 H18 V8 H14 z", transform: "rotate(225 16 16)", children: /* @__PURE__ */ jsx73("animate", { attributeName: "opacity", from: "1", to: ".1", dur: "1s", repeatCount: "indefinite", begin: "0.675s" }) }), /* @__PURE__ */ jsx73("path", { opacity: ".1", d: "M14 0 H18 V8 H14 z", transform: "rotate(270 16 16)", children: /* @__PURE__ */ jsx73("animate", { attributeName: "opacity", from: "1", to: ".1", dur: "1s", repeatCount: "indefinite", begin: "0.75s" }) }), /* @__PURE__ */ jsx73("path", { opacity: ".1", d: "M14 0 H18 V8 H14 z", transform: "rotate(315 16 16)", children: /* @__PURE__ */ jsx73("animate", { attributeName: "opacity", from: "1", to: ".1", dur: "1s", repeatCount: "indefinite", begin: "0.875s" }) }) ] }); }; // src/error-overlay/remotion-overlay/ErrorTitle.tsx import { jsx as jsx74, jsxs as jsxs30, Fragment as Fragment12 } from "react/jsx-runtime"; var title2 = { marginBottom: 8, display: "flex", flexDirection: "row", justifyContent: "center" }; var left = { flex: 1, paddingRight: 14, fontWeight: "bold", maxWidth: "100%" }; var errName = { fontSize: 18, color: BLUE, display: "inline-block" }; var row2 = { display: "flex", flexDirection: "row", alignItems: "center" }; var spacer = { width: 5 }; var ErrorTitle = ({ name, message, symbolicating, canHaveDismissButton }) => { return /* @__PURE__ */ jsxs30("div", { style: title2, className: "css-reset", children: [ /* @__PURE__ */ jsxs30("div", { style: left, children: [ /* @__PURE__ */ jsx74("span", { style: errName, children: name }), /* @__PURE__ */ jsx74("br", {}), /* @__PURE__ */ jsxs30("div", { style: row2, children: [ symbolicating ? /* @__PURE__ */ jsxs30(Fragment12, { children: [ /* @__PURE__ */ jsx74(Symbolicating, {}), /* @__PURE__ */ jsx74("div", { style: spacer }) ] }) : null, /* @__PURE__ */ jsx74(ErrorMessage, { message }) ] }) ] }), didUnmountReactApp() ? null : canHaveDismissButton ? /* @__PURE__ */ jsx74(DismissButton, {}) : null ] }); }; // src/error-overlay/remotion-overlay/HelpLink.tsx import { useCallback as useCallback33, useEffect as useEffect28 } from "react"; import { jsx as jsx75, jsxs as jsxs31 } from "react/jsx-runtime"; var buttonStyle2 = { backgroundColor: BLUE, color: "white" }; var HelpLink = ({ canHaveKeyboardShortcuts, link }) => { const openLink = useCallback33(() => { window.open(link.url, "_blank"); }, [link]); const { registerKeybinding } = useKeybinding(); useEffect28(() => { if (!canHaveKeyboardShortcuts) { return; } const onEditor = () => { openLink(); }; const { unregister } = registerKeybinding({ event: "keydown", key: "h", callback: onEditor, commandCtrlKey: true, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); return () => unregister(); }, [canHaveKeyboardShortcuts, openLink, registerKeybinding]); return /* @__PURE__ */ jsxs31(Button, { style: buttonStyle2, onClick: openLink, children: [ "Help: ", '"', link.title, '"', canHaveKeyboardShortcuts ? /* @__PURE__ */ jsx75(ShortcutHint, { keyToPress: "h", cmdOrCtrl: true }) : null ] }); }; // src/error-overlay/remotion-overlay/OpenInEditor.tsx import { useCallback as useCallback34, useEffect as useEffect29, useMemo as useMemo43, useReducer, useRef as useRef21 } from "react"; import { jsx as jsx76, jsxs as jsxs32 } from "react/jsx-runtime"; var initialState = { type: "idle" }; var reducer = (state, action) => { if (action.type === "start") { return { type: "load" }; } if (action.type === "fail") { return { type: "error" }; } if (action.type === "reset") { return { type: "idle" }; } if (action.type === "succeed") { return { type: "success" }; } return state; }; var OpenInEditor = ({ stack, canHaveKeyboardShortcuts }) => { const isMounted = useRef21(true); const [state, dispatch] = useReducer(reducer, initialState); const { registerKeybinding } = useKeybinding(); const dispatchIfMounted = useCallback34((payload) => { if (isMounted.current === false) return; dispatch(payload); }, []); const openInBrowser = useCallback34(() => { dispatch({ type: "start" }); openInEditor(stack).then((res) => res.json()).then((data) => { if (data.success) { dispatchIfMounted({ type: "succeed" }); } else { dispatchIfMounted({ type: "fail" }); } }).catch((err) => { dispatchIfMounted({ type: "fail" }); console.log("Could not open browser", err); }).finally(() => { setTimeout(() => { dispatchIfMounted({ type: "reset" }); }, 2000); }); }, [dispatchIfMounted, stack]); useEffect29(() => { return () => { isMounted.current = false; }; }, []); useEffect29(() => { if (!canHaveKeyboardShortcuts) { return; } const onEditor = () => { openInBrowser(); }; const { unregister } = registerKeybinding({ event: "keydown", key: "o", callback: onEditor, commandCtrlKey: true, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); return () => unregister(); }, [canHaveKeyboardShortcuts, openInBrowser, registerKeybinding]); const label4 = useMemo43(() => { switch (state.type) { case "error": return "Failed to open"; case "idle": return `Open in ${window.remotion_editorName}`; case "success": return `Opened in ${window.remotion_editorName}`; case "load": return `Opening...`; default: throw new Error("invalid state"); } }, [state.type]); return /* @__PURE__ */ jsxs32(Button, { onClick: openInBrowser, disabled: state.type !== "idle", children: [ label4, canHaveKeyboardShortcuts ? /* @__PURE__ */ jsx76(ShortcutHint, { keyToPress: "o", cmdOrCtrl: true }) : null ] }); }; // src/error-overlay/remotion-overlay/Retry.tsx import { jsx as jsx77 } from "react/jsx-runtime"; var RetryButton = ({ onClick }) => { return /* @__PURE__ */ jsx77(Button, { onClick, children: "Retry calculateMetadata()" }); }; // src/error-overlay/remotion-overlay/SearchGitHubIssues.tsx import { useCallback as useCallback35, useEffect as useEffect30 } from "react"; import { jsx as jsx78, jsxs as jsxs33 } from "react/jsx-runtime"; var SearchGithubIssues = ({ message, canHaveKeyboardShortcuts }) => { const openInBrowser = useCallback35(() => { window.open(`https://github.com/remotion-dev/remotion/issues?q=${encodeURIComponent(message)}`, "_blank"); }, [message]); const { registerKeybinding } = useKeybinding(); useEffect30(() => { if (!canHaveKeyboardShortcuts) { return; } const onEditor = () => { openInBrowser(); }; const { unregister } = registerKeybinding({ event: "keydown", key: "g", callback: onEditor, commandCtrlKey: true, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); return () => unregister(); }, [canHaveKeyboardShortcuts, openInBrowser, registerKeybinding]); return /* @__PURE__ */ jsxs33(Button, { onClick: openInBrowser, children: [ "Search GitHub Issues", " ", canHaveKeyboardShortcuts ? /* @__PURE__ */ jsx78(ShortcutHint, { keyToPress: "g", cmdOrCtrl: true }) : null ] }); }; // src/error-overlay/remotion-overlay/StackFrame.tsx import { useCallback as useCallback36, useState as useState33 } from "react"; // src/error-overlay/remotion-overlay/CodeFrame.tsx import { jsx as jsx79, jsxs as jsxs34 } from "react/jsx-runtime"; var container17 = { display: "flex", flexDirection: "row", width: "100%" }; var frame = { backgroundColor: "#070707", marginBottom: 20, overflowY: "auto" }; var lineNumber = { whiteSpace: "pre", paddingRight: 12, color: "inherit", fontSize: 14, lineHeight: 1.7, width: 60, flexShrink: 0, display: "inline-flex", alignItems: "center", justifyContent: "flex-end", fontFamily: "monospace" }; var CodeFrame = ({ source, lineNumberWidth }) => { return /* @__PURE__ */ jsx79("div", { style: frame, className: HORIZONTAL_SCROLLBAR_CLASSNAME, children: source.map((s, j) => { return /* @__PURE__ */ jsxs34("div", { style: container17, children: [ /* @__PURE__ */ jsx79("div", { style: { ...lineNumber, backgroundColor: s.highlight ? "white" : "#121212", color: s.highlight ? "black" : "rgba(255, 255, 255, 0.6)" }, children: String(s.lineNumber).padStart(lineNumberWidth, " ") }), /* @__PURE__ */ jsx79("div", { style: { fontFamily: "monospace", whiteSpace: "pre", tabSize: 2, color: s.highlight ? "white" : "rgba(255, 255, 255, 0.6)", backgroundColor: s.highlight ? BLUE : "transparent", lineHeight: 1.7, paddingRight: 12, paddingLeft: 12 }, children: s.content }) ] }, j); }) }); }; // src/error-overlay/remotion-overlay/format-location.ts var formatLocation = (location) => { if (location.startsWith("webpack://")) { return location.replace("webpack://", ""); } return location; }; // src/error-overlay/remotion-overlay/StackFrame.tsx import { jsx as jsx80, jsxs as jsxs35 } from "react/jsx-runtime"; var location = { color: "rgba(255, 255, 255, 0.6)", fontFamily: "monospace", fontSize: 14 }; var header = { paddingLeft: 14, paddingTop: 10, paddingBottom: 10, paddingRight: 14, display: "flex", flexDirection: "row", alignItems: "center", borderBottom: "1px solid rgb(66, 144, 245)", backgroundColor: "black" }; var left2 = { paddingRight: 14, flex: 1 }; var fnName = { fontSize: 14, lineHeight: 1.5, marginBottom: 3 }; var StackElement = ({ s, lineNumberWidth, isFirst, defaultFunctionName }) => { const [showCodeFrame, setShowCodeFrame] = useState33(() => !s.originalFileName?.includes("node_modules") && !s.originalFileName?.startsWith("webpack/") || isFirst); const toggleCodeFrame = useCallback36(() => { setShowCodeFrame((f) => !f); }, []); return /* @__PURE__ */ jsxs35("div", { className: "css-reset", children: [ /* @__PURE__ */ jsxs35("div", { style: header, children: [ /* @__PURE__ */ jsxs35("div", { style: left2, children: [ /* @__PURE__ */ jsx80("div", { style: fnName, children: s.originalFunctionName ?? defaultFunctionName }), s.originalFileName ? /* @__PURE__ */ jsxs35("div", { style: location, children: [ formatLocation(s.originalFileName), ":", s.originalLineNumber ] }) : null ] }), s.originalScriptCode && s.originalScriptCode.length > 0 ? /* @__PURE__ */ jsx80(Button, { onClick: toggleCodeFrame, children: showCodeFrame ? /* @__PURE__ */ jsx80(CaretDown2, { invert: false }) : /* @__PURE__ */ jsx80(CaretRight2, {}) }) : null ] }), /* @__PURE__ */ jsx80("div", { children: s.originalScriptCode && s.originalScriptCode.length > 0 && showCodeFrame ? /* @__PURE__ */ jsx80(CodeFrame, { lineNumberWidth, source: s.originalScriptCode }) : null }) ] }); }; // src/error-overlay/remotion-overlay/get-help-link.ts var getHelpLink = (message) => { if (message.includes("See https://www.remotion.dev/docs/the-fundamentals#defining-compositions")) { return { title: "Defining compositions", url: "See https://www.remotion.dev/docs/the-fundamentals#defining-compositions" }; } if (message.includes("https://remotion.dev/docs/wrong-composition-mount")) { return { title: "Wrongly mounted ", url: "https://remotion.dev/docs/wrong-composition-mount" }; } if (message.includes("https://remotion.dev/docs/staticfile-relative-paths")) { return { title: "staticFile() relative paths", url: "https://remotion.dev/docs/staticfile-relative-paths" }; } if (message.includes("https://remotion.dev/docs/staticfile-remote-urls")) { return { title: "staticFile() remote URLs", url: "https://remotion.dev/docs/staticfile-remote-urls" }; } if (message.includes("https://remotion.dev/docs/non-seekable-media")) { return { title: "Non-seekable media", url: "https://remotion.dev/docs/non-seekable-media" }; } if (message.includes("https://remotion.dev/docs/media-playback-error")) { return { title: "Media playback error", url: "https://remotion.dev/docs/media-playback-error" }; } if (message.includes("Div is not part of the THREE")) { return { title: " inside ", url: "https://remotion.dev/docs/sequence#note-for-remotionthree" }; } return null; }; // src/error-overlay/remotion-overlay/ErrorDisplay.tsx import { jsx as jsx81, jsxs as jsxs36, Fragment as Fragment13 } from "react/jsx-runtime"; var stack = { marginTop: 17, overflowX: "scroll", marginBottom: "10vh" }; var spacer2 = { width: 5, display: "inline-block" }; var ErrorDisplay = ({ display, keyboardShortcuts, onRetry, canHaveDismissButton, calculateMetadata }) => { const compositionIds = window?.remotion_seenCompositionIds ?? []; const highestLineNumber = Math.max(...display.stackFrames.map((s) => s.originalScriptCode).flat(1).map((s) => s?.lineNumber ?? 0)); const message = useMemo44(() => { const location2 = getLocationFromBuildError2(display.error); if (!location2) { return display.error.message; } return location2.message.replace(/\\n/g, ` `).replace(/\\t/g, " ").replace(/^error:/, "").trim(); }, [display.error]); const lineNumberWidth = String(highestLineNumber).length; const helpLink = getHelpLink(message); const getCurrentCompositionId = () => { const route = getRoute(); const id = route.startsWith("/") ? route.slice(1) : route; return compositionIds.includes(id) ? id : compositionIds[0] ?? null; }; return /* @__PURE__ */ jsxs36("div", { children: [ /* @__PURE__ */ jsx81(ErrorTitle, { symbolicating: false, name: display.error.name, message, canHaveDismissButton }), helpLink ? /* @__PURE__ */ jsxs36(Fragment13, { children: [ /* @__PURE__ */ jsx81(HelpLink, { link: helpLink, canHaveKeyboardShortcuts: keyboardShortcuts }), /* @__PURE__ */ jsx81("div", { style: spacer2 }) ] }) : null, display.stackFrames.length > 0 && window.remotion_editorName ? /* @__PURE__ */ jsxs36(Fragment13, { children: [ /* @__PURE__ */ jsx81(OpenInEditor, { canHaveKeyboardShortcuts: keyboardShortcuts, stack: display.stackFrames[0] }), /* @__PURE__ */ jsx81("div", { style: spacer2 }) ] }) : null, compositionIds.length > 0 ? /* @__PURE__ */ jsxs36(Fragment13, { children: [ /* @__PURE__ */ jsx81(CompositionIdsDropdown, { compositionIds, currentId: getCurrentCompositionId() }), /* @__PURE__ */ jsx81("div", { style: spacer2 }) ] }) : null, /* @__PURE__ */ jsx81(SearchGithubIssues, { canHaveKeyboardShortcuts: keyboardShortcuts, message: display.error.message }), /* @__PURE__ */ jsx81("div", { style: spacer2 }), /* @__PURE__ */ jsx81(AskOnDiscord, { canHaveKeyboardShortcuts: keyboardShortcuts }), onRetry ? /* @__PURE__ */ jsxs36(Fragment13, { children: [ /* @__PURE__ */ jsx81("div", { style: spacer2 }), /* @__PURE__ */ jsx81(RetryButton, { onClick: onRetry }) ] }) : null, calculateMetadata ? /* @__PURE__ */ jsxs36(Fragment13, { children: [ /* @__PURE__ */ jsx81("br", {}), /* @__PURE__ */ jsx81(Spacing, { y: 0.5 }), /* @__PURE__ */ jsx81(CalculateMetadataErrorExplainer, {}) ] }) : null, /* @__PURE__ */ jsx81("div", { style: stack, className: HORIZONTAL_SCROLLBAR_CLASSNAME, children: display.stackFrames.map((s, i) => { return /* @__PURE__ */ jsx81(StackElement, { isFirst: i === 0, s, lineNumberWidth, defaultFunctionName: "(anonymous function)" }, i); }) }) ] }); }; // src/error-overlay/remotion-overlay/ErrorLoader.tsx import { jsx as jsx82, jsxs as jsxs37 } from "react/jsx-runtime"; var container18 = { width: "100%", maxWidth: 1000, paddingLeft: 14, paddingRight: 14, marginLeft: "auto", marginRight: "auto", fontFamily: "SF Pro Text, sans-serif", paddingTop: "5vh" }; var errorWhileErrorStyle = { color: "white", lineHeight: 1.5, whiteSpace: "pre" }; var ErrorLoader = ({ error, keyboardShortcuts, onRetry, canHaveDismissButton, calculateMetadata }) => { const [state, setState] = useState34({ type: "loading" }); useEffect31(() => { getErrorRecord(error).then((record) => { if (record) { setState({ type: "symbolicated", record }); } else { setState({ type: "no-record" }); } }).catch((err) => { setState({ err, type: "error" }); }); }, [error]); if (state.type === "loading") { return /* @__PURE__ */ jsx82("div", { style: container18, children: /* @__PURE__ */ jsx82(ErrorTitle, { symbolicating: true, name: error.name, message: error.message, canHaveDismissButton }) }); } if (state.type === "error") { return /* @__PURE__ */ jsxs37("div", { style: container18, children: [ /* @__PURE__ */ jsx82(ErrorTitle, { symbolicating: false, name: error.name, message: error.message, canHaveDismissButton }), /* @__PURE__ */ jsx82("div", { style: errorWhileErrorStyle, children: "Error while getting stack trace:" }), /* @__PURE__ */ jsx82("div", { style: errorWhileErrorStyle, children: state.err.stack }), /* @__PURE__ */ jsx82("div", { style: errorWhileErrorStyle, children: "Report this in the Remotion repo." }) ] }); } if (state.type === "no-record") { return /* @__PURE__ */ jsxs37("div", { style: container18, children: [ /* @__PURE__ */ jsx82(ErrorTitle, { symbolicating: false, name: error.name, message: error.message, canHaveDismissButton }), /* @__PURE__ */ jsx82("div", { style: errorWhileErrorStyle, children: "Check the Terminal and browser console for error messages." }) ] }); } return /* @__PURE__ */ jsx82("div", { style: container18, children: /* @__PURE__ */ jsx82(ErrorDisplay, { keyboardShortcuts, display: state.record, onRetry, canHaveDismissButton, calculateMetadata }) }); }; // src/components/Canvas.tsx import { useCallback as useCallback40, useContext as useContext28, useEffect as useEffect34, useMemo as useMemo50, useState as useState36 } from "react"; import { Internals as Internals22, watchStaticFile } from "remotion"; // src/helpers/get-effective-translation.ts var getEffectiveXTranslation = ({ canvasSize, scale, compositionWidth, translation }) => { const maxTranslation = Math.abs(canvasSize.width / 2 + scale * compositionWidth / 2 - MUST_BE_INSIDE_CANVAS); return Math.max(-maxTranslation, Math.min(translation.x, maxTranslation)); }; var MUST_BE_INSIDE_CANVAS = 50; var getEffectiveYTranslation = ({ canvasSize, scale, compositionHeight, translation }) => { const maxTranslation = Math.abs(canvasSize.height / 2 + scale * compositionHeight / 2) - MUST_BE_INSIDE_CANVAS; return Math.max(-maxTranslation, Math.min(translation.y, maxTranslation)); }; var getEffectiveTranslation = ({ canvasSize, scale, compositionHeight, compositionWidth, translation }) => { return { x: getEffectiveXTranslation({ canvasSize, compositionWidth, scale, translation }), y: getEffectiveYTranslation({ canvasSize, compositionHeight, scale, translation }) }; }; var getCenterPointWhileScrolling = ({ size: size2, clientX, clientY, compositionWidth, compositionHeight, scale, translation }) => { const mouseLeft = clientX - size2.left; const mouseTop = clientY - size2.top; const contentLeftPoint = size2.width / 2 - compositionWidth * scale / 2 - translation.x; const contentTopPoint = size2.height / 2 - compositionHeight * scale / 2 - translation.y; const offsetFromVideoLeft = Math.min(compositionWidth, Math.max(0, (mouseLeft - contentLeftPoint) / scale)); const offsetFromVideoTop = Math.min(compositionHeight, Math.max(0, (mouseTop - contentTopPoint) / scale)); return { centerX: offsetFromVideoLeft, centerY: offsetFromVideoTop }; }; // src/helpers/smooth-zoom.ts var BASE = Math.E / 4; var MIN_ZOOM = 0.05; var MAX_ZOOM = 10; function logN(val) { return Math.log(val) / Math.log(BASE); } var smoothenZoom = (input) => { return BASE ** (input - 1); }; var unsmoothenZoom = (input) => { if (input < 0) { return MAX_ZOOM; } return Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, logN(input) + 1)); }; // src/components/EditorGuides/index.tsx import { useContext as useContext24, useMemo as useMemo47 } from "react"; import { Internals as Internals19 } from "remotion"; // src/helpers/use-studio-canvas-dimensions.ts import { PlayerInternals as PlayerInternals10 } from "@remotion/player"; import { useContext as useContext22, useMemo as useMemo45 } from "react"; import { Internals as Internals18 } from "remotion"; var useStudioCanvasDimensions = ({ canvasSize, contentDimensions, assetMetadata }) => { const { size: previewSize } = useContext22(Internals18.PreviewSizeContext); const { centerX, centerY, scale } = useMemo45(() => { if (contentDimensions === "none" || contentDimensions === null || assetMetadata && assetMetadata.type === "not-found" || !canvasSize) { return { centerX: previewSize.translation.x, centerY: previewSize.translation.y, scale: 1 }; } return PlayerInternals10.calculateCanvasTransformation({ canvasSize, compositionHeight: contentDimensions.height, compositionWidth: contentDimensions.width, previewSize: previewSize.size }); }, [ canvasSize, contentDimensions, previewSize.size, previewSize.translation.y, previewSize.translation.x, assetMetadata ]); const canvasPosition = useMemo45(() => { return { left: centerX - previewSize.translation.x, top: centerY - previewSize.translation.y, width: contentDimensions === "none" || !contentDimensions ? canvasSize?.width || 0 : contentDimensions.width * scale, height: contentDimensions === "none" || !contentDimensions ? canvasSize?.height || 0 : contentDimensions.height * scale }; }, [ scale, centerX, previewSize.translation.x, previewSize.translation.y, centerY, canvasSize, contentDimensions ]); return { canvasPosition, scale }; }; // src/components/EditorGuides/Guide.tsx import { memo, useCallback as useCallback37, useContext as useContext23, useMemo as useMemo46 } from "react"; import { NoReactInternals as NoReactInternals6 } from "remotion/no-react"; import { jsx as jsx83 } from "react/jsx-runtime"; var PADDING_FOR_EASY_DRAG = 4; var GuideComp = ({ guide, canvasDimensions, scale }) => { const { shouldCreateGuideRef, setGuidesList, setSelectedGuideId, selectedGuideId, setHoveredGuideId, hoveredGuideId } = useContext23(EditorShowGuidesContext); const onPointerEnter = useCallback37(() => { setHoveredGuideId(() => guide.id); }, [guide.id, setHoveredGuideId]); const onPointerLeave = useCallback37(() => { setHoveredGuideId(() => null); }, [setHoveredGuideId]); const isVerticalGuide = guide.orientation === "vertical"; const guideStyle = useMemo46(() => { const canvasPosition = isVerticalGuide ? canvasDimensions.left : canvasDimensions.top; const guidePosition = guide.position * scale + canvasPosition; return { position: "absolute", width: `${isVerticalGuide ? "1px" : "100%"}`, height: `${isVerticalGuide ? "100%" : "1px"}`, left: `${isVerticalGuide ? guidePosition - PADDING_FOR_EASY_DRAG : 0}px`, top: `${isVerticalGuide ? 0 : guidePosition - PADDING_FOR_EASY_DRAG}px`, cursor: `${isVerticalGuide ? "ew-resize" : "ns-resize"}`, padding: isVerticalGuide ? `0 ${PADDING_FOR_EASY_DRAG}px` : `${PADDING_FOR_EASY_DRAG}px 0` }; }, [guide, scale, canvasDimensions, isVerticalGuide]); const guideContentStyle = useMemo46(() => { return { position: "relative", minWidth: `${isVerticalGuide ? "1px" : `calc(100% + ${RULER_WIDTH}px`}`, minHeight: `${isVerticalGuide ? `calc(100% + ${RULER_WIDTH}px` : "1px"}`, top: `${isVerticalGuide ? `-${RULER_WIDTH}px` : "0px"}`, left: `${isVerticalGuide ? "0px" : `-${RULER_WIDTH}px`}`, display: guide.show ? "block" : "none", backgroundColor: selectedGuideId === guide.id || hoveredGuideId === guide.id ? SELECTED_GUIDE : UNSELECTED_GUIDE }; }, [isVerticalGuide, guide.show, guide.id, selectedGuideId, hoveredGuideId]); const onMouseDown = useCallback37((e) => { e.preventDefault(); if (e.button !== 0) { return; } shouldCreateGuideRef.current = true; document.body.style.cursor = "no-drop"; setSelectedGuideId(() => guide.id); }, [shouldCreateGuideRef, setSelectedGuideId, guide.id]); const values = useMemo46(() => { return [ { id: "1", keyHint: null, label: "Remove guide", leftItem: null, onClick: () => { setGuidesList((prevState) => { const newGuides = prevState.filter((selected) => { return selected.id !== guide.id; }); persistGuidesList(newGuides); return newGuides; }); }, quickSwitcherLabel: null, subMenu: null, type: "item", value: "remove" } ]; }, [guide.id, setGuidesList]); return /* @__PURE__ */ jsx83(ContextMenu, { values, children: /* @__PURE__ */ jsx83("div", { style: guideStyle, onMouseDown, className: "__remotion_editor_guide", onPointerEnter, onPointerLeave, children: /* @__PURE__ */ jsx83("div", { style: guideContentStyle, className: [ "__remotion_editor_guide_content", selectedGuideId === guide.id || hoveredGuideId === guide.id ? "__remotion_editor_guide_selected" : null ].filter(NoReactInternals6.truthy).join(" ") }) }) }); }; var Guide_default = memo(GuideComp); // src/components/EditorGuides/index.tsx import { jsx as jsx84, Fragment as Fragment14 } from "react/jsx-runtime"; var EditorGuides = ({ canvasSize, contentDimensions, assetMetadata }) => { const { canvasPosition: canvasDimensions, scale } = useStudioCanvasDimensions({ canvasSize, contentDimensions, assetMetadata }); const { canvasContent } = useContext24(Internals19.CompositionManager); if (canvasContent === null || canvasContent.type !== "composition") { throw new Error("Expected to be in a composition"); } const { guidesList } = useContext24(EditorShowGuidesContext); const guidesForThisComposition = useMemo47(() => { return guidesList.filter((guide) => { return guide.compositionId === canvasContent.compositionId; }); }, [canvasContent.compositionId, guidesList]); return /* @__PURE__ */ jsx84(Fragment14, { children: guidesForThisComposition.map((guide) => { return /* @__PURE__ */ jsx84(Guide_default, { guide, canvasDimensions, scale }, guide.id); }) }); }; var EditorGuides_default = EditorGuides; // src/components/EditorRuler/index.tsx import { useCallback as useCallback39, useContext as useContext26, useEffect as useEffect33, useMemo as useMemo49, useRef as useRef23 } from "react"; // src/helpers/editor-ruler.ts var drawLabel = ({ orientation, context, label: label4, originDistance, color }) => { context.fillStyle = color; if (orientation === "horizontal") { context.fillText(label4, originDistance + 4, 16); } else { context.rotate(-Math.PI / 2); context.fillText(label4, -originDistance + 4, 16); context.rotate(Math.PI / 2); } }; var drawGradient = ({ orientation, context, originDistance, canvasHeight, canvasWidth }) => { const size2 = 250; const startX = orientation === "horizontal" ? originDistance - size2 / 2 : 0; const startY = orientation === "horizontal" ? 0 : originDistance - size2 / 2; const endX = orientation === "horizontal" ? originDistance + size2 / 2 : canvasWidth; const endY = orientation === "horizontal" ? canvasHeight : originDistance + size2 / 2; const grd = context.createLinearGradient(startX, startY, endX, endY); grd.addColorStop(0, BACKGROUND__TRANSPARENT); grd.addColorStop(0.25, BACKGROUND); grd.addColorStop(0.75, BACKGROUND); grd.addColorStop(1, BACKGROUND__TRANSPARENT); context.fillStyle = grd; context.fillRect(startX, startY, endX - startX, endY - startY); }; var drawGuide = ({ selectedGuide, scale, startMarking, context, canvasHeight, canvasWidth, orientation, originOffset }) => { const originDistance = rulerValueToPosition({ value: selectedGuide.position, startMarking, scale }) + originOffset - startMarking * scale; drawGradient({ canvasHeight, context, orientation, originDistance, canvasWidth }); context.strokeStyle = SELECTED_GUIDE; context.lineWidth = 1; context.beginPath(); if (orientation === "horizontal" && selectedGuide.orientation === "horizontal") { return; } if (orientation === "vertical" && selectedGuide.orientation === "vertical") { return; } if (orientation === "vertical" && selectedGuide.orientation === "horizontal") { context.moveTo(0, originDistance); context.lineTo(canvasWidth, originDistance); drawLabel({ context, label: selectedGuide.position.toString(), originDistance, orientation, color: SELECTED_GUIDE }); } else if (orientation === "horizontal" && selectedGuide.orientation === "vertical") { context.moveTo(originDistance, 0); context.lineTo(originDistance, canvasHeight); drawLabel({ context, label: selectedGuide.position.toString(), originDistance, orientation, color: SELECTED_GUIDE }); } context.stroke(); }; var drawMarkingOnRulerCanvas = ({ scale, points, startMarking, originOffset, markingGaps, orientation, rulerCanvasRef, selectedGuide, canvasHeight, canvasWidth }) => { const canvas = rulerCanvasRef.current; if (!canvas) return; const context = canvas.getContext("2d"); if (!context) return; canvas.width = canvasWidth; canvas.height = canvasHeight; context.scale(window.devicePixelRatio, window.devicePixelRatio); context.clearRect(0, 0, canvasWidth, canvasHeight); context.strokeStyle = RULER_COLOR; context.lineWidth = 1; context.beginPath(); points.forEach((point) => { context.strokeStyle = RULER_COLOR; context.lineWidth = 1; const originDistance = point.position + originOffset - startMarking * scale; context.beginPath(); if (orientation === "horizontal") { context.moveTo(originDistance, 0); context.lineTo(originDistance, canvasHeight); } else { context.moveTo(0, originDistance); context.lineTo(canvasWidth, originDistance); } for (let i = 1;i < 5; i++) { const markingOffsetXY = i * markingGaps * scale; if (orientation === "horizontal") { context.moveTo(originDistance + markingOffsetXY / 5, 0); context.lineTo(originDistance + markingOffsetXY / 5, 4); } else { context.moveTo(0, originDistance + markingOffsetXY / 5); context.lineTo(4, originDistance + markingOffsetXY / 5); } } context.stroke(); context.font = "10px Arial, Helvetica, sans-serif"; context.textAlign = "left"; context.fillStyle = RULER_COLOR; drawLabel({ orientation, context, label: point.value.toString(), originDistance, color: RULER_COLOR }); }); if (selectedGuide && orientation !== selectedGuide.orientation) { drawGuide({ canvasHeight, canvasWidth, context, orientation, originOffset, scale, selectedGuide, startMarking }); } }; var getRulerPoints = ({ rulerScaleRange, rulerMarkingGaps, scale }) => { const points = []; const startPoint = Math.ceil(rulerScaleRange.start / rulerMarkingGaps); const endPoint = Math.floor(rulerScaleRange.end / rulerMarkingGaps); const startMarking = startPoint * rulerMarkingGaps; for (let i = startPoint;i <= endPoint; i++) { points.push({ value: i * rulerMarkingGaps, position: rulerValueToPosition({ scale, startMarking, value: i * rulerMarkingGaps }) }); } return { points, startMarking }; }; var rulerValueToPosition = ({ value, startMarking, scale }) => { return (value + startMarking) * scale; }; var getRulerScaleRange = ({ canvasLength, scale, canvasSize }) => { const scaleRangeBeyondCanvas = (canvasSize.width || MINIMUM_VISIBLE_CANVAS_SIZE - MINIMUM_VISIBLE_CANVAS_SIZE) / scale; return { start: -scaleRangeBeyondCanvas, end: scaleRangeBeyondCanvas + canvasLength }; }; // src/components/EditorRuler/Ruler.tsx import { useCallback as useCallback38, useContext as useContext25, useEffect as useEffect32, useMemo as useMemo48, useRef as useRef22, useState as useState35 } from "react"; import { Internals as Internals20 } from "remotion"; import { jsx as jsx85 } from "react/jsx-runtime"; var makeGuideId = () => { return Math.random().toString(36).substring(7); }; var Ruler = ({ scale, points, originOffset, startMarking, size: size2, markingGaps, orientation }) => { const rulerCanvasRef = useRef22(null); const isVerticalRuler = orientation === "vertical"; const { shouldCreateGuideRef, setGuidesList, selectedGuideId, hoveredGuideId, setSelectedGuideId, guidesList, setEditorShowGuides } = useContext25(EditorShowGuidesContext); const unsafeVideoConfig = Internals20.useUnsafeVideoConfig(); if (!unsafeVideoConfig) { throw new Error("Video config not set"); } const [cursor, setCursor] = useState35(isVerticalRuler ? "ew-resize" : "ns-resize"); const selectedOrHoveredGuide = useMemo48(() => { return guidesList.find((guide) => guide.id === selectedGuideId) ?? guidesList.find((guide) => guide.id === hoveredGuideId) ?? null; }, [guidesList, hoveredGuideId, selectedGuideId]); const rulerWidth = isVerticalRuler ? RULER_WIDTH : size2.width - RULER_WIDTH; const rulerHeight = isVerticalRuler ? size2.height - RULER_WIDTH : RULER_WIDTH; useEffect32(() => { drawMarkingOnRulerCanvas({ scale, points, startMarking, originOffset, markingGaps, orientation, rulerCanvasRef, selectedGuide: selectedOrHoveredGuide, canvasHeight: rulerHeight * window.devicePixelRatio, canvasWidth: rulerWidth * window.devicePixelRatio }); }, [ scale, points, startMarking, originOffset, markingGaps, orientation, selectedOrHoveredGuide, size2, rulerHeight, rulerWidth ]); const rulerStyle = useMemo48(() => ({ position: "absolute", background: BACKGROUND, width: rulerWidth, height: rulerHeight, left: isVerticalRuler ? 0 : "unset", top: isVerticalRuler ? "unset" : 0, borderBottom: isVerticalRuler ? undefined : "1px solid " + RULER_COLOR, borderRight: isVerticalRuler ? "1px solid " + RULER_COLOR : undefined, cursor }), [rulerWidth, rulerHeight, cursor, isVerticalRuler]); const onMouseDown = useCallback38((e) => { if (e.button !== 0) { return; } e.preventDefault(); shouldCreateGuideRef.current = true; document.body.style.cursor = "no-drop"; const guideId = makeGuideId(); setEditorShowGuides(() => true); setSelectedGuideId(() => guideId); setGuidesList((prevState) => { return [ ...prevState, { orientation, position: -originOffset, show: false, id: guideId, compositionId: unsafeVideoConfig.id } ]; }); }, [ shouldCreateGuideRef, setEditorShowGuides, setSelectedGuideId, setGuidesList, orientation, originOffset, unsafeVideoConfig.id ]); const changeCursor = useCallback38((e) => { e.preventDefault(); if (selectedGuideId !== null) { setCursor("no-drop"); } }, [setCursor, selectedGuideId]); useEffect32(() => { if (selectedGuideId === null) { setCursor(isVerticalRuler ? "ew-resize" : "ns-resize"); } }, [selectedGuideId, isVerticalRuler]); return /* @__PURE__ */ jsx85("canvas", { ref: rulerCanvasRef, width: rulerWidth * window.devicePixelRatio, height: rulerHeight * window.devicePixelRatio, style: rulerStyle, onPointerDown: onMouseDown, onPointerEnter: changeCursor, onPointerLeave: changeCursor }); }; var Ruler_default = Ruler; // src/components/EditorRuler/index.tsx import { jsx as jsx86, jsxs as jsxs38, Fragment as Fragment15 } from "react/jsx-runtime"; var originBlockStyles = { position: "absolute", top: 0, left: 0, borderBottom: "1px solid " + RULER_COLOR, borderRight: "1px solid " + RULER_COLOR, width: `${RULER_WIDTH}px`, height: `${RULER_WIDTH}px`, background: BACKGROUND }; var EditorRulers = ({ contentDimensions, canvasSize, assetMetadata, containerRef }) => { const { scale, canvasPosition } = useStudioCanvasDimensions({ canvasSize, contentDimensions, assetMetadata }); const { shouldCreateGuideRef, shouldDeleteGuideRef, setGuidesList, selectedGuideId, setSelectedGuideId } = useContext26(EditorShowGuidesContext); const rulerMarkingGaps = useMemo49(() => { const minimumGap = MINIMUM_RULER_MARKING_GAP_PX; const predefinedGap = PREDEFINED_RULER_SCALE_GAPS.find((gap) => gap * scale > minimumGap); return predefinedGap || MAXIMUM_PREDEFINED_RULER_SCALE_GAP; }, [scale]); const horizontalRulerScaleRange = useMemo49(() => getRulerScaleRange({ canvasLength: canvasPosition.width, scale, canvasSize }), [canvasPosition.width, canvasSize, scale]); const verticalRulerScaleRange = useMemo49(() => getRulerScaleRange({ canvasLength: canvasPosition.height, scale, canvasSize }), [canvasPosition.height, canvasSize, scale]); const { points: horizontalRulerPoints, startMarking: horizontalRulerStartMarking } = useMemo49(() => getRulerPoints({ rulerScaleRange: horizontalRulerScaleRange, rulerMarkingGaps, scale }), [horizontalRulerScaleRange, rulerMarkingGaps, scale]); const { points: verticalRulerPoints, startMarking: verticalRulerStartMarking } = useMemo49(() => getRulerPoints({ rulerScaleRange: verticalRulerScaleRange, rulerMarkingGaps, scale }), [verticalRulerScaleRange, rulerMarkingGaps, scale]); const requestAnimationFrameRef = useRef23(null); const onMouseMove = useCallback39((e) => { if (requestAnimationFrameRef.current) { cancelAnimationFrame(requestAnimationFrameRef.current); } requestAnimationFrameRef.current = requestAnimationFrame(() => { const { clientX: mouseX, clientY: mouseY } = e; const { left: containerLeft = 0, top: containerTop = 0, right: containerRight = 0, bottom: containerBottom = 0 } = containerRef.current?.getBoundingClientRect() || {}; if (mouseX < containerLeft || mouseX > containerRight || mouseY < containerTop || mouseY > containerBottom) { if (!shouldDeleteGuideRef.current) { shouldDeleteGuideRef.current = true; } if (document.body.style.cursor !== "no-drop") { document.body.style.cursor = "no-drop"; } setGuidesList((prevState) => { const newGuides = prevState.map((guide) => { if (guide.id !== selectedGuideId) { return guide; } return { ...guide, show: false }; }); persistGuidesList(newGuides); return newGuides; }); } else { if (shouldDeleteGuideRef.current) { shouldDeleteGuideRef.current = false; } setGuidesList((prevState) => { return prevState.map((guide) => { if (guide.id !== selectedGuideId) { return guide; } const position = guide.orientation === "vertical" ? (mouseX - containerLeft) / scale - canvasPosition.left / scale : (mouseY - containerTop) / scale - canvasPosition.top / scale; const desiredCursor = guide.orientation === "vertical" ? "ew-resize" : "ns-resize"; if (document.body.style.cursor !== desiredCursor) { document.body.style.cursor = desiredCursor; } return { ...guide, position: Math.floor(position / 1), show: true }; }); }); } }); }, [ containerRef, shouldDeleteGuideRef, setGuidesList, selectedGuideId, scale, canvasPosition.left, canvasPosition.top ]); const onMouseUp = useCallback39(() => { setGuidesList((prevState) => { const newGuides = prevState.filter((selected) => { if (!shouldDeleteGuideRef.current) { return true; } return selected.id !== selectedGuideId; }); persistGuidesList(newGuides); return newGuides; }); shouldDeleteGuideRef.current = false; document.body.style.cursor = "auto"; shouldCreateGuideRef.current = false; setSelectedGuideId(() => null); document.removeEventListener("pointerup", onMouseUp); document.removeEventListener("pointermove", onMouseMove); }, [ selectedGuideId, shouldCreateGuideRef, shouldDeleteGuideRef, setSelectedGuideId, setGuidesList, onMouseMove ]); useEffect33(() => { if (selectedGuideId !== null) { document.addEventListener("pointermove", onMouseMove); document.addEventListener("pointerup", onMouseUp); } return () => { document.removeEventListener("pointermove", onMouseMove); document.removeEventListener("pointerup", onMouseUp); if (requestAnimationFrameRef.current) { cancelAnimationFrame(requestAnimationFrameRef.current); } }; }, [selectedGuideId, onMouseMove, onMouseUp]); return /* @__PURE__ */ jsxs38(Fragment15, { children: [ /* @__PURE__ */ jsx86("div", { style: originBlockStyles }), /* @__PURE__ */ jsx86(Ruler_default, { orientation: "horizontal", scale, points: horizontalRulerPoints, startMarking: horizontalRulerStartMarking, markingGaps: rulerMarkingGaps, originOffset: canvasPosition.left, size: canvasSize }), /* @__PURE__ */ jsx86(Ruler_default, { orientation: "vertical", scale, points: verticalRulerPoints, startMarking: verticalRulerStartMarking, markingGaps: rulerMarkingGaps, originOffset: canvasPosition.top, size: canvasSize }) ] }); }; // src/components/EditorRuler/use-is-ruler-visible.ts import { useContext as useContext27 } from "react"; import { Internals as Internals21 } from "remotion"; var useIsRulerVisible = () => { const { canvasContent } = useContext27(Internals21.CompositionManager); const { editorShowRulers } = useContext27(EditorShowRulersContext); return editorShowRulers && canvasContent && canvasContent.type === "composition"; }; // src/components/ResetZoomButton.tsx import { jsx as jsx87 } from "react/jsx-runtime"; var ResetZoomButton = ({ onClick }) => { return /* @__PURE__ */ jsx87(Button, { onClick, children: "Reset zoom" }); }; // src/components/Canvas.tsx import { jsx as jsx88, jsxs as jsxs39, Fragment as Fragment16 } from "react/jsx-runtime"; var container19 = { flex: 1, display: "flex", overflow: "hidden", position: "relative", backgroundColor: BACKGROUND }; var resetZoom = { position: "absolute", top: SPACING_UNIT * 2, right: SPACING_UNIT * 2 }; var ZOOM_PX_FACTOR = 0.003; var Canvas = ({ canvasContent, size: size2 }) => { const { setSize, size: previewSize } = useContext28(Internals22.PreviewSizeContext); const { editorZoomGestures } = useContext28(EditorZoomGesturesContext); const keybindings = useKeybinding(); const config = Internals22.useUnsafeVideoConfig(); const areRulersVisible = useIsRulerVisible(); const { editorShowGuides } = useContext28(EditorShowGuidesContext); const [assetResolution, setAssetResolution] = useState36(null); const contentDimensions = useMemo50(() => { if ((canvasContent.type === "asset" || canvasContent.type === "output" || canvasContent.type === "output-blob") && assetResolution && assetResolution.type === "found") { return assetResolution.dimensions; } if (config) { return { width: config.width, height: config.height }; } return null; }, [assetResolution, config, canvasContent]); const isFit = previewSize.size === "auto"; const onWheel = useCallback40((e) => { if (!editorZoomGestures) { return; } if (!size2) { return; } if (!contentDimensions || contentDimensions === "none") { return; } const wantsToZoom = e.ctrlKey || e.metaKey; if (!wantsToZoom && isFit) { return; } e.preventDefault(); setSize((prevSize) => { const scale = Internals22.calculateScale({ canvasSize: size2, compositionHeight: contentDimensions.height, compositionWidth: contentDimensions.width, previewSize: prevSize.size }); if (wantsToZoom) { const oldSize = prevSize.size === "auto" ? scale : prevSize.size; const smoothened = smoothenZoom(oldSize); const added = smoothened + e.deltaY * ZOOM_PX_FACTOR; const unsmoothened = unsmoothenZoom(added); const { centerX, centerY } = getCenterPointWhileScrolling({ size: size2, clientX: e.clientX, clientY: e.clientY, compositionWidth: contentDimensions.width, compositionHeight: contentDimensions.height, scale, translation: prevSize.translation }); const zoomDifference = unsmoothened - oldSize; const uvCoordinatesX = centerX / contentDimensions.width; const uvCoordinatesY = centerY / contentDimensions.height; const correctionLeft = -uvCoordinatesX * (zoomDifference * contentDimensions.width) + (1 - uvCoordinatesX) * zoomDifference * contentDimensions.width; const correctionTop = -uvCoordinatesY * (zoomDifference * contentDimensions.height) + (1 - uvCoordinatesY) * zoomDifference * contentDimensions.height; return { translation: getEffectiveTranslation({ translation: { x: prevSize.translation.x - correctionLeft / 2, y: prevSize.translation.y - correctionTop / 2 }, canvasSize: size2, compositionHeight: contentDimensions.height, compositionWidth: contentDimensions.width, scale }), size: unsmoothened }; } const effectiveTranslation = getEffectiveTranslation({ translation: prevSize.translation, canvasSize: size2, compositionHeight: contentDimensions.height, compositionWidth: contentDimensions.width, scale }); return { ...prevSize, translation: getEffectiveTranslation({ translation: { x: effectiveTranslation.x + e.deltaX, y: effectiveTranslation.y + e.deltaY }, canvasSize: size2, compositionHeight: contentDimensions.height, compositionWidth: contentDimensions.width, scale }) }; }); }, [editorZoomGestures, contentDimensions, isFit, setSize, size2]); useEffect34(() => { const { current } = canvasRef; if (!current) { return; } current.addEventListener("wheel", onWheel, { passive: false }); return () => current.removeEventListener("wheel", onWheel, { passive: false }); }, [onWheel]); const onReset = useCallback40(() => { setSize(() => { return { translation: { x: 0, y: 0 }, size: "auto" }; }); }, [setSize]); const onZoomIn = useCallback40(() => { if (!contentDimensions || contentDimensions === "none") { return; } if (!size2) { return; } setSize((prevSize) => { const scale = Internals22.calculateScale({ canvasSize: size2, compositionHeight: contentDimensions.height, compositionWidth: contentDimensions.width, previewSize: prevSize.size }); return { translation: { x: 0, y: 0 }, size: Math.min(MAX_ZOOM, scale * 2) }; }); }, [contentDimensions, setSize, size2]); const onZoomOut = useCallback40(() => { if (!contentDimensions || contentDimensions === "none") { return; } if (!size2) { return; } setSize((prevSize) => { const scale = Internals22.calculateScale({ canvasSize: size2, compositionHeight: contentDimensions.height, compositionWidth: contentDimensions.width, previewSize: prevSize.size }); return { translation: { x: 0, y: 0 }, size: Math.max(MIN_ZOOM, scale / 2) }; }); }, [contentDimensions, setSize, size2]); useEffect34(() => { const resetBinding = keybindings.registerKeybinding({ event: "keydown", key: "0", commandCtrlKey: false, callback: onReset, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const zoomIn = keybindings.registerKeybinding({ event: "keydown", key: "+", commandCtrlKey: false, callback: onZoomIn, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const zoomOut = keybindings.registerKeybinding({ event: "keydown", key: "-", commandCtrlKey: false, callback: onZoomOut, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); return () => { resetBinding.unregister(); zoomIn.unregister(); zoomOut.unregister(); }; }, [keybindings, onReset, onZoomIn, onZoomOut]); const fetchMetadata = useCallback40(async () => { setAssetResolution(null); if (canvasContent.type === "composition") { return; } const metadata = await getAssetMetadata(canvasContent, canvasContent.type === "asset"); setAssetResolution(metadata); }, [canvasContent]); useEffect34(() => { if (canvasContent.type !== "asset") { return; } const file = watchStaticFile(canvasContent.asset, () => { fetchMetadata(); }); return () => { file.cancel(); }; }, [canvasContent, fetchMetadata]); useEffect34(() => { fetchMetadata(); }, [fetchMetadata]); return /* @__PURE__ */ jsxs39(Fragment16, { children: [ /* @__PURE__ */ jsxs39("div", { ref: canvasRef, style: container19, children: [ size2 ? /* @__PURE__ */ jsx88(VideoPreview, { canvasContent, contentDimensions, canvasSize: size2, assetMetadata: assetResolution }) : null, isFit ? null : /* @__PURE__ */ jsx88("div", { style: resetZoom, className: "css-reset", children: /* @__PURE__ */ jsx88(ResetZoomButton, { onClick: onReset }) }), editorShowGuides && canvasContent.type === "composition" && /* @__PURE__ */ jsx88(EditorGuides_default, { canvasSize: size2, contentDimensions, assetMetadata: assetResolution }) ] }), areRulersVisible && /* @__PURE__ */ jsx88(EditorRulers, { contentDimensions, canvasSize: size2, assetMetadata: assetResolution, containerRef: canvasRef }) ] }); }; // src/components/FramePersistor.tsx import { useEffect as useEffect35 } from "react"; import { Internals as Internals23, useVideoConfig as useVideoConfig2 } from "remotion"; var FramePersistor = () => { const [playing] = Internals23.Timeline.usePlayingState(); const config = useVideoConfig2(); const frame2 = Internals23.Timeline.useTimelinePosition(); const setFrame = Internals23.useTimelineSetFrame(); useEffect35(() => { if (!playing) { setFrame((f) => { const newObj = { ...f, [config.id]: frame2 }; Internals23.persistCurrentFrame(newObj); return newObj; }); } }, [config.id, frame2, playing, setFrame]); return null; }; // src/components/RefreshCompositionOverlay.tsx import { AbsoluteFill as AbsoluteFill2 } from "remotion"; // src/components/RunningCalculateMetadata.tsx import { jsx as jsx89, jsxs as jsxs40 } from "react/jsx-runtime"; var loaderLabel = { fontSize: 14, color: LIGHT_TEXT, fontFamily: "sans-serif", lineHeight: 1.5 }; var container20 = { backgroundColor: BACKGROUND, display: "inline-flex", justifyContent: "center", alignItems: "center", flexDirection: "row", padding: 20 }; var RunningCalculateMetadata = () => { return /* @__PURE__ */ jsxs40("div", { style: container20, children: [ /* @__PURE__ */ jsx89(Spinner, { size: 24, duration: 1 }), /* @__PURE__ */ jsx89(Spacing, { x: 2 }), /* @__PURE__ */ jsxs40("div", { style: loaderLabel, children: [ "Running ", /* @__PURE__ */ jsx89("code", { style: inlineCodeSnippet, children: "calculateMetadata()" }), "..." ] }) ] }); }; // src/components/RefreshCompositionOverlay.tsx import { jsx as jsx90 } from "react/jsx-runtime"; var container21 = { justifyContent: "flex-end", alignItems: "flex-start", padding: 20, pointerEvents: "none" }; var shadow = { boxShadow: "0 0 4px black" }; var RefreshCompositionOverlay = () => { return /* @__PURE__ */ jsx90(AbsoluteFill2, { style: container21, children: /* @__PURE__ */ jsx90("div", { style: shadow, children: /* @__PURE__ */ jsx90(RunningCalculateMetadata, {}) }) }); }; // src/components/CanvasOrLoading.tsx import { jsx as jsx91, jsxs as jsxs41, Fragment as Fragment17 } from "react/jsx-runtime"; var container22 = { color: "white", flex: 1, justifyContent: "center", alignItems: "center", display: "flex", backgroundColor: BACKGROUND, flexDirection: "column" }; var CanvasOrLoading = ({ size: size2 }) => { const resolved = Internals24.useResolvedVideoConfig(null); const { setZoom } = useContext29(TimelineZoomCtx); const { canvasContent } = useContext29(Internals24.CompositionManager); useEffect36(() => { if (resolved?.type !== "success" && resolved?.type !== "success-and-refreshing") { return; } const c = resolved.result; setTimeout(() => { ensureFrameIsInViewport({ direction: "center", frame: getCurrentFrame(), durationInFrames: c.durationInFrames }); }); }, [resolved, setZoom]); if (!canvasContent) { const compname = window.location.pathname.replace("/", ""); return /* @__PURE__ */ jsx91("div", { style: container22, className: "css-reset", children: /* @__PURE__ */ jsxs41("div", { style: loaderLabel, children: [ "Composition with ID ", compname, " not found." ] }) }); } const content = /* @__PURE__ */ jsxs41(Fragment17, { children: [ /* @__PURE__ */ jsx91(ZoomPersistor, {}), /* @__PURE__ */ jsx91(Canvas, { size: size2, canvasContent }), resolved?.type === "success-and-refreshing" ? /* @__PURE__ */ jsx91(RefreshCompositionOverlay, {}) : null ] }); if (canvasContent.type === "asset" || canvasContent.type === "output" || canvasContent.type === "output-blob") { return content; } if (!resolved) { return null; } if (resolved.type === "loading") { return /* @__PURE__ */ jsx91("div", { style: container22, className: "css-reset", children: /* @__PURE__ */ jsx91(RunningCalculateMetadata, {}) }); } if (resolved.type === "error") { return /* @__PURE__ */ jsx91(ErrorLoading, { error: resolved.error }); } return /* @__PURE__ */ jsxs41(Fragment17, { children: [ /* @__PURE__ */ jsx91(FramePersistor, {}), " ", content ] }); }; var loaderContainer = { marginLeft: "auto", marginRight: "auto", width: "100%", position: "absolute", height: "100%", overflowY: "auto" }; var ErrorLoading = ({ error }) => { return /* @__PURE__ */ jsx91("div", { style: loaderContainer, className: VERTICAL_SCROLLBAR_CLASSNAME, children: /* @__PURE__ */ jsx91(ErrorLoader, { canHaveDismissButton: false, keyboardShortcuts: false, error, onRetry: () => Internals24.resolveCompositionsRef.current?.reloadCurrentlySelectedComposition(), calculateMetadata: true }, error.stack) }); }; // src/components/CanvasIfSizeIsAvailable.tsx import { jsx as jsx92 } from "react/jsx-runtime"; var CanvasIfSizeIsAvailable = () => { const rulersAreVisible = useIsRulerVisible(); const context = useContext30(Internals25.CurrentScaleContext); const sizeWithRulersApplied = useMemo51(() => { const size2 = context && context.type === "canvas-size" ? context.canvasSize : null; if (!rulersAreVisible) { return size2; } if (!size2) { return null; } return { ...size2, width: size2.width - RULER_WIDTH, height: size2.height - RULER_WIDTH }; }, [context, rulersAreVisible]); if (!sizeWithRulersApplied) { return null; } return /* @__PURE__ */ jsx92(CanvasOrLoading, { size: sizeWithRulersApplied }); }; // src/components/CurrentCompositionSideEffects.tsx import { useCallback as useCallback41, useContext as useContext31, useEffect as useEffect37 } from "react"; import { Internals as Internals26 } from "remotion"; // src/helpers/document-title.ts import { NoReactInternals as NoReactInternals7 } from "remotion/no-react"; var currentItemName = null; var unsavedProps = false; var tabInactive = false; var renderJobs = []; var setCurrentCanvasContentId = (id) => { if (!id) { currentItemName = id; updateTitle(); return; } const idWithoutFolder = id.split("/").pop(); currentItemName = idWithoutFolder; updateTitle(); }; var setUnsavedProps = (unsaved) => { window.remotion_unsavedProps = unsaved; unsavedProps = unsaved; }; var setRenderJobs = (jobs) => { renderJobs = jobs; updateTitle(); }; document.addEventListener("visibilitychange", () => { tabInactive = document.visibilityState === "hidden"; updateTitle(); }); var productName = "Remotion Studio"; var suffix = `- ${productName}`; var updateTitle = () => { if (!currentItemName) { document.title = productName; return; } const currentCompTitle = `${currentItemName} / ${window.remotion_projectName}`; document.title = [ getProgressInBrackets(currentItemName, renderJobs), unsavedProps && tabInactive ? "✏️" : null, `${currentCompTitle} ${suffix}` ].filter(NoReactInternals7.truthy).join(" "); }; var getProgressInBrackets = (selectedCompositionId, jobs) => { const currentRender = jobs.find((job) => job.status === "running"); if (!currentRender) { return null; } if (currentRender.status !== "running") { throw new Error("expected running job"); } let progInPercent; if (isClientRenderJob(currentRender)) { const { renderedFrames, totalFrames } = currentRender.progress; progInPercent = totalFrames > 0 ? Math.ceil(renderedFrames / totalFrames * 100) : 0; } else { progInPercent = Math.ceil(currentRender.progress.value * 100); } const progressInBrackets = currentRender.compositionId === selectedCompositionId ? `[${progInPercent}%]` : `[${progInPercent}% ${currentRender.compositionId}]`; return progressInBrackets; }; document.addEventListener("visibilitychange", () => { tabInactive = document.visibilityState === "hidden"; updateTitle(); }); // src/components/CurrentCompositionSideEffects.tsx var TitleUpdater = () => { const renderQueue = useContext31(RenderQueueContext); const { canvasContent } = useContext31(Internals26.CompositionManager); const { jobs } = renderQueue; useEffect37(() => { if (!canvasContent) { setCurrentCanvasContentId(null); return; } if (canvasContent.type === "composition") { setCurrentCanvasContentId(canvasContent.compositionId); return; } if (canvasContent.type === "output") { setCurrentCanvasContentId(canvasContent.path); return; } if (canvasContent.type === "output-blob") { setCurrentCanvasContentId(canvasContent.displayName); return; } setCurrentCanvasContentId(canvasContent.asset); }, [canvasContent]); useEffect37(() => { setRenderJobs(jobs); }, [jobs]); return null; }; var CurrentCompositionKeybindings = ({ readOnlyStudio }) => { const keybindings = useKeybinding(); const video = Internals26.useVideo(); const { type } = useContext31(StudioServerConnectionCtx).previewServerState; const openRenderModal = useCallback41(() => { if (!video) { return; } if (readOnlyStudio) { return showNotification("Studio is read-only", 2000); } if (type !== "connected") { showNotification("Studio server is offline", 2000); return; } const renderButton = document.getElementById("render-modal-button"); renderButton.click(); }, [readOnlyStudio, type, video]); useEffect37(() => { const binding = keybindings.registerKeybinding({ event: "keydown", key: "r", commandCtrlKey: false, callback: openRenderModal, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); return () => { binding.unregister(); }; }, [keybindings, openRenderModal]); return null; }; // src/components/MobilePanel.tsx import ReactDOM6 from "react-dom"; import { jsx as jsx93, jsxs as jsxs42 } from "react/jsx-runtime"; var container23 = { position: "fixed", top: 0, left: 0, width: "100%", height: "100%", padding: "0 0px 50px 0px", background: BACKGROUND }; var buttonContainer = { height: "40px", width: "100%", alignItems: "center", display: "flex", justifyContent: "flex-end" }; var button2 = { height: 20, width: 20 }; function MobilePanel({ children, onClose }) { const { currentZIndex } = useZIndex(); return ReactDOM6.createPortal(/* @__PURE__ */ jsxs42("div", { style: container23, children: [ /* @__PURE__ */ jsx93("div", { style: buttonContainer, children: /* @__PURE__ */ jsx93(CancelButton, { style: button2, onPress: onClose }) }), children ] }), getPortal(currentZIndex)); } // src/components/OptionsPanel.tsx import { createRef as createRef9, useCallback as useCallback81, useContext as useContext48, useEffect as useEffect52, useImperativeHandle as useImperativeHandle12, useMemo as useMemo90, useState as useState56 } from "react"; import { Internals as Internals36 } from "remotion"; // src/visual-controls/VisualControls.tsx import { createContext as createContext16, createRef as createRef8, useCallback as useCallback42, useEffect as useEffect39, useImperativeHandle as useImperativeHandle10, useMemo as useMemo53, useRef as useRef24, useState as useState38 } from "react"; import { useRemotionEnvironment } from "remotion"; // src/api/get-zod-schema-from-primitive.ts var getZodSchemaFromPrimitive = (value, z) => { if (typeof value === "string") { return z.string(); } if (typeof value === "number") { return z.number(); } let stringified; try { stringified = JSON.stringify(value); } catch {} throw new Error(`visualControl(): Specify a schema for this value: ${stringified ?? "[non-serializable value]"}. See https://remotion.dev/docs/studio/visual-control`); }; // src/components/get-zod-if-possible.tsx import { createContext as createContext15, useContext as useContext32, useEffect as useEffect38, useMemo as useMemo52, useState as useState37 } from "react"; import { jsx as jsx94 } from "react/jsx-runtime"; var getZodIfPossible = async () => { try { const { z } = await import("zod"); return z; } catch { return null; } }; var getZTypesIfPossible = async () => { try { const mod = await import("@remotion/zod-types"); return mod; } catch { return null; } }; var useZodIfPossible = () => { const context = useContext32(ZodContext); return context?.zod ?? null; }; var useZodTypesIfPossible = () => { const context = useContext32(ZodContext); return context?.zodTypes ?? null; }; var ZodContext = createContext15(null); var ZodProvider = ({ children }) => { const [zod, setZod] = useState37(null); const [zodTypes, setZodTypes] = useState37(null); useEffect38(() => { getZodIfPossible().then((z) => setZod(z)); }, []); useEffect38(() => { getZTypesIfPossible().then((z) => setZodTypes(z)); }, []); const contextValue = useMemo52(() => { return { zod, zodTypes }; }, [zod, zodTypes]); return /* @__PURE__ */ jsx94(ZodContext.Provider, { value: contextValue, children }); }; // src/visual-controls/get-current-edited-value.ts var getVisualControlEditedValue = ({ handles, key }) => { return handles?.[key]?.unsavedValue ?? null; }; // src/visual-controls/VisualControls.tsx import { jsx as jsx95 } from "react/jsx-runtime"; var VisualControlsTabActivatedContext = createContext16(false); var VisualControlsContext = createContext16({ handles: {} }); var visualControlRef = createRef8(); var SetVisualControlsContext = createContext16({ updateHandles: () => { throw new Error("updateHandles is not implemented"); }, updateValue: () => { throw new Error("updateValue is not implemented"); }, visualControl: () => { throw new Error("visualControl is not implemented"); } }); var VisualControlsProvider = ({ children }) => { const imperativeHandles = useRef24({}); const [handles, setHandles] = useState38({}); const state = useMemo53(() => { return { handles }; }, [handles]); const setControl = useCallback42((key, value) => { const currentUnsaved = imperativeHandles.current?.[key]?.unsavedValue; const currentSavedState = imperativeHandles.current?.[key]?.valueInCode; const changedSavedValue = value.valueInCode !== currentSavedState; const changedUnsavedValue = currentUnsaved === undefined && value.valueInCode !== undefined; imperativeHandles.current = { ...imperativeHandles.current, [key]: { ...value, unsavedValue: currentUnsaved ?? value.valueInCode, valueInCode: value.valueInCode } }; return { changed: changedSavedValue || changedUnsavedValue, currentValue: getVisualControlEditedValue({ key, handles: imperativeHandles.current }) }; }, []); const z = useZodIfPossible(); const changedRef = useRef24(false); const env = useRemotionEnvironment(); const visualControl = useCallback42(function(key, value, schema) { if (handles && false) {} if (!env.isStudio) { return value; } if (!z) { return value; } const { changed, currentValue } = setControl(key, { valueInCode: value, schema: schema ?? getZodSchemaFromPrimitive(value, z), stack: new Error().stack }); if (changed) { changedRef.current = true; } return currentValue; }, [setControl, handles, z, env.isStudio]); const updateHandles = useCallback42(() => { setHandles(() => { return imperativeHandles.current; }); }, []); const updateValue = useCallback42((key, value) => { imperativeHandles.current = { ...imperativeHandles.current, [key]: { ...imperativeHandles.current[key], unsavedValue: value } }; updateHandles(); }, [updateHandles]); useImperativeHandle10(visualControlRef, () => { return { globalVisualControl: visualControl }; }, [visualControl]); useEffect39(() => { const callback = () => { if (imperativeHandles.current) { updateHandles(); changedRef.current = false; } }; const interval = setInterval(callback, 100); return () => { clearInterval(interval); }; }, [updateHandles]); const setState = useMemo53(() => { return { setControl, updateHandles, updateValue, visualControl }; }, [setControl, updateHandles, updateValue, visualControl]); return /* @__PURE__ */ jsx95(VisualControlsTabActivatedContext.Provider, { value: Object.keys(state.handles).length > 0, children: /* @__PURE__ */ jsx95(VisualControlsContext.Provider, { value: state, children: /* @__PURE__ */ jsx95(SetVisualControlsContext.Provider, { value: setState, children }) }) }); }; // src/components/GlobalPropsEditorUpdateButton.tsx import React63, { useCallback as useCallback45, useContext as useContext33 } from "react"; import { Internals as Internals28 } from "remotion"; // src/api/save-default-props.ts import { getRemotionEnvironment as getRemotionEnvironment3 } from "remotion"; // src/components/RenderModal/SchemaEditor/extract-enum-json-paths.ts var extractEnumJsonPaths = ({ schema, zodRuntime, currentPath, zodTypes }) => { const def = schema._def; const typeName = def.typeName; switch (typeName) { case zodRuntime.ZodFirstPartyTypeKind.ZodObject: { const shape = def.shape(); const keys = Object.keys(shape); return keys.map((key) => { return extractEnumJsonPaths({ schema: shape[key], zodRuntime, currentPath: [...currentPath, key], zodTypes }); }).flat(1); } case zodRuntime.ZodFirstPartyTypeKind.ZodArray: { return extractEnumJsonPaths({ schema: def.type, zodRuntime, currentPath: [...currentPath, "[]"], zodTypes }); } case zodRuntime.ZodFirstPartyTypeKind.ZodUnion: { return def.options.map((option) => { return extractEnumJsonPaths({ schema: option, zodRuntime, currentPath, zodTypes }); }).flat(1); } case zodRuntime.ZodFirstPartyTypeKind.ZodDiscriminatedUnion: { return def.options.map((op) => { return extractEnumJsonPaths({ schema: op, zodRuntime, currentPath, zodTypes }); }).flat(1); } case zodRuntime.ZodFirstPartyTypeKind.ZodLiteral: { return [currentPath]; } case zodRuntime.ZodFirstPartyTypeKind.ZodEffects: { if (zodTypes && schema._def.description === zodTypes.ZodZypesInternals.REMOTION_MATRIX_BRAND) { return [currentPath]; } return extractEnumJsonPaths({ schema: def.schema, zodRuntime, currentPath, zodTypes }); } case zodRuntime.ZodFirstPartyTypeKind.ZodIntersection: { const { left: left3, right } = def; const leftValue = extractEnumJsonPaths({ schema: left3, zodRuntime, currentPath, zodTypes }); const rightValue = extractEnumJsonPaths({ schema: right, zodRuntime, currentPath, zodTypes }); return [...leftValue, ...rightValue]; } case zodRuntime.ZodFirstPartyTypeKind.ZodTuple: { return def.items.map((item, i) => extractEnumJsonPaths({ schema: item, zodRuntime, currentPath: [...currentPath, i], zodTypes })).flat(1); } case zodRuntime.ZodFirstPartyTypeKind.ZodRecord: { const values = extractEnumJsonPaths({ schema: def.valueType, zodRuntime, currentPath: [...currentPath, "{}"], zodTypes }); return values; } case zodRuntime.ZodFirstPartyTypeKind.ZodFunction: { throw new Error("Cannot create a value for type function"); } case zodRuntime.ZodFirstPartyTypeKind.ZodEnum: { return [currentPath]; } case zodRuntime.ZodFirstPartyTypeKind.ZodNativeEnum: { return []; } case zodRuntime.ZodFirstPartyTypeKind.ZodOptional: { const defType = def; const value = extractEnumJsonPaths({ schema: defType.innerType, zodRuntime, currentPath, zodTypes }); return value; } case zodRuntime.ZodFirstPartyTypeKind.ZodNullable: { const defType = def; const value = extractEnumJsonPaths({ schema: defType.innerType, zodRuntime, currentPath, zodTypes }); return value; } case zodRuntime.ZodFirstPartyTypeKind.ZodDefault: { const defType = def; return extractEnumJsonPaths({ schema: defType.innerType, zodRuntime, currentPath, zodTypes }); } case zodRuntime.ZodFirstPartyTypeKind.ZodCatch: { const defType = def; return extractEnumJsonPaths({ schema: defType.innerType, zodRuntime, currentPath, zodTypes }); } case zodRuntime.ZodFirstPartyTypeKind.ZodPromise: { return []; } case zodRuntime.ZodFirstPartyTypeKind.ZodBranded: { const defType = def; const value = extractEnumJsonPaths({ schema: defType.type, zodRuntime, currentPath, zodTypes }); return value; } case zodRuntime.ZodFirstPartyTypeKind.ZodPipeline: { const defType = def; const value = extractEnumJsonPaths({ schema: defType.out, zodRuntime, currentPath, zodTypes }); return value; } case zodRuntime.ZodFirstPartyTypeKind.ZodString: case zodRuntime.ZodFirstPartyTypeKind.ZodNumber: case zodRuntime.ZodFirstPartyTypeKind.ZodBigInt: case zodRuntime.ZodFirstPartyTypeKind.ZodBoolean: case zodRuntime.ZodFirstPartyTypeKind.ZodNaN: case zodRuntime.ZodFirstPartyTypeKind.ZodDate: case zodRuntime.ZodFirstPartyTypeKind.ZodSymbol: case zodRuntime.ZodFirstPartyTypeKind.ZodUndefined: case zodRuntime.ZodFirstPartyTypeKind.ZodNull: case zodRuntime.ZodFirstPartyTypeKind.ZodAny: case zodRuntime.ZodFirstPartyTypeKind.ZodUnknown: case zodRuntime.ZodFirstPartyTypeKind.ZodNever: case zodRuntime.ZodFirstPartyTypeKind.ZodVoid: case zodRuntime.ZodFirstPartyTypeKind.ZodMap: case zodRuntime.ZodFirstPartyTypeKind.ZodLazy: case zodRuntime.ZodFirstPartyTypeKind.ZodSet: { return []; } default: throw new Error("Not implemented: " + typeName); } }; // src/api/helpers/calc-new-props.ts import { Internals as Internals27, 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, editorPropsProviderRef } = Internals27; 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 propsStore = editorPropsProviderRef.current; if (!propsStore) { throw new Error("No props store found. Are you in the Remotion Studio and are the Remotion versions aligned?"); } const savedDefaultProps = composition.defaultProps ?? {}; const unsavedDefaultProps = propsStore.getProps()[compositionId] ?? savedDefaultProps; const generatedDefaultProps = defaultProps({ schema: composition.schema, savedDefaultProps, unsavedDefaultProps }); return { composition, generatedDefaultProps }; }; // src/api/save-default-props.ts var saveDefaultProps = async ({ compositionId, defaultProps }) => { if (!getRemotionEnvironment3().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/fast-refresh-context.ts import { createContext as createContext17 } from "react"; var FastRefreshContext = createContext17({ fastRefreshes: 0, manualRefreshes: 0, increaseManualRefreshes: () => {} }); // src/components/RenderModal/SchemaEditor/SchemaResetButton.tsx import { useCallback as useCallback43 } from "react"; import { jsx as jsx96 } from "react/jsx-runtime"; var icon2 = { height: 14, color: "currentColor" }; var SchemaResetButton = ({ onClick }) => { const renderAction = useCallback43((color) => { return /* @__PURE__ */ jsx96("svg", { style: icon2, viewBox: "0 0 512 512", children: /* @__PURE__ */ jsx96("path", { fill: color, d: "M48.5 224H40c-13.3 0-24-10.7-24-24V72c0-9.7 5.8-18.5 14.8-22.2s19.3-1.7 26.2 5.2L98.6 96.6c87.6-86.5 228.7-86.2 315.8 1c87.5 87.5 87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3c-62.2-62.2-162.7-62.5-225.3-1L185 183c6.9 6.9 8.9 17.2 5.2 26.2s-12.5 14.8-22.2 14.8H48.5z" }) }); }, []); return /* @__PURE__ */ jsx96(InlineAction, { renderAction, onClick }); }; // src/components/RenderModal/SchemaEditor/SchemaSaveButton.tsx import { useCallback as useCallback44 } from "react"; import { jsx as jsx97 } from "react/jsx-runtime"; var icon3 = { height: 14, color: "currentColor" }; var SchemaSaveButton = ({ onClick, disabled }) => { const renderAction = useCallback44((color) => { return /* @__PURE__ */ jsx97("svg", { style: icon3, viewBox: "0 0 448 512", children: /* @__PURE__ */ jsx97("path", { fill: disabled ? LIGHT_TEXT : color, d: "M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V173.3c0-17-6.7-33.3-18.7-45.3L352 50.7C340 38.7 323.7 32 306.7 32H64zm0 96c0-17.7 14.3-32 32-32H288c17.7 0 32 14.3 32 32v64c0 17.7-14.3 32-32 32H96c-17.7 0-32-14.3-32-32V128zM224 288a64 64 0 1 1 0 128 64 64 0 1 1 0-128z" }) }); }, [disabled]); return /* @__PURE__ */ jsx97(InlineAction, { renderAction, onClick, disabled }); }; // src/components/GlobalPropsEditorUpdateButton.tsx import { jsx as jsx98, jsxs as jsxs43 } from "react/jsx-runtime"; var container24 = { display: "inline-block", flexDirection: "row" }; var GlobalPropsEditorUpdateButton = ({ compositionId, currentDefaultProps }) => { const { fastRefreshes } = useContext33(FastRefreshContext); const [disabled, setDisabled] = React63.useState(false); const onClicked = useCallback45(() => { setDisabled(true); window.remotion_ignoreFastRefreshUpdate = fastRefreshes + 1; saveDefaultProps({ compositionId, defaultProps: () => currentDefaultProps }).catch((err) => { showNotification(`Cannot update default props: ${err.stack}`, 2000); }).finally(() => { setDisabled(true); }); }, [compositionId, currentDefaultProps, fastRefreshes]); const onReset = useCallback45(() => { window.remotion_ignoreFastRefreshUpdate = null; window.dispatchEvent(new CustomEvent(Internals28.PROPS_UPDATED_EXTERNALLY, { detail: { resetUnsaved: compositionId } })); }, [compositionId]); return /* @__PURE__ */ jsxs43("div", { style: container24, children: [ /* @__PURE__ */ jsx98(SchemaResetButton, { onClick: onReset }), /* @__PURE__ */ jsx98(SchemaSaveButton, { disabled, onClick: onClicked }) ] }); }; // src/components/RenderModal/DataEditor.tsx import React94, { useCallback as useCallback69, useContext as useContext36, useEffect as useEffect47, useMemo as useMemo80, useState as useState51 } from "react"; import { getInputProps, Internals as Internals31 } from "remotion"; import { NoReactInternals as NoReactInternals10 } from "remotion/no-react"; // src/components/NewComposition/ValidationMessage.tsx import { useMemo as useMemo54 } from "react"; import { jsx as jsx99, jsxs as jsxs44 } from "react/jsx-runtime"; var WarningTriangle = (props) => { return /* @__PURE__ */ jsx99("svg", { viewBox: "0 0 576 512", ...props, children: /* @__PURE__ */ jsx99("path", { d: "M248.747 204.705l6.588 112c.373 6.343 5.626 11.295 11.979 11.295h41.37a12 12 0 0 0 11.979-11.295l6.588-112c.405-6.893-5.075-12.705-11.979-12.705h-54.547c-6.903 0-12.383 5.812-11.978 12.705zM330 384c0 23.196-18.804 42-42 42s-42-18.804-42-42 18.804-42 42-42 42 18.804 42 42zm-.423-360.015c-18.433-31.951-64.687-32.009-83.154 0L6.477 440.013C-11.945 471.946 11.118 512 48.054 512H527.94c36.865 0 60.035-39.993 41.577-71.987L329.577 23.985zM53.191 455.002L282.803 57.008c2.309-4.002 8.085-4.002 10.394 0l229.612 397.993c2.308 4-.579 8.998-5.197 8.998H58.388c-4.617.001-7.504-4.997-5.197-8.997z" }) }); }; var style6 = { width: 12, height: 12, flexShrink: 0 }; var container25 = { maxWidth: 500 }; var label4 = { fontSize: 13, color: "white", fontFamily: "sans-serif" }; var ValidationMessage = ({ message, align, type }) => { const finalStyle = useMemo54(() => { return { ...style6, fill: type === "warning" ? WARNING_COLOR : FAIL_COLOR }; }, [type]); return /* @__PURE__ */ jsx99("div", { style: container25, children: /* @__PURE__ */ jsxs44(Row, { align: "center", justify: align, children: [ /* @__PURE__ */ jsx99(WarningTriangle, { style: finalStyle }), /* @__PURE__ */ jsx99(Spacing, { x: 1 }), /* @__PURE__ */ jsx99("div", { style: label4, children: message }) ] }) }); }; // src/components/SegmentedControl.tsx import { useCallback as useCallback46, useMemo as useMemo55, useState as useState39 } from "react"; import { jsx as jsx100 } from "react/jsx-runtime"; var container26 = { display: "flex", flexDirection: "row", overflow: "hidden", border: "1px solid " + INPUT_BORDER_COLOR_UNHOVERED, flexWrap: "wrap", maxWidth: 350, justifyContent: "flex-end" }; var item = { display: "flex", fontSize: 15, padding: "4px 12px", cursor: "pointer", appearance: "none", border: "none", flex: 1, justifyContent: "center", whiteSpace: "nowrap" }; var SegmentedControl = ({ items, needsWrapping }) => { const controlStyle = useMemo55(() => { if (needsWrapping) { return { ...container26, flexWrap: "wrap", maxWidth: "248px", justifyContent: "flex-end", marginBottom: "8px" }; } return { ...container26 }; }, [needsWrapping]); return /* @__PURE__ */ jsx100("div", { style: controlStyle, children: items.map((i) => { return /* @__PURE__ */ jsx100(Item, { onClick: i.onClick, selected: i.selected, children: i.label }, i.key); }) }); }; var Item = ({ selected, onClick, children }) => { const [hovered, setHovered] = useState39(false); const { tabIndex } = useZIndex(); const onPointerEnter = useCallback46(() => { setHovered(true); }, []); const onPointerLeave = useCallback46(() => { setHovered(false); }, []); const itemStyle3 = useMemo55(() => { return { ...item, backgroundColor: selected ? INPUT_BACKGROUND : "transparent", color: selected ? "white" : hovered ? "white" : LIGHT_TEXT }; }, [hovered, selected]); return /* @__PURE__ */ jsx100("button", { type: "button", onPointerEnter, onPointerLeave, style: itemStyle3, tabIndex, onClick, children }); }; // src/components/RenderModal/RenderModalJSONPropsEditor.tsx import React67, { useCallback as useCallback47, useEffect as useEffect40, useMemo as useMemo57 } from "react"; import { NoReactInternals as NoReactInternals8 } from "remotion/no-react"; // src/components/RenderModal/SchemaEditor/ZodErrorMessages.tsx import { useMemo as useMemo56 } from "react"; import { jsx as jsx101, jsxs as jsxs45 } from "react/jsx-runtime"; var schemaLabel = { fontSize: 14, color: LIGHT_TEXT }; var jsonLabel = { color: "white", fontSize: 13, fontFamily: "sans-serif", display: "flex", alignItems: "center" }; var triangleStyle = { width: 12, height: 12, flexShrink: 0, fill: FAIL_COLOR }; var ZodErrorMessages = ({ zodValidationResult, viewTab }) => { if (zodValidationResult.success) { throw new Error("Expected error"); } const style7 = useMemo56(() => { return viewTab === "json" ? jsonLabel : schemaLabel; }, [viewTab]); const code = useMemo56(() => { return { ...schemaLabel, fontFamily: "monospace" }; }, []); if (viewTab === "json") { return /* @__PURE__ */ jsx101("div", { children: zodValidationResult.error.errors.map((error) => { return /* @__PURE__ */ jsxs45("div", { style: style7, children: [ /* @__PURE__ */ jsx101(WarningTriangle, { style: triangleStyle }), /* @__PURE__ */ jsx101(Spacing, { x: 1 }), error.path.length === 0 ? "Root" : error.path.join("."), ":", " ", error.message ] }, error.path.join(".")); }) }); } return /* @__PURE__ */ jsx101("div", { children: zodValidationResult.error.errors.map((error) => { return /* @__PURE__ */ jsxs45("div", { style: style7, children: [ "-", " ", /* @__PURE__ */ jsx101("code", { style: code, children: error.path.length === 0 ? "Root" : error.path.join(".") }), ": ", error.message ] }, error.path.join(".")); }) }); }; // src/components/RenderModal/SchemaEditor/deep-equal.ts function deepEqual(a, b) { if (a === b) { return true; } if (a instanceof Date && b instanceof Date) { return a.getTime() === b.getTime(); } if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) { return false; } const keysA = Object.keys(a); const keysB = Object.keys(b); if (keysA.length !== keysB.length) { return false; } for (const key of keysA) { if (!keysB.includes(key) || !deepEqual(a[key], b[key])) { return false; } } return true; } // src/components/RenderModal/RenderModalJSONPropsEditor.tsx import { jsx as jsx102, jsxs as jsxs46 } from "react/jsx-runtime"; var style7 = { fontFamily: "monospace", flex: 1 }; var scrollable = { padding: "8px 12px", display: "flex", flexDirection: "column", flex: 1 }; var parseJSON = (str, schema) => { try { const value = NoReactInternals8.deserializeJSONWithSpecialTypes(str); const zodValidation = schema.safeParse(value); return { str, value, validJSON: true, zodValidation }; } catch (e) { return { str, validJSON: false, error: e.message }; } }; var RenderModalJSONPropsEditor = ({ setValue, value, defaultProps, onSave, showSaveButton, serializedJSON, schema }) => { if (serializedJSON === null) { throw new Error("expecting serializedJSON to be defined"); } const keybindings = useKeybinding(); const [localValue, setLocalValue] = React67.useState(() => { return parseJSON(serializedJSON.serializedString, schema); }); const onPretty = useCallback47(() => { if (!localValue.validJSON) { return; } const parsed = JSON.parse(localValue.str); setLocalValue({ ...localValue, str: JSON.stringify(parsed, null, 2) }); }, [localValue, setLocalValue]); const onChange = useCallback47((e) => { const parsed = parseJSON(e.target.value, schema); if (parsed.validJSON) { const validationResult = schema.safeParse(parsed.value); setLocalValue({ str: e.target.value, value: parsed.value, validJSON: parsed.validJSON, zodValidation: validationResult }); if (validationResult.success) { setValue(parsed.value); } } else { setLocalValue({ str: e.target.value, validJSON: parsed.validJSON, error: parsed.error }); } }, [schema, setValue]); const hasChanged = useMemo57(() => { return !deepEqual(value, defaultProps); }, [defaultProps, value]); useEffect40(() => { setUnsavedProps(hasChanged); }, [hasChanged]); const onQuickSave = useCallback47(() => { if (hasChanged) { onSave(); } }, [hasChanged, onSave]); useEffect40(() => { setLocalValue(parseJSON(localValue.str, schema)); }, [localValue.str, schema]); useEffect40(() => { const save = keybindings.registerKeybinding({ event: "keydown", key: "s", commandCtrlKey: true, callback: onQuickSave, preventDefault: true, triggerIfInputFieldFocused: true, keepRegisteredWhenNotHighestContext: false }); return () => { save.unregister(); }; }, [keybindings, onQuickSave, onSave]); const reset = useCallback47(() => { setValue(defaultProps); setLocalValue(parseJSON(JSON.stringify(defaultProps, null, 2), schema)); }, [defaultProps, schema, setValue]); const textAreaStyle = useMemo57(() => { const fail = !localValue.validJSON || !localValue.zodValidation.success; if (!fail) { return style7; } return { ...style7, borderColor: FAIL_COLOR }; }, [localValue]); return /* @__PURE__ */ jsxs46("div", { style: scrollable, children: [ /* @__PURE__ */ jsx102(RemTextarea, { onChange, value: localValue.str, status: localValue.validJSON ? "ok" : "error", style: textAreaStyle }), /* @__PURE__ */ jsx102(Spacing, { y: 1 }), localValue.validJSON === false ? /* @__PURE__ */ jsx102(ValidationMessage, { align: "flex-start", message: localValue.error, type: "error" }) : localValue.zodValidation.success === false ? /* @__PURE__ */ jsx102(ZodErrorMessages, { zodValidationResult: localValue.zodValidation, viewTab: "json" }) : null, /* @__PURE__ */ jsx102(Spacing, { y: 1 }), /* @__PURE__ */ jsxs46(Row, { children: [ /* @__PURE__ */ jsx102(Button, { disabled: !(hasChanged || !localValue.validJSON), onClick: reset, children: "Reset" }), /* @__PURE__ */ jsx102(Flex, {}), /* @__PURE__ */ jsx102(Button, { disabled: !localValue.validJSON, onClick: onPretty, children: "Format" }), /* @__PURE__ */ jsx102(Spacing, { x: 1 }), showSaveButton ? /* @__PURE__ */ jsx102(Button, { onClick: onSave, disabled: !(localValue.validJSON && localValue.zodValidation.success) || !localValue.validJSON || !hasChanged, children: "Save" }) : null ] }) ] }); }; // src/components/RenderModal/SchemaEditor/SchemaEditor.tsx import { useCallback as useCallback67, useEffect as useEffect46, useMemo as useMemo78, useState as useState50 } from "react"; import { Internals as Internals30 } from "remotion"; // src/components/RenderModal/SchemaEditor/SchemaErrorMessages.tsx import { jsx as jsx103, jsxs as jsxs47 } from "react/jsx-runtime"; var explainer = { display: "flex", flex: 1, flexDirection: "column", padding: "0 12px", justifyContent: "center", alignItems: "center", textAlign: "center", background: BACKGROUND }; var errorExplanation = { fontSize: 14, color: LIGHT_TEXT, fontFamily: "sans-serif", lineHeight: 1.5 }; var codeSnippet = { fontSize: 14, color: BLUE, fontFamily: "monospace" }; var errorContainer = { padding: "8px 12px", overflowY: "auto" }; var openDocs = () => { window.open("https://www.remotion.dev/docs/schemas"); }; var ZodNotInstalled = () => { return /* @__PURE__ */ jsxs47("div", { style: explainer, children: [ /* @__PURE__ */ jsxs47("div", { style: errorExplanation, children: [ "Install ", /* @__PURE__ */ jsx103("code", { style: inlineCodeSnippet, children: "zod" }), " as a dependency to interactively control the props of the composition." ] }), /* @__PURE__ */ jsx103(Spacing, { y: 2, block: true }), /* @__PURE__ */ jsx103(Button, { onClick: openDocs, children: "Learn how" }) ] }); }; var NoSchemaDefined = () => { return /* @__PURE__ */ jsxs47("div", { style: explainer, children: [ /* @__PURE__ */ jsxs47("div", { style: errorExplanation, children: [ "Make the props of this composition interactively editable by adding a", " ", /* @__PURE__ */ jsx103("code", { style: inlineCodeSnippet, children: "schema" }), " prop to the", " ", /* @__PURE__ */ jsx103("code", { style: inlineCodeSnippet, children: "" }), " component." ] }), /* @__PURE__ */ jsx103(Spacing, { y: 2, block: true }), /* @__PURE__ */ jsx103(Button, { onClick: openDocs, children: "Learn how" }) ] }); }; var NoDefaultProps = () => { return /* @__PURE__ */ jsxs47("div", { style: explainer, children: [ /* @__PURE__ */ jsxs47("div", { style: errorExplanation, children: [ "The schema can not be edited because the", " ", /* @__PURE__ */ jsx103("code", { style: inlineCodeSnippet, children: "defaultProps" }), " prop in the", " ", /* @__PURE__ */ jsx103("code", { style: inlineCodeSnippet, children: "" }), " does not exist." ] }), /* @__PURE__ */ jsx103(Spacing, { y: 1 }), /* @__PURE__ */ jsxs47("div", { style: errorExplanation, children: [ "Fix the schema by adding a", " ", /* @__PURE__ */ jsx103("code", { style: inlineCodeSnippet, children: "defaultProps" }), " prop to your composition." ] }), /* @__PURE__ */ jsx103(Spacing, { y: 2, block: true }), /* @__PURE__ */ jsx103(Button, { onClick: openDocs, children: "Learn more" }) ] }); }; var InvalidDefaultProps = ({ zodValidationResult }) => { return /* @__PURE__ */ jsxs47("div", { style: errorContainer, children: [ /* @__PURE__ */ jsxs47("div", { style: errorExplanation, children: [ "The schema can not be edited because the", " ", /* @__PURE__ */ jsx103("code", { style: inlineCodeSnippet, children: "defaultProps" }), " prop in the", " ", /* @__PURE__ */ jsx103("code", { style: inlineCodeSnippet, children: "" }), " is not valid:" ] }), /* @__PURE__ */ jsx103(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx103(ZodErrorMessages, { zodValidationResult, viewTab: "schema" }), /* @__PURE__ */ jsx103(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsxs47("div", { style: errorExplanation, children: [ "Fix the schema by changing the", " ", /* @__PURE__ */ jsx103("code", { style: inlineCodeSnippet, children: "defaultProps" }), " prop in your composition so it does not give a type error." ] }) ] }); }; var InvalidSchema = ({ zodValidationResult, reset }) => { return /* @__PURE__ */ jsxs47("div", { style: errorContainer, children: [ /* @__PURE__ */ jsx103("div", { style: errorExplanation, children: "The data does not satisfy the schema:" }), /* @__PURE__ */ jsx103(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx103(ZodErrorMessages, { zodValidationResult, viewTab: "schema" }), /* @__PURE__ */ jsx103(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx103("div", { style: errorExplanation, children: "Fix the schema using the JSON editor." }), /* @__PURE__ */ jsx103(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsxs47("div", { style: errorExplanation, children: [ "Alternatively, reset the data to the", " ", /* @__PURE__ */ jsx103("code", { style: inlineCodeSnippet, children: "defaultProps" }), " that you have defined." ] }), /* @__PURE__ */ jsx103(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx103(Button, { onClick: reset, children: "Reset props" }) ] }); }; var TopLevelZodValue = ({ typeReceived }) => { return /* @__PURE__ */ jsxs47("div", { style: explainer, children: [ /* @__PURE__ */ jsxs47("div", { style: errorExplanation, children: [ "The top-level type of the schema must be a pure", " ", /* @__PURE__ */ jsx103("code", { style: codeSnippet, children: "z.object" }), ". Instead got a schema of type", " ", /* @__PURE__ */ jsx103("code", { style: codeSnippet, children: typeReceived }) ] }), /* @__PURE__ */ jsx103(Spacing, { y: 1 }), /* @__PURE__ */ jsx103("div", { style: errorExplanation, children: "Fix the schema by changing the top-level Zod type to an object." }), /* @__PURE__ */ jsx103(Spacing, { y: 2, block: true }), /* @__PURE__ */ jsx103(Button, { onClick: openDocs, children: "Learn more" }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodObjectEditor.tsx import React91, { useMemo as useMemo77, useState as useState49 } from "react"; // src/components/RenderModal/layout.ts var optionRow = { display: "flex", flexDirection: "row", minHeight: 40, paddingLeft: 16, paddingRight: 16, paddingTop: 8, paddingBottom: 8 }; var label5 = { width: 290, fontSize: 15, lineHeight: "40px", color: LIGHT_TEXT, fontFamily: "sans-serif", display: "flex", flexDirection: "row", alignItems: "center" }; var rightRow = { display: "flex", flexDirection: "row", justifyContent: "flex-end", alignSelf: "center", flex: 1 }; var input = { minWidth: 250 }; var fieldSetText = { color: LIGHT_TEXT, fontSize: 14, fontFamily: "monospace" }; var fieldsetLabel = { ...fieldSetText, display: "flex", flexDirection: "row", alignItems: "center", width: "100%" }; // src/components/RenderModal/SchemaEditor/Fieldset.tsx import { createContext as createContext18, useContext as useContext34, useMemo as useMemo58 } from "react"; import { jsx as jsx104 } from "react/jsx-runtime"; var SCHEMA_EDITOR_FIELDSET_PADDING = 10; var AlreadyPaddedRightContext = createContext18(false); var Fieldset = ({ children, shouldPad }) => { const alreadyPadded = useContext34(AlreadyPaddedRightContext); const style8 = useMemo58(() => { if (shouldPad) { return { padding: SCHEMA_EDITOR_FIELDSET_PADDING, paddingTop: SCHEMA_EDITOR_FIELDSET_PADDING / 2, paddingRight: alreadyPadded ? 0 : SCHEMA_EDITOR_FIELDSET_PADDING }; } return {}; }, [alreadyPadded, shouldPad]); const content = /* @__PURE__ */ jsx104("div", { style: style8, children }); if (shouldPad) { return /* @__PURE__ */ jsx104(AlreadyPaddedRightContext.Provider, { value: true, children: content }); } return content; }; // src/components/RenderModal/SchemaEditor/SchemaLabel.tsx import { useCallback as useCallback49, useMemo as useMemo59, useState as useState40 } from "react"; // src/components/RenderModal/InlineRemoveButton.tsx import { useCallback as useCallback48 } from "react"; import { jsx as jsx105 } from "react/jsx-runtime"; var clearIcon = { height: 14, color: "currentColor" }; var InlineRemoveButton = ({ onClick }) => { const renderAction = useCallback48((color) => { return /* @__PURE__ */ jsx105("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 320 512", style: clearIcon, children: /* @__PURE__ */ jsx105("path", { d: "M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z", fill: color }) }); }, []); return /* @__PURE__ */ jsx105(InlineAction, { renderAction, onClick }); }; // src/components/RenderModal/SchemaEditor/get-schema-label.ts var getSchemaLabel = (jsonPath) => { const lastKey = jsonPath[jsonPath.length - 1]; if (typeof lastKey === "number") { const secondLastKey = jsonPath[jsonPath.length - 2]; if (typeof secondLastKey === "undefined") { return `[${lastKey}]:`; } return `${lastKey}:`; } return `${lastKey}:`; }; // src/components/RenderModal/SchemaEditor/scroll-to-default-props-path.ts import React69 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 = React69.createRef(); // src/components/RenderModal/SchemaEditor/SchemaLabel.tsx import { jsx as jsx106, jsxs as jsxs48 } from "react/jsx-runtime"; var compactStyles = { fontSize: 15, color: LIGHT_TEXT, fontFamily: "sans-serif", display: "flex", flexDirection: "row", alignItems: "center", flex: 1 }; var SchemaLabel = ({ jsonPath, isDefaultValue, onReset, onSave, showSaveButton, onRemove, saving, valid, saveDisabledByParent, suffix: suffix2, handleClick }) => { const [clickableButtonHovered, setClickableButtonHovered] = useState40(false); const disableSave = saving || !valid || saveDisabledByParent; const labelStyle4 = useMemo59(() => { return { fontFamily: "monospace", fontSize: 14, color: valid ? clickableButtonHovered ? "white" : LIGHT_TEXT : FAIL_COLOR, lineHeight: "24px" }; }, [clickableButtonHovered, valid]); const onClickablePointerEnter = useCallback49(() => { setClickableButtonHovered(true); }, []); const onClickablePointerLeave = useCallback49(() => { setClickableButtonHovered(false); }, []); const labelContent = /* @__PURE__ */ jsxs48("span", { style: labelStyle4, children: [ getSchemaLabel(jsonPath), " ", suffix2 ? suffix2 : null ] }); return /* @__PURE__ */ jsxs48("div", { style: compactStyles, className: DEFAULT_PROPS_PATH_CLASSNAME, "data-json-path": jsonPath.join("."), children: [ handleClick ? /* @__PURE__ */ jsx106("button", { onPointerEnter: onClickablePointerEnter, onPointerLeave: onClickablePointerLeave, type: "button", onClick: handleClick, style: { border: "none", padding: 0 }, children: labelContent }) : labelContent, /* @__PURE__ */ jsx106(Flex, {}), isDefaultValue ? null : /* @__PURE__ */ jsx106(SchemaResetButton, { onClick: onReset }), isDefaultValue ? null : showSaveButton ? /* @__PURE__ */ jsx106(SchemaSaveButton, { onClick: onSave, disabled: disableSave }) : null, onRemove ? /* @__PURE__ */ jsx106(InlineRemoveButton, { onClick: onRemove }) : null ] }); }; // src/components/RenderModal/SchemaEditor/SchemaSeparationLine.tsx import { useCallback as useCallback50, useMemo as useMemo60, useState as useState41 } from "react"; // src/icons/plus.tsx import { jsx as jsx107 } from "react/jsx-runtime"; var Plus = ({ color, ...props }) => { return /* @__PURE__ */ jsx107("svg", { ...props, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 448 512", children: /* @__PURE__ */ jsx107("path", { fill: color, d: "M432 256c0 17.69-14.33 32.01-32 32.01H256v144c0 17.69-14.33 31.99-32 31.99s-32-14.3-32-31.99v-144H48c-17.67 0-32-14.32-32-32.01s14.33-31.99 32-31.99H192v-144c0-17.69 14.33-32.01 32-32.01s32 14.32 32 32.01v144h144C417.7 224 432 238.3 432 256z" }) }); }; // src/components/RenderModal/SchemaEditor/create-zod-values.ts var createZodValues = (schema, zodRuntime, zodTypes) => { if (!schema) { throw new Error("Invalid zod schema"); } const def = schema._def; const typeName = def.typeName; switch (typeName) { case zodRuntime.ZodFirstPartyTypeKind.ZodString: return ""; case zodRuntime.ZodFirstPartyTypeKind.ZodNumber: { for (const check of def.checks) { if (check.kind === "min") { return check.value; } if (check.kind === "max" && check.value < 0) { return check.value; } } return 0; } case zodRuntime.ZodFirstPartyTypeKind.ZodBigInt: return BigInt(0); case zodRuntime.ZodFirstPartyTypeKind.ZodBoolean: return false; case zodRuntime.ZodFirstPartyTypeKind.ZodNaN: return NaN; case zodRuntime.ZodFirstPartyTypeKind.ZodDate: return new Date; case zodRuntime.ZodFirstPartyTypeKind.ZodSymbol: return Symbol("remotion"); case zodRuntime.ZodFirstPartyTypeKind.ZodUndefined: return; case zodRuntime.ZodFirstPartyTypeKind.ZodNull: return null; case zodRuntime.ZodFirstPartyTypeKind.ZodAny: throw new Error("Cannot create a value for type z.any()"); case zodRuntime.ZodFirstPartyTypeKind.ZodUnknown: throw new Error("Cannot create a value for type z.unknown()"); case zodRuntime.ZodFirstPartyTypeKind.ZodNever: throw new Error("Cannot create a value for type z.never()"); case zodRuntime.ZodFirstPartyTypeKind.ZodVoid: return; case zodRuntime.ZodFirstPartyTypeKind.ZodObject: { const shape = def.shape(); const keys = Object.keys(shape); const returnValue = keys.reduce((existing, key) => { existing[key] = createZodValues(shape[key], zodRuntime, zodTypes); return existing; }, {}); return returnValue; } case zodRuntime.ZodFirstPartyTypeKind.ZodArray: { return [ createZodValues(def.type, zodRuntime, zodTypes) ]; } case zodRuntime.ZodFirstPartyTypeKind.ZodUnion: { const firstOptions = def.options[0]; return firstOptions ? createZodValues(firstOptions, zodRuntime, zodTypes) : undefined; } case zodRuntime.ZodFirstPartyTypeKind.ZodDiscriminatedUnion: { const options = def.options[0]; return createZodValues(options, zodRuntime, zodTypes); } case zodRuntime.ZodFirstPartyTypeKind.ZodLiteral: { return def.value; } case zodRuntime.ZodFirstPartyTypeKind.ZodEffects: { if (zodTypes && schema._def.description === zodTypes.ZodZypesInternals.REMOTION_COLOR_BRAND) { return "#ffffff"; } if (zodTypes && schema._def.description === zodTypes.ZodZypesInternals.REMOTION_TEXTAREA_BRAND) { return ""; } if (zodTypes && schema._def.description === zodTypes.ZodZypesInternals.REMOTION_MATRIX_BRAND) { return [ [1, 0, 0], [0, 1, 0], [0, 0, 1] ]; } return createZodValues(def.schema, zodRuntime, zodTypes); } case zodRuntime.ZodFirstPartyTypeKind.ZodIntersection: { const { left: left3, right } = def; const leftValue = createZodValues(left3, zodRuntime, zodTypes); if (typeof leftValue !== "object") { throw new Error("Cannot create value for type z.intersection: Left side is not an object"); } const rightValue = createZodValues(right, zodRuntime, zodTypes); if (typeof rightValue !== "object") { throw new Error("Cannot create value for type z.intersection: Right side is not an object"); } return { ...leftValue, ...rightValue }; } case zodRuntime.ZodFirstPartyTypeKind.ZodTuple: { const items = def.items.map((item2) => createZodValues(item2, zodRuntime, zodTypes)); return items; } case zodRuntime.ZodFirstPartyTypeKind.ZodRecord: { const values = createZodValues(def.valueType, zodRuntime, zodTypes); return { key: values }; } case zodRuntime.ZodFirstPartyTypeKind.ZodMap: { const defType = def; const values = createZodValues(defType.valueType, zodRuntime, zodTypes); const key = createZodValues(defType.keyType, zodRuntime, zodTypes); return new Map([[key, values]]); } case zodRuntime.ZodFirstPartyTypeKind.ZodLazy: { const defType = def; const type = defType.getter(); return createZodValues(type, zodRuntime, zodTypes); } case zodRuntime.ZodFirstPartyTypeKind.ZodSet: { const defType = def; const values = createZodValues(defType.valueType, zodRuntime, zodTypes); return new Set([values]); } case zodRuntime.ZodFirstPartyTypeKind.ZodFunction: { throw new Error("Cannot create a value for type function"); } case zodRuntime.ZodFirstPartyTypeKind.ZodEnum: { const { values } = def; return values[0]; } case zodRuntime.ZodFirstPartyTypeKind.ZodNativeEnum: { return 0; } case zodRuntime.ZodFirstPartyTypeKind.ZodOptional: { const defType = def; const value = createZodValues(defType.innerType, zodRuntime, zodTypes); return value; } case zodRuntime.ZodFirstPartyTypeKind.ZodNullable: { const defType = def; const value = createZodValues(defType.innerType, zodRuntime, zodTypes); return value; } case zodRuntime.ZodFirstPartyTypeKind.ZodDefault: { const defType = def; return defType.defaultValue(); } case zodRuntime.ZodFirstPartyTypeKind.ZodCatch: { const defType = def; const value = createZodValues(defType.innerType, zodRuntime, zodTypes); return value; } case zodRuntime.ZodFirstPartyTypeKind.ZodPromise: { const defType = def; const value = createZodValues(defType.type, zodRuntime, zodTypes); return Promise.resolve(value); } case zodRuntime.ZodFirstPartyTypeKind.ZodBranded: { const defType = def; const value = createZodValues(defType.type, zodRuntime, zodTypes); return value; } case zodRuntime.ZodFirstPartyTypeKind.ZodPipeline: { const defType = def; const value = createZodValues(defType.out, zodRuntime, zodTypes); return value; } default: throw new Error("Not implemented: " + typeName); } }; // src/components/RenderModal/SchemaEditor/SchemaSeparationLine.tsx import { jsx as jsx108, jsxs as jsxs49, Fragment as Fragment18 } from "react/jsx-runtime"; var VERTICAL_GUIDE_HEIGHT = 24; var line2 = { borderBottom: "1px solid " + LINE_COLOR }; var SchemaSeparationLine = () => { return /* @__PURE__ */ jsx108("div", { style: line2 }); }; var arraySeparationLine = { borderBottom: "1px solid " + LINE_COLOR, marginTop: -VERTICAL_GUIDE_HEIGHT / 2, pointerEvents: "none", width: "100%", flexBasis: "100%" }; var SchemaArrayItemSeparationLine = ({ onChange, index, schema, isLast, showAddButton }) => { const [outerHovered, setOuterHovered] = useState41(false); const [innerHovered, setInnerHovered] = useState41(false); const zodTypes = useZodTypesIfPossible(); const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const def = schema._def; const onAdd = useCallback50(() => { onChange((oldV) => { return [ ...oldV.slice(0, index + 1), createZodValues(def.type, z, zodTypes), ...oldV.slice(index + 1) ]; }, false, true); }, [def.type, index, onChange, z, zodTypes]); const dynamicAddButtonStyle = useMemo60(() => { return { display: "flex", justifyContent: "center", height: VERTICAL_GUIDE_HEIGHT, opacity: outerHovered || isLast ? 1 : 0, position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)" }; }, [isLast, outerHovered]); const inner = useMemo60(() => { return { background: BACKGROUND, paddingLeft: 10, paddingRight: 10 }; }, []); const onOuterMouseEnter = useCallback50(() => { setOuterHovered(true); }, []); const onOuterMouseLeave = useCallback50(() => { setOuterHovered(false); }, []); const onInnerMouseEnter = useCallback50(() => { setInnerHovered(true); }, []); const onInnerMouseLeave = useCallback50(() => { setInnerHovered(false); }, []); return /* @__PURE__ */ jsxs49("div", { style: { display: "flex", flexDirection: "row", height: VERTICAL_GUIDE_HEIGHT }, children: [ /* @__PURE__ */ jsxs49("div", { style: { flex: 1, position: "relative", display: "flex", flexDirection: "column", alignItems: "flex-end" }, children: [ showAddButton && /* @__PURE__ */ jsx108("div", { style: dynamicAddButtonStyle, onPointerEnter: onOuterMouseEnter, onPointerLeave: onOuterMouseLeave, children: /* @__PURE__ */ jsx108("div", { onClick: onAdd, style: inner, onPointerEnter: onInnerMouseEnter, onPointerLeave: onInnerMouseLeave, children: /* @__PURE__ */ jsx108(Plus, { color: innerHovered ? "white" : LIGHT_TEXT, style: { height: VERTICAL_GUIDE_HEIGHT / 2 } }) }) }), /* @__PURE__ */ jsx108("div", { style: arraySeparationLine }) ] }), isLast ? /* @__PURE__ */ jsxs49(Fragment18, { children: [ /* @__PURE__ */ jsx108(Spacing, { x: 1 }), /* @__PURE__ */ jsx108("div", { style: { ...fieldSetText, alignItems: "center", display: "flex" }, children: "]" }) ] }) : null ] }); }; // src/components/RenderModal/SchemaEditor/SchemaVerticalGuide.tsx import { useMemo as useMemo61 } from "react"; import { jsx as jsx109, jsxs as jsxs50 } from "react/jsx-runtime"; var flex = { flex: 1 }; var SchemaVerticalGuide = ({ isRoot, children }) => { const outer = useMemo61(() => { return { display: "flex", flexDirection: "row", position: "relative", marginLeft: isRoot ? 0 : 4 }; }, [isRoot]); const inner = useMemo61(() => { return isRoot ? {} : { height: `calc(100% - ${VERTICAL_GUIDE_HEIGHT / 2}px)`, width: 1, background: "#363A3E", position: "absolute" }; }, [isRoot]); return /* @__PURE__ */ jsxs50("div", { style: outer, children: [ /* @__PURE__ */ jsx109("div", { style: inner }), /* @__PURE__ */ jsx109("div", { style: flex, children }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodArrayEditor.tsx import React76, { useMemo as useMemo66, useState as useState44 } from "react"; // src/components/RenderModal/SchemaEditor/ZodArrayItemEditor.tsx import { useCallback as useCallback51, useMemo as useMemo62 } from "react"; import { jsx as jsx110 } from "react/jsx-runtime"; var ZodArrayItemEditor = ({ def, onChange, jsonPath, index, value, defaultValue, onSave: onSaveObject, showSaveButton, saving, saveDisabledByParent, mayPad, mayRemove }) => { const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const onRemove = useCallback51(() => { onChange((oldV) => [...oldV.slice(0, index), ...oldV.slice(index + 1)], false, true); }, [index, onChange]); const setValue = useCallback51((val) => { onChange((oldV) => [ ...oldV.slice(0, index), typeof val === "function" ? val(oldV[index]) : val, ...oldV.slice(index + 1) ], false, false); }, [index, onChange]); const newJsonPath = useMemo62(() => [...jsonPath, index], [index, jsonPath]); const onSave = useCallback51((updater) => { onSaveObject((oldV) => [ ...oldV.slice(0, index), updater(oldV[index]), ...oldV.slice(index + 1) ], false, false); }, [index, onSaveObject]); return /* @__PURE__ */ jsx110("div", { children: /* @__PURE__ */ jsx110(ZodSwitch, { jsonPath: newJsonPath, schema: def.type, value, setValue, defaultValue, onSave, showSaveButton, onRemove: mayRemove ? onRemove : null, saving, saveDisabledByParent, mayPad }) }); }; // src/components/RenderModal/InfoBubble.tsx import { PlayerInternals as PlayerInternals11 } from "@remotion/player"; import { useCallback as useCallback52, useEffect as useEffect41, useMemo as useMemo64, useRef as useRef25, useState as useState42 } from "react"; import ReactDOM7 from "react-dom"; // src/components/RenderModal/InfoTooltip.tsx import { useMemo as useMemo63 } from "react"; import { jsx as jsx111, jsxs as jsxs51 } from "react/jsx-runtime"; var arrow = { height: 7, display: "block", overflow: "visible", marginLeft: 7 }; var arrowUp = { ...arrow, transform: `translateY(1px)` }; var arrowDown = { ...arrow, marginTop: -1 }; var InfoTooltip = ({ children, arrowDirection, backgroundColor }) => { const container27 = useMemo63(() => { return { boxShadow: arrowDirection === "down" ? SHADOW_TOWARDS_TOP : SHADOW_TOWARDS_BOTTOM, background: backgroundColor, color: "white", border: "0.5px solid " + BORDER_COLOR, maxHeight: 200, overflow: "auto", borderRadius: "4px" }; }, [arrowDirection, backgroundColor]); return /* @__PURE__ */ jsxs51("div", { style: { display: "flex", flexDirection: arrowDirection === "up" ? "column-reverse" : "column", alignItems: "flex-start" }, children: [ /* @__PURE__ */ jsx111("div", { style: container27, className: VERTICAL_SCROLLBAR_CLASSNAME, children }), arrowDirection === "down" ? /* @__PURE__ */ jsx111("svg", { viewBox: "0 0 14 7", style: arrowDown, children: /* @__PURE__ */ jsx111("path", { d: `M 14 0 L 7 7 L 0 0`, fill: backgroundColor, strokeLinecap: "butt", stroke: BORDER_COLOR, strokeWidth: 0.5 }) }) : null, arrowDirection === "up" ? /* @__PURE__ */ jsx111("svg", { viewBox: "0 0 14 7", style: arrowUp, children: /* @__PURE__ */ jsx111("path", { d: `M 0 7 L 7 0 L 14 7`, fill: backgroundColor, strokeLinecap: "butt", stroke: BORDER_COLOR, strokeWidth: 0.5 }) }) : null ] }); }; // src/components/RenderModal/InfoBubble.tsx import { jsx as jsx112, jsxs as jsxs52, Fragment as Fragment19 } from "react/jsx-runtime"; var icon4 = { color: LIGHT_TEXT, height: 15 }; var container27 = { display: "inline-block", border: "none", fontSize: 14, verticalAlign: "text-bottom" }; var InfoBubble = ({ title: title3, children }) => { const [hovered, setIsHovered] = useState42(false); const [opened, setOpened] = useState42(false); const ref = useRef25(null); const { tabIndex, currentZIndex } = useZIndex(); const size2 = PlayerInternals11.useElementSize(ref, { triggerOnWindowResize: true, shouldApplyCssTransforms: true }); const refresh = size2?.refresh; const onHide = useCallback52(() => { setOpened(false); }, []); useEffect41(() => { const { current } = ref; if (!current) { return; } const onMouseEnter = () => setIsHovered(true); const onMouseLeave = () => setIsHovered(false); const onPointerUp = () => { return setOpened((o) => { if (!o) { refresh?.(); } return !o; }); }; const onClick = (e) => { e.stopPropagation(); const isKeyboardInitiated = e.detail === 0; if (!isKeyboardInitiated) { return; } return setOpened((o) => { return !o; }); }; current.addEventListener("mouseenter", onMouseEnter); current.addEventListener("mouseleave", onMouseLeave); current.addEventListener("pointerup", onPointerUp); current.addEventListener("click", onClick); return () => { current.removeEventListener("mouseenter", onMouseEnter); current.removeEventListener("mouseleave", onMouseLeave); current.removeEventListener("pointerup", onPointerUp); current.removeEventListener("click", onClick); }; }, [refresh]); const layout = useMemo64(() => { if (!size2) { return "down"; } const spaceToBottom = size2.windowSize.height - (size2.top + size2.height); const spaceToTop = size2.top; const l = spaceToTop > spaceToBottom ? "down" : "up"; return l; }, [size2]); const portalStyle = useMemo64(() => { if (!size2 || !opened) { return null; } return { ...layout === "up" ? { position: "fixed", top: size2.top + size2.height } : { position: "fixed", bottom: size2.windowSize.height - size2.top }, left: size2.left }; }, [layout, opened, size2]); const style8 = useMemo64(() => { return { ...container27, userSelect: "none", WebkitUserSelect: "none", color: "white", display: "inline-flex", flexDirection: "row", alignItems: "center", padding: 6 }; }, []); return /* @__PURE__ */ jsxs52(Fragment19, { children: [ /* @__PURE__ */ jsx112("button", { ref, tabIndex, style: style8, title: title3, type: "button", children: /* @__PURE__ */ jsx112("svg", { style: icon4, viewBox: "0 0 512 512", children: /* @__PURE__ */ jsx112("path", { fill: hovered ? "white" : LIGHT_TEXT, d: "M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336c-13.3 0-24 10.7-24 24s10.7 24 24 24h80c13.3 0 24-10.7 24-24s-10.7-24-24-24h-8V248c0-13.3-10.7-24-24-24H216c-13.3 0-24 10.7-24 24s10.7 24 24 24h24v64H216zm40-144a32 32 0 1 0 0-64 32 32 0 1 0 0 64z" }) }) }), portalStyle ? ReactDOM7.createPortal(/* @__PURE__ */ jsx112("div", { style: outerPortal, className: "css-reset", children: /* @__PURE__ */ jsx112(HigherZIndex, { onOutsideClick: onHide, onEscape: onHide, children: /* @__PURE__ */ jsx112("div", { style: portalStyle, children: /* @__PURE__ */ jsx112(InfoTooltip, { backgroundColor: INPUT_BACKGROUND, arrowDirection: layout, children }) }) }) }), getPortal(currentZIndex)) : null ] }); }; // src/components/RenderModal/SchemaEditor/ZodFieldValidation.tsx import { jsx as jsx113, jsxs as jsxs53 } from "react/jsx-runtime"; var legend = { display: "flex", flexDirection: "row", alignItems: "center" }; var stackTrace = { padding: 10 }; var stackTraceLabel = { fontSize: 14 }; var ZodFieldValidation = ({ localValue, path }) => { if (localValue.zodValidation.success) { return null; } return /* @__PURE__ */ jsxs53("div", { style: legend, children: [ /* @__PURE__ */ jsx113(ValidationMessage, { align: "flex-start", message: localValue.zodValidation.error.format()._errors[0], type: "error" }), /* @__PURE__ */ jsx113(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx113(InfoBubble, { title: "Zod validation failure", children: /* @__PURE__ */ jsxs53("div", { style: stackTrace, children: [ /* @__PURE__ */ jsx113("div", { style: stackTraceLabel, children: "Zod Validation has failed:" }), localValue.zodValidation.error.errors.map((error, index) => /* @__PURE__ */ jsxs53("div", { style: stackTraceLabel, children: [ "Type: ", error.code, " ", /* @__PURE__ */ jsx113("br", {}), "Message: ", error.message, /* @__PURE__ */ jsx113("br", {}), "Path: ", path.join(".") ] }, index)) ] }) }), /* @__PURE__ */ jsx113(Spacing, { x: 0.5 }) ] }); }; // src/components/RenderModal/SchemaEditor/local-state.tsx import { createContext as createContext19, useCallback as useCallback53, useContext as useContext35, useEffect as useEffect42, useMemo as useMemo65, useRef as useRef26, useState as useState43 } from "react"; import { Internals as Internals29 } from "remotion"; import { jsx as jsx114 } from "react/jsx-runtime"; var RevisionContext = createContext19({ childResetRevision: 0 }); var useLocalState = ({ unsavedValue, schema, setValue, savedValue }) => { const parentRevision = useContext35(RevisionContext).childResetRevision; const [resetRevision, setResetRevision] = useState43(0); const [localValueOrNull, setLocalValue] = useState43(() => { return { [parentRevision]: { value: unsavedValue, keyStabilityRevision: 0, zodValidation: schema.safeParse(unsavedValue) } }; }); const localUnsavedValue = useMemo65(() => { if ((localValueOrNull[parentRevision] ?? null) === null) { return { value: unsavedValue, keyStabilityRevision: 0, zodValidation: schema.safeParse(unsavedValue) }; } return localValueOrNull[parentRevision]; }, [localValueOrNull, parentRevision, schema, unsavedValue]); useEffect42(() => { const checkFile = () => { setResetRevision((old) => old + 1); setLocalValue({}); }; window.addEventListener(Internals29.PROPS_UPDATED_EXTERNALLY, checkFile); return () => { window.removeEventListener(Internals29.PROPS_UPDATED_EXTERNALLY, checkFile); }; }, []); const currentLocalValue = useMemo65(() => { return localUnsavedValue ?? { value: savedValue, keyStabilityRevision: 0, zodValidation: schema.safeParse(savedValue) }; }, [localUnsavedValue, savedValue, schema]); const stateRef = useRef26(currentLocalValue); stateRef.current = currentLocalValue; const onChange = useCallback53((updater, forceApply, increment) => { const newValue = updater(stateRef.current.value); const isSame = deepEqual(newValue, stateRef.current.value); if (isSame) { return; } const safeParse = schema.safeParse(newValue); if (safeParse.success || forceApply) { setValue(updater, forceApply, increment); } setLocalValue(() => { const newState = { keyStabilityRevision: currentLocalValue.keyStabilityRevision + (increment ? 1 : 0), value: newValue, zodValidation: safeParse }; stateRef.current = newState; return { ...localUnsavedValue, [parentRevision]: newState }; }); }, [ currentLocalValue.keyStabilityRevision, localUnsavedValue, parentRevision, schema, setValue ]); const contextValue = useMemo65(() => { return { childResetRevision: resetRevision }; }, [resetRevision]); const reset = useCallback53(() => { onChange(() => savedValue, true, true); setResetRevision((old) => old + 1); }, [savedValue, onChange]); const RevisionContextProvider = useCallback53(({ children }) => { return /* @__PURE__ */ jsx114(RevisionContext.Provider, { value: contextValue, children }); }, [contextValue]); return useMemo65(() => ({ localValue: currentLocalValue, onChange, reset, RevisionContextProvider }), [RevisionContextProvider, currentLocalValue, onChange, reset]); }; // src/components/RenderModal/SchemaEditor/ZodArrayEditor.tsx import { jsx as jsx115, jsxs as jsxs54 } from "react/jsx-runtime"; var ZodArrayEditor = ({ schema, jsonPath, setValue, defaultValue, value, onSave, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }) => { const { localValue, onChange, RevisionContextProvider, reset } = useLocalState({ unsavedValue: value, schema, setValue, savedValue: defaultValue }); const [expanded, setExpanded] = useState44(true); const def = schema._def; const suffix2 = useMemo66(() => { return expanded ? " [" : " [...] "; }, [expanded]); const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const zodTypes = useZodTypesIfPossible(); const typeName = def.typeName; if (typeName !== z.ZodFirstPartyTypeKind.ZodArray) { throw new Error("expected object"); } const isDefaultValue = useMemo66(() => { return deepEqual(localValue.value, defaultValue); }, [defaultValue, localValue]); return /* @__PURE__ */ jsxs54(Fieldset, { shouldPad: mayPad, success: localValue.zodValidation.success, children: [ /* @__PURE__ */ jsx115("div", { style: { display: "flex", flexDirection: "row" }, children: /* @__PURE__ */ jsx115(SchemaLabel, { onReset: reset, isDefaultValue, jsonPath, onRemove, suffix: suffix2, onSave: () => { onSave(() => localValue.value, false, false); }, saveDisabledByParent, saving, showSaveButton, valid: localValue.zodValidation.success, handleClick: () => setExpanded(!expanded) }) }), expanded ? /* @__PURE__ */ jsx115(RevisionContextProvider, { children: /* @__PURE__ */ jsxs54(SchemaVerticalGuide, { isRoot: false, children: [ localValue.value.map((child, i) => { return /* @__PURE__ */ jsxs54(React76.Fragment, { children: [ /* @__PURE__ */ jsx115(ZodArrayItemEditor, { onChange, value: child, def, index: i, jsonPath, defaultValue: defaultValue?.[i] ?? createZodValues(def.type, z, zodTypes), onSave, showSaveButton, saving, saveDisabledByParent, mayPad, mayRemove: true }), /* @__PURE__ */ jsx115(SchemaArrayItemSeparationLine, { schema, index: i, onChange, isLast: i === localValue.value.length - 1, showAddButton: true }) ] }, `${i}${localValue.keyStabilityRevision}`); }), value.length === 0 ? /* @__PURE__ */ jsx115(SchemaArrayItemSeparationLine, { schema, index: 0, onChange, isLast: true, showAddButton: true }) : null ] }) }) : null, /* @__PURE__ */ jsx115(ZodFieldValidation, { path: jsonPath, localValue }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodBooleanEditor.tsx import { useCallback as useCallback54 } from "react"; // src/components/Checkbox.tsx import { useEffect as useEffect43, useMemo as useMemo67, useRef as useRef27 } from "react"; import { jsx as jsx116, jsxs as jsxs55 } from "react/jsx-runtime"; var size2 = 20; var background = { height: size2, width: size2, position: "relative" }; var bullet = { width: 10, height: 10, backgroundColor: LIGHT_TEXT, borderRadius: "50%" }; var box = { display: "flex", justifyContent: "center", alignItems: "center", position: "absolute", height: size2, width: size2, top: 0, left: 0, pointerEvents: "none", color: "white" }; var Checkbox = ({ checked, onChange, disabled, name, rounded }) => { const ref = useRef27(null); const input2 = useMemo67(() => { return { appearance: "none", background: disabled ? "transparent" : INPUT_BACKGROUND, border: "1px solid " + INPUT_BORDER_COLOR_UNHOVERED, height: size2, width: size2, top: 0, left: 0, position: "absolute", margin: 0 }; }, [disabled]); useEffect43(() => { if (ref.current) { ref.current.style.setProperty("border-radius", rounded ? "50%" : "0%", "important"); } }, [rounded]); return /* @__PURE__ */ jsxs55("div", { style: background, children: [ /* @__PURE__ */ jsx116("input", { ref, style: input2, type: "checkbox", checked, onChange, disabled, name }), /* @__PURE__ */ jsx116("div", { style: box, children: checked ? rounded ? /* @__PURE__ */ jsx116("div", { style: bullet }) : /* @__PURE__ */ jsx116(Checkmark, {}) : null }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodBooleanEditor.tsx import { jsx as jsx117, jsxs as jsxs56 } from "react/jsx-runtime"; var fullWidth = { width: "100%" }; var ZodBooleanEditor = ({ schema, jsonPath, value, setValue, onSave, defaultValue, onRemove, showSaveButton, saving, saveDisabledByParent, mayPad }) => { const { localValue, onChange, reset } = useLocalState({ schema, setValue, unsavedValue: value, savedValue: defaultValue }); const onToggle = useCallback54((e) => { onChange(() => e.target.checked, false, false); }, [onChange]); const save = useCallback54(() => { onSave(() => value, false, false); }, [onSave, value]); return /* @__PURE__ */ jsxs56(Fieldset, { shouldPad: mayPad, success: localValue.zodValidation.success, children: [ /* @__PURE__ */ jsx117(SchemaLabel, { handleClick: null, isDefaultValue: localValue.value === defaultValue, jsonPath, onReset: reset, onSave: save, showSaveButton, onRemove, saving, valid: true, saveDisabledByParent, suffix: null }), /* @__PURE__ */ jsx117("div", { style: fullWidth, children: /* @__PURE__ */ jsx117(Checkbox, { name: jsonPath.join("."), checked: localValue.value, onChange: onToggle, disabled: false }) }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodColorEditor.tsx import { useCallback as useCallback56, useMemo as useMemo70 } from "react"; // src/helpers/color-math.ts var colorWithNewOpacity = (color, opacity, zodTypes) => { const { r, g, b } = zodTypes.ZodZypesInternals.parseColor(color); if (opacity >= 255) { return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`; } return `rgba(${r}, ${g}, ${b}, ${(opacity / 255).toFixed(2)})`; }; // src/components/NewComposition/InputDragger.tsx import React79, { useCallback as useCallback55, useEffect as useEffect44, useMemo as useMemo68, useRef as useRef28, useState as useState45 } from "react"; import { interpolate as interpolate2 } from "remotion"; import { jsx as jsx118 } from "react/jsx-runtime"; var isInt = (num) => { return num % 1 === 0; }; var InputDraggerForwardRefFn = ({ onValueChange, min: _min, max: _max, step: _step, value, onTextChange, formatter = (q) => String(q), status, rightAlign, ...props }, ref) => { const [inputFallback, setInputFallback] = useState45(false); const fallbackRef = useRef28(null); const style8 = useMemo68(() => { return { ...inputBaseStyle, backgroundColor: "transparent", borderColor: "transparent" }; }, []); const span = useMemo68(() => ({ borderBottom: "1px dotted " + BLUE, paddingBottom: 1, color: BLUE, cursor: "ew-resize", userSelect: "none", WebkitUserSelect: "none", fontSize: 13, fontVariantNumeric: "tabular-nums" }), []); const onClick = useCallback55((e) => { if (!getClickLock()) { e.stopPropagation(); } if (getClickLock()) { return; } setInputFallback(true); }, []); const onEscape = useCallback55(() => { setInputFallback(false); }, []); const onBlur = useCallback55(() => { if (!fallbackRef.current) { return; } const newValue = fallbackRef.current.value; if (newValue.trim() === "") { onEscape(); return; } if (fallbackRef.current.checkValidity()) { onTextChange?.(newValue); setInputFallback(false); } else { fallbackRef.current.reportValidity(); } }, [onEscape, onTextChange]); const onKeyPress = useCallback55((e) => { if (e.key === "Enter") { fallbackRef.current?.blur(); } }, []); const roundToStep = (val, stepSize) => { const factor = 1 / stepSize; return Math.ceil(val * factor) / factor; }; const onPointerDown = useCallback55((e) => { const { pageX, pageY, button: button3 } = e; if (button3 !== 0) { return; } const moveListener = (ev) => { const xDistance = ev.pageX - pageX; const distanceFromStart = Math.sqrt(xDistance ** 2 + (ev.pageY - pageY) ** 2); const step = Number(_step ?? 1); const min = Number(_min ?? 0); const max = Number(_max ?? Infinity); if (distanceFromStart > 4) { setClickLock(true); } const diff = interpolate2(xDistance, [-5, -4, 0, 4, 5], [-step, 0, 0, 0, step]); const newValue = Math.min(max, Math.max(min, Number(value) + diff)); const roundedToStep = roundToStep(newValue, step); onValueChange(roundedToStep); }; window.addEventListener("mousemove", moveListener); window.addEventListener("pointerup", () => { window.removeEventListener("mousemove", moveListener); setTimeout(() => { setClickLock(false); }, 2); }, { once: true }); }, [_step, _min, _max, value, onValueChange]); useEffect44(() => { if (inputFallback) { fallbackRef.current?.select(); } }, [inputFallback]); const deriveStep = useMemo68(() => { if (_step !== undefined) { return _step; } if (typeof _min === "number" && isInt(_min)) { return 1; } return 0.0001; }, [_min, _step]); if (inputFallback) { return /* @__PURE__ */ jsx118(HigherZIndex, { onEscape, onOutsideClick: noop, children: /* @__PURE__ */ jsx118(RemotionInput, { ref: fallbackRef, autoFocus: true, onKeyPress, onBlur, min: _min, max: _max, step: deriveStep, defaultValue: value, status, pattern: "[0-9]*[.]?[0-9]*", rightAlign, ...props }) }); } return /* @__PURE__ */ jsx118("button", { ref, type: "button", style: style8, onClick, onPointerDown, children: /* @__PURE__ */ jsx118("span", { style: span, children: formatter(value) }) }); }; var InputDragger = React79.forwardRef(InputDraggerForwardRefFn); // src/components/NewComposition/RemInputTypeColor.tsx import { forwardRef as forwardRef4, useEffect as useEffect45, useImperativeHandle as useImperativeHandle11, useMemo as useMemo69, useRef as useRef29, useState as useState46 } from "react"; import { jsx as jsx119 } from "react/jsx-runtime"; var inputBaseStyle3 = { padding: 0, borderStyle: "solid", borderWidth: 1 }; var RemInputTypeColorForwardRef = ({ status, ...props }, ref) => { const [isFocused, setIsFocused] = useState46(false); const [isHovered, setIsHovered] = useState46(false); const inputRef = useRef29(null); const { tabIndex } = useZIndex(); const style8 = useMemo69(() => { return { backgroundColor: INPUT_BACKGROUND, ...inputBaseStyle3, borderColor: getInputBorderColor({ isFocused, isHovered, status }), ...props.style ?? {} }; }, [isFocused, isHovered, props.style, status]); useImperativeHandle11(ref, () => { return inputRef.current; }, []); useEffect45(() => { if (!inputRef.current) { return; } const { current } = inputRef; const onFocus = () => setIsFocused(true); const onBlur = () => setIsFocused(false); const onMouseEnter = () => setIsHovered(true); const onMouseLeave = () => setIsHovered(false); current.addEventListener("focus", onFocus); current.addEventListener("blur", onBlur); current.addEventListener("mouseenter", onMouseEnter); current.addEventListener("mouseleave", onMouseLeave); return () => { current.removeEventListener("focus", onFocus); current.removeEventListener("blur", onBlur); current.removeEventListener("mouseenter", onMouseEnter); current.removeEventListener("mouseleave", onMouseLeave); }; }, [inputRef]); return /* @__PURE__ */ jsx119("input", { ref: inputRef, type: "color", tabIndex, ...props, name: props.name, style: style8 }); }; var RemInputTypeColor = forwardRef4(RemInputTypeColorForwardRef); // src/components/RenderModal/SchemaEditor/ZodColorEditor.tsx import { jsx as jsx120, jsxs as jsxs57 } from "react/jsx-runtime"; var fullWidth2 = { width: "100%" }; var ZodColorEditor = ({ jsonPath, value, setValue, showSaveButton, defaultValue, schema, onSave, onRemove, saving, saveDisabledByParent, mayPad }) => { const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const zodTypes = useZodTypesIfPossible(); if (!zodTypes) { throw new Error("expected zod color"); } const { localValue, onChange: onValueChange, reset } = useLocalState({ schema, setValue, unsavedValue: value, savedValue: defaultValue }); const { a, b, g, r } = localValue.zodValidation.success ? zodTypes.ZodZypesInternals.parseColor(localValue.value) : { a: 1, b: 0, g: 0, r: 0 }; const onChange = useCallback56((e) => { const newColor = colorWithNewOpacity(e.target.value, Math.round(a), zodTypes); onValueChange(() => newColor, false, false); }, [a, onValueChange, zodTypes]); const onTextChange = useCallback56((e) => { const newValue = e.target.value; onValueChange(() => newValue, false, false); }, [onValueChange]); const save = useCallback56(() => { onSave(() => value, false, false); }, [onSave, value]); const rgb = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`; const status = localValue.zodValidation.success ? "ok" : "error"; const colorPicker = useMemo70(() => { return { height: 39, width: 45, display: "inline-block" }; }, []); const onOpacityChange = useCallback56((newValue) => { const newColor = colorWithNewOpacity(localValue.value, Math.round(Number(newValue) / 100 * 255), zodTypes); onValueChange(() => newColor, false, false); }, [localValue.value, onValueChange, zodTypes]); const onOpacityValueChange = useCallback56((newValue) => { const newColor = colorWithNewOpacity(localValue.value, Math.round(Number(newValue) / 100 * 255), zodTypes); onValueChange(() => newColor, false, false); }, [localValue.value, onValueChange, zodTypes]); return /* @__PURE__ */ jsxs57(Fieldset, { shouldPad: mayPad, success: localValue.zodValidation.success, children: [ /* @__PURE__ */ jsx120(SchemaLabel, { handleClick: null, isDefaultValue: localValue.value === defaultValue, jsonPath, onReset: reset, onSave: save, showSaveButton, onRemove, saving, valid: localValue.zodValidation.success, saveDisabledByParent, suffix: null }), /* @__PURE__ */ jsxs57("div", { style: fullWidth2, children: [ /* @__PURE__ */ jsxs57(Row, { align: "center", children: [ /* @__PURE__ */ jsx120("div", { style: colorPicker, children: /* @__PURE__ */ jsx120(RemInputTypeColor, { type: "color", style: { height: 39 }, value: rgb, onChange, className: "__remotion_color_picker", status, name: jsonPath.join(".") }) }), /* @__PURE__ */ jsx120(Spacing, { x: 1, block: true }), /* @__PURE__ */ jsx120(RemotionInput, { value: localValue.value, status, placeholder: jsonPath.join("."), onChange: onTextChange, rightAlign: false }), /* @__PURE__ */ jsx120(Spacing, { x: 1 }), /* @__PURE__ */ jsx120(InputDragger, { onTextChange: onOpacityChange, onValueChange: onOpacityValueChange, status, value: a / 255 * 100, min: 0, max: 100, step: 1, formatter: (v) => `${Math.round(Number(v))}%`, rightAlign: false }) ] }), /* @__PURE__ */ jsx120(ZodFieldValidation, { path: jsonPath, localValue }) ] }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodDateEditor.tsx import { useCallback as useCallback57 } from "react"; import { jsx as jsx121, jsxs as jsxs58 } from "react/jsx-runtime"; var fullWidth3 = { width: "100%" }; var explainer2 = { fontFamily: "sans-serif", fontSize: 12, color: VERY_LIGHT_TEXT }; var inputStyle = { colorScheme: "dark" }; var formatDate = (date) => { const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); const hours = date.getHours(); const minutes = date.getMinutes(); const seconds = date.getSeconds(); const milliseconds = date.getMilliseconds(); const formattedDate = `${year}-${month.toString().padStart(2, "0")}-${day.toString().padStart(2, "0")}T${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}.${milliseconds.toString().padStart(3, "0")}`; return formattedDate; }; var ZodDateEditor = ({ jsonPath, value, setValue, showSaveButton, defaultValue, schema, onSave, onRemove, saving, saveDisabledByParent, mayPad }) => { const { localValue, onChange: setLocalValue, reset } = useLocalState({ schema, setValue, unsavedValue: value, savedValue: defaultValue }); const onChange = useCallback57((e) => { setLocalValue(() => new Date(e.target.value), false, false); }, [setLocalValue]); const save = useCallback57(() => { onSave(() => value, false, false); }, [onSave, value]); return /* @__PURE__ */ jsxs58(Fieldset, { shouldPad: mayPad, success: localValue.zodValidation.success, children: [ /* @__PURE__ */ jsx121(SchemaLabel, { handleClick: null, isDefaultValue: localValue.value.getTime() === defaultValue.getTime(), jsonPath, onReset: reset, onSave: save, showSaveButton, onRemove, saving, valid: localValue.zodValidation.success, saveDisabledByParent, suffix: null }), /* @__PURE__ */ jsxs58("div", { style: fullWidth3, children: [ /* @__PURE__ */ jsx121(RemotionInput, { value: formatDate(localValue.value), type: "datetime-local", status: localValue.zodValidation.success ? "ok" : "error", placeholder: jsonPath.join("."), onChange, style: inputStyle, rightAlign: false }), /* @__PURE__ */ jsx121(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx121("div", { style: explainer2, children: "Date is in local format" }), /* @__PURE__ */ jsx121(ZodFieldValidation, { path: jsonPath, localValue }) ] }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodDefaultEditor.tsx import { jsx as jsx122 } from "react/jsx-runtime"; var ZodDefaultEditor = ({ jsonPath, schema, setValue, onSave, defaultValue, value, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }) => { const { innerType } = schema._def; return /* @__PURE__ */ jsx122(ZodSwitch, { defaultValue, jsonPath, onRemove, onSave, schema: innerType, setValue, showSaveButton, value, saving, saveDisabledByParent, mayPad }); }; // src/components/RenderModal/SchemaEditor/ZodDiscriminatedUnionEditor.tsx import { useCallback as useCallback58, useMemo as useMemo71 } from "react"; import { jsx as jsx123, jsxs as jsxs59 } from "react/jsx-runtime"; var ZodDiscriminatedUnionEditor = ({ schema, setValue, showSaveButton, saving, value, defaultValue, saveDisabledByParent, onSave, mayPad, jsonPath, onRemove }) => { const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const zodTypes = useZodTypesIfPossible(); const typedSchema = schema._def; const options = useMemo71(() => [...typedSchema.optionsMap.keys()], [typedSchema.optionsMap]); const { localValue, onChange: setLocalValue, reset } = useLocalState({ schema, setValue, unsavedValue: value, savedValue: defaultValue }); const comboBoxValues = useMemo71(() => { return options.map((option) => { return { value: option, label: option, id: option, keyHint: null, leftItem: option === value[typedSchema.discriminator] ? /* @__PURE__ */ jsx123(Checkmark, {}) : null, onClick: () => { const val = createZodValues(typedSchema.optionsMap.get(option), z, zodTypes); setLocalValue(() => val, false, false); }, quickSwitcherLabel: null, subMenu: null, type: "item" }; }); }, [ options, setLocalValue, typedSchema.discriminator, typedSchema.optionsMap, value, z, zodTypes ]); const save = useCallback58(() => { onSave(() => value, false, false); }, [onSave, value]); const discriminatedUnionReplacement = useMemo71(() => { return { discriminator: typedSchema.discriminator, markup: /* @__PURE__ */ jsxs59(Fieldset, { shouldPad: mayPad, success: true, children: [ /* @__PURE__ */ jsx123(SchemaLabel, { handleClick: null, isDefaultValue: localValue.value[typedSchema.discriminator] === defaultValue[typedSchema.discriminator], jsonPath: [...jsonPath, typedSchema.discriminator], onRemove, onReset: reset, onSave: save, saveDisabledByParent, saving, showSaveButton, suffix: null, valid: localValue.zodValidation.success }), /* @__PURE__ */ jsx123(Combobox, { title: "Select type", values: comboBoxValues, selectedId: value[typedSchema.discriminator] }) ] }, "replacement") }; }, [ comboBoxValues, defaultValue, jsonPath, localValue.value, localValue.zodValidation.success, mayPad, onRemove, reset, save, saveDisabledByParent, saving, showSaveButton, typedSchema.discriminator, value ]); return /* @__PURE__ */ jsx123(ZodObjectEditor, { jsonPath, mayPad, savedValue: defaultValue, onRemove, onSave, saveDisabledByParent, saving, schema: typedSchema.optionsMap.get(value[typedSchema.discriminator]), setValue: setLocalValue, showSaveButton, unsavedValue: value, discriminatedUnionReplacement }, value[typedSchema.discriminator]); }; // src/components/RenderModal/SchemaEditor/ZodEffectEditor.tsx import { jsx as jsx124, jsxs as jsxs60 } from "react/jsx-runtime"; var fullWidth4 = { width: "100%" }; var ZodEffectEditor = ({ schema, jsonPath, value, setValue: updateValue, defaultValue, onSave, onRemove, showSaveButton, saving, mayPad }) => { const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const { localValue, onChange } = useLocalState({ unsavedValue: value, schema, setValue: updateValue, savedValue: defaultValue }); const def = schema._def; const typeName = def.typeName; if (typeName !== z.ZodFirstPartyTypeKind.ZodEffects) { throw new Error("expected effect"); } return /* @__PURE__ */ jsxs60(Fieldset, { shouldPad: mayPad, success: localValue.zodValidation.success, children: [ /* @__PURE__ */ jsx124("div", { style: fullWidth4, children: /* @__PURE__ */ jsx124(ZodSwitch, { value, setValue: onChange, jsonPath, schema: def.schema, defaultValue, onSave, showSaveButton, onRemove, saving, saveDisabledByParent: !localValue.zodValidation.success, mayPad: false }) }), /* @__PURE__ */ jsx124(ZodFieldValidation, { path: jsonPath, localValue }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodEnumEditor.tsx import { useCallback as useCallback59, useMemo as useMemo72 } from "react"; import { jsx as jsx125, jsxs as jsxs61 } from "react/jsx-runtime"; var container28 = { width: "100%" }; var ZodEnumEditor = ({ schema, jsonPath, setValue, defaultValue, value, onSave, showSaveButton, onRemove, saving }) => { const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const { localValue, onChange: setLocalValue, reset } = useLocalState({ schema, setValue, unsavedValue: value, savedValue: defaultValue }); const def = schema._def; const typeName = def.typeName; if (typeName !== z.ZodFirstPartyTypeKind.ZodEnum) { throw new Error("expected enum"); } const isRoot = jsonPath.length === 0; const comboBoxValues = useMemo72(() => { return def.values.map((option) => { return { value: option, label: option, id: option, keyHint: null, leftItem: option === value ? /* @__PURE__ */ jsx125(Checkmark, {}) : null, onClick: (id) => { setLocalValue(() => id, false, false); }, quickSwitcherLabel: null, subMenu: null, type: "item" }; }); }, [def.values, setLocalValue, value]); const save = useCallback59(() => { onSave(() => value, false, false); }, [onSave, value]); return /* @__PURE__ */ jsxs61(Fieldset, { shouldPad: true, success: localValue.zodValidation.success, children: [ /* @__PURE__ */ jsx125(SchemaLabel, { handleClick: null, onSave: save, showSaveButton, isDefaultValue: localValue.value === defaultValue, onReset: reset, jsonPath, onRemove, saving, valid: localValue.zodValidation.success, saveDisabledByParent: !localValue.zodValidation.success, suffix: null }), /* @__PURE__ */ jsx125("div", { style: isRoot ? undefined : container28, children: /* @__PURE__ */ jsx125(Combobox, { values: comboBoxValues, selectedId: value, title: value }) }), /* @__PURE__ */ jsx125(ZodFieldValidation, { path: jsonPath, localValue }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodMatrixEditor.tsx import React84, { useMemo as useMemo73, useState as useState47 } from "react"; import { jsx as jsx126, jsxs as jsxs62 } from "react/jsx-runtime"; var rowStyle = { display: "flex", flexDirection: "row", width: "100%" }; var ZodMatrixEditor = ({ schema, jsonPath, setValue, defaultValue, value, onSave, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }) => { const { localValue, onChange, RevisionContextProvider, reset } = useLocalState({ unsavedValue: value, schema, setValue, savedValue: defaultValue }); const [expanded, setExpanded] = useState47(true); const def = schema._def; const suffix2 = useMemo73(() => { return expanded ? " [" : " [...] "; }, [expanded]); const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const zodTypes = useZodTypesIfPossible(); const isDefaultValue = useMemo73(() => { return deepEqual(localValue.value, defaultValue); }, [defaultValue, localValue]); const dimensions = Math.sqrt(localValue.value.length); if (!Number.isInteger(dimensions)) { throw new Error("Invalid matrix"); } const chunkedItems = useMemo73(() => { return localValue.value.reduce((acc, item2, index) => { const chunkIndex = Math.floor(index / dimensions); acc[chunkIndex] = acc[chunkIndex] || []; acc[chunkIndex].push(item2); return acc; }, []); }, [localValue.value, dimensions]); return /* @__PURE__ */ jsxs62(Fieldset, { shouldPad: mayPad, success: localValue.zodValidation.success, children: [ /* @__PURE__ */ jsx126(SchemaLabel, { onReset: reset, isDefaultValue, jsonPath, onRemove, suffix: suffix2, onSave: () => { onSave(() => localValue.value, false, false); }, saveDisabledByParent, saving, showSaveButton, valid: localValue.zodValidation.success, handleClick: () => setExpanded(!expanded) }), expanded ? /* @__PURE__ */ jsx126(RevisionContextProvider, { children: /* @__PURE__ */ jsxs62(SchemaVerticalGuide, { isRoot: false, children: [ chunkedItems.map((row3, rowIndex) => { return /* @__PURE__ */ jsx126(React84.Fragment, { children: /* @__PURE__ */ jsx126("div", { style: rowStyle, children: row3.map((item2, _index) => { const actualIndex = rowIndex * dimensions + _index; return /* @__PURE__ */ jsx126("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx126(ZodArrayItemEditor, { onChange, value: item2, def, index: actualIndex, jsonPath, defaultValue: defaultValue?.[actualIndex] ?? createZodValues(def.type, z, zodTypes), onSave, showSaveButton, saving, saveDisabledByParent, mayPad, mayRemove: false }) }, `${_index}${localValue.keyStabilityRevision}`); }) }) }, `${rowIndex}${localValue.keyStabilityRevision}`); }), value.length === 0 ? /* @__PURE__ */ jsx126(SchemaArrayItemSeparationLine, { schema, index: 0, onChange, isLast: true, showAddButton: true }) : null ] }) }) : null, /* @__PURE__ */ jsx126(ZodFieldValidation, { path: jsonPath, localValue }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodNonEditableValue.tsx import { useCallback as useCallback60 } from "react"; import { jsx as jsx127, jsxs as jsxs63 } from "react/jsx-runtime"; var fullWidth5 = { width: "100%" }; var emptyLabel = { width: "100%", color: VERY_LIGHT_TEXT, fontFamily: "sans-serif", fontSize: 14 }; var wideEmptyLabel = { ...emptyLabel, lineHeight: "37px" }; var ZonNonEditableValue = ({ jsonPath, label: label6, showSaveButton, saving, mayPad }) => { const save = useCallback60(() => { return; }, []); const reset = useCallback60(() => { return; }, []); return /* @__PURE__ */ jsxs63(Fieldset, { shouldPad: mayPad, success: true, children: [ /* @__PURE__ */ jsx127(SchemaLabel, { handleClick: null, isDefaultValue: true, jsonPath, onReset: reset, onSave: save, showSaveButton, onRemove: null, saving, valid: true, saveDisabledByParent: true, suffix: null }), /* @__PURE__ */ jsx127("div", { style: fullWidth5, children: /* @__PURE__ */ jsx127("em", { style: wideEmptyLabel, children: label6 }) }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodOrNullishEditor.tsx import { useCallback as useCallback61 } from "react"; import { jsx as jsx128, jsxs as jsxs64 } from "react/jsx-runtime"; var labelStyle4 = { fontFamily: "sans-serif", fontSize: 14, color: LIGHT_TEXT }; var checkBoxWrapper = { display: "flex", flexDirection: "row", alignItems: "center", marginTop: "5px" }; var ZodOrNullishEditor = ({ jsonPath, schema, setValue, onSave, defaultValue, value, showSaveButton, onRemove, nullishValue, saving, saveDisabledByParent, mayPad, innerSchema }) => { const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const zodTypes = useZodTypesIfPossible(); const isChecked = value === nullishValue; const { localValue, onChange: setLocalValue, reset } = useLocalState({ schema, setValue, unsavedValue: value, savedValue: defaultValue }); const onCheckBoxChange = useCallback61((e) => { const val = e.target.checked ? nullishValue : createZodValues(innerSchema, z, zodTypes); setLocalValue(() => val, false, false); }, [innerSchema, nullishValue, setLocalValue, z, zodTypes]); const save = useCallback61(() => { onSave(() => value, false, false); }, [onSave, value]); return /* @__PURE__ */ jsxs64(Fieldset, { shouldPad: mayPad, success: localValue.zodValidation.success, children: [ localValue.value === nullishValue ? /* @__PURE__ */ jsx128(SchemaLabel, { handleClick: null, isDefaultValue: localValue.value === defaultValue, jsonPath, onReset: reset, onSave: save, showSaveButton, onRemove, saving, valid: localValue.zodValidation.success, saveDisabledByParent, suffix: null }) : /* @__PURE__ */ jsx128(ZodSwitch, { value: localValue.value, setValue: setLocalValue, jsonPath, schema: innerSchema, defaultValue, onSave, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad: false }), /* @__PURE__ */ jsxs64("div", { style: checkBoxWrapper, children: [ /* @__PURE__ */ jsx128(Checkbox, { checked: isChecked, onChange: onCheckBoxChange, disabled: false, name: jsonPath.join(".") }), /* @__PURE__ */ jsx128(Spacing, { x: 1 }), /* @__PURE__ */ jsx128("div", { style: labelStyle4, children: String(nullishValue) }) ] }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodNullableEditor.tsx import { jsx as jsx129 } from "react/jsx-runtime"; var ZodNullableEditor = ({ jsonPath, schema, setValue, onSave, defaultValue, value, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }) => { const { innerType } = schema._def; return /* @__PURE__ */ jsx129(ZodOrNullishEditor, { defaultValue, jsonPath, onRemove, onSave, schema, innerSchema: innerType, setValue, showSaveButton, value, nullishValue: null, saving, saveDisabledByParent, mayPad }); }; // src/components/RenderModal/SchemaEditor/ZodNumberEditor.tsx import { useCallback as useCallback62 } from "react"; import { jsx as jsx130, jsxs as jsxs65 } from "react/jsx-runtime"; var fullWidth6 = { width: "100%" }; var getMinValue = (schema) => { const minCheck = schema._def.checks.find((c) => c.kind === "min"); if (!minCheck) { return -Infinity; } if (minCheck.kind !== "min") { throw new Error("Expected min check"); } if (!minCheck.inclusive) { return -Infinity; } return minCheck.value; }; var getMaxValue = (schema) => { const maxCheck = schema._def.checks.find((c) => c.kind === "max"); if (!maxCheck) { return Infinity; } if (maxCheck.kind !== "max") { throw new Error("Expected max check"); } if (!maxCheck.inclusive) { return Infinity; } return maxCheck.value; }; var getStep = (schema) => { const multipleStep = schema._def.checks.find((c) => c.kind === "multipleOf"); if (!multipleStep) { return; } if (multipleStep.kind !== "multipleOf") { throw new Error("Expected multipleOf check"); } return multipleStep.value; }; var ZodNumberEditor = ({ jsonPath, value, schema, setValue, onSave, defaultValue, onRemove, showSaveButton, saving, saveDisabledByParent, mayPad }) => { const { localValue, onChange: setLocalValue, reset } = useLocalState({ unsavedValue: value, schema, setValue, savedValue: defaultValue }); const onNumberChange = useCallback62((newValue) => { setLocalValue(() => newValue, false, false); }, [setLocalValue]); const isDefault = localValue.value === defaultValue; const onTextChange = useCallback62((newValue) => { setLocalValue(() => Number(newValue), false, false); }, [setLocalValue]); const save = useCallback62(() => { onSave(() => value, false, false); }, [onSave, value]); return /* @__PURE__ */ jsxs65(Fieldset, { shouldPad: mayPad, success: localValue.zodValidation.success, children: [ /* @__PURE__ */ jsx130(SchemaLabel, { handleClick: null, isDefaultValue: isDefault, jsonPath, onReset: reset, onSave: save, showSaveButton, onRemove, saving, valid: localValue.zodValidation.success, saveDisabledByParent, suffix: null }), /* @__PURE__ */ jsxs65("div", { style: fullWidth6, children: [ /* @__PURE__ */ jsx130(InputDragger, { type: "number", value: localValue.value, style: fullWidth6, status: localValue.zodValidation.success ? "ok" : "error", placeholder: jsonPath.join("."), onTextChange, onValueChange: onNumberChange, min: getMinValue(schema), max: getMaxValue(schema), step: getStep(schema), rightAlign: false }), /* @__PURE__ */ jsx130(ZodFieldValidation, { path: jsonPath, localValue }) ] }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodOptionalEditor.tsx import { jsx as jsx131 } from "react/jsx-runtime"; var ZodOptionalEditor = ({ jsonPath, schema, setValue, onSave, defaultValue, value, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }) => { const { innerType } = schema._def; return /* @__PURE__ */ jsx131(ZodOrNullishEditor, { defaultValue, jsonPath, onRemove, onSave, schema, setValue, showSaveButton, value, nullishValue: undefined, saving, saveDisabledByParent, mayPad, innerSchema: innerType }); }; // src/components/RenderModal/SchemaEditor/ZodStaticFileEditor.tsx import { useCallback as useCallback63, useMemo as useMemo74 } from "react"; import { jsx as jsx132, jsxs as jsxs66 } from "react/jsx-runtime"; var container29 = { width: "100%" }; var ZodStaticFileEditor = ({ schema, jsonPath, setValue, defaultValue, value, onSave, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }) => { const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const { localValue, onChange: setLocalValue, reset } = useLocalState({ schema, setValue, unsavedValue: value, savedValue: defaultValue }); const def = schema._def; const typeName = def.typeName; if (typeName !== z.ZodFirstPartyTypeKind.ZodString) { throw new Error("expected enum"); } const isRoot = jsonPath.length === 0; const comboBoxValues = useMemo74(() => { return getStaticFiles().map((option) => { return { value: option.src, label: option.name, id: option.src, keyHint: null, leftItem: option.src === value ? /* @__PURE__ */ jsx132(Checkmark, {}) : null, onClick: (id) => { setLocalValue(() => id, false, false); }, quickSwitcherLabel: null, subMenu: null, type: "item" }; }); }, [setLocalValue, value]); const save = useCallback63(() => { onSave(() => value); }, [onSave, value]); return /* @__PURE__ */ jsxs66(Fieldset, { shouldPad: mayPad, success: localValue.zodValidation.success, children: [ /* @__PURE__ */ jsx132(SchemaLabel, { handleClick: null, onSave: save, showSaveButton, isDefaultValue: localValue.value === defaultValue, onReset: reset, jsonPath, onRemove, saving, valid: localValue.zodValidation.success, saveDisabledByParent, suffix: null }), /* @__PURE__ */ jsx132("div", { style: isRoot ? undefined : container29, children: /* @__PURE__ */ jsx132(Combobox, { values: comboBoxValues, selectedId: localValue.value, title: value }) }), /* @__PURE__ */ jsx132(ZodFieldValidation, { path: jsonPath, localValue }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodStringEditor.tsx import { useCallback as useCallback64 } from "react"; import { jsx as jsx133, jsxs as jsxs67 } from "react/jsx-runtime"; var fullWidth7 = { width: "100%" }; var ZodStringEditor = ({ jsonPath, value, setValue, showSaveButton, defaultValue, schema, onSave, onRemove, saving, saveDisabledByParent, mayPad }) => { const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const { localValue, onChange: setLocalValue, reset } = useLocalState({ schema, setValue, unsavedValue: value, savedValue: defaultValue }); const onChange = useCallback64((e) => { setLocalValue(() => e.target.value, false, false); }, [setLocalValue]); const save = useCallback64(() => { onSave(() => value, false, false); }, [onSave, value]); return /* @__PURE__ */ jsxs67(Fieldset, { shouldPad: mayPad, success: false, children: [ /* @__PURE__ */ jsx133(SchemaLabel, { handleClick: null, isDefaultValue: localValue.value === defaultValue, jsonPath, onReset: reset, onSave: save, showSaveButton, onRemove, saving, valid: localValue.zodValidation.success, saveDisabledByParent, suffix: null }), /* @__PURE__ */ jsxs67("div", { style: fullWidth7, children: [ /* @__PURE__ */ jsx133(RemotionInput, { value: localValue.value, status: localValue.zodValidation ? "ok" : "error", placeholder: jsonPath.join("."), onChange, rightAlign: false, name: jsonPath.join(".") }), /* @__PURE__ */ jsx133(ZodFieldValidation, { path: jsonPath, localValue }) ] }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodTextareaEditor.tsx import { useCallback as useCallback65 } from "react"; import { jsx as jsx134, jsxs as jsxs68 } from "react/jsx-runtime"; var fullWidth8 = { width: "100%" }; var textareaStyle = { resize: "vertical", minHeight: 100 }; var ZodTextareaEditor = ({ jsonPath, value, setValue, showSaveButton, defaultValue, schema, onSave, onRemove, saving, saveDisabledByParent, mayPad }) => { const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const zodTypes = useZodTypesIfPossible(); if (!zodTypes) { throw new Error("expected zod textarea"); } const { localValue, onChange: setLocalValue, reset } = useLocalState({ schema, setValue, unsavedValue: value, savedValue: defaultValue }); const onChange = useCallback65((e) => { setLocalValue(() => e.target.value, false, false); }, [setLocalValue]); const save = useCallback65(() => { onSave(() => value, false, false); }, [onSave, value]); return /* @__PURE__ */ jsxs68(Fieldset, { shouldPad: mayPad, success: localValue.zodValidation.success, children: [ /* @__PURE__ */ jsx134(SchemaLabel, { handleClick: null, isDefaultValue: localValue.value === defaultValue, jsonPath, onReset: reset, onSave: save, showSaveButton, onRemove, saving, valid: localValue.zodValidation.success, saveDisabledByParent, suffix: null }), /* @__PURE__ */ jsxs68("div", { style: fullWidth8, children: [ /* @__PURE__ */ jsx134(RemTextarea, { onChange, value: localValue.value, status: localValue.zodValidation ? "ok" : "error", placeholder: jsonPath.join("."), name: jsonPath.join("."), style: textareaStyle }), /* @__PURE__ */ jsx134(ZodFieldValidation, { path: jsonPath, localValue }) ] }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodTupleEditor.tsx import React90, { useMemo as useMemo76, useState as useState48 } from "react"; // src/components/RenderModal/SchemaEditor/ZodTupleItemEditor.tsx import { useCallback as useCallback66, useMemo as useMemo75 } from "react"; import { jsx as jsx135 } from "react/jsx-runtime"; var ZodTupleItemEditor = ({ def, onChange, jsonPath, index, value, defaultValue, onSave: onSaveObject, showSaveButton, saving, saveDisabledByParent, mayPad }) => { const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const setValue = useCallback66((val) => { onChange((oldV) => [ ...oldV.slice(0, index), typeof val === "function" ? val(oldV[index]) : val, ...oldV.slice(index + 1) ], false, false); }, [index, onChange]); const newJsonPath = useMemo75(() => [...jsonPath, index], [index, jsonPath]); const onSave = useCallback66((updater) => { onSaveObject((oldV) => [ ...oldV.slice(0, index), updater(oldV[index]), ...oldV.slice(index + 1) ], false, false); }, [index, onSaveObject]); return /* @__PURE__ */ jsx135("div", { children: /* @__PURE__ */ jsx135(ZodSwitch, { jsonPath: newJsonPath, schema: def.items[index], value, setValue, defaultValue, onSave, showSaveButton, onRemove: null, saving, saveDisabledByParent, mayPad }) }); }; // src/components/RenderModal/SchemaEditor/ZodTupleEditor.tsx import { jsx as jsx136, jsxs as jsxs69 } from "react/jsx-runtime"; var ZodTupleEditor = ({ schema, jsonPath, setValue, defaultValue, value, onSave, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }) => { const { localValue, onChange, RevisionContextProvider, reset } = useLocalState({ unsavedValue: value, schema, setValue, savedValue: defaultValue }); const [expanded, setExpanded] = useState48(true); const def = schema._def; const suffix2 = useMemo76(() => { return expanded ? " [" : " [...] "; }, [expanded]); const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const zodTypes = useZodTypesIfPossible(); const typeName = def.typeName; if (typeName !== z.ZodFirstPartyTypeKind.ZodTuple) { throw new Error("expected object"); } const isDefaultValue = useMemo76(() => { return deepEqual(localValue.value, defaultValue); }, [defaultValue, localValue]); return /* @__PURE__ */ jsxs69(Fieldset, { shouldPad: mayPad, success: localValue.zodValidation.success, children: [ /* @__PURE__ */ jsx136("div", { style: { display: "flex", flexDirection: "row" }, children: /* @__PURE__ */ jsx136(SchemaLabel, { onReset: reset, isDefaultValue, jsonPath, onRemove, suffix: suffix2, onSave: () => { onSave(() => localValue.value, false, false); }, saveDisabledByParent, saving, showSaveButton, valid: localValue.zodValidation.success, handleClick: () => setExpanded(!expanded) }) }), expanded ? /* @__PURE__ */ jsx136(RevisionContextProvider, { children: /* @__PURE__ */ jsxs69(SchemaVerticalGuide, { isRoot: false, children: [ localValue.value.map((child, i) => { return /* @__PURE__ */ jsxs69(React90.Fragment, { children: [ /* @__PURE__ */ jsx136(ZodTupleItemEditor, { onChange, value: child, def, index: i, jsonPath, defaultValue: defaultValue?.[i] ?? createZodValues(def.items[i], z, zodTypes), onSave, showSaveButton, saving, saveDisabledByParent, mayPad }), /* @__PURE__ */ jsx136(SchemaArrayItemSeparationLine, { schema, index: i, onChange, isLast: i === localValue.value.length - 1, showAddButton: false }) ] }, `${i}${localValue.keyStabilityRevision}`); }), value.length === 0 ? /* @__PURE__ */ jsx136(SchemaArrayItemSeparationLine, { schema, index: 0, onChange, isLast: true, showAddButton: false }) : null ] }) }) : null, /* @__PURE__ */ jsx136(ZodFieldValidation, { path: jsonPath, localValue }) ] }); }; // src/components/RenderModal/SchemaEditor/ZodUnionEditor.tsx import { jsx as jsx137 } from "react/jsx-runtime"; var findNull = (value, zodType) => { const nullIndex = value.findIndex((v) => v._def.typeName === zodType.ZodFirstPartyTypeKind.ZodNull || v._def.typeName === zodType.ZodFirstPartyTypeKind.ZodUndefined); if (nullIndex === -1) { return null; } const nullishValue = value[nullIndex]._def.typeName === zodType.ZodFirstPartyTypeKind.ZodNull ? null : undefined; const otherSchema = value[nullIndex === 0 ? 1 : 0]; const otherSchemaIsAlsoNullish = otherSchema._def.typeName === zodType.ZodFirstPartyTypeKind.ZodNull || otherSchema._def.typeName === zodType.ZodFirstPartyTypeKind.ZodUndefined; return { nullIndex, nullishValue, otherSchema, otherSchemaIsAlsoNullish }; }; var ZodUnionEditor = ({ jsonPath, schema, setValue, onSave, defaultValue, value, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }) => { const { options } = schema._def; const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } if (options.length > 2) { return /* @__PURE__ */ jsx137(ZonNonEditableValue, { jsonPath, label: "Union with more than 2 options not editable", showSaveButton, saving, mayPad }); } if (options.length < 2) { return /* @__PURE__ */ jsx137(ZonNonEditableValue, { jsonPath, label: "Union with less than 2 options not editable", showSaveButton, saving, mayPad }); } const nullResult = findNull(options, z); if (!nullResult) { return /* @__PURE__ */ jsx137(ZonNonEditableValue, { jsonPath, label: "Union only editable with 1 value being null", showSaveButton, saving, mayPad }); } const { otherSchema, nullishValue, otherSchemaIsAlsoNullish } = nullResult; if (otherSchemaIsAlsoNullish) { return /* @__PURE__ */ jsx137(ZonNonEditableValue, { jsonPath, label: "Not editable - both union values are nullish", showSaveButton, saving, mayPad }); } return /* @__PURE__ */ jsx137(ZodOrNullishEditor, { defaultValue, jsonPath, onRemove, onSave, schema, innerSchema: otherSchema, setValue, showSaveButton, value, nullishValue, saving, saveDisabledByParent, mayPad }); }; // src/components/RenderModal/SchemaEditor/ZodSwitch.tsx import { jsx as jsx138 } from "react/jsx-runtime"; var ZodSwitch = ({ schema, jsonPath, value, setValue, defaultValue, onSave, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }) => { const def = schema._def; const typeName = def.typeName; const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const zodTypes = useZodTypesIfPossible(); if (typeName === z.ZodFirstPartyTypeKind.ZodObject) { return /* @__PURE__ */ jsx138(ZodObjectEditor, { setValue, unsavedValue: value, savedValue: defaultValue, jsonPath, schema, onSave, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad, discriminatedUnionReplacement: null }); } if (typeName === z.ZodFirstPartyTypeKind.ZodString) { if (value.startsWith(window.remotion_staticBase)) { return /* @__PURE__ */ jsx138(ZodStaticFileEditor, { setValue, value, jsonPath, schema, defaultValue, onSave, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }); } if (zodTypes && schema._def.description === zodTypes.ZodZypesInternals.REMOTION_TEXTAREA_BRAND) { return /* @__PURE__ */ jsx138(ZodTextareaEditor, { value, setValue, jsonPath, schema, onSave, defaultValue, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }); } return /* @__PURE__ */ jsx138(ZodStringEditor, { value, setValue, jsonPath, schema, onSave, defaultValue, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }); } if (typeName === z.ZodFirstPartyTypeKind.ZodDate) { return /* @__PURE__ */ jsx138(ZodDateEditor, { value, setValue, jsonPath, schema, onSave, defaultValue, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }); } if (typeName === z.ZodFirstPartyTypeKind.ZodNumber) { return /* @__PURE__ */ jsx138(ZodNumberEditor, { value, setValue, jsonPath, schema, defaultValue, onSave, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }); } if (typeName === z.ZodFirstPartyTypeKind.ZodBoolean) { return /* @__PURE__ */ jsx138(ZodBooleanEditor, { value, setValue, jsonPath, defaultValue, onSave, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad, schema }); } if (typeName === z.ZodFirstPartyTypeKind.ZodUndefined) { return /* @__PURE__ */ jsx138(ZonNonEditableValue, { jsonPath, showSaveButton, label: "undefined", saving, mayPad }); } if (typeName === z.ZodFirstPartyTypeKind.ZodNull) { return /* @__PURE__ */ jsx138(ZonNonEditableValue, { jsonPath, showSaveButton, label: "null", saving, mayPad }); } if (typeName === z.ZodFirstPartyTypeKind.ZodAny) { return /* @__PURE__ */ jsx138(ZonNonEditableValue, { jsonPath, showSaveButton, label: "any (not editable)", saving, mayPad }); } if (typeName === z.ZodFirstPartyTypeKind.ZodBigInt) { return /* @__PURE__ */ jsx138(ZonNonEditableValue, { jsonPath, showSaveButton, label: "BigInt (not editable)", saving, mayPad }); } if (typeName === z.ZodFirstPartyTypeKind.ZodUnknown) { return /* @__PURE__ */ jsx138(ZonNonEditableValue, { jsonPath, showSaveButton, label: "unknown (not editable)", saving, mayPad }); } if (typeName === z.ZodFirstPartyTypeKind.ZodArray) { return /* @__PURE__ */ jsx138(ZodArrayEditor, { setValue, value, jsonPath, schema, defaultValue, onSave, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }); } if (typeName === z.ZodFirstPartyTypeKind.ZodEnum) { return /* @__PURE__ */ jsx138(ZodEnumEditor, { setValue, value, jsonPath, schema, defaultValue, onSave, showSaveButton, onRemove, saving }); } if (typeName === z.ZodFirstPartyTypeKind.ZodEffects) { if (zodTypes && schema._def.description === zodTypes.ZodZypesInternals.REMOTION_COLOR_BRAND) { return /* @__PURE__ */ jsx138(ZodColorEditor, { value, setValue, jsonPath, schema, onSave, defaultValue, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }); } if (zodTypes && schema._def.description === zodTypes.ZodZypesInternals.REMOTION_MATRIX_BRAND) { return /* @__PURE__ */ jsx138(ZodMatrixEditor, { setValue, value, jsonPath, schema: schema._def.schema, defaultValue, onSave, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }); } return /* @__PURE__ */ jsx138(ZodEffectEditor, { value, setValue, jsonPath, schema, defaultValue, onSave, showSaveButton, onRemove, saving, mayPad }); } if (typeName === z.ZodFirstPartyTypeKind.ZodUnion) { return /* @__PURE__ */ jsx138(ZodUnionEditor, { schema, showSaveButton, jsonPath, value, defaultValue, setValue, onSave, onRemove, saving, saveDisabledByParent, mayPad }); } if (typeName === z.ZodFirstPartyTypeKind.ZodOptional) { return /* @__PURE__ */ jsx138(ZodOptionalEditor, { jsonPath, showSaveButton, defaultValue, value, setValue, onSave, onRemove, schema, saving, saveDisabledByParent, mayPad }); } if (typeName === z.ZodFirstPartyTypeKind.ZodNullable) { return /* @__PURE__ */ jsx138(ZodNullableEditor, { jsonPath, showSaveButton, defaultValue, value, setValue, onSave, onRemove, schema, saving, saveDisabledByParent, mayPad }); } if (typeName === z.ZodFirstPartyTypeKind.ZodDefault) { return /* @__PURE__ */ jsx138(ZodDefaultEditor, { jsonPath, showSaveButton, defaultValue, value, setValue, onSave, onRemove, schema, saving, saveDisabledByParent, mayPad }); } if (typeName === z.ZodFirstPartyTypeKind.ZodDiscriminatedUnion) { return /* @__PURE__ */ jsx138(ZodDiscriminatedUnionEditor, { defaultValue, mayPad, schema, setValue, value, jsonPath, onRemove, onSave, saving, saveDisabledByParent, showSaveButton }); } if (typeName === z.ZodFirstPartyTypeKind.ZodTuple) { return /* @__PURE__ */ jsx138(ZodTupleEditor, { setValue, value, jsonPath, schema, defaultValue, onSave, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad }); } return /* @__PURE__ */ jsx138(ZonNonEditableValue, { jsonPath, showSaveButton, label: `${typeName} (not editable)`, saving, mayPad }); }; // src/components/RenderModal/SchemaEditor/ZodObjectEditor.tsx import { jsx as jsx139, jsxs as jsxs70 } from "react/jsx-runtime"; var ZodObjectEditor = ({ schema, jsonPath, setValue, unsavedValue, savedValue, onSave, showSaveButton, onRemove, saving, saveDisabledByParent, mayPad, discriminatedUnionReplacement }) => { const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const [expanded, setExpanded] = useState49(true); const { localValue, onChange, RevisionContextProvider, reset } = useLocalState({ schema, setValue, unsavedValue, savedValue }); const def = schema._def; const typeName = def.typeName; if (typeName !== z.ZodFirstPartyTypeKind.ZodObject) { throw new Error("expected object"); } const shape = def.shape(); const keys = Object.keys(shape); const isRoot = jsonPath.length === 0; const isDefaultValue = useMemo77(() => { return deepEqual(localValue.value, savedValue); }, [savedValue, localValue]); const suffix2 = useMemo77(() => { return expanded ? " {" : " {...}"; }, [expanded]); return /* @__PURE__ */ jsxs70(Fieldset, { shouldPad: !isRoot && mayPad, success: localValue.zodValidation.success, children: [ isRoot ? null : /* @__PURE__ */ jsx139(SchemaLabel, { isDefaultValue, onReset: reset, jsonPath, onRemove, suffix: suffix2, onSave: () => { onSave(() => { return localValue.value; }, false, false); }, saveDisabledByParent, saving, showSaveButton, valid: localValue.zodValidation.success, handleClick: () => setExpanded(!expanded) }), expanded ? /* @__PURE__ */ jsx139(RevisionContextProvider, { children: /* @__PURE__ */ jsx139(SchemaVerticalGuide, { isRoot, children: keys.map((key, i) => { if (discriminatedUnionReplacement && key === discriminatedUnionReplacement.discriminator) { return discriminatedUnionReplacement.markup; } return /* @__PURE__ */ jsxs70(React91.Fragment, { children: [ /* @__PURE__ */ jsx139(ZodSwitch, { mayPad: true, jsonPath: [...jsonPath, key], schema: shape[key], value: localValue.value[key], defaultValue: (savedValue ?? unsavedValue)[key], setValue: (val, forceApply) => { onChange((oldVal) => { return { ...oldVal, [key]: typeof val === "function" ? val(oldVal[key]) : val }; }, forceApply, false); }, onSave: (val, forceApply) => { onSave((oldVal) => { return { ...oldVal, [key]: typeof val === "function" ? val(oldVal[key]) : val }; }, forceApply, false); }, onRemove: null, showSaveButton, saving, saveDisabledByParent }), i === keys.length - 1 ? null : /* @__PURE__ */ jsx139(SchemaSeparationLine, {}) ] }, key); }) }) }) : null, isRoot || !expanded ? null : /* @__PURE__ */ jsx139("div", { style: fieldsetLabel, children: "}" }) ] }); }; // src/components/RenderModal/SchemaEditor/SchemaEditor.tsx import { jsx as jsx140 } from "react/jsx-runtime"; var scrollable2 = { display: "flex", flexDirection: "column", overflowY: "auto" }; var SchemaEditor = ({ schema, unsavedDefaultProps, setValue, zodValidationResult, savedDefaultProps, onSave, showSaveButton, saving, saveDisabledByParent }) => { const keybindings = useKeybinding(); const [revision, setRevision] = useState50(0); const revisionState = useMemo78(() => { return { childResetRevision: revision }; }, [revision]); useEffect46(() => { const bumpRevision = () => { setRevision((old) => old + 1); }; window.addEventListener(Internals30.PROPS_UPDATED_EXTERNALLY, bumpRevision); return () => { window.removeEventListener(Internals30.PROPS_UPDATED_EXTERNALLY, bumpRevision); }; }, []); const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const hasChanged = useMemo78(() => { return !deepEqual(savedDefaultProps, unsavedDefaultProps); }, [savedDefaultProps, unsavedDefaultProps]); useEffect46(() => { setUnsavedProps(hasChanged); }, [hasChanged]); const onQuickSave = useCallback67(() => { if (hasChanged && showSaveButton) { onSave(() => { return unsavedDefaultProps; }); } }, [hasChanged, onSave, showSaveButton, unsavedDefaultProps]); useEffect46(() => { const save = keybindings.registerKeybinding({ event: "keydown", key: "s", commandCtrlKey: true, callback: onQuickSave, preventDefault: true, triggerIfInputFieldFocused: true, keepRegisteredWhenNotHighestContext: true }); return () => { save.unregister(); }; }, [keybindings, onQuickSave, onSave]); const def = schema._def; const typeName = def.typeName; const reset = useCallback67(() => { setValue(savedDefaultProps); }, [savedDefaultProps, setValue]); if (!zodValidationResult.success) { const defaultPropsValid = schema.safeParse(savedDefaultProps); if (!defaultPropsValid.success) { return /* @__PURE__ */ jsx140(InvalidDefaultProps, { zodValidationResult }); } return /* @__PURE__ */ jsx140(InvalidSchema, { reset, zodValidationResult }); } if (typeName !== z.ZodFirstPartyTypeKind.ZodObject) { return /* @__PURE__ */ jsx140(TopLevelZodValue, { typeReceived: typeName }); } return /* @__PURE__ */ jsx140("div", { ref: defaultPropsEditorScrollableAreaRef, style: scrollable2, className: VERTICAL_SCROLLBAR_CLASSNAME, children: /* @__PURE__ */ jsx140(RevisionContext.Provider, { value: revisionState, children: /* @__PURE__ */ jsx140(ZodObjectEditor, { discriminatedUnionReplacement: null, unsavedValue: unsavedDefaultProps, setValue, jsonPath: [], schema, savedValue: savedDefaultProps, onSave, showSaveButton, onRemove: null, saving, saveDisabledByParent, mayPad: true }) }) }); }; // src/components/RenderModal/WarningIndicatorButton.tsx import { useCallback as useCallback68, useMemo as useMemo79 } from "react"; import { jsx as jsx141, jsxs as jsxs71 } from "react/jsx-runtime"; var style8 = { fontSize: 12, display: "inline-flex", justifyContent: "center", alignItems: "center", backgroundColor: "transparent", color: LIGHT_TEXT, borderStyle: "solid", borderWidth: 1, cursor: "pointer", paddingLeft: 8, paddingRight: 8, paddingTop: 4, paddingBottom: 4 }; var triangleStyle2 = { width: 12, height: 12, flexShrink: 0, fill: WARNING_COLOR }; var WarningTriangle2 = (props) => { return /* @__PURE__ */ jsx141("svg", { viewBox: "0 0 576 512", ...props, children: /* @__PURE__ */ jsx141("path", { d: "M248.747 204.705l6.588 112c.373 6.343 5.626 11.295 11.979 11.295h41.37a12 12 0 0 0 11.979-11.295l6.588-112c.405-6.893-5.075-12.705-11.979-12.705h-54.547c-6.903 0-12.383 5.812-11.978 12.705zM330 384c0 23.196-18.804 42-42 42s-42-18.804-42-42 18.804-42 42-42 42 18.804 42 42zm-.423-360.015c-18.433-31.951-64.687-32.009-83.154 0L6.477 440.013C-11.945 471.946 11.118 512 48.054 512H527.94c36.865 0 60.035-39.993 41.577-71.987L329.577 23.985zM53.191 455.002L282.803 57.008c2.309-4.002 8.085-4.002 10.394 0l229.612 397.993c2.308 4-.579 8.998-5.197 8.998H58.388c-4.617.001-7.504-4.997-5.197-8.997z" }) }); }; var WarningIndicatorButton = ({ setShowWarning, showWarning, warningCount }) => { const onClick = useCallback68(() => { setShowWarning((s) => !s); }, [setShowWarning]); const buttonStyle3 = useMemo79(() => { return { ...style8, backgroundColor: showWarning ? INPUT_BACKGROUND : "transparent", borderColor: showWarning ? INPUT_BORDER_COLOR_HOVERED : INPUT_BORDER_COLOR_UNHOVERED, color: showWarning ? "white" : LIGHT_TEXT }; }, [showWarning]); return /* @__PURE__ */ jsxs71("button", { type: "button", style: buttonStyle3, onClick, children: [ /* @__PURE__ */ jsx141(WarningTriangle2, { style: triangleStyle2 }), /* @__PURE__ */ jsx141(Spacing, { x: 0.5 }), warningCount, /* @__PURE__ */ jsx141(Spacing, { x: 1 }), /* @__PURE__ */ jsx141(AngleDown, { down: showWarning }) ] }); }; // src/components/RenderModal/get-render-modal-warnings.ts import { NoReactInternals as NoReactInternals9 } from "remotion/no-react"; var defaultTypeCanSaveState = { canUpdate: false, reason: "Loading...", determined: false }; var getInputPropsWarning = ({ cliProps, propsEditType }) => { if (Object.keys(cliProps).length > 0 && propsEditType === "default-props") { return "The data that was passed using --props takes priority over the data you enter here."; } return null; }; var getCannotSaveDefaultProps = (canSaveDefaultProps) => { if (canSaveDefaultProps.canUpdate) { return null; } if (!canSaveDefaultProps.determined) { return null; } return `Can't save default props: ${canSaveDefaultProps.reason}.`; }; var customDateUsed = (used, inJSONEditor) => { if (used && inJSONEditor) { return "There is a Date in the schema which was serialized. Note the custom syntax."; } return null; }; var staticFileUsed = (used, inJSONEditor) => { if (used && inJSONEditor) { return "There is a staticFile() in the schema which was serialized. Note the custom syntax."; } return null; }; var mapUsed = (used, inJSONEditor) => { if (used && inJSONEditor) { return "A `Map` was used in the schema which can not be serialized to JSON."; } return null; }; var setUsed = (used, inJSONEditor) => { if (used && inJSONEditor) { return "A `Set` was used in the schema which can not be serialized to JSON."; } return null; }; var getRenderModalWarnings = ({ cliProps, canSaveDefaultProps, isCustomDateUsed, customFileUsed, jsMapUsed, jsSetUsed, inJSONEditor, propsEditType }) => { return [ getInputPropsWarning({ cliProps, propsEditType }), getCannotSaveDefaultProps(canSaveDefaultProps), customDateUsed(isCustomDateUsed, inJSONEditor), staticFileUsed(customFileUsed, inJSONEditor), mapUsed(jsMapUsed, inJSONEditor), setUsed(jsSetUsed, inJSONEditor) ].filter(NoReactInternals9.truthy); }; // src/components/RenderModal/DataEditor.tsx import { jsx as jsx142, jsxs as jsxs72 } from "react/jsx-runtime"; var errorExplanation2 = { fontSize: 14, color: LIGHT_TEXT, fontFamily: "sans-serif", lineHeight: 1.5 }; var explainer3 = { display: "flex", flex: 1, flexDirection: "column", padding: "0 12px", justifyContent: "center", alignItems: "center", textAlign: "center" }; var outer = { display: "flex", flexDirection: "column", flex: 1, overflow: "hidden", backgroundColor: BACKGROUND }; var controlContainer = { flexDirection: "column", display: "flex", padding: 12, borderBottom: `1px solid ${BORDER_COLOR}` }; var tabWrapper = { display: "flex", marginBottom: "4px", flexDirection: "row", alignItems: "center" }; var persistanceKey = "remotion.show-render-modalwarning"; var getPersistedShowWarningState = () => { const val = localStorage.getItem(persistanceKey); if (!val) { return true; } return val === "true"; }; var setPersistedShowWarningState = (val) => { localStorage.setItem(persistanceKey, String(Boolean(val))); }; var DataEditor = ({ unresolvedComposition, defaultProps, setDefaultProps, mayShowSaveButton, propsEditType, saving, setSaving, readOnlyStudio }) => { const [mode, setMode] = useState51("schema"); const [showWarning, setShowWarningWithoutPersistance] = useState51(() => getPersistedShowWarningState()); const { updateCompositionDefaultProps } = useContext36(Internals31.CompositionSetters); const inJSONEditor = mode === "json"; const serializedJSON = useMemo80(() => { if (!inJSONEditor) { return null; } const value = defaultProps; return NoReactInternals10.serializeJSONWithSpecialTypes({ data: value, indent: 2, staticBase: window.remotion_staticBase }); }, [inJSONEditor, defaultProps]); const cliProps = getInputProps(); const [canSaveDefaultPropsObjectState, setCanSaveDefaultProps] = useState51({ [unresolvedComposition.id]: defaultTypeCanSaveState }); const z = useZodIfPossible(); const zodTypes = useZodTypesIfPossible(); const schema = useMemo80(() => { if (!z) { return "no-zod"; } if (!unresolvedComposition.schema) { return "no-schema"; } if (!(typeof unresolvedComposition.schema.safeParse === "function")) { throw new Error("A value which is not a Zod schema was passed to `schema`"); } return unresolvedComposition.schema; }, [unresolvedComposition.schema, z]); const zodValidationResult = useMemo80(() => { if (schema === "no-zod") { return "no-zod"; } if (schema === "no-schema") { return "no-schema"; } return schema.safeParse(defaultProps); }, [defaultProps, schema]); const setShowWarning = useCallback69((val) => { setShowWarningWithoutPersistance((prevVal) => { if (typeof val === "boolean") { setPersistedShowWarningState(val); return val; } setPersistedShowWarningState(val(prevVal)); return val(prevVal); }); }, []); const canSaveDefaultProps = useMemo80(() => { return canSaveDefaultPropsObjectState[unresolvedComposition.id] ? canSaveDefaultPropsObjectState[unresolvedComposition.id] : defaultTypeCanSaveState; }, [canSaveDefaultPropsObjectState, unresolvedComposition.id]); const showSaveButton = mayShowSaveButton && canSaveDefaultProps.canUpdate; const { fastRefreshes } = useContext36(FastRefreshContext); const checkIfCanSaveDefaultProps = useCallback69(async () => { try { const can = await canUpdateDefaultProps(unresolvedComposition.id, readOnlyStudio); if (can.canUpdate) { setCanSaveDefaultProps((prevState) => ({ ...prevState, [unresolvedComposition.id]: { canUpdate: true } })); } else { setCanSaveDefaultProps((prevState) => ({ ...prevState, [unresolvedComposition.id]: { canUpdate: false, reason: can.reason, determined: true } })); } } catch (err) { setCanSaveDefaultProps((prevState) => ({ ...prevState, [unresolvedComposition.id]: { canUpdate: false, reason: err.message, determined: true } })); } }, [readOnlyStudio, unresolvedComposition.id]); useEffect47(() => { checkIfCanSaveDefaultProps(); }, [checkIfCanSaveDefaultProps]); const { previewServerState, subscribeToEvent } = useContext36(StudioServerConnectionCtx); useEffect47(() => { const unsub = subscribeToEvent("root-file-changed", checkIfCanSaveDefaultProps); return () => { unsub(); }; }, [checkIfCanSaveDefaultProps, subscribeToEvent]); const modeItems = useMemo80(() => { return [ { key: "schema", label: "Schema", onClick: () => { setMode("schema"); }, selected: mode === "schema" }, { key: "json", label: "JSON", onClick: () => { setMode("json"); }, selected: mode === "json" } ]; }, [mode]); const onUpdate = useCallback69(() => { if (schema === "no-zod" || schema === "no-schema" || z === null) { showNotification("Cannot update default props: No Zod schema", 2000); return; } callUpdateDefaultPropsApi(unresolvedComposition.id, defaultProps, extractEnumJsonPaths({ schema, zodRuntime: z, currentPath: [], zodTypes })).then((response) => { if (!response.success) { showNotification(`Cannot update default props: ${response.reason}`, 2000); } }); }, [schema, z, unresolvedComposition.id, defaultProps, zodTypes]); const onSave = useCallback69((updater) => { if (schema === "no-zod" || schema === "no-schema" || z === null) { showNotification("Cannot update default props: No Zod schema", 2000); return; } window.remotion_ignoreFastRefreshUpdate = fastRefreshes + 1; setSaving(true); const newDefaultProps = updater(unresolvedComposition.defaultProps ?? {}); callUpdateDefaultPropsApi(unresolvedComposition.id, newDefaultProps, extractEnumJsonPaths({ schema, zodRuntime: z, currentPath: [], zodTypes })).then((response) => { if (!response.success) { console.log(response.stack); showNotification(`Cannot update default props: ${response.reason}. See console for more information.`, 2000); } updateCompositionDefaultProps(unresolvedComposition.id, newDefaultProps); }).catch((err) => { showNotification(`Cannot update default props: ${err.message}`, 2000); }).finally(() => { setSaving(false); }); }, [ schema, z, zodTypes, fastRefreshes, setSaving, unresolvedComposition.defaultProps, unresolvedComposition.id, updateCompositionDefaultProps ]); const connectionStatus = previewServerState.type; const warnings = useMemo80(() => { return getRenderModalWarnings({ canSaveDefaultProps, cliProps, isCustomDateUsed: serializedJSON ? serializedJSON.customDateUsed : false, customFileUsed: serializedJSON ? serializedJSON.customFileUsed : false, inJSONEditor, propsEditType, jsMapUsed: serializedJSON ? serializedJSON.mapUsed : false, jsSetUsed: serializedJSON ? serializedJSON.setUsed : false }); }, [ cliProps, canSaveDefaultProps, inJSONEditor, propsEditType, serializedJSON ]); if (connectionStatus === "disconnected") { return /* @__PURE__ */ jsxs72("div", { style: explainer3, children: [ /* @__PURE__ */ jsx142(Spacing, { y: 5 }), /* @__PURE__ */ jsx142("div", { style: errorExplanation2, children: "The studio server has disconnected. Reconnect to edit the schema." }), /* @__PURE__ */ jsx142(Spacing, { y: 2, block: true }) ] }); } if (schema === "no-zod") { return /* @__PURE__ */ jsx142(ZodNotInstalled, {}); } if (schema === "no-schema") { return /* @__PURE__ */ jsx142(NoSchemaDefined, {}); } if (!z) { throw new Error("expected zod"); } if (zodValidationResult === "no-zod") { throw new Error("expected zod"); } if (zodValidationResult === "no-schema") { throw new Error("expected schema"); } const def = schema._def; const typeName = def.typeName; if (typeName === z.ZodFirstPartyTypeKind.ZodAny) { return /* @__PURE__ */ jsx142(NoSchemaDefined, {}); } if (!unresolvedComposition.defaultProps) { return /* @__PURE__ */ jsx142(NoDefaultProps, {}); } return /* @__PURE__ */ jsxs72("div", { style: outer, children: [ /* @__PURE__ */ jsxs72("div", { style: controlContainer, children: [ /* @__PURE__ */ jsxs72("div", { style: tabWrapper, children: [ /* @__PURE__ */ jsx142(SegmentedControl, { items: modeItems, needsWrapping: false }), /* @__PURE__ */ jsx142(Flex, {}), warnings.length > 0 ? /* @__PURE__ */ jsx142(WarningIndicatorButton, { setShowWarning, showWarning, warningCount: warnings.length }) : null ] }), showWarning && warnings.length > 0 ? warnings.map((warning) => /* @__PURE__ */ jsxs72(React94.Fragment, { children: [ /* @__PURE__ */ jsx142(Spacing, { y: 1 }), /* @__PURE__ */ jsx142(ValidationMessage, { message: warning, align: "flex-start", type: "warning" }) ] }, warning)) : null ] }), mode === "schema" ? /* @__PURE__ */ jsx142(SchemaEditor, { unsavedDefaultProps: defaultProps, setValue: setDefaultProps, schema, zodValidationResult, savedDefaultProps: unresolvedComposition.defaultProps, onSave, showSaveButton, saving, saveDisabledByParent: !zodValidationResult.success }) : /* @__PURE__ */ jsx142(RenderModalJSONPropsEditor, { value: defaultProps ?? {}, setValue: setDefaultProps, onSave: onUpdate, showSaveButton, serializedJSON, defaultProps: unresolvedComposition.defaultProps, schema }) ] }); }; // src/components/RenderQueue/index.tsx import React104, { useContext as useContext44, useEffect as useEffect49, useMemo as useMemo87 } from "react"; import { Internals as Internals34 } from "remotion"; // src/components/RenderQueue/RenderQueueItem.tsx import { useCallback as useCallback78, useContext as useContext43, useEffect as useEffect48, useMemo as useMemo86, useState as useState52 } from "react"; import { Internals as Internals33 } from "remotion"; // src/components/RenderQueue/item-style.ts var renderQueueItemSubtitleStyle = { fontSize: 13, color: LIGHT_TEXT, appearance: "none", border: "none", padding: 0, cursor: "pointer", lineHeight: 1.2, textAlign: "left", whiteSpace: "nowrap", marginRight: SPACING_UNIT, overflowX: "hidden", maxWidth: 500, textOverflow: "ellipsis" }; // src/components/RenderQueue/RenderQueueCancelledMessage.tsx import { jsx as jsx143 } from "react/jsx-runtime"; var cancelledStyle = { ...renderQueueItemSubtitleStyle, color: LIGHT_TEXT, cursor: "default" }; var RenderQueueCancelledMessage = () => { return /* @__PURE__ */ jsx143("span", { style: cancelledStyle, children: "Cancelled" }); }; // src/components/RenderQueue/RenderQueueCopyToClipboard.tsx import { useCallback as useCallback70 } from "react"; import { jsx as jsx144 } from "react/jsx-runtime"; var revealIconStyle2 = { height: 12, color: "currentColor" }; var supportsCopyingToClipboard = (job) => { if (job.status !== "done") { return false; } if (job.type !== "still") { return false; } if (job.imageFormat === "png") { return true; } if (job.imageFormat === "jpeg") { return true; } return false; }; var RenderQueueCopyToClipboard = ({ job }) => { const renderCopyAction = useCallback70((color) => { return /* @__PURE__ */ jsx144(ClipboardIcon, { style: revealIconStyle2, color }); }, []); const onClick = useCallback70(async (e) => { e.stopPropagation(); try { const src = `${remotion_outputsBase}/${job.outName}`; const content = await fetch(src); const contentType = content.headers.get("content-type"); if (!contentType) { throw new Error("Expected content-type header"); } const blob = await content.blob(); await navigator.clipboard.write([ new ClipboardItem({ [contentType]: blob }) ]); showNotification("Copied to clipboard!", 1000); } catch (err) { showNotification(`Could not copy to clipboard: ${err.message}`, 2000); } }, [job.outName]); return /* @__PURE__ */ jsx144(InlineAction, { title: "Copy to clipboard", renderAction: renderCopyAction, onClick }); }; // src/components/RenderQueue/RenderQueueError.tsx import { useCallback as useCallback71, useContext as useContext37 } from "react"; import { jsx as jsx145 } from "react/jsx-runtime"; var outputLocation = { ...renderQueueItemSubtitleStyle }; var RenderQueueError = ({ job }) => { const { setSelectedModal } = useContext37(ModalsContext); const { tabIndex } = useZIndex(); const onClick = useCallback71(() => { setSelectedModal({ type: "render-progress", jobId: job.id }); }, [job.id, setSelectedModal]); if (job.status !== "failed") { throw new Error("should not have rendered this component"); } return /* @__PURE__ */ jsx145("button", { onClick, type: "button", style: outputLocation, tabIndex, title: job.error.message, children: job.error.message }); }; // src/components/RenderQueue/RenderQueueItemCancelButton.tsx import { useCallback as useCallback72, useContext as useContext38, useMemo as useMemo81 } from "react"; import { jsx as jsx146 } from "react/jsx-runtime"; var RenderQueueCancelButton = ({ job }) => { const isClientJob = isClientRenderJob(job); const { cancelClientJob } = useContext38(RenderQueueContext); const onClick = useCallback72((e) => { e.stopPropagation(); if (isClientJob) { cancelClientJob(job.id); return; } cancelRenderJob(job).catch((err) => { showNotification(`Could not cancel job: ${err.message}`, 2000); }); }, [job, isClientJob, cancelClientJob]); const icon5 = useMemo81(() => { return { height: 14, color: "currentColor" }; }, []); const renderAction = useCallback72((color) => { return /* @__PURE__ */ jsx146("svg", { style: icon5, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", children: /* @__PURE__ */ jsx146("path", { fill: color, d: "M367.2 412.5L99.5 144.8C77.1 176.1 64 214.5 64 256c0 106 86 192 192 192c41.5 0 79.9-13.1 111.2-35.5zm45.3-45.3C434.9 335.9 448 297.5 448 256c0-106-86-192-192-192c-41.5 0-79.9 13.1-111.2 35.5L412.5 367.2zM512 256c0 141.4-114.6 256-256 256S0 397.4 0 256S114.6 0 256 0S512 114.6 512 256z" }) }); }, [icon5]); return /* @__PURE__ */ jsx146(InlineAction, { renderAction, onClick }); }; // src/components/RenderQueue/RenderQueueItemStatus.tsx import React97, { useCallback as useCallback73, useContext as useContext39 } from "react"; // src/components/RenderQueue/CircularProgress.tsx import { jsx as jsx147 } from "react/jsx-runtime"; var RENDER_STATUS_INDICATOR_SIZE = 16; var STROKE_WIDTH = 3; var container30 = { height: RENDER_STATUS_INDICATOR_SIZE, width: RENDER_STATUS_INDICATOR_SIZE, transform: `rotate(-90deg)` }; var CircularProgress = ({ progress }) => { const r = RENDER_STATUS_INDICATOR_SIZE / 2 - STROKE_WIDTH; const circumference = r * Math.PI * 2; return /* @__PURE__ */ jsx147("svg", { style: container30, viewBox: `0 0 ${RENDER_STATUS_INDICATOR_SIZE} ${RENDER_STATUS_INDICATOR_SIZE}`, children: /* @__PURE__ */ jsx147("circle", { r: RENDER_STATUS_INDICATOR_SIZE / 2 - STROKE_WIDTH, stroke: LIGHT_TEXT, fill: "none", strokeWidth: STROKE_WIDTH, cx: RENDER_STATUS_INDICATOR_SIZE / 2, cy: RENDER_STATUS_INDICATOR_SIZE / 2, strokeDasharray: `${circumference} ${circumference}`, strokeMiterlimit: 0, strokeDashoffset: (1 - progress) * circumference }) }); }; // src/components/RenderQueue/RenderQueueItemStatus.tsx import { jsx as jsx148 } from "react/jsx-runtime"; var iconStyle3 = { height: RENDER_STATUS_INDICATOR_SIZE, width: RENDER_STATUS_INDICATOR_SIZE }; var invisibleStyle = { appearance: "none", border: "none", padding: 0, cursor: "pointer", display: "flex" }; var RenderQueueItemStatus = ({ job }) => { const { setSelectedModal } = useContext39(ModalsContext); const [hovered, setHovered] = React97.useState(false); const isClientJob = isClientRenderJob(job); const onPointerEnter = useCallback73(() => { setHovered(true); }, []); const onPointerLeave = useCallback73(() => { setHovered(false); }, []); const onClick = useCallback73((e) => { e.stopPropagation(); setSelectedModal({ type: "render-progress", jobId: job.id }); }, [job.id, setSelectedModal]); if (job.status === "failed") { return /* @__PURE__ */ jsx148("button", { type: "button", style: invisibleStyle, onClick, children: /* @__PURE__ */ jsx148("svg", { style: iconStyle3, viewBox: "0 0 512 512", children: /* @__PURE__ */ jsx148("path", { fill: FAIL_COLOR, d: "M0 160V352L160 512H352L512 352V160L352 0H160L0 160zm353.9 32l-17 17-47 47 47 47 17 17L320 353.9l-17-17-47-47-47 47-17 17L158.1 320l17-17 47-47-47-47-17-17L192 158.1l17 17 47 47 47-47 17-17L353.9 192z" }) }) }); } if (job.status === "idle") { return /* @__PURE__ */ jsx148("svg", { style: iconStyle3, viewBox: "0 0 512 512", children: /* @__PURE__ */ jsx148("path", { fill: LIGHT_TEXT, d: "M256 512C114.6 512 0 397.4 0 256S114.6 0 256 0S512 114.6 512 256s-114.6 256-256 256zM232 120V256c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2V120c0-13.3-10.7-24-24-24s-24 10.7-24 24z" }) }); } if (job.status === "done") { return /* @__PURE__ */ jsx148("button", { type: "button", style: invisibleStyle, onPointerEnter, onPointerLeave, onClick, children: /* @__PURE__ */ jsx148("svg", { style: iconStyle3, viewBox: "0 0 512 512", children: /* @__PURE__ */ jsx148("path", { fill: hovered ? "white" : LIGHT_TEXT, d: "M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM369 209L241 337l-17 17-17-17-64-64-17-17L160 222.1l17 17 47 47L335 175l17-17L385.9 192l-17 17z" }) }) }); } if (job.status === "running") { let progressValue; if (isClientJob) { const { renderedFrames, totalFrames } = job.progress; progressValue = totalFrames > 0 ? renderedFrames / totalFrames : 0; } else { progressValue = job.progress.value; } return /* @__PURE__ */ jsx148("button", { type: "button", style: invisibleStyle, onClick, children: /* @__PURE__ */ jsx148(CircularProgress, { progress: Math.max(0.07, progressValue) }) }); } if (job.status === "cancelled") { return /* @__PURE__ */ jsx148("svg", { style: iconStyle3, viewBox: "0 0 512 512", children: /* @__PURE__ */ jsx148("path", { fill: FAIL_COLOR, d: "M0 160V352L160 512H352L512 352V160L352 0H160L0 160zm353.9 32l-17 17-47 47 47 47 17 17L320 353.9l-17-17-47-47-47 47-17 17L158.1 320l17-17 47-47-47-47-17-17L192 158.1l17 17 47 47 47-47 17-17L353.9 192z" }) }); } throw new Error("Unknown job status"); }; // src/components/RenderQueue/RenderQueueOpenInFolder.tsx import { useCallback as useCallback74, useMemo as useMemo82 } from "react"; import { jsx as jsx149 } from "react/jsx-runtime"; var RenderQueueOpenInFinderItem = ({ job }) => { const onClick = useCallback74((e) => { e.stopPropagation(); openInFileExplorer({ directory: job.outName }).catch((err) => { showNotification(`Could not open file: ${err.message}`, 2000); }); }, [job.outName]); const icon5 = useMemo82(() => { return { height: 12, color: "currentColor" }; }, []); const renderAction = useCallback74((color) => { return /* @__PURE__ */ jsx149(ExpandedFolderIconSolid, { style: icon5, color }); }, [icon5]); return /* @__PURE__ */ jsx149(InlineAction, { renderAction, onClick }); }; // src/components/RenderQueue/RenderQueueOutputName.tsx import { useMemo as useMemo83 } from "react"; import { jsx as jsx150 } from "react/jsx-runtime"; var RenderQueueOutputName = ({ job }) => { const isClientJob = isClientRenderJob(job); const deletedOutputLocation = isClientJob ? false : job.deletedOutputLocation; const style9 = useMemo83(() => { return { ...renderQueueItemSubtitleStyle, textDecoration: deletedOutputLocation ? "line-through" : "none", color: renderQueueItemSubtitleStyle.color, cursor: "inherit" }; }, [deletedOutputLocation]); const getTitle = () => { if (isClientJob) { return `Downloaded as ${job.outName}`; } if (deletedOutputLocation) { return "File was deleted"; } return job.outName; }; return /* @__PURE__ */ jsx150("span", { style: style9, title: getTitle(), children: job.outName }); }; // src/components/RenderQueue/RenderQueueProgressMessage.tsx import { useCallback as useCallback75, useContext as useContext40 } from "react"; import { jsx as jsx151 } from "react/jsx-runtime"; var outputLocation2 = { ...renderQueueItemSubtitleStyle }; var RenderQueueProgressMessage = ({ job }) => { if (job.status !== "running") { throw new Error("should not have rendered this component"); } const { setSelectedModal } = useContext40(ModalsContext); const { tabIndex } = useZIndex(); const isClientJob = isClientRenderJob(job); const onClick = useCallback75(() => { setSelectedModal({ type: "render-progress", jobId: job.id }); }, [job.id, setSelectedModal]); const message = isClientJob ? `Rendering frame ${job.progress.renderedFrames}/${job.progress.totalFrames}` : job.progress.message; return /* @__PURE__ */ jsx151("button", { onClick, type: "button", style: outputLocation2, tabIndex, title: message, children: message }); }; // src/components/RenderQueue/RenderQueueRemoveItem.tsx import { useCallback as useCallback76, useContext as useContext41, useMemo as useMemo84 } from "react"; import { Internals as Internals32 } from "remotion"; import { jsx as jsx152 } from "react/jsx-runtime"; var RenderQueueRemoveItem = ({ job }) => { const isClientJob = isClientRenderJob(job); const { removeClientJob } = useContext41(RenderQueueContext); const { canvasContent } = useContext41(Internals32.CompositionManager); const { setCanvasContent } = useContext41(Internals32.CompositionSetters); const onClick = useCallback76((e) => { e.stopPropagation(); if (isClientJob) { if (canvasContent && canvasContent.type === "output-blob" && job.status === "done" && canvasContent.getBlob === job.getBlob) { setCanvasContent(null); } removeClientJob(job.id); showNotification("Removed job", 2000); return; } removeRenderJob(job).then(() => { showNotification("Removed job", 2000); }).catch((err) => { showNotification(`Could not remove item: ${err.message}`, 2000); }); }, [job, isClientJob, removeClientJob, canvasContent, setCanvasContent]); const icon5 = useMemo84(() => { return { height: 16, color: "currentColor" }; }, []); const renderAction = useCallback76((color) => { return /* @__PURE__ */ jsx152("svg", { style: icon5, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 320 512", children: /* @__PURE__ */ jsx152("path", { fill: color, d: "M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z" }) }); }, [icon5]); return /* @__PURE__ */ jsx152(InlineAction, { renderAction, onClick }); }; // src/components/RenderQueue/RenderQueueRepeat.tsx import { useCallback as useCallback77, useContext as useContext42, useMemo as useMemo85 } from "react"; // src/helpers/retry-payload.ts import { NoReactInternals as NoReactInternals11 } from "remotion/no-react"; var makeRetryPayload = (job) => { const defaults = window.remotion_renderDefaults; if (!defaults) { throw new Error("defaults not set"); } if (job.type === "still") { return { type: "server-render", compositionId: job.compositionId, initialFrame: job.frame, initialStillImageFormat: job.imageFormat, initialVideoImageFormat: null, initialJpegQuality: job.jpegQuality ?? defaults.jpegQuality, initialScale: job.scale, initialLogLevel: job.logLevel, initialConcurrency: defaults.concurrency, maxConcurrency: defaults.maxConcurrency, minConcurrency: defaults.minConcurrency, initialMuted: defaults.muted, initialEnforceAudioTrack: defaults.enforceAudioTrack, initialProResProfile: null, initialx264Preset: defaults.x264Preset, initialPixelFormat: defaults.pixelFormat, initialAudioBitrate: defaults.audioBitrate, initialVideoBitrate: defaults.videoBitrate, initialEveryNthFrame: defaults.everyNthFrame, initialNumberOfGifLoops: defaults.numberOfGifLoops, initialDelayRenderTimeout: job.delayRenderTimeout, defaultConfigurationAudioCodec: defaults.audioCodec, initialEnvVariables: job.envVariables, initialDisableWebSecurity: job.chromiumOptions.disableWebSecurity, initialOpenGlRenderer: job.chromiumOptions.gl, initialHeadless: job.chromiumOptions.headless, initialIgnoreCertificateErrors: job.chromiumOptions.ignoreCertificateErrors, initialDarkMode: job.chromiumOptions.darkMode, defaultProps: NoReactInternals11.deserializeJSONWithSpecialTypes(job.serializedInputPropsWithCustomSchema), inFrameMark: null, outFrameMark: null, initialOffthreadVideoCacheSizeInBytes: job.offthreadVideoCacheSizeInBytes, initialOffthreadVideoThreads: job.offthreadVideoThreads, initialColorSpace: defaults.colorSpace, initialMultiProcessOnLinux: job.multiProcessOnLinux, defaultConfigurationVideoCodec: defaults.codec, initialEncodingBufferSize: defaults.encodingBufferSize, initialEncodingMaxRate: defaults.encodingMaxRate, initialUserAgent: job.chromiumOptions.userAgent, initialBeep: job.beepOnFinish, initialRepro: job.repro, initialForSeamlessAacConcatenation: defaults.forSeamlessAacConcatenation, defaulMetadata: job.metadata, renderTypeOfLastRender: "still", initialHardwareAcceleration: defaults.hardwareAcceleration, initialChromeMode: job.chromeMode, initialMediaCacheSizeInBytes: job.mediaCacheSizeInBytes, renderDefaults: defaults }; } if (job.type === "sequence") { return { type: "server-render", initialFrame: 0, compositionId: job.compositionId, initialVideoImageFormat: null, initialJpegQuality: job.jpegQuality ?? defaults.jpegQuality, initialScale: job.scale, initialLogLevel: job.logLevel, initialConcurrency: defaults.concurrency, maxConcurrency: defaults.maxConcurrency, minConcurrency: defaults.minConcurrency, initialMuted: defaults.muted, initialEnforceAudioTrack: defaults.enforceAudioTrack, initialProResProfile: null, initialx264Preset: defaults.x264Preset, initialPixelFormat: defaults.pixelFormat, initialAudioBitrate: defaults.audioBitrate, initialVideoBitrate: defaults.videoBitrate, initialEveryNthFrame: defaults.everyNthFrame, initialNumberOfGifLoops: defaults.numberOfGifLoops, initialDelayRenderTimeout: job.delayRenderTimeout, initialEnvVariables: job.envVariables, initialDisableWebSecurity: job.chromiumOptions.disableWebSecurity, initialOpenGlRenderer: job.chromiumOptions.gl, initialHeadless: job.chromiumOptions.headless, initialIgnoreCertificateErrors: job.chromiumOptions.ignoreCertificateErrors, initialDarkMode: job.chromiumOptions.darkMode, defaultProps: NoReactInternals11.deserializeJSONWithSpecialTypes(job.serializedInputPropsWithCustomSchema), initialStillImageFormat: defaults.stillImageFormat, inFrameMark: job.startFrame, outFrameMark: job.endFrame, initialOffthreadVideoCacheSizeInBytes: job.offthreadVideoCacheSizeInBytes, initialOffthreadVideoThreads: job.offthreadVideoThreads, initialColorSpace: defaults.colorSpace, initialMultiProcessOnLinux: job.multiProcessOnLinux, defaultConfigurationVideoCodec: defaults.codec, defaultConfigurationAudioCodec: defaults.audioCodec, initialEncodingBufferSize: defaults.encodingBufferSize, initialEncodingMaxRate: defaults.encodingMaxRate, initialUserAgent: job.chromiumOptions.userAgent, initialBeep: job.beepOnFinish, initialRepro: job.repro, initialForSeamlessAacConcatenation: defaults.forSeamlessAacConcatenation, defaulMetadata: job.metadata, renderTypeOfLastRender: "sequence", initialHardwareAcceleration: defaults.hardwareAcceleration, initialChromeMode: job.chromeMode, initialMediaCacheSizeInBytes: job.mediaCacheSizeInBytes, renderDefaults: defaults }; } if (job.type === "video") { return { type: "server-render", compositionId: job.compositionId, initialStillImageFormat: defaults.stillImageFormat, initialVideoImageFormat: job.imageFormat, initialJpegQuality: job.jpegQuality ?? defaults.jpegQuality, initialScale: job.scale, initialLogLevel: job.logLevel, initialFrame: 0, initialConcurrency: job.concurrency, maxConcurrency: defaults.maxConcurrency, minConcurrency: defaults.minConcurrency, initialMuted: job.muted, initialEnforceAudioTrack: job.enforceAudioTrack, initialProResProfile: job.proResProfile ?? null, initialx264Preset: job.x264Preset ?? defaults.x264Preset, initialPixelFormat: job.pixelFormat, initialAudioBitrate: job.audioBitrate, initialVideoBitrate: job.videoBitrate, initialEveryNthFrame: job.everyNthFrame, initialNumberOfGifLoops: job.numberOfGifLoops, initialDelayRenderTimeout: job.delayRenderTimeout, initialEnvVariables: job.envVariables, initialDisableWebSecurity: job.chromiumOptions.disableWebSecurity, initialOpenGlRenderer: job.chromiumOptions.gl, initialHeadless: job.chromiumOptions.headless, initialIgnoreCertificateErrors: job.chromiumOptions.ignoreCertificateErrors, initialDarkMode: job.chromiumOptions.darkMode, defaultProps: NoReactInternals11.deserializeJSONWithSpecialTypes(job.serializedInputPropsWithCustomSchema), inFrameMark: job.startFrame, outFrameMark: job.endFrame, initialOffthreadVideoCacheSizeInBytes: job.offthreadVideoCacheSizeInBytes, initialOffthreadVideoThreads: job.offthreadVideoThreads, initialColorSpace: job.colorSpace, initialMultiProcessOnLinux: job.multiProcessOnLinux, defaultConfigurationVideoCodec: job.codec, defaultConfigurationAudioCodec: job.audioCodec, initialEncodingBufferSize: job.encodingBufferSize, initialEncodingMaxRate: job.encodingMaxRate, initialUserAgent: job.chromiumOptions.userAgent, initialBeep: job.beepOnFinish, initialRepro: job.repro, initialForSeamlessAacConcatenation: job.forSeamlessAacConcatenation, defaulMetadata: job.metadata, renderTypeOfLastRender: "video", initialHardwareAcceleration: job.hardwareAcceleration, initialChromeMode: job.chromeMode, initialMediaCacheSizeInBytes: job.mediaCacheSizeInBytes, renderDefaults: defaults }; } throw new Error(`Job ${JSON.stringify(job)} Not implemented`); }; var makeClientRetryPayload = (job) => { return { type: "web-render", compositionId: job.compositionId, initialFrame: job.type === "client-still" ? job.frame : 0, initialLogLevel: job.logLevel, initialLicenseKey: job.licenseKey, defaultProps: job.inputProps, inFrameMark: job.type === "client-video" ? job.startFrame : null, outFrameMark: job.type === "client-video" ? job.endFrame : null, initialDefaultOutName: job.outName, initialScale: job.scale, initialDelayRenderTimeout: job.delayRenderTimeout, initialMediaCacheSizeInBytes: job.mediaCacheSizeInBytes, initialAudioBitrate: job.type === "client-video" ? job.audioBitrate : null, initialAudioCodec: job.type === "client-video" ? job.audioCodec : null, initialContainer: job.type === "client-video" ? job.container : null, initialHardwareAcceleration: job.type === "client-video" ? job.hardwareAcceleration : null, initialVideoBitrate: job.type === "client-video" ? job.videoBitrate : null, initialVideoCodec: job.type === "client-video" ? job.videoCodec : null, initialStillImageFormat: job.type === "client-still" ? job.imageFormat : "png", initialKeyframeIntervalInSeconds: job.type === "client-video" ? job.keyframeIntervalInSeconds : null, initialMuted: job.type === "client-video" ? job.muted : null, initialTransparent: job.type === "client-video" ? job.transparent : null }; }; // src/components/RenderQueue/RenderQueueRepeat.tsx import { jsx as jsx153 } from "react/jsx-runtime"; var RenderQueueRepeatItem = ({ job }) => { const { setSelectedModal } = useContext42(ModalsContext); const isMobileLayout = useMobileLayout(); const { setSidebarCollapsedState } = useContext42(SidebarContext); const isClientJob = isClientRenderJob(job); const onClick = useCallback77((e) => { e.stopPropagation(); if (isClientJob) { const retryPayload = makeClientRetryPayload(job); setSelectedModal(retryPayload); } else { const retryPayload = makeRetryPayload(job); setSelectedModal(retryPayload); } if (isMobileLayout) { setSidebarCollapsedState({ left: "collapsed", right: "collapsed" }); } }, [ isMobileLayout, job, isClientJob, setSelectedModal, setSidebarCollapsedState ]); const icon5 = useMemo85(() => { return { height: 12, color: "currentColor" }; }, []); const renderAction = useCallback77((color) => { return /* @__PURE__ */ jsx153("svg", { style: icon5, viewBox: "0 0 512 512", children: /* @__PURE__ */ jsx153("path", { fill: color, d: "M386.3 160H336c-17.7 0-32 14.3-32 32s14.3 32 32 32H464c17.7 0 32-14.3 32-32V64c0-17.7-14.3-32-32-32s-32 14.3-32 32v51.2L414.4 97.6c-87.5-87.5-229.3-87.5-316.8 0s-87.5 229.3 0 316.8s229.3 87.5 316.8 0c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0c-62.5 62.5-163.8 62.5-226.3 0s-62.5-163.8 0-226.3s163.8-62.5 226.3 0L386.3 160z" }) }); }, [icon5]); return /* @__PURE__ */ jsx153(InlineAction, { onClick, renderAction }); }; // src/components/RenderQueue/RenderQueueItem.tsx import { jsx as jsx154, jsxs as jsxs73 } from "react/jsx-runtime"; var container31 = { padding: 12, display: "flex", flexDirection: "row", paddingBottom: 10, paddingRight: 4 }; var title3 = { fontSize: 13, lineHeight: 1 }; var right = { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }; var subtitle2 = { maxWidth: "100%", flex: 1, display: "flex", overflow: "hidden" }; var SELECTED_CLASSNAME = "__remotion_selected_classname"; var RenderQueueItem = ({ job, selected }) => { const [hovered, setHovered] = useState52(false); const { setCanvasContent } = useContext43(Internals33.CompositionSetters); const isClientJob = isClientRenderJob(job); const onPointerEnter = useCallback78(() => { setHovered(true); }, []); const onPointerLeave = useCallback78(() => { setHovered(false); }, []); const isHoverable = job.status === "done" && (isClientJob || job.type !== "sequence"); const containerStyle3 = useMemo86(() => { return { ...container31, backgroundColor: getBackgroundFromHoverState({ hovered: isHoverable && hovered, selected }), userSelect: "none", WebkitUserSelect: "none" }; }, [hovered, isHoverable, selected]); const scrollCurrentIntoView = useCallback78(() => { document.querySelector(`.${SELECTED_CLASSNAME}`)?.scrollIntoView({ behavior: "smooth" }); }, []); const onClick = useCallback78(() => { if (job.status !== "done") { return; } if (isClientJob) { const clientJob = job; setCanvasContent({ type: "output-blob", displayName: job.outName, getBlob: clientJob.getBlob, width: clientJob.metadata.width, height: clientJob.metadata.height, sizeInBytes: clientJob.metadata.sizeInBytes }); return; } if (job.type === "sequence") { return; } setCanvasContent((c) => { const isAlreadySelected = c && c.type === "output" && c.path === `/${job.outName}`; if (isAlreadySelected && !selected) { scrollCurrentIntoView(); return c; } return { type: "output", path: `/${job.outName}` }; }); pushUrl(`/outputs/${job.outName}`); }, [job, isClientJob, scrollCurrentIntoView, selected, setCanvasContent]); useEffect48(() => { if (selected) { scrollCurrentIntoView(); } }, [scrollCurrentIntoView, selected]); return /* @__PURE__ */ jsxs73(Row, { onPointerEnter, onPointerLeave, style: containerStyle3, align: "center", onClick, className: selected ? SELECTED_CLASSNAME : undefined, children: [ /* @__PURE__ */ jsx154(RenderQueueItemStatus, { job }), /* @__PURE__ */ jsx154(Spacing, { x: 1 }), /* @__PURE__ */ jsxs73("div", { style: right, children: [ /* @__PURE__ */ jsx154("div", { style: title3, children: job.compositionId }), /* @__PURE__ */ jsx154("div", { style: subtitle2, children: job.status === "done" ? /* @__PURE__ */ jsx154(RenderQueueOutputName, { job }) : job.status === "failed" ? /* @__PURE__ */ jsx154(RenderQueueError, { job }) : job.status === "running" ? /* @__PURE__ */ jsx154(RenderQueueProgressMessage, { job }) : job.status === "cancelled" ? /* @__PURE__ */ jsx154(RenderQueueCancelledMessage, {}) : null }) ] }), /* @__PURE__ */ jsx154(Spacing, { x: 1 }), !isClientJob && supportsCopyingToClipboard(job) ? /* @__PURE__ */ jsx154(RenderQueueCopyToClipboard, { job }) : null, job.status === "done" || job.status === "failed" || job.status === "cancelled" ? /* @__PURE__ */ jsx154(RenderQueueRepeatItem, { job }) : null, job.status === "running" ? /* @__PURE__ */ jsx154(RenderQueueCancelButton, { job }) : /* @__PURE__ */ jsx154(RenderQueueRemoveItem, { job }), job.status === "done" && !isClientJob ? /* @__PURE__ */ jsx154(RenderQueueOpenInFinderItem, { job }) : null ] }); }; // src/components/RenderQueue/index.tsx import { jsx as jsx155, jsxs as jsxs74 } from "react/jsx-runtime"; var separatorStyle = { borderBottom: `1px solid ${BORDER_COLOR}` }; var errorExplanation3 = { fontSize: 14, color: LIGHT_TEXT, fontFamily: "sans-serif", lineHeight: 1.5 }; var explainer4 = { display: "flex", flex: 1, flexDirection: "column", padding: "0 12px", justifyContent: "center", alignItems: "center", textAlign: "center", background: BACKGROUND }; var renderQueue = { background: BACKGROUND, flex: 1, overflowY: "auto" }; var RenderQueue = () => { const connectionStatus = useContext44(StudioServerConnectionCtx).previewServerState.type; const { jobs } = useContext44(RenderQueueContext); const { canvasContent } = useContext44(Internals34.CompositionManager); const previousJobCount = React104.useRef(jobs.length); const jobCount = jobs.length; const divRef = React104.useRef(null); useEffect49(() => { if (!divRef.current) { return; } if (jobCount > previousJobCount.current) { divRef.current.scrollTo({ top: divRef.current.scrollHeight, behavior: "smooth" }); } previousJobCount.current = jobCount; }, [jobCount]); const selectedJob = useMemo87(() => { if (!canvasContent) { return -1; } if (canvasContent.type === "output-blob") { for (let i = 0;i < jobs.length; i++) { const job = jobs[i]; if (isClientRenderJob(job) && job.status === "done") { if (canvasContent.getBlob === job.getBlob) { return i; } } } return -1; } if (canvasContent.type === "output") { for (let i = 0;i < jobs.length; i++) { const job = jobs[i]; if (!isClientRenderJob(job) && job.status === "done" && canvasContent.path === `/${job.outName}`) { return i; } } } return -1; }, [canvasContent, jobs]); if (connectionStatus === "disconnected") { return /* @__PURE__ */ jsxs74("div", { style: explainer4, children: [ /* @__PURE__ */ jsx155(Spacing, { y: 5 }), /* @__PURE__ */ jsx155("div", { style: errorExplanation3, children: "The studio server has disconnected." }), /* @__PURE__ */ jsx155(Spacing, { y: 2, block: true }) ] }); } if (jobCount === 0) { return /* @__PURE__ */ jsxs74("div", { style: explainer4, children: [ /* @__PURE__ */ jsx155(Spacing, { y: 5 }), /* @__PURE__ */ jsx155("div", { style: errorExplanation3, children: "No renders in the queue." }), /* @__PURE__ */ jsx155(Spacing, { y: 2, block: true }) ] }); } return /* @__PURE__ */ jsx155("div", { ref: divRef, style: renderQueue, className: ["css-reset", VERTICAL_SCROLLBAR_CLASSNAME].join(" "), children: jobs.map((j, index) => { return /* @__PURE__ */ jsx155("div", { style: index === jobs.length - 1 ? undefined : separatorStyle, children: /* @__PURE__ */ jsx155(RenderQueueItem, { selected: selectedJob === index, job: j }) }, j.id); }) }); }; // src/components/RendersTab.tsx import { useContext as useContext45, useMemo as useMemo88 } from "react"; import { Internals as Internals35 } from "remotion"; import { jsx as jsx156, jsxs as jsxs75, Fragment as Fragment20 } from "react/jsx-runtime"; var row3 = { display: "flex", flexDirection: "row", fontSize: 14, color: "inherit", alignItems: "center", flex: 1 }; var badge = { height: 16, width: 16, borderRadius: 3, fontSize: 10, display: "inline-flex", justifyContent: "center", alignItems: "center" }; var RendersTab = ({ selected, onClick }) => { const { jobs } = useContext45(RenderQueueContext); const { canvasContent } = useContext45(Internals35.CompositionManager); const failedJobs = jobs.filter((j) => j.status === "failed").length; const jobCount = jobs.length; const isActuallySelected = useMemo88(() => { if (!canvasContent || canvasContent.type !== "composition") { return true; } return selected; }, [canvasContent, selected]); const badgeStyle = useMemo88(() => { return { ...badge, backgroundColor: failedJobs > 0 ? FAIL_COLOR : "transparent", color: failedJobs > 0 ? "white" : LIGHT_TEXT, borderWidth: failedJobs > 0 ? 0 : 1, borderStyle: "solid", borderColor: LIGHT_TEXT }; }, [failedJobs]); return /* @__PURE__ */ jsx156(Tab, { selected: isActuallySelected, onClick, children: /* @__PURE__ */ jsxs75("div", { style: row3, children: [ "Renders", jobCount > 0 ? /* @__PURE__ */ jsxs75(Fragment20, { children: [ /* @__PURE__ */ jsx156(Flex, {}), /* @__PURE__ */ jsx156("div", { style: badgeStyle, children: jobCount }) ] }) : null ] }) }); }; // src/components/VisualControls/VisualControlsContent.tsx import React107, { useContext as useContext47 } from "react"; // src/components/VisualControls/VisualControlHandle.tsx import { useCallback as useCallback80, useContext as useContext46, useEffect as useEffect51, useState as useState55 } from "react"; import { NoReactInternals as NoReactInternals12 } from "remotion/no-react"; // src/components/VisualControls/ClickableFileName.tsx import { useCallback as useCallback79, useMemo as useMemo89, useState as useState53 } from "react"; // src/components/Timeline/TimelineStack/source-attribution.ts var getOriginalSourceAttribution = (originalLocation) => { if (!originalLocation.source) { return ""; } const split = originalLocation.source.split("/"); const last = split[split.length - 1]; if (last.startsWith("index")) { const lastTwo = split[split.length - 2]; return `${lastTwo}/${last}:${originalLocation.line}`; } return `${last}:${originalLocation.line}`; }; // src/components/VisualControls/ClickableFileName.tsx import { jsx as jsx157 } from "react/jsx-runtime"; var container32 = { paddingLeft: SCHEMA_EDITOR_FIELDSET_PADDING, paddingTop: SCHEMA_EDITOR_FIELDSET_PADDING / 2 }; var ClickableFileName = ({ originalFileName }) => { const [titleHovered, setTitleHovered] = useState53(false); const hoverEffect = titleHovered && originalFileName.type === "loaded"; const onTitlePointerEnter = useCallback79(() => { setTitleHovered(true); }, []); const onTitlePointerLeave = useCallback79(() => { setTitleHovered(false); }, []); const style9 = useMemo89(() => { return { fontSize: 12, cursor: originalFileName.type === "loaded" ? "pointer" : undefined, borderBottom: hoverEffect ? "1px solid #fff" : "none", color: hoverEffect ? "#fff" : LIGHT_COLOR }; }, [originalFileName, hoverEffect]); const onClick = useCallback79(async () => { if (originalFileName.type !== "loaded") { return; } await openOriginalPositionInEditor(originalFileName.originalFileName); }, [originalFileName]); return /* @__PURE__ */ jsx157("div", { style: container32, children: /* @__PURE__ */ jsx157("span", { style: style9, onClick, onPointerEnter: onTitlePointerEnter, onPointerLeave: onTitlePointerLeave, children: originalFileName.type === "loaded" ? getOriginalSourceAttribution(originalFileName.originalFileName) : originalFileName.type === "loading" ? "Loading..." : "Error loading" }) }); }; // src/components/VisualControls/VisualControlHandleHeader.tsx import { jsx as jsx158 } from "react/jsx-runtime"; var VisualControlHandleHeader = ({ originalFileName }) => { return /* @__PURE__ */ jsx158(ClickableFileName, { originalFileName }); }; // src/components/VisualControls/get-original-stack-trace.ts import { useEffect as useEffect50, useState as useState54 } from "react"; // src/components/Timeline/TimelineStack/get-stack.ts import { SourceMapConsumer as SourceMapConsumer2 } from "source-map"; // src/helpers/get-location-of-sequence.ts var getLocationOfSequence = (stack2) => { if (!stack2) { return null; } const parsed = parseStack(stack2.split(` `)); let i = 0; while (i < parsed.length) { const frame2 = parsed[i]; if (frame2.functionName === "apply") { i++; continue; } return frame2; } return null; }; var getLocationOfFunctionCall = (stack2, functionName) => { if (!stack2) { return null; } const parsed = parseStack(stack2.split(` `)); let i = 0; while (i < parsed.length) { const frame2 = parsed[i]; if (frame2.functionName !== functionName) { i++; continue; } return parsed[i + 1]; } return null; }; // src/components/Timeline/TimelineStack/get-stack.ts var waiters = []; var sourceMapConsumerCache = {}; var isCreating = {}; var getSourceMapCache = async (fileName) => { if (sourceMapConsumerCache[fileName]) { return sourceMapConsumerCache[fileName]; } if (isCreating[fileName]) { return new Promise((resolve) => { waiters.push({ id: String(Math.random()), forFileName: fileName, resolve }); }); } isCreating[fileName] = true; const res = await fetch(`${fileName}.map`); const json = await res.json(); const map = await new Promise((resolve) => { SourceMapConsumer2.with(json, null, (consumer) => { resolve(consumer); }); }); waiters.filter((w) => { if (w.forFileName === fileName) { w.resolve(map); return false; } return true; }); sourceMapConsumerCache[fileName] = map; isCreating[fileName] = false; return map; }; var getOriginalLocationFromStack = async (stack2, type) => { const location2 = type === "sequence" ? getLocationOfSequence(stack2) : getLocationOfFunctionCall(stack2, "visualControl"); if (!location2) { return null; } const map = await getSourceMapCache(location2.fileName); const originalPosition = getOriginalPosition(map, location2.lineNumber, location2.columnNumber); return originalPosition; }; // src/components/VisualControls/get-original-stack-trace.ts var useOriginalFileName = (stack2) => { const [originalFileName, setOriginalFileName] = useState54({ type: "loading" }); useEffect50(() => { if (!stack2) { return; } getOriginalLocationFromStack(stack2, "visual-control").then((frame2) => { if (frame2 === null) { setOriginalFileName({ type: "error", error: new Error("No frame found") }); } else { setOriginalFileName({ type: "loaded", originalFileName: frame2 }); } }).catch((err) => { console.error("Could not get original location of Sequence", err); }); }, [stack2]); return originalFileName; }; // src/components/VisualControls/VisualControlHandle.tsx import { jsx as jsx159, jsxs as jsxs76, Fragment as Fragment21 } from "react/jsx-runtime"; var VisualControlHandle = ({ value, keyName }) => { const z = useZodIfPossible(); if (!z) { throw new Error("expected zod"); } const zodTypes = useZodTypesIfPossible(); const state = useContext46(VisualControlsContext); const { updateValue } = useContext46(SetVisualControlsContext); const { fastRefreshes, increaseManualRefreshes } = useContext46(FastRefreshContext); const [saving, setSaving] = useState55(false); const currentValue = getVisualControlEditedValue({ handles: state.handles, key: keyName }); const originalFileName = useOriginalFileName(value.stack); const { localValue, RevisionContextProvider, onChange } = useLocalState({ schema: value.schema, setValue: (updater) => { updateValue(keyName, updater(currentValue)); increaseManualRefreshes(); }, unsavedValue: currentValue, savedValue: value.valueInCode }); const disableSave = window.remotion_isReadOnlyStudio || originalFileName.type !== "loaded"; const onSave = useCallback80((updater) => { if (disableSave) { return; } if (originalFileName.type !== "loaded") { throw new Error("Original file name is not loaded"); } const val = updater(value.valueInCode); window.remotion_ignoreFastRefreshUpdate = fastRefreshes + 1; const enumPaths = extractEnumJsonPaths({ schema: value.schema, zodRuntime: z, currentPath: [], zodTypes }); setSaving(true); Promise.resolve().then(() => { return applyVisualControlChange({ fileName: originalFileName.originalFileName.source, changes: [ { id: keyName, newValueSerialized: NoReactInternals12.serializeJSONWithSpecialTypes({ data: val, indent: 2, staticBase: window.remotion_staticBase }).serializedString, enumPaths } ] }); }).catch((e) => { showNotification(`Could not save visual control: ${e.message}`, 3000); }); }, [ disableSave, value.valueInCode, value.schema, fastRefreshes, z, originalFileName, keyName, zodTypes ]); useEffect51(() => { setSaving(false); }, [fastRefreshes]); return /* @__PURE__ */ jsxs76(Fragment21, { children: [ /* @__PURE__ */ jsx159(VisualControlHandleHeader, { originalFileName }), /* @__PURE__ */ jsx159(Spacing, { block: true, y: 0.5 }), /* @__PURE__ */ jsx159(RevisionContextProvider, { children: /* @__PURE__ */ jsx159(ZodSwitch, { mayPad: true, schema: value.schema, showSaveButton: !disableSave, saving, saveDisabledByParent: false, onSave, jsonPath: [keyName], value: localValue.value, defaultValue: value.valueInCode, setValue: onChange, onRemove: null }) }) ] }); }; // src/components/VisualControls/VisualControlsContent.tsx import { jsx as jsx160, jsxs as jsxs77 } from "react/jsx-runtime"; var container33 = { overflowY: "auto" }; var VisualControlsContent = () => { const { handles } = useContext47(VisualControlsContext); const entries = Object.entries(handles); return /* @__PURE__ */ jsx160("div", { style: container33, className: VERTICAL_SCROLLBAR_CLASSNAME, children: entries.map(([key, value], i) => { return /* @__PURE__ */ jsxs77(React107.Fragment, { children: [ /* @__PURE__ */ jsx160(VisualControlHandle, { keyName: key, value }), i === entries.length - 1 ? null : /* @__PURE__ */ jsx160(SchemaSeparationLine, {}) ] }, key); }) }); }; // src/components/OptionsPanel.tsx import { jsx as jsx161, jsxs as jsxs78 } from "react/jsx-runtime"; var localStorageKey3 = "remotion.sidebarPanel"; var getSelectedPanel2 = (readOnlyStudio) => { if (readOnlyStudio) { return "input-props"; } const panel2 = localStorage.getItem(localStorageKey3); if (panel2 === "renders") { return "renders"; } if (panel2 === "visual-controls") { return "visual-controls"; } return "input-props"; }; var tabsContainer3 = { backgroundColor: BACKGROUND }; var persistSelectedOptionsSidebarPanel2 = (panel2) => { localStorage.setItem(localStorageKey3, panel2); }; var optionsSidebarTabs = createRef9(); var OptionsPanel = ({ readOnlyStudio }) => { const { props, updateProps, resetUnsaved } = useContext48(Internals36.EditorPropsContext); const [saving, setSaving] = useState56(false); const isMobileLayout = useMobileLayout(); const visualControlsTabActivated = useContext48(VisualControlsTabActivatedContext); const container34 = useMemo90(() => ({ height: "100%", width: "100%", display: "flex", position: isMobileLayout ? "relative" : "absolute", flexDirection: "column", flex: 1 }), [isMobileLayout]); const [panel2, setPanel] = useState56(() => getSelectedPanel2(readOnlyStudio)); const onPropsSelected = useCallback81(() => { setPanel("input-props"); persistSelectedOptionsSidebarPanel2("input-props"); }, []); const onRendersSelected = useCallback81(() => { setPanel("renders"); persistSelectedOptionsSidebarPanel2("renders"); }, []); const onVisualControlsSelected = useCallback81(() => { setPanel("visual-controls"); persistSelectedOptionsSidebarPanel2("visual-controls"); }, []); useImperativeHandle12(optionsSidebarTabs, () => { return { selectRendersPanel: () => { setPanel("renders"); persistSelectedOptionsSidebarPanel2("renders"); } }; }, []); const { compositions, canvasContent } = useContext48(Internals36.CompositionManager); const composition = useMemo90(() => { if (canvasContent === null || canvasContent.type !== "composition") { return null; } for (const comp of compositions) { if (comp.id === canvasContent.compositionId) { return comp; } } return null; }, [canvasContent, compositions]); const setDefaultProps = useCallback81((newProps) => { if (composition === null) { return; } window.remotion_ignoreFastRefreshUpdate = null; updateProps({ id: composition.id, defaultProps: composition.defaultProps, newProps }); }, [composition, updateProps]); const currentDefaultProps = useMemo90(() => { if (composition === null) { return {}; } return props[composition.id] ?? composition.defaultProps ?? {}; }, [composition, props]); const unsavedChangesExist = useMemo90(() => { if (composition === null || composition.defaultProps === undefined) { return false; } return !deepEqual(composition.defaultProps, currentDefaultProps); }, [currentDefaultProps, composition]); const reset = useCallback81((e) => { if (e.detail.resetUnsaved) { resetUnsaved(e.detail.resetUnsaved); } }, [resetUnsaved]); useEffect52(() => { window.addEventListener(Internals36.PROPS_UPDATED_EXTERNALLY, reset); return () => { window.removeEventListener(Internals36.PROPS_UPDATED_EXTERNALLY, reset); }; }, [reset]); return /* @__PURE__ */ jsxs78("div", { style: container34, className: "css-reset", children: [ /* @__PURE__ */ jsx161("div", { style: tabsContainer3, children: /* @__PURE__ */ jsxs78(Tabs, { children: [ visualControlsTabActivated ? /* @__PURE__ */ jsx161(Tab, { selected: panel2 === "visual-controls", onClick: onVisualControlsSelected, children: "Controls" }) : null, composition ? /* @__PURE__ */ jsxs78(Tab, { selected: panel2 === "input-props", onClick: onPropsSelected, style: { justifyContent: "space-between" }, children: [ "Props", unsavedChangesExist ? /* @__PURE__ */ jsx161(GlobalPropsEditorUpdateButton, { compositionId: composition.id, currentDefaultProps }) : null ] }) : null, readOnlyStudio ? null : /* @__PURE__ */ jsx161(RendersTab, { onClick: onRendersSelected, selected: panel2 === "renders" }) ] }) }), panel2 === `input-props` && composition ? /* @__PURE__ */ jsx161(DataEditor, { unresolvedComposition: composition, defaultProps: currentDefaultProps, setDefaultProps, mayShowSaveButton: true, propsEditType: "default-props", saving, setSaving, readOnlyStudio }, composition.id) : panel2 === "visual-controls" && visualControlsTabActivated ? /* @__PURE__ */ jsx161(VisualControlsContent, {}) : readOnlyStudio ? null : /* @__PURE__ */ jsx161(RenderQueue, {}) ] }); }; // src/components/PreviewToolbar.tsx import { useContext as useContext55, useEffect as useEffect58, useRef as useRef32, useState as useState60 } from "react"; import { Internals as Internals44 } from "remotion"; // src/helpers/should-show-render-button.ts var shouldShowRenderButton = (readOnlyStudio) => { if (readOnlyStudio) { return SHOW_BROWSER_RENDERING; } return true; }; // src/state/loop.ts var key = "remotion.loop"; var persistLoopOption = (option) => { localStorage.setItem(key, String(option)); }; var loadLoopOption = () => { const item2 = localStorage.getItem(key); return item2 !== "false"; }; // src/components/CheckboardToggle.tsx import { useCallback as useCallback82, useContext as useContext49 } from "react"; import { NoReactInternals as NoReactInternals13 } from "remotion/no-react"; import { jsx as jsx162 } from "react/jsx-runtime"; var accessibilityLabel2 = [ "Show transparency as checkerboard", areKeyboardShortcutsDisabled() ? null : "(T)" ].filter(NoReactInternals13.truthy).join(" "); var CheckboardToggle = () => { const { checkerboard, setCheckerboard } = useContext49(CheckerboardContext); const onClick = useCallback82(() => { setCheckerboard((c) => { return !c; }); }, [setCheckerboard]); return /* @__PURE__ */ jsx162(ControlButton, { title: accessibilityLabel2, "aria-label": accessibilityLabel2, onClick, children: /* @__PURE__ */ jsx162("svg", { "aria-hidden": "true", focusable: "false", "data-prefix": "fas", "data-icon": "game-board-alt", className: "svg-inline--fa fa-game-board-alt fa-w-16", role: "img", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", style: { width: 16, height: 16 }, children: /* @__PURE__ */ jsx162("path", { fill: checkerboard ? BLUE : "white", d: "M480 0H32A32 32 0 0 0 0 32v448a32 32 0 0 0 32 32h448a32 32 0 0 0 32-32V32a32 32 0 0 0-32-32zm-32 256H256v192H64V256h192V64h192z" }) }) }); }; // src/components/FpsCounter.tsx import { useEffect as useEffect53, useLayoutEffect, useMemo as useMemo91, useRef as useRef30, useState as useState57 } from "react"; import { Internals as Internals37 } from "remotion"; import { jsxs as jsxs79 } from "react/jsx-runtime"; var label6 = { color: "white", fontSize: 15, fontFamily: "Arial, Helvetica, sans-serif", whiteSpace: "nowrap" }; var pushWithMaxSize = (arr, value, maxSize) => { arr.push(value); return arr.slice(-maxSize); }; var FpsCounter = ({ playbackSpeed }) => { const videoConfig = Internals37.useUnsafeVideoConfig(); const [playing] = Internals37.Timeline.usePlayingState(); const frame2 = Internals37.Timeline.useTimelinePosition(); const [marker, rerender] = useState57({}); const [fps, setFps] = useState57(0); const previousUpdates = useRef30([]); const fpsRef = useRef30(0); const playingRef = useRef30(playing); useLayoutEffect(() => { fpsRef.current = 0; previousUpdates.current = []; playingRef.current = playing; }, [playing]); useLayoutEffect(() => { if (playingRef.current === false) return; previousUpdates.current = pushWithMaxSize(previousUpdates.current, performance.now(), 15); if (previousUpdates.current.length < 2) return; const diff = Math.max(...previousUpdates.current) - Math.min(...previousUpdates.current); const averageDistanceBetween = diff / (previousUpdates.current.length - 1); fpsRef.current = 1000 / averageDistanceBetween; if (previousUpdates.current.length === 2) setFps(fpsRef.current); }, [frame2]); useEffect53(() => { if (playing) { const t = setTimeout(() => { rerender({}); setFps(fpsRef.current); }, 1000); return () => clearTimeout(t); } }, [marker, playing]); const style9 = useMemo91(() => { if (!videoConfig) { return {}; } const expectedFps = Math.abs(playbackSpeed) * videoConfig.fps; return { ...label6, color: fps < expectedFps * 0.9 ? "red" : "white" }; }, [fps, playbackSpeed, videoConfig]); if (fps === 0) { return null; } if (playing === false) { return null; } if (videoConfig === null) { return null; } return /* @__PURE__ */ jsxs79("div", { style: style9, children: [ fps.toFixed(1), " FPS" ] }); }; // src/components/FullscreenToggle.tsx import { useCallback as useCallback83, useContext as useContext50, useEffect as useEffect54 } from "react"; import { Internals as Internals38 } from "remotion"; import { NoReactInternals as NoReactInternals14 } from "remotion/no-react"; import { jsx as jsx163 } from "react/jsx-runtime"; var accessibilityLabel3 = [ "Enter fullscreen preview", areKeyboardShortcutsDisabled() ? null : "(F)" ].filter(NoReactInternals14.truthy).join(" "); var FullScreenToggle = () => { const keybindings = useKeybinding(); const { setSize } = useContext50(Internals38.PreviewSizeContext); const onClick = useCallback83(() => { drawRef.current?.requestFullscreen(); if (document.fullscreenElement) setSize(() => ({ size: "auto", translation: { x: 0, y: 0 } })); }, [setSize]); useEffect54(() => { const f = keybindings.registerKeybinding({ event: "keydown", key: "f", callback: onClick, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); return () => { f.unregister(); }; }, [keybindings, onClick]); return /* @__PURE__ */ jsx163(ControlButton, { title: accessibilityLabel3, "aria-label": accessibilityLabel3, onClick, children: /* @__PURE__ */ jsx163("svg", { style: { width: 18, height: 18 }, viewBox: "0 0 448 512", fill: "#fff", children: /* @__PURE__ */ jsx163("path", { d: "M0 180V56c0-13.3 10.7-24 24-24h124c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12H64v84c0 6.6-5.4 12-12 12H12c-6.6 0-12-5.4-12-12zM288 44v40c0 6.6 5.4 12 12 12h84v84c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12V56c0-13.3-10.7-24-24-24H300c-6.6 0-12 5.4-12 12zm148 276h-40c-6.6 0-12 5.4-12 12v84h-84c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h124c13.3 0 24-10.7 24-24V332c0-6.6-5.4-12-12-12zM160 468v-40c0-6.6-5.4-12-12-12H64v-84c0-6.6-5.4-12-12-12H12c-6.6 0-12 5.4-12 12v124c0 13.3 10.7 24 24 24h124c6.6 0 12-5.4 12-12z" }) }) }); }; // src/components/LoopToggle.tsx import { useCallback as useCallback84 } from "react"; import { jsx as jsx164 } from "react/jsx-runtime"; var accessibilityLabel4 = "Loop video"; var LoopToggle = ({ loop, setLoop }) => { const onClick = useCallback84(() => { setLoop((c) => { persistLoopOption(!c); return !c; }); }, [setLoop]); return /* @__PURE__ */ jsx164(ControlButton, { title: accessibilityLabel4, "aria-label": accessibilityLabel4, onClick, children: /* @__PURE__ */ jsx164("svg", { viewBox: "0 0 512 512", style: { width: 18, height: 18 }, children: /* @__PURE__ */ jsx164("path", { fill: loop ? BLUE : "white", d: "M493.544 181.463c11.956 22.605 18.655 48.4 18.452 75.75C511.339 345.365 438.56 416 350.404 416H192v47.495c0 22.475-26.177 32.268-40.971 17.475l-80-80c-9.372-9.373-9.372-24.569 0-33.941l80-80C166.138 271.92 192 282.686 192 304v48h158.875c52.812 0 96.575-42.182 97.12-94.992.155-15.045-3.17-29.312-9.218-42.046-4.362-9.185-2.421-20.124 4.8-27.284 4.745-4.706 8.641-8.555 11.876-11.786 11.368-11.352 30.579-8.631 38.091 5.571zM64.005 254.992c.545-52.81 44.308-94.992 97.12-94.992H320v47.505c0 22.374 26.121 32.312 40.971 17.465l80-80c9.372-9.373 9.372-24.569 0-33.941l-80-80C346.014 16.077 320 26.256 320 48.545V96H161.596C73.44 96 .661 166.635.005 254.788c-.204 27.35 6.495 53.145 18.452 75.75 7.512 14.202 26.723 16.923 38.091 5.57 3.235-3.231 7.13-7.08 11.876-11.786 7.22-7.16 9.162-18.098 4.8-27.284-6.049-12.735-9.374-27.001-9.219-42.046z" }) }) }); }; // src/components/MuteToggle.tsx import { useCallback as useCallback85 } from "react"; // src/icons/media-volume.tsx import { jsx as jsx165 } from "react/jsx-runtime"; var size3 = 22; var VolumeOffIcon = () => { return /* @__PURE__ */ jsx165("svg", { width: size3, height: size3, viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx165("path", { d: "M3.63 3.63a.996.996 0 000 1.41L7.29 8.7 7 9H4c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1h3l3.29 3.29c.63.63 1.71.18 1.71-.71v-4.17l4.18 4.18c-.49.37-1.02.68-1.6.91-.36.15-.58.53-.58.92 0 .72.73 1.18 1.39.91.8-.33 1.55-.77 2.22-1.31l1.34 1.34a.996.996 0 101.41-1.41L5.05 3.63c-.39-.39-1.02-.39-1.42 0zM19 12c0 .82-.15 1.61-.41 2.34l1.53 1.53c.56-1.17.88-2.48.88-3.87 0-3.83-2.4-7.11-5.78-8.4-.59-.23-1.22.23-1.22.86v.19c0 .38.25.71.61.85C17.18 6.54 19 9.06 19 12zm-8.71-6.29l-.17.17L12 7.76V6.41c0-.89-1.08-1.33-1.71-.7zM16.5 12A4.5 4.5 0 0014 7.97v1.79l2.48 2.48c.01-.08.02-.16.02-.24z", fill: BLUE }) }); }; var VolumeOnIcon = () => { return /* @__PURE__ */ jsx165("svg", { width: size3, height: size3, viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx165("path", { d: "M3 10v4c0 .55.45 1 1 1h3l3.29 3.29c.63.63 1.71.18 1.71-.71V6.41c0-.89-1.08-1.34-1.71-.71L7 9H4c-.55 0-1 .45-1 1zm13.5 2A4.5 4.5 0 0014 7.97v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 4.45v.2c0 .38.25.71.6.85C17.18 6.53 19 9.06 19 12s-1.82 5.47-4.4 6.5c-.36.14-.6.47-.6.85v.2c0 .63.63 1.07 1.21.85C18.6 19.11 21 15.84 21 12s-2.4-7.11-5.79-8.4c-.58-.23-1.21.22-1.21.85z", fill: "#fff" }) }); }; // src/state/mute.ts var key2 = "remotion.mute"; var persistMuteOption = (option) => { localStorage.setItem(key2, String(option)); }; var loadMuteOption = () => { const item2 = localStorage.getItem(key2); return item2 === "true"; }; // src/components/MuteToggle.tsx import { jsx as jsx166 } from "react/jsx-runtime"; var MuteToggle = ({ muted, setMuted }) => { const onClick = useCallback85(() => { setMuted((m) => { persistMuteOption(!m); return !m; }); }, [setMuted]); const accessibilityLabel5 = muted ? "Unmute video" : "Mute video"; return /* @__PURE__ */ jsx166(ControlButton, { title: accessibilityLabel5, "aria-label": accessibilityLabel5, onClick, children: muted ? /* @__PURE__ */ jsx166(VolumeOffIcon, {}) : /* @__PURE__ */ jsx166(VolumeOnIcon, {}) }); }; // src/components/PlayPause.tsx import { PlayerInternals as PlayerInternals12 } from "@remotion/player"; import { useCallback as useCallback86, useEffect as useEffect55, useState as useState58 } from "react"; import { Internals as Internals39 } from "remotion"; // src/icons/jump-to-start.tsx import { jsx as jsx167 } from "react/jsx-runtime"; var JumpToStart = (props) => { return /* @__PURE__ */ jsx167("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", ...props, children: /* @__PURE__ */ jsx167("path", { fill: "currentColor", d: "M0 415.1V96.03c0-17.67 14.33-31.1 31.1-31.1C49.67 64.03 64 78.36 64 96.03v131.8l171.5-156.5C256.1 54.28 288 68.66 288 96.03v131.9l171.5-156.5C480.1 54.28 512 68.66 512 96.03v319.9c0 27.37-31.88 41.74-52.5 24.62L288 285.2v130.7c0 27.37-31.88 41.74-52.5 24.62L64 285.2v130.7c0 17.67-14.33 31.1-31.1 31.1C14.33 447.1 0 433.6 0 415.1z" }) }); }; // src/icons/pause.tsx import { jsx as jsx168 } from "react/jsx-runtime"; var Pause = (props) => /* @__PURE__ */ jsx168("svg", { ...props, "aria-hidden": "true", focusable: "false", "data-prefix": "fas", "data-icon": "pause", className: "svg-inline--fa fa-pause fa-w-14", role: "img", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 448 512", children: /* @__PURE__ */ jsx168("path", { fill: "currentColor", d: "M144 479H48c-26.5 0-48-21.5-48-48V79c0-26.5 21.5-48 48-48h96c26.5 0 48 21.5 48 48v352c0 26.5-21.5 48-48 48zm304-48V79c0-26.5-21.5-48-48-48h-96c-26.5 0-48 21.5-48 48v352c0 26.5 21.5 48 48 48h96c26.5 0 48-21.5 48-48z" }) }); // src/icons/play.tsx import { jsx as jsx169 } from "react/jsx-runtime"; var Play = (props) => /* @__PURE__ */ jsx169("svg", { ...props, "aria-hidden": "true", focusable: "false", "data-prefix": "fas", "data-icon": "play", className: "svg-inline--fa fa-play fa-w-14", role: "img", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 448 512", children: /* @__PURE__ */ jsx169("path", { fill: "currentColor", d: "M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z" }) }); // src/icons/step-back.tsx import { jsx as jsx170 } from "react/jsx-runtime"; var StepBack = (props) => { return /* @__PURE__ */ jsx170("svg", { viewBox: "0 0 448 512", ...props, children: /* @__PURE__ */ jsx170("path", { fill: "currentColor", d: "M64 468V44c0-6.6 5.4-12 12-12h48c6.6 0 12 5.4 12 12v176.4l195.5-181C352.1 22.3 384 36.6 384 64v384c0 27.4-31.9 41.7-52.5 24.6L136 292.7V468c0 6.6-5.4 12-12 12H76c-6.6 0-12-5.4-12-12z" }) }); }; // src/icons/step-forward.tsx import { jsx as jsx171 } from "react/jsx-runtime"; var StepForward = (props) => { return /* @__PURE__ */ jsx171("svg", { viewBox: "0 0 448 512", ...props, children: /* @__PURE__ */ jsx171("path", { fill: "currentColor", d: "M384 44v424c0 6.6-5.4 12-12 12h-48c-6.6 0-12-5.4-12-12V291.6l-195.5 181C95.9 489.7 64 475.4 64 448V64c0-27.4 31.9-41.7 52.5-24.6L312 219.3V44c0-6.6 5.4-12 12-12h48c6.6 0 12 5.4 12 12z" }) }); }; // src/components/PlayPause.tsx import { jsx as jsx172, jsxs as jsxs80, Fragment as Fragment22 } from "react/jsx-runtime"; var backStyle = { height: 18, color: "white" }; var forwardBackStyle = { height: 16, color: "white" }; var iconButton = { height: 14, width: 14, color: "white" }; var PlayPause = ({ playbackRate, loop, bufferStateDelayInMilliseconds }) => { const { inFrame, outFrame } = useTimelineInOutFramePosition(); const videoConfig = Internals39.useUnsafeVideoConfig(); const [showBufferIndicator, setShowBufferState] = useState58(false); const { playing, play, pause, pauseAndReturnToPlayStart, frameBack, seek, frameForward, isLastFrame, isFirstFrame, emitter, getCurrentFrame: getCurrentFrame2 } = PlayerInternals12.usePlayer(); PlayerInternals12.usePlayback({ loop, playbackRate, moveToBeginningWhenEnded: true, inFrame, outFrame, getCurrentFrame: getCurrentFrame2, browserMediaControlsBehavior: { mode: "register-media-session" } }); const isStill = useIsStill(); useEffect55(() => { if (isStill) { pause(); } }, [isStill, pause]); const onSpace = useCallback86((e) => { if (playing) { pause(); } else { play(); } e.preventDefault(); }, [pause, play, playing]); const onEnter = useCallback86((e) => { if (playing) { e.preventDefault(); pauseAndReturnToPlayStart(); } }, [pauseAndReturnToPlayStart, playing]); const onArrowLeft = useCallback86((e) => { e.preventDefault(); if (e.altKey) { seek(0); ensureFrameIsInViewport({ direction: "fit-left", durationInFrames: getCurrentDuration(), frame: 0 }); } else if (e.shiftKey) { frameBack(getCurrentFps()); ensureFrameIsInViewport({ direction: "fit-left", durationInFrames: getCurrentDuration(), frame: Math.max(0, getCurrentFrame2() - getCurrentFps()) }); } else { frameBack(1); ensureFrameIsInViewport({ direction: "fit-left", durationInFrames: getCurrentDuration(), frame: Math.max(0, getCurrentFrame2() - 1) }); } }, [frameBack, seek, getCurrentFrame2]); const onArrowRight = useCallback86((e) => { if (e.altKey) { seek(getCurrentDuration() - 1); ensureFrameIsInViewport({ direction: "fit-right", durationInFrames: getCurrentDuration() - 1, frame: getCurrentDuration() - 1 }); } else if (e.shiftKey) { frameForward(getCurrentFps()); ensureFrameIsInViewport({ direction: "fit-right", durationInFrames: getCurrentDuration(), frame: Math.min(getCurrentDuration() - 1, getCurrentFrame2() + getCurrentFps()) }); } else { frameForward(1); ensureFrameIsInViewport({ direction: "fit-right", durationInFrames: getCurrentDuration(), frame: Math.min(getCurrentDuration() - 1, getCurrentFrame2() + 1) }); } e.preventDefault(); }, [frameForward, seek, getCurrentFrame2]); const oneFrameBack = useCallback86(() => { frameBack(1); }, [frameBack]); const oneFrameForward = useCallback86(() => { frameForward(1); }, [frameForward]); const jumpToStart = useCallback86(() => { seek(inFrame ?? 0); }, [seek, inFrame]); const jumpToEnd = useCallback86(() => { seek(outFrame ?? getCurrentDuration() - 1); }, [seek, outFrame]); const keybindings = useKeybinding(); useEffect55(() => { const arrowLeft = keybindings.registerKeybinding({ event: "keydown", key: "ArrowLeft", callback: onArrowLeft, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const arrowRight = keybindings.registerKeybinding({ event: "keydown", key: "ArrowRight", callback: onArrowRight, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const space = keybindings.registerKeybinding({ event: "keydown", key: " ", callback: onSpace, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const enter = keybindings.registerKeybinding({ event: "keydown", key: "enter", callback: onEnter, commandCtrlKey: false, preventDefault: false, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const a = keybindings.registerKeybinding({ event: "keydown", key: "a", callback: jumpToStart, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const e = keybindings.registerKeybinding({ event: "keydown", key: "e", callback: jumpToEnd, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); return () => { arrowLeft.unregister(); arrowRight.unregister(); space.unregister(); enter.unregister(); a.unregister(); e.unregister(); }; }, [ jumpToEnd, jumpToStart, keybindings, onArrowLeft, onArrowRight, onEnter, onSpace ]); useEffect55(() => { let timeout = null; let stopped = false; const onBuffer = () => { requestAnimationFrame(() => { stopped = false; timeout = setTimeout(() => { if (!stopped) { setShowBufferState(true); } }, bufferStateDelayInMilliseconds); }); }; const onResume = () => { requestAnimationFrame(() => { setShowBufferState(false); stopped = true; if (timeout) { clearTimeout(timeout); } }); }; emitter.addEventListener("waiting", onBuffer); emitter.addEventListener("resume", onResume); return () => { emitter.removeEventListener("waiting", onBuffer); emitter.removeEventListener("resume", onResume); setShowBufferState(false); if (timeout) { clearTimeout(timeout); } stopped = true; }; }, [bufferStateDelayInMilliseconds, emitter]); return /* @__PURE__ */ jsxs80(Fragment22, { children: [ /* @__PURE__ */ jsx172(ControlButton, { "aria-label": "Jump to beginning", title: "Jump to beginning", disabled: !videoConfig || isFirstFrame, onClick: jumpToStart, children: /* @__PURE__ */ jsx172(JumpToStart, { style: backStyle }) }), /* @__PURE__ */ jsx172(ControlButton, { "aria-label": "Step back one frame", title: "Step back one frame", disabled: !videoConfig || isFirstFrame, onClick: oneFrameBack, children: /* @__PURE__ */ jsx172(StepBack, { style: forwardBackStyle }) }), /* @__PURE__ */ jsx172(ControlButton, { "aria-label": playing ? "Pause" : "Play", title: playing ? "Pause" : "Play", onClick: playing ? pause : play, disabled: !videoConfig, children: playing ? showBufferIndicator ? /* @__PURE__ */ jsx172(PlayerInternals12.BufferingIndicator, { type: "studio" }) : /* @__PURE__ */ jsx172(Pause, { style: iconButton }) : /* @__PURE__ */ jsx172(Play, { style: iconButton }) }), /* @__PURE__ */ jsx172(ControlButton, { "aria-label": "Step forward one frame", title: "Step forward one frame", disabled: !videoConfig || isLastFrame, onClick: oneFrameForward, children: /* @__PURE__ */ jsx172(StepForward, { style: forwardBackStyle }) }) ] }); }; // src/components/PlaybackKeyboardShortcutsManager.tsx import { PlayerInternals as PlayerInternals13 } from "@remotion/player"; import { useCallback as useCallback87, useEffect as useEffect56 } from "react"; var PlaybackKeyboardShortcutsManager = ({ setPlaybackRate }) => { const keybindings = useKeybinding(); const { play, pause, playing } = PlayerInternals13.usePlayer(); const onJKey = useCallback87(() => { setPlaybackRate((prevPlaybackRate) => { if (!playing) { return -1; } if (prevPlaybackRate > -1) { return -1; } if (prevPlaybackRate > -2) { return -2; } return -4; }); play(); }, [play, playing, setPlaybackRate]); const onKKey = useCallback87(() => { setPlaybackRate(1); pause(); }, [pause, setPlaybackRate]); const onLKey = useCallback87(() => { setPlaybackRate((prevPlaybackRate) => { if (!playing) { return 1; } if (prevPlaybackRate < 1) { return 1; } if (prevPlaybackRate < 2) { return 2; } return 4; }); play(); }, [play, playing, setPlaybackRate]); useEffect56(() => { const jKey = keybindings.registerKeybinding({ event: "keydown", key: "j", callback: onJKey, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const kKey = keybindings.registerKeybinding({ event: "keydown", key: "k", callback: onKKey, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const lKey = keybindings.registerKeybinding({ event: "keydown", key: "l", callback: onLKey, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); return () => { jKey.unregister(); kKey.unregister(); lKey.unregister(); }; }, [keybindings, onJKey, onKKey, onLKey]); return null; }; // src/components/PlaybackRatePersistor.tsx import { useContext as useContext51, useEffect as useEffect57 } from "react"; import { Internals as Internals40 } from "remotion"; // src/state/playbackrate.ts var key3 = "remotion.playbackrate"; var persistPlaybackRate = (option) => { localStorage.setItem(key3, String(option)); }; var loadPlaybackRate = () => { if (typeof window !== "undefined") { return 1; } const item2 = localStorage.getItem(key3); if (item2 === null) { return 1; } return Number(item2); }; // src/components/PlaybackRatePersistor.tsx var PlaybackRatePersistor = () => { const { setPlaybackRate, playbackRate } = useContext51(Internals40.TimelineContext); useEffect57(() => { setPlaybackRate(loadPlaybackRate()); }, [setPlaybackRate]); useEffect57(() => { persistPlaybackRate(playbackRate); }, [playbackRate]); return null; }; // src/components/PlaybackRateSelector.tsx import { useContext as useContext52, useMemo as useMemo92 } from "react"; import { Internals as Internals41 } from "remotion"; import { jsx as jsx173 } from "react/jsx-runtime"; var commonPlaybackRates = [ -4, -2, -1, -0.5, -0.25, 0.25, 0.5, 1, 1.5, 2, 4 ]; var getPlaybackRateLabel = (playbackRate) => { return `${playbackRate}x`; }; var accessibilityLabel5 = "Change the playback rate"; var comboStyle2 = { width: 80 }; var PlaybackRateSelector = ({ playbackRate, setPlaybackRate }) => { const { canvasContent } = useContext52(Internals41.CompositionManager); const isStill = useIsStill(); const style9 = useMemo92(() => { return { padding: CONTROL_BUTTON_PADDING }; }, []); const items = useMemo92(() => { const divider = { type: "divider", id: "divider" }; const values = commonPlaybackRates.map((newPlaybackRate) => { return { id: String(newPlaybackRate), label: getPlaybackRateLabel(newPlaybackRate), onClick: () => { return setPlaybackRate(() => { persistPlaybackRate(newPlaybackRate); return newPlaybackRate; }); }, type: "item", value: newPlaybackRate, keyHint: null, leftItem: String(playbackRate) === String(newPlaybackRate) ? /* @__PURE__ */ jsx173(Checkmark, {}) : null, subMenu: null, quickSwitcherLabel: null }; }); const middle = Math.floor(commonPlaybackRates.length / 2); return [...values.slice(0, middle), divider, ...values.slice(middle)]; }, [playbackRate, setPlaybackRate]); if (isStill || canvasContent === null || canvasContent.type === "asset") { return null; } return /* @__PURE__ */ jsx173("div", { style: style9, "aria-label": accessibilityLabel5, children: /* @__PURE__ */ jsx173(Combobox, { title: accessibilityLabel5, style: comboStyle2, selectedId: String(playbackRate), values: items }) }); }; // src/components/RenderButton.tsx import { PlayerInternals as PlayerInternals14 } from "@remotion/player"; import { useCallback as useCallback88, useContext as useContext53, useMemo as useMemo93, useRef as useRef31, useState as useState59 } from "react"; import ReactDOM8 from "react-dom"; import { Internals as Internals42 } from "remotion"; import { jsx as jsx174, jsxs as jsxs81, Fragment as Fragment23 } from "react/jsx-runtime"; var splitButtonContainer = { display: "inline-flex", flexDirection: "row", alignItems: "stretch", borderRadius: 4, border: `1px solid ${INPUT_BORDER_COLOR_UNHOVERED}`, backgroundColor: INPUT_BACKGROUND, overflow: "hidden" }; var mainButtonStyle = { paddingLeft: 7, paddingRight: 7, paddingTop: 7, paddingBottom: 7, background: "transparent", border: "none", color: "white", cursor: "pointer", display: "flex", alignItems: "center", fontSize: 14, fontFamily: "inherit" }; var dividerStyle = { width: 1, backgroundColor: INPUT_BORDER_COLOR_UNHOVERED, alignSelf: "stretch" }; var dropdownTriggerStyle = { paddingLeft: 6, paddingRight: 6, paddingTop: 7, paddingBottom: 7, background: "transparent", border: "none", color: "white", cursor: "pointer", display: "flex", alignItems: "center" }; var mainButtonContent = { paddingLeft: 4, paddingRight: 6 }; var label7 = { fontSize: 14 }; var RENDER_TYPE_STORAGE_KEY = "remotion.renderType"; var getInitialRenderType = (readOnlyStudio) => { if (!SHOW_BROWSER_RENDERING) { return "server-render"; } if (readOnlyStudio) { return "client-render"; } try { const stored = localStorage.getItem(RENDER_TYPE_STORAGE_KEY); if (stored === "server-render" || stored === "client-render") { return stored; } } catch {} return "server-render"; }; var RenderButton = ({ readOnlyStudio }) => { const { inFrame, outFrame } = useTimelineInOutFramePosition(); const { setSelectedModal } = useContext53(ModalsContext); const [renderType, setRenderType] = useState59(() => getInitialRenderType(readOnlyStudio)); const [dropdownOpened, setDropdownOpened] = useState59(false); const dropdownRef = useRef31(null); const containerRef = useRef31(null); const { currentZIndex } = useZIndex(); const size4 = PlayerInternals14.useElementSize(dropdownRef, { triggerOnWindowResize: true, shouldApplyCssTransforms: true }); const refresh = size4?.refresh; const onPointerDown = useCallback88(() => { setDropdownOpened((o) => { if (!o) { refresh?.(); } return !o; }); }, [refresh]); const onClickDropdown = useCallback88((e) => { e.stopPropagation(); const isKeyboardInitiated = e.detail === 0; if (!isKeyboardInitiated) { return; } setDropdownOpened((o) => { if (!o) { refresh?.(); window.addEventListener("pointerup", (evt) => { if (!isMenuItem(evt.target)) { setDropdownOpened(false); } }, { once: true }); } return !o; }); }, [refresh]); const connectionStatus = useContext53(StudioServerConnectionCtx).previewServerState.type; const shortcut = areKeyboardShortcutsDisabled() ? "" : "(R)"; const tooltip = connectionStatus === "connected" ? "Export the current composition " + shortcut : "Connect to the Studio server to render"; const iconStyle4 = useMemo93(() => { return { style: { height: 16, color: "currentColor" } }; }, []); const video = Internals42.useVideo(); const getCurrentFrame2 = PlayerInternals14.useFrameImperative(); const { props } = useContext53(Internals42.EditorPropsContext); const openServerRenderModal = useCallback88(() => { if (!video) { return null; } const defaults = window.remotion_renderDefaults; if (!defaults) { throw new TypeError("Expected defaults"); } setSelectedModal({ type: "server-render", compositionId: video.id, initialFrame: getCurrentFrame2(), initialStillImageFormat: defaults.stillImageFormat, initialVideoImageFormat: null, initialJpegQuality: defaults.jpegQuality, initialScale: window.remotion_renderDefaults?.scale ?? 1, initialLogLevel: defaults.logLevel, initialConcurrency: defaults.concurrency, maxConcurrency: defaults.maxConcurrency, minConcurrency: defaults.minConcurrency, initialMuted: defaults.muted, initialEnforceAudioTrack: defaults.enforceAudioTrack, initialProResProfile: defaults.proResProfile, initialx264Preset: defaults.x264Preset, initialPixelFormat: null, initialAudioBitrate: defaults.audioBitrate, initialVideoBitrate: defaults.videoBitrate, initialEveryNthFrame: defaults.everyNthFrame, initialNumberOfGifLoops: defaults.numberOfGifLoops, initialDelayRenderTimeout: defaults.delayRenderTimeout, defaultConfigurationAudioCodec: defaults.audioCodec, initialEnvVariables: window.process.env, initialDisableWebSecurity: defaults.disableWebSecurity, initialDarkMode: defaults.darkMode, initialOpenGlRenderer: defaults.openGlRenderer, initialHeadless: defaults.headless, initialIgnoreCertificateErrors: defaults.ignoreCertificateErrors, initialOffthreadVideoCacheSizeInBytes: defaults.offthreadVideoCacheSizeInBytes, initialOffthreadVideoThreads: defaults.offthreadVideoThreads, defaultProps: props[video.id] ?? video.defaultProps, inFrameMark: inFrame, outFrameMark: outFrame, initialColorSpace: defaults.colorSpace, initialMultiProcessOnLinux: defaults.multiProcessOnLinux, defaultConfigurationVideoCodec: defaults.codec, initialEncodingBufferSize: defaults.encodingBufferSize, initialEncodingMaxRate: defaults.encodingMaxRate, initialUserAgent: defaults.userAgent, initialBeep: defaults.beepOnFinish, initialRepro: defaults.repro, initialForSeamlessAacConcatenation: defaults.forSeamlessAacConcatenation, renderTypeOfLastRender: null, defaulMetadata: defaults.metadata, initialHardwareAcceleration: defaults.hardwareAcceleration, initialChromeMode: defaults.chromeMode, initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes, renderDefaults: defaults }); }, [video, setSelectedModal, getCurrentFrame2, props, inFrame, outFrame]); const openClientRenderModal = useCallback88(() => { if (!video) { return null; } const defaults = window.remotion_renderDefaults; if (!defaults) { throw new TypeError("Expected defaults"); } setSelectedModal({ type: "web-render", compositionId: video.id, initialFrame: getCurrentFrame2(), defaultProps: props[video.id] ?? video.defaultProps, inFrameMark: inFrame, outFrameMark: outFrame, initialLogLevel: defaults.logLevel, initialLicenseKey: defaults.publicLicenseKey, initialStillImageFormat: defaults.stillImageFormat, initialScale: defaults.scale, initialDelayRenderTimeout: defaults.delayRenderTimeout, initialDefaultOutName: null, initialContainer: null, initialVideoCodec: null, initialAudioCodec: null, initialAudioBitrate: null, initialVideoBitrate: null, initialHardwareAcceleration: null, initialKeyframeIntervalInSeconds: null, initialTransparent: null, initialMuted: null, initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes }); }, [video, setSelectedModal, getCurrentFrame2, props, inFrame, outFrame]); const onClick = useCallback88(() => { if (!SHOW_BROWSER_RENDERING || renderType === "server-render") { openServerRenderModal(); } else { openClientRenderModal(); } }, [renderType, openServerRenderModal, openClientRenderModal]); const onHideDropdown = useCallback88(() => { setDropdownOpened(false); }, []); const handleRenderTypeChange = useCallback88((newType) => { setRenderType(newType); try { localStorage.setItem(RENDER_TYPE_STORAGE_KEY, newType); } catch {} setDropdownOpened(false); if (newType === "server-render") { openServerRenderModal(); } else { openClientRenderModal(); } }, [openServerRenderModal, openClientRenderModal]); const dropdownValues = useMemo93(() => { return [ { type: "item", id: "server-render", label: "Server-side render", value: "server-render", onClick: () => handleRenderTypeChange("server-render"), keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: null }, { type: "item", id: "client-render", label: "Client-side render", value: "client-render", onClick: () => handleRenderTypeChange("client-render"), keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: null } ]; }, [handleRenderTypeChange]); const spaceToBottom = useMemo93(() => { const margin2 = 10; if (size4 && dropdownOpened) { return size4.windowSize.height - (size4.top + size4.height) - margin2; } return 0; }, [dropdownOpened, size4]); const spaceToTop = useMemo93(() => { const margin2 = 10; if (size4 && dropdownOpened) { return size4.top - margin2; } return 0; }, [dropdownOpened, size4]); const derivedMaxHeight = useMemo93(() => { return spaceToTop > spaceToBottom ? spaceToTop : spaceToBottom; }, [spaceToBottom, spaceToTop]); const portalStyle = useMemo93(() => { if (!dropdownOpened || !size4) { return null; } const verticalLayout = spaceToTop > spaceToBottom ? "bottom" : "top"; return { ...verticalLayout === "top" ? { ...menuContainerTowardsBottom, top: size4.top + size4.height } : { ...menuContainerTowardsTop, bottom: size4.windowSize.height - size4.top }, right: size4.windowSize.width - size4.left - size4.width }; }, [dropdownOpened, size4, spaceToBottom, spaceToTop]); const containerStyle3 = useMemo93(() => { return { ...splitButtonContainer, borderColor: INPUT_BORDER_COLOR_UNHOVERED, opacity: connectionStatus !== "connected" ? 0.7 : 1, cursor: connectionStatus !== "connected" ? "inherit" : "pointer" }; }, [connectionStatus]); const renderLabel = renderType === "server-render" ? "Render" : "Render on web"; const shouldShowDropdown = useMemo93(() => { if (readOnlyStudio) { return false; } if (!SHOW_BROWSER_RENDERING) { return false; } return true; }, [readOnlyStudio]); if (!video) { return null; } return /* @__PURE__ */ jsxs81(Fragment23, { children: [ /* @__PURE__ */ jsx174("button", { style: { display: "none" }, id: "render-modal-button-server", disabled: connectionStatus !== "connected" && renderType === "server-render", onClick: openServerRenderModal, type: "button" }), " ", /* @__PURE__ */ jsx174("button", { style: { display: "none" }, id: "render-modal-button-client", onClick: openClientRenderModal, type: "button" }), /* @__PURE__ */ jsxs81("div", { ref: containerRef, style: containerStyle3, title: tooltip, children: [ /* @__PURE__ */ jsx174("button", { type: "button", style: mainButtonStyle, onClick, id: "render-modal-button", disabled: connectionStatus !== "connected" && renderType === "server-render", children: /* @__PURE__ */ jsxs81(Row, { align: "center", style: mainButtonContent, children: [ /* @__PURE__ */ jsx174(ThinRenderIcon, { fill: "currentcolor", svgProps: iconStyle4 }), /* @__PURE__ */ jsx174(Spacing, { x: 1 }), /* @__PURE__ */ jsx174("span", { style: label7, children: renderLabel }) ] }) }), shouldShowDropdown ? /* @__PURE__ */ jsxs81(Fragment23, { children: [ /* @__PURE__ */ jsx174("div", { style: dividerStyle }), /* @__PURE__ */ jsx174("button", { ref: dropdownRef, type: "button", style: dropdownTriggerStyle, disabled: connectionStatus !== "connected", className: MENU_INITIATOR_CLASSNAME, onPointerDown, onClick: onClickDropdown, children: /* @__PURE__ */ jsx174(CaretDown, {}) }) ] }) : null ] }), portalStyle ? ReactDOM8.createPortal(/* @__PURE__ */ jsx174("div", { style: fullScreenOverlay, children: /* @__PURE__ */ jsx174("div", { style: outerPortal, className: "css-reset", children: /* @__PURE__ */ jsx174(HigherZIndex, { onOutsideClick: onHideDropdown, onEscape: onHideDropdown, children: /* @__PURE__ */ jsx174("div", { style: portalStyle, children: /* @__PURE__ */ jsx174(MenuContent, { onNextMenu: () => {}, onPreviousMenu: () => {}, values: dropdownValues, onHide: onHideDropdown, leaveLeftSpace: false, preselectIndex: dropdownValues.findIndex((v) => v.id === renderType), topItemCanBeUnselected: false, fixedHeight: derivedMaxHeight }) }) }) }) }), getPortal(currentZIndex)) : null ] }); }; // src/components/Timeline/TimelineZoomControls.tsx import { useCallback as useCallback89, useContext as useContext54 } from "react"; import { Internals as Internals43 } from "remotion"; // src/icons/minus.tsx import { jsx as jsx175 } from "react/jsx-runtime"; var Minus = (props) => { return /* @__PURE__ */ jsx175("svg", { ...props, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 448 512", children: /* @__PURE__ */ jsx175("path", { fill: "currentColor", d: "M400 288h-352c-17.69 0-32-14.32-32-32.01s14.31-31.99 32-31.99h352c17.69 0 32 14.3 32 31.99S417.7 288 400 288z" }) }); }; // src/components/Timeline/TimelineZoomControls.tsx import { jsx as jsx176, jsxs as jsxs82 } from "react/jsx-runtime"; var container34 = { color: "black", flexDirection: "row", display: "flex", alignItems: "center" }; var buttonStyle3 = { fontSize: 24 }; var iconStyle4 = { color: "white", width: 14 }; var TimelineZoomControls = () => { const { canvasContent } = useContext54(Internals43.CompositionManager); const { setZoom, zoom: zoomMap } = useContext54(TimelineZoomCtx); const { tabIndex } = useZIndex(); const onMinusClicked = useCallback89(() => { if (canvasContent === null || canvasContent.type !== "composition") { return; } setZoom(canvasContent.compositionId, (z) => Math.max(TIMELINE_MIN_ZOOM, z - 0.2)); }, [canvasContent, setZoom]); const onPlusClicked = useCallback89(() => { if (canvasContent === null || canvasContent.type !== "composition") { return; } setZoom(canvasContent.compositionId, (z) => Math.min(TIMELINE_MAX_ZOOM, z + 0.2)); }, [canvasContent, setZoom]); const onChange = useCallback89((e) => { if (canvasContent === null || canvasContent.type !== "composition") { return; } setZoom(canvasContent.compositionId, () => Number(e.target.value)); }, [canvasContent, setZoom]); const isStill = useIsStill(); if (isStill || canvasContent === null || canvasContent.type !== "composition") { return null; } const zoom = zoomMap[canvasContent.compositionId] ?? TIMELINE_MIN_ZOOM; return /* @__PURE__ */ jsxs82("div", { style: container34, children: [ /* @__PURE__ */ jsx176(ControlButton, { onClick: onMinusClicked, style: buttonStyle3, title: "Zoom out timeline", role: "ControlButton", type: "button", disabled: TIMELINE_MIN_ZOOM === zoom, children: /* @__PURE__ */ jsx176(Minus, { style: iconStyle4 }) }), /* @__PURE__ */ jsx176(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx176("input", { title: `Timeline zoom (${zoom}x)`, alt: `Timeline zoom (${zoom}x)`, type: "range", min: TIMELINE_MIN_ZOOM, step: 0.1, value: zoom, max: TIMELINE_MAX_ZOOM, onChange, className: "__remotion-timeline-slider", tabIndex }), /* @__PURE__ */ jsx176(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx176(ControlButton, { onClick: onPlusClicked, style: buttonStyle3, title: "Zoom in timeline", role: "button", type: "button", disabled: TIMELINE_MAX_ZOOM === zoom, children: /* @__PURE__ */ jsx176(Plus, { color: "currentcolor", style: iconStyle4 }) }) ] }); }; // src/components/PreviewToolbar.tsx import { jsx as jsx177, jsxs as jsxs83, Fragment as Fragment24 } from "react/jsx-runtime"; var container35 = { display: "flex", justifyContent: "center", borderTop: "1px solid rgba(0, 0, 0, 0.5)", paddingTop: 2, paddingBottom: 2, alignItems: "center", flexDirection: "row", background: BACKGROUND }; var mobileContainer = { ...container35, position: "relative", overflowY: "auto", justifyContent: "flex-start" }; var scrollIndicatorLeft = { position: "fixed", display: "none", top: 0, left: 0, width: 40, height: "100%", pointerEvents: "none", background: `linear-gradient(to right, ${BACKGROUND}, ${BACKGROUND__TRANSPARENT})` }; var scrollIndicatorRight = { position: "fixed", display: "none", top: 0, right: 0, width: 40, height: "100%", pointerEvents: "none", background: `linear-gradient(to left, ${BACKGROUND}, ${BACKGROUND__TRANSPARENT})` }; var sideContainer = { width: 300, height: 38, display: "flex", flexDirection: "row", alignItems: "center" }; var padding2 = { width: TIMELINE_PADDING }; var PreviewToolbar = ({ readOnlyStudio, bufferStateDelayInMilliseconds }) => { const { playbackRate, setPlaybackRate } = useContext55(Internals44.TimelineContext); const { mediaMuted } = useContext55(Internals44.MediaVolumeContext); const { setMediaMuted } = useContext55(Internals44.SetMediaVolumeContext); const isVideoComposition = useIsVideoComposition(); const previewToolbarRef = useRef32(null); const leftScrollIndicatorRef = useRef32(null); const rightScrollIndicatorRef = useRef32(null); const isStill = useIsStill(); const [loop, setLoop] = useState60(loadLoopOption()); const isFullscreenSupported = checkFullscreenSupport(); const isMobileLayout = useMobileLayout(); useEffect58(() => { if (isMobileLayout && previewToolbarRef.current) { const updateScrollableIndicatorProps = (target) => { const boundingBox = target.getBoundingClientRect(); const { scrollLeft, scrollWidth, clientWidth } = target; const scrollRight = scrollWidth - clientWidth - scrollLeft; if (!leftScrollIndicatorRef.current || !rightScrollIndicatorRef.current) { return; } if (scrollLeft !== 0) { Object.assign(leftScrollIndicatorRef.current.style, { display: "block", height: `${boundingBox.height}px`, top: `${boundingBox.top}px`, left: `${boundingBox.left}px` }); } else { Object.assign(leftScrollIndicatorRef.current.style, { display: "none" }); } if (scrollRight !== 0) { const itemWidth = rightScrollIndicatorRef.current?.clientWidth || 0; Object.assign(rightScrollIndicatorRef.current.style, { display: "block", height: `${boundingBox.height}px`, top: `${boundingBox.top}px`, left: `${boundingBox.left + boundingBox.width - itemWidth}px` }); } else { Object.assign(rightScrollIndicatorRef.current.style, { display: "none" }); } }; const previewToolbar = previewToolbarRef.current; const scrollHandler = () => { updateScrollableIndicatorProps(previewToolbar); }; previewToolbar.addEventListener("scroll", scrollHandler); scrollHandler(); return () => { previewToolbar.removeEventListener("scroll", scrollHandler); }; } }); return /* @__PURE__ */ jsxs83("div", { ref: previewToolbarRef, style: isMobileLayout ? mobileContainer : container35, className: "css-reset", children: [ /* @__PURE__ */ jsx177("div", { ref: leftScrollIndicatorRef, style: scrollIndicatorLeft }), isMobileLayout ? null : /* @__PURE__ */ jsxs83(Fragment24, { children: [ /* @__PURE__ */ jsxs83("div", { style: sideContainer, children: [ /* @__PURE__ */ jsx177("div", { style: padding2 }), /* @__PURE__ */ jsx177(TimelineZoomControls, {}) ] }), /* @__PURE__ */ jsx177(Flex, {}), /* @__PURE__ */ jsx177(SizeSelector, {}), isStill || isVideoComposition ? /* @__PURE__ */ jsx177(PlaybackRateSelector, { setPlaybackRate, playbackRate }) : null ] }), isVideoComposition ? /* @__PURE__ */ jsxs83(Fragment24, { children: [ /* @__PURE__ */ jsx177(Spacing, { x: 2 }), /* @__PURE__ */ jsx177(PlayPause, { bufferStateDelayInMilliseconds, loop, playbackRate }), /* @__PURE__ */ jsx177(Spacing, { x: 2 }), /* @__PURE__ */ jsx177(LoopToggle, { loop, setLoop }), /* @__PURE__ */ jsx177(MuteToggle, { muted: mediaMuted, setMuted: setMediaMuted }), /* @__PURE__ */ jsx177(Spacing, { x: 2 }), /* @__PURE__ */ jsx177(TimelineInOutPointToggle, {}), /* @__PURE__ */ jsx177(Spacing, { x: 2 }) ] }) : null, /* @__PURE__ */ jsx177(CheckboardToggle, {}), /* @__PURE__ */ jsx177(Spacing, { x: 1 }), isFullscreenSupported && /* @__PURE__ */ jsx177(FullScreenToggle, {}), /* @__PURE__ */ jsx177(Flex, {}), isMobileLayout && /* @__PURE__ */ jsxs83(Fragment24, { children: [ /* @__PURE__ */ jsx177(Flex, {}), /* @__PURE__ */ jsx177(SizeSelector, {}), isStill || isVideoComposition ? /* @__PURE__ */ jsx177(PlaybackRateSelector, { setPlaybackRate, playbackRate }) : null ] }), /* @__PURE__ */ jsxs83("div", { style: sideContainer, children: [ /* @__PURE__ */ jsx177(Flex, {}), !isMobileLayout && /* @__PURE__ */ jsx177(FpsCounter, { playbackSpeed: playbackRate }), /* @__PURE__ */ jsx177(Spacing, { x: 2 }), shouldShowRenderButton(readOnlyStudio) ? /* @__PURE__ */ jsx177(RenderButton, { readOnlyStudio }) : null, /* @__PURE__ */ jsx177(Spacing, { x: 1.5 }) ] }), /* @__PURE__ */ jsx177(PlaybackKeyboardShortcutsManager, { setPlaybackRate }), /* @__PURE__ */ jsx177(PlaybackRatePersistor, {}), /* @__PURE__ */ jsx177("div", { ref: rightScrollIndicatorRef, style: scrollIndicatorRight }) ] }); }; // src/components/Splitter/SplitterContainer.tsx import { useMemo as useMemo94, useRef as useRef33, useState as useState61 } from "react"; // src/state/timeline.ts var localStorageKey4 = (id) => `remotion.editor.timelineFlex.${id}`; var persistTimelineFlex = (value, id) => { localStorage.setItem(localStorageKey4(id), String(value)); }; var loadTimelineFlex = (id) => { const item2 = localStorage.getItem(localStorageKey4(id)); return item2 ? parseFloat(item2) : null; }; var useTimelineFlex = (id) => { return [ loadTimelineFlex(id), (value) => persistTimelineFlex(value, id) ]; }; // src/components/Splitter/SplitterContext.tsx import React117 from "react"; var SplitterContext = React117.createContext({ flexValue: 1, ref: { current: null }, setFlexValue: () => { return; }, isDragging: { current: false }, orientation: "horizontal", maxFlex: 1, minFlex: 1, defaultFlex: 1, id: "--", persistFlex: () => { return; } }); // src/components/Splitter/SplitterContainer.tsx import { jsx as jsx178 } from "react/jsx-runtime"; var containerRow = { display: "flex", flexDirection: "row", flex: 1, height: "100%", width: "100%" }; var containerColumn = { display: "flex", flexDirection: "column", flex: 1, height: 0 }; var SplitterContainer = ({ orientation, children, defaultFlex, maxFlex, minFlex, id }) => { const [initialTimelineFlex, persistFlex] = useTimelineFlex(id); const [flexValue, setFlexValue] = useState61(initialTimelineFlex ?? defaultFlex); const ref = useRef33(null); const isDragging = useRef33(false); const value = useMemo94(() => { return { flexValue, ref, setFlexValue, isDragging, orientation, id, maxFlex, minFlex, defaultFlex, persistFlex }; }, [ defaultFlex, flexValue, id, maxFlex, minFlex, orientation, persistFlex, ref ]); return /* @__PURE__ */ jsx178(SplitterContext.Provider, { value, children: /* @__PURE__ */ jsx178("div", { ref, style: orientation === "horizontal" ? containerColumn : containerRow, children }) }); }; // src/components/Splitter/SplitterElement.tsx import { useContext as useContext56, useMemo as useMemo95 } from "react"; import { interpolateColors, random } from "remotion"; import { jsx as jsx179, jsxs as jsxs84, Fragment as Fragment25 } from "react/jsx-runtime"; var SplitterElement = ({ children, type, sticky }) => { const context = useContext56(SplitterContext); const style9 = useMemo95(() => { return { flex: (type === "flexer" ? context.flexValue : 1 - context.flexValue) * 1000, display: "flex", position: "relative", overflow: "hidden", flexDirection: "column" }; }, [context.flexValue, type]); const stickStyle = useMemo95(() => { return { position: "absolute", left: (type === "flexer" ? 0 : context.flexValue) * 100 + "%", width: (type === "flexer" ? context.flexValue : 1 - context.flexValue) * 100 + "%", backgroundColor: interpolateColors(random(context.flexValue), [0, 1], ["red", "blue"]) }; }, [context.flexValue, type]); return /* @__PURE__ */ jsxs84(Fragment25, { children: [ /* @__PURE__ */ jsx179("div", { style: style9, children }), /* @__PURE__ */ jsx179("div", { style: stickStyle, children: sticky ?? null }) ] }); }; // src/components/Splitter/SplitterHandle.tsx import { PlayerInternals as PlayerInternals15 } from "@remotion/player"; import { useContext as useContext57, useEffect as useEffect59, useRef as useRef34, useState as useState62 } from "react"; import { jsx as jsx180 } from "react/jsx-runtime"; var SPLITTER_HANDLE_SIZE = 3; var containerRow2 = { height: SPLITTER_HANDLE_SIZE }; var containerColumn2 = { width: SPLITTER_HANDLE_SIZE }; var SplitterHandle = ({ allowToCollapse, onCollapse }) => { const context = useContext57(SplitterContext); if (!context) { throw new Error("Cannot find splitter context"); } const [lastPointerUp, setLastPointerUp] = useState62(() => Date.now()); const ref = useRef34(null); useEffect59(() => { if (context.isDragging.current) { return; } const { current } = ref; if (!current) { return; } const getNewValue = (e, clamp) => { if (!context.isDragging.current) { throw new Error("cannot get value if not dragging"); } if (!context.ref.current) { throw new Error("domRect is not mounted"); } const { width, height } = context.ref.current.getBoundingClientRect(); const change = (() => { if (context.orientation === "vertical") { return (e.clientX - context.isDragging.current.x) / (width - SPLITTER_HANDLE_SIZE); } return (e.clientY - context.isDragging.current.y) / (height - SPLITTER_HANDLE_SIZE); })(); const newFlex = context.flexValue + change; if (clamp) { return Math.min(context.maxFlex, Math.max(context.minFlex, newFlex)); } return newFlex; }; const onPointerDown = (e) => { if (e.button !== 0) { return; } context.isDragging.current = { x: e.clientX, y: e.clientY }; ref.current?.classList.add("remotion-splitter-active"); window.addEventListener("pointerup", (ev) => { if (!context.isDragging.current) { return; } context.persistFlex(getNewValue(ev, true)); cleanup(); setLastPointerUp(Date.now()); }, { once: true }); window.addEventListener("pointermove", onPointerMove); }; const onPointerMove = (e) => { if (context.isDragging.current) { const val = getNewValue(e, true); context.setFlexValue(val); if (allowToCollapse === "left") { const unclamped = getNewValue(e, false); if (unclamped < context.minFlex / 2) { cleanup(); onCollapse(); setLastPointerUp(Date.now()); } } if (allowToCollapse === "right") { const unclamped = 1 - getNewValue(e, false); if (unclamped < (1 - context.maxFlex) / 2) { cleanup(); onCollapse(); setLastPointerUp(Date.now()); } } } }; const cleanup = () => { context.isDragging.current = false; ref.current?.classList.remove("remotion-splitter-active"); current.removeEventListener("pointerdown", onPointerDown); window.removeEventListener("pointermove", onPointerMove); PlayerInternals15.updateAllElementsSizes(); }; current.addEventListener("pointerdown", onPointerDown); return () => { if (!context.isDragging.current) { cleanup(); } }; }, [allowToCollapse, context, context.flexValue, lastPointerUp, onCollapse]); useEffect59(() => { const { current } = ref; if (!current) { return; } let isMouseDown = false; const onMouseDown = () => { isMouseDown = true; }; const onMouseUp = () => { isMouseDown = false; }; const onMouseEnter = (e) => { if (e.button !== 0) { return; } if (isMouseDown) { return; } current.classList.add("remotion-splitter-hover"); }; const onMouseLeave = (e) => { if (e.button !== 0) { return; } current.classList.remove("remotion-splitter-hover"); }; current.addEventListener("mouseenter", onMouseEnter); current.addEventListener("mouseleave", onMouseLeave); window.addEventListener("mousedown", onMouseDown); window.addEventListener("mouseup", onMouseUp); return () => { current.removeEventListener("mouseenter", onMouseEnter); current.removeEventListener("mouseleave", onMouseLeave); window.removeEventListener("mousedown", onMouseDown); window.removeEventListener("mouseup", onMouseUp); }; }, []); return /* @__PURE__ */ jsx180("div", { ref, className: [ "remotion-splitter", context.orientation === "horizontal" ? "remotion-splitter-horizontal" : "remotion-splitter-vertical" ].join(" "), style: context.orientation === "horizontal" ? containerRow2 : containerColumn2 }); }; // src/components/TopPanel.tsx import { jsx as jsx181, jsxs as jsxs85 } from "react/jsx-runtime"; var container36 = { height: "100%", width: "100%", display: "flex", flexDirection: "column", flex: 1 }; var row4 = { display: "flex", flexDirection: "row", flex: 1, minHeight: 0 }; var useResponsiveSidebarStatus = () => { const { sidebarCollapsedStateLeft } = useContext58(SidebarContext); const responsiveLeftStatus = useBreakpoint(1200) ? "collapsed" : "expanded"; const actualStateLeft = useMemo96(() => { if (sidebarCollapsedStateLeft === "collapsed") { return "collapsed"; } if (sidebarCollapsedStateLeft === "expanded") { return "expanded"; } return responsiveLeftStatus; }, [sidebarCollapsedStateLeft, responsiveLeftStatus]); return actualStateLeft; }; var TopPanel = ({ readOnlyStudio, onMounted, drawRef: drawRef2, bufferStateDelayInMilliseconds }) => { const { setSidebarCollapsedState, sidebarCollapsedStateRight } = useContext58(SidebarContext); const rulersAreVisible = useIsRulerVisible(); const actualStateLeft = useResponsiveSidebarStatus(); const actualStateRight = useMemo96(() => { if (sidebarCollapsedStateRight === "collapsed") { return "collapsed"; } return "expanded"; }, [sidebarCollapsedStateRight]); useEffect60(() => { onMounted(); }, [onMounted]); const canvasContainerStyle = useMemo96(() => ({ flex: 1, display: "flex", paddingTop: rulersAreVisible ? RULER_WIDTH : 0, paddingLeft: rulersAreVisible ? RULER_WIDTH : 0 }), [rulersAreVisible]); const onCollapseLeft = useCallback90(() => { setSidebarCollapsedState({ left: "collapsed", right: null }); }, [setSidebarCollapsedState]); const onCollapseRight = useCallback90(() => { setSidebarCollapsedState({ left: null, right: "collapsed" }); }, [setSidebarCollapsedState]); const isMobileLayout = useMobileLayout(); return /* @__PURE__ */ jsxs85("div", { style: container36, children: [ /* @__PURE__ */ jsx181("div", { style: row4, children: /* @__PURE__ */ jsxs85(SplitterContainer, { minFlex: 0.15, maxFlex: 0.4, defaultFlex: 0.2, id: "sidebar-to-preview", orientation: "vertical", children: [ actualStateLeft === "expanded" ? isMobileLayout ? /* @__PURE__ */ jsx181(MobilePanel, { onClose: onCollapseLeft, children: /* @__PURE__ */ jsx181(ExplorerPanel, { readOnlyStudio }) }) : /* @__PURE__ */ jsx181(SplitterElement, { sticky: null, type: "flexer", children: /* @__PURE__ */ jsx181(ExplorerPanel, { readOnlyStudio }) }) : null, actualStateLeft === "expanded" ? /* @__PURE__ */ jsx181(SplitterHandle, { allowToCollapse: "left", onCollapse: onCollapseLeft }) : null, /* @__PURE__ */ jsx181(SplitterElement, { sticky: null, type: "anti-flexer", children: /* @__PURE__ */ jsxs85(SplitterContainer, { minFlex: 0.5, maxFlex: 0.8, defaultFlex: 0.7, id: "canvas-to-right-sidebar", orientation: "vertical", children: [ /* @__PURE__ */ jsx181(SplitterElement, { sticky: null, type: "flexer", children: /* @__PURE__ */ jsx181("div", { ref: drawRef2, style: canvasContainerStyle, children: /* @__PURE__ */ jsx181(CanvasIfSizeIsAvailable, {}) }) }), actualStateRight === "expanded" ? /* @__PURE__ */ jsx181(SplitterHandle, { allowToCollapse: "right", onCollapse: onCollapseRight }) : null, actualStateRight === "expanded" ? isMobileLayout ? /* @__PURE__ */ jsx181(MobilePanel, { onClose: onCollapseRight, children: /* @__PURE__ */ jsx181(OptionsPanel, { readOnlyStudio }) }) : /* @__PURE__ */ jsx181(SplitterElement, { sticky: null, type: "anti-flexer", children: /* @__PURE__ */ jsx181(OptionsPanel, { readOnlyStudio }) }) : null ] }) }) ] }) }), /* @__PURE__ */ jsx181(PreviewToolbar, { bufferStateDelayInMilliseconds, readOnlyStudio }), /* @__PURE__ */ jsx181(CurrentCompositionKeybindings, { readOnlyStudio }), /* @__PURE__ */ jsx181(TitleUpdater, {}) ] }); }; // src/components/SidebarCollapserControls.tsx import { jsx as jsx182, jsxs as jsxs86, Fragment as Fragment26 } from "react/jsx-runtime"; var style9 = { width: 16, height: 16, minWidth: 16, border: "1px solid currentColor", borderRadius: 3, color: "currentColor", position: "relative" }; var SidebarCollapserControls = () => { const { setSidebarCollapsedState, sidebarCollapsedStateRight } = useContext59(SidebarContext); const keybindings = useKeybinding(); const leftSidebarStatus = useResponsiveSidebarStatus(); const leftIcon = useCallback91((color) => { return { width: "35%", height: "100%", borderRight: "1px solid " + color, background: leftSidebarStatus === "expanded" ? color : "transparent" }; }, [leftSidebarStatus]); const rightIcon = useCallback91((color) => { return { width: "35%", height: "100%", right: 0, position: "absolute", borderLeft: "1px solid " + color, background: sidebarCollapsedStateRight === "expanded" ? color : "transparent" }; }, [sidebarCollapsedStateRight]); const toggleLeft = useCallback91(() => { setSidebarCollapsedState({ left: (s) => { if (s === "responsive") { return leftSidebarStatus === "collapsed" ? "expanded" : "collapsed"; } return s === "collapsed" ? "expanded" : "collapsed"; }, right: null }); }, [leftSidebarStatus, setSidebarCollapsedState]); const toggleRight = useCallback91(() => { setSidebarCollapsedState({ right: (s) => s === "collapsed" ? "expanded" : "collapsed", left: null }); }, [setSidebarCollapsedState]); const toggleBoth = useCallback91(() => { if (sidebarCollapsedStateRight === leftSidebarStatus) { setSidebarCollapsedState({ left: (s) => { if (s === "responsive") { return leftSidebarStatus === "collapsed" ? "expanded" : "collapsed"; } return s === "collapsed" ? "expanded" : "collapsed"; }, right: (s) => s === "collapsed" ? "expanded" : "collapsed" }); } else if (sidebarCollapsedStateRight === "expanded") { toggleRight(); } else if (leftSidebarStatus === "expanded") { toggleLeft(); } }, [ leftSidebarStatus, setSidebarCollapsedState, sidebarCollapsedStateRight, toggleLeft, toggleRight ]); useEffect61(() => { const left3 = keybindings.registerKeybinding({ event: "keydown", key: "b", commandCtrlKey: true, callback: toggleLeft, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const right2 = keybindings.registerKeybinding({ event: "keydown", key: "j", commandCtrlKey: true, callback: toggleRight, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const zen = keybindings.registerKeybinding({ event: "keydown", key: "g", commandCtrlKey: true, callback: toggleBoth, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); return () => { left3.unregister(); right2.unregister(); zen.unregister(); }; }, [keybindings, toggleBoth, toggleLeft, toggleRight]); const toggleLeftTooltip = areKeyboardShortcutsDisabled() ? "Toggle Left Sidebar" : `Toggle Left Sidebar (${cmdOrCtrlCharacter}+B)`; const toggleRightTooltip = areKeyboardShortcutsDisabled() ? "Toggle Right Sidebar" : `Toggle Right Sidebar (${cmdOrCtrlCharacter}+J)`; const colorStyle = useCallback91((color) => { return { ...style9, color }; }, []); const toggleLeftAction = useCallback91((color) => { return /* @__PURE__ */ jsx182("div", { style: colorStyle(color), title: toggleLeftTooltip, children: /* @__PURE__ */ jsx182("div", { style: leftIcon(color) }) }); }, [colorStyle, leftIcon, toggleLeftTooltip]); const toggleRightAction = useCallback91((color) => { return /* @__PURE__ */ jsx182("div", { style: colorStyle(color), title: toggleRightTooltip, children: /* @__PURE__ */ jsx182("div", { style: rightIcon(color) }) }); }, [colorStyle, rightIcon, toggleRightTooltip]); return /* @__PURE__ */ jsxs86(Fragment26, { children: [ /* @__PURE__ */ jsx182(InlineAction, { onClick: toggleLeft, renderAction: toggleLeftAction }), /* @__PURE__ */ jsx182(InlineAction, { onClick: toggleRight, renderAction: toggleRightAction }) ] }); }; // src/components/UpdateCheck.tsx import { useCallback as useCallback92, useContext as useContext60, useEffect as useEffect62, useMemo as useMemo97, useState as useState63 } from "react"; import { VERSION } from "remotion"; import { jsx as jsx183 } from "react/jsx-runtime"; var buttonStyle4 = { appearance: "none", color: BLUE, border: "none", fontWeight: "bold", backgroundColor: "transparent", cursor: "pointer", fontSize: 14, display: "inline-flex", justifyContent: "center" }; var UpdateCheck = () => { const [info, setInfo] = useState63(null); const { setSelectedModal } = useContext60(ModalsContext); const { tabIndex } = useZIndex(); const [knownBugs, setKnownBugs] = useState63(null); const hasKnownBugs = useMemo97(() => { return knownBugs && knownBugs.length > 0; }, [knownBugs]); const checkForUpdates = useCallback92(() => { const controller = new AbortController; updateAvailable(controller.signal).then((d) => { setInfo(d); }).catch((err) => { if (err.message.includes("aborted")) { return; } console.log("Could not check for updates", err); }); return controller; }, []); const checkForBugs = useCallback92(() => { const controller = new AbortController; fetch(`https://bugs.remotion.dev/api/${VERSION}`, { signal: controller.signal }).then(async (res) => { const body = await res.json(); setKnownBugs(body.bugs); }).catch((err) => { if (err.message.includes("aborted")) { return; } console.log("Could not check for bugs in this version", err); }); return controller; }, []); useEffect62(() => { const abortUpdate = checkForUpdates(); const abortBugs = checkForBugs(); return () => { abortUpdate.abort(); abortBugs.abort(); }; }, [checkForBugs, checkForUpdates]); const openModal = useCallback92(() => { setSelectedModal({ type: "update", info, knownBugs }); }, [info, knownBugs, setSelectedModal]); const dynButtonStyle = useMemo97(() => { return { ...buttonStyle4, color: hasKnownBugs ? WARNING_COLOR : LIGHT_TEXT }; }, [hasKnownBugs]); if (!info) { return null; } if (!info.updateAvailable) { return null; } return /* @__PURE__ */ jsx183("button", { tabIndex, style: dynButtonStyle, onClick: openModal, type: "button", title: hasKnownBugs ? "Bugfixes available" : "Update available", children: hasKnownBugs ? "Bugfixes available" : /* @__PURE__ */ jsx183("svg", { xmlns: "http://www.w3.org/2000/svg", style: { height: 16, width: 16 }, viewBox: "0 0 512 512", children: /* @__PURE__ */ jsx183("path", { fill: "currentcolor", d: "M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM135.1 217.4c-4.5 4.2-7.1 10.1-7.1 16.3c0 12.3 10 22.3 22.3 22.3H208v96c0 17.7 14.3 32 32 32h32c17.7 0 32-14.3 32-32V256h57.7c12.3 0 22.3-10 22.3-22.3c0-6.2-2.6-12.1-7.1-16.3L269.8 117.5c-3.8-3.5-8.7-5.5-13.8-5.5s-10.1 2-13.8 5.5L135.1 217.4z" }) }) }); }; // src/components/MenuToolbar.tsx import { jsx as jsx184, jsxs as jsxs87 } from "react/jsx-runtime"; var row5 = { alignItems: "center", flexDirection: "row", display: "flex", color: "white", borderBottom: "1px solid black", fontSize: 13, paddingLeft: 6, paddingRight: 10, backgroundColor: BACKGROUND }; var flex2 = { flex: 1 }; var MenuToolbar = ({ readOnlyStudio }) => { const [selected, setSelected] = useState64(null); const mobileLayout = useMobileLayout(); const fixedWidthRight = useMemo98(() => { return { ...mobileLayout ? { width: "fit-content" } : { width: "330px" }, display: "flex", alignItems: "center", justifyContent: "flex-end" }; }, [mobileLayout]); const fixedWidthLeft = useMemo98(() => { return { ...mobileLayout ? { minWidth: "0px" } : { minWidth: "330px" }, display: "flex", alignItems: "center", justifyContent: "flex-start" }; }, [mobileLayout]); const itemClicked = useCallback93((itemId) => { setSelected(itemId); }, [setSelected]); const itemHovered = useCallback93((itemId) => { if (selected) { setSelected(itemId); } }, [selected, setSelected]); const closeMenu = useCallback93(() => { setSelected(null); }, []); const structure = useMenuStructure(closeMenu, readOnlyStudio); const menus = useMemo98(() => { return structure.map((s) => s.id); }, [structure]); const onPreviousMenu = useCallback93(() => { setSelected((s) => { if (s === null) { return null; } return menus[(menus.indexOf(s) + 1) % menus.length]; }); }, [menus]); const onNextMenu = useCallback93(() => { setSelected((s) => { if (s === null) { return null; } if (menus.indexOf(s) === 0) { return menus[menus.length - 1]; } return menus[(menus.indexOf(s) - 1) % menus.length]; }); }, [menus]); const onItemQuit = useCallback93(() => { setSelected(null); }, [setSelected]); return /* @__PURE__ */ jsxs87(Row, { align: "center", className: "css-reset", style: row5, children: [ /* @__PURE__ */ jsxs87("div", { style: fixedWidthLeft, children: [ structure.map((s) => { return /* @__PURE__ */ jsx184(MenuItem, { selected: selected === s.id, onItemSelected: itemClicked, onItemHovered: itemHovered, id: s.id, label: s.label, onItemQuit, menu: s, onPreviousMenu, onNextMenu, leaveLeftPadding: s.leaveLeftPadding }, s.id); }), readOnlyStudio ? null : /* @__PURE__ */ jsx184(UpdateCheck, {}) ] }), /* @__PURE__ */ jsx184("div", { style: flex2 }), /* @__PURE__ */ jsx184(MenuBuildIndicator, {}), /* @__PURE__ */ jsx184("div", { style: flex2 }), /* @__PURE__ */ jsx184("div", { style: fixedWidthRight, children: /* @__PURE__ */ jsx184(SidebarCollapserControls, {}) }), /* @__PURE__ */ jsx184(Spacing, { x: 1 }) ] }); }; // src/components/Timeline/Timeline.tsx import { useContext as useContext69, useMemo as useMemo109 } from "react"; import { Internals as Internals53 } from "remotion"; // src/helpers/get-sequence-visible-range.ts var getCascadedStart = (sequence, sequences) => { if (!sequence.parent) { return sequence.from; } const parent = sequences.find((s) => s.id === sequence.parent); if (!parent) { throw new TypeError("Parent not found for sequence " + sequence.id); } return getCascadedStart(parent, sequences) + sequence.from; }; var getTimelineVisibleStart = (sequence, sequences) => { const cascadedStart = Math.max(0, getCascadedStart(sequence, sequences)); if (!sequence.parent) { return cascadedStart; } const parent = sequences.find((s) => s.id === sequence.parent); if (!parent) { throw new TypeError("Parent not found for sequence " + sequence.id); } const timelineVisibleStart = getTimelineVisibleStart(parent, sequences); return Math.max(timelineVisibleStart, cascadedStart); }; var getTimelineVisibleDuration = (sequence, sequences) => { const visibleDuration = sequence.duration + Math.min(sequence.from, 0); if (!sequence.parent) { return visibleDuration; } const parent = sequences.find((s) => s.id === sequence.parent); if (!parent) { throw new TypeError("Parent not found for sequence " + sequence.id); } return Math.min(visibleDuration, getTimelineVisibleDuration(parent, sequences)); }; // src/helpers/get-timeline-nestedness.ts var getTimelineNestedLevel = (sequence, allSequences, depth) => { if (!sequence.parent) { return depth; } const parentSequence = allSequences.find((s) => s.id === sequence.parent); if (!parentSequence) { throw new Error("has parentId but no parent"); } return getTimelineNestedLevel(parentSequence, allSequences, depth + 1); }; // src/helpers/get-timeline-sequence-hash.ts var getTimelineSequenceHash = (sequence, allSequences, hashesUsedInRoot, cache) => { if (cache[sequence.id]) { return cache[sequence.id]; } const parent = allSequences.find((a) => a.id === sequence.parent); const baseHash = [ parent ? getTimelineSequenceHash(parent, allSequences, hashesUsedInRoot, cache) : null, sequence.displayName, sequence.duration, sequence.from, sequence.type, sequence.type === "audio" ? sequence.src : null, sequence.type === "audio" ? sequence.volume : null, sequence.type === "video" ? sequence.src : null, sequence.type === "video" ? sequence.volume : null ].join("-"); const actualHash = baseHash + hashesUsedInRoot[sequence.rootId].filter((h) => h === baseHash).length; hashesUsedInRoot[sequence.rootId].push(baseHash); cache[sequence.id] = actualHash; return actualHash; }; // src/helpers/get-timeline-sequence-sort-key.ts var getTimelineSequenceSequenceSortKey = (track, tracks, sameHashes = {}) => { const firstSequenceWithSameHash = tracks.find((t) => sameHashes[track.hash].includes(t.sequence.id)); const id = String(firstSequenceWithSameHash.sequence.nonce).padStart(6, "0"); if (!track.sequence.parent) { return id; } const parent = tracks.find((t) => t.sequence.id === track.sequence.parent); if (!parent) { return id; } const firstParentWithSameHash = tracks.find((a) => { return sameHashes[parent.hash].includes(a.sequence.id); }); if (!firstParentWithSameHash) { throw new Error("could not find parent: " + track.sequence.parent); } return `${getTimelineSequenceSequenceSortKey(firstParentWithSameHash, tracks, sameHashes)}-${id}`; }; // src/helpers/calculate-timeline.ts var calculateTimeline = ({ sequences, sequenceDuration }) => { const tracks = []; if (sequences.length === 0) { return [ { sequence: { displayName: "", duration: sequenceDuration, from: 0, id: "seq", parent: null, type: "sequence", rootId: "-", showInTimeline: true, nonce: 0, loopDisplay: undefined, stack: null, premountDisplay: null, postmountDisplay: null }, depth: 0, hash: "-" } ]; } const sameHashes = {}; const hashesUsedInRoot = {}; const cache = {}; for (let i = 0;i < sequences.length; i++) { const sequence = sequences[i]; if (!hashesUsedInRoot[sequence.rootId]) { hashesUsedInRoot[sequence.rootId] = []; } const actualHash = getTimelineSequenceHash(sequence, sequences, hashesUsedInRoot, cache); if (!sameHashes[actualHash]) { sameHashes[actualHash] = []; } sameHashes[actualHash].push(sequence.id); const cascadedStart = getCascadedStart(sequence, sequences); const visibleStart = getTimelineVisibleStart(sequence, sequences); const visibleDuration = getTimelineVisibleDuration(sequence, sequences); tracks.push({ sequence: { ...sequence, from: visibleStart, duration: visibleDuration }, depth: getTimelineNestedLevel(sequence, sequences, 0), hash: actualHash, cascadedStart, cascadedDuration: sequence.duration }); } const uniqueTracks = []; for (const track of tracks) { if (!uniqueTracks.find((t) => t.hash === track.hash)) { const { cascadedDuration, cascadedStart, ...cleanTrack } = track; uniqueTracks.push(cleanTrack); } } return uniqueTracks.sort((a, b) => { const sortKeyA = getTimelineSequenceSequenceSortKey(a, tracks, sameHashes); const sortKeyB = getTimelineSequenceSequenceSortKey(b, tracks, sameHashes); return sortKeyA.localeCompare(sortKeyB); }); }; // src/components/Timeline/MaxTimelineTracks.tsx import { DEFAULT_TIMELINE_TRACKS } from "@remotion/studio-shared"; import { jsxs as jsxs88 } from "react/jsx-runtime"; var MAX_TIMELINE_TRACKS = typeof process.env.MAX_TIMELINE_TRACKS === "undefined" || process.env.MAX_TIMELINE_TRACKS === null ? DEFAULT_TIMELINE_TRACKS : Number(process.env.MAX_TIMELINE_TRACKS); var MAX_TIMELINE_TRACKS_NOTICE_HEIGHT = 24; var container37 = { height: MAX_TIMELINE_TRACKS_NOTICE_HEIGHT, display: "flex", alignItems: "center", color: "rgba(255, 255, 255, 0.6)", fontFamily: "sans-serif", fontSize: 12, backgroundColor: "rgba(255, 255, 255, 0.1)", paddingLeft: TIMELINE_PADDING + 5 }; var MaxTimelineTracksReached = () => { return /* @__PURE__ */ jsxs88("div", { style: container37, children: [ "Limited display to ", MAX_TIMELINE_TRACKS, " tracks to sustain performance.", "", "You can change this by setting Config.setMaxTimelineTracks() in your remotion.config.ts file." ] }); }; // src/components/Timeline/TimelineDragHandler.tsx import { PlayerInternals as PlayerInternals16 } from "@remotion/player"; import { useCallback as useCallback94, useContext as useContext63, useEffect as useEffect63, useMemo as useMemo100, useRef as useRef35, useState as useState65 } from "react"; import { Internals as Internals46, useVideoConfig as useVideoConfig4 } from "remotion"; // src/components/Timeline/TimelineInOutPointer.tsx import { createRef as createRef10, useContext as useContext61 } from "react"; import { Internals as Internals45 } from "remotion"; import { jsx as jsx185, jsxs as jsxs89, Fragment as Fragment27 } from "react/jsx-runtime"; var areaHighlight = { position: "absolute", backgroundColor: "rgba(0, 0, 0, 0.5)", height: "100%", bottom: 0, top: 0 }; var inMarkerAreaRef = createRef10(); var outMarkerAreaRef = createRef10(); var TimelineInOutPointer = () => { const { inFrame, outFrame } = useTimelineInOutFramePosition(); const videoConfig = Internals45.useUnsafeVideoConfig(); const timelineWidth = useContext61(TimelineWidthContext); if (!videoConfig || timelineWidth === null) { return null; } return /* @__PURE__ */ jsxs89(Fragment27, { children: [ inFrame !== null && /* @__PURE__ */ jsx185("div", { ref: inMarkerAreaRef, style: { ...areaHighlight, left: 0, width: getXPositionOfItemInTimelineImperatively(inFrame, videoConfig.durationInFrames, timelineWidth) } }), outFrame !== null && /* @__PURE__ */ jsx185("div", { ref: outMarkerAreaRef, style: { ...areaHighlight, left: getXPositionOfItemInTimelineImperatively(outFrame, videoConfig.durationInFrames, timelineWidth), width: timelineWidth - getXPositionOfItemInTimelineImperatively(outFrame, videoConfig.durationInFrames, timelineWidth) } }) ] }); }; // src/components/Timeline/TimelineInOutPointerHandle.tsx import { createRef as createRef11, useContext as useContext62, useMemo as useMemo99 } from "react"; import { useVideoConfig as useVideoConfig3 } from "remotion"; import { jsx as jsx186 } from "react/jsx-runtime"; var line3 = { height: "100%", width: 1, position: "absolute", backgroundColor: "rgba(255, 255, 255, 0.1)", cursor: "ew-resize", paddingLeft: 1, paddingRight: 1 }; var inPointerHandle = createRef11(); var outPointerHandle = createRef11(); var InnerTimelineInOutPointerHandle = ({ atFrame, dragging, timelineWidth, type }) => { const videoConfig = useVideoConfig3(); const style10 = useMemo99(() => { return { ...line3, backgroundColor: dragging ? LIGHT_TRANSPARENT : "rgba(255, 255, 255, 0.1)", transform: `translateX(${getXPositionOfItemInTimelineImperatively(atFrame, videoConfig.durationInFrames, timelineWidth)}px)` }; }, [atFrame, dragging, timelineWidth, videoConfig.durationInFrames]); return /* @__PURE__ */ jsx186("div", { ref: type === "in" ? inPointerHandle : outPointerHandle, style: style10 }); }; var TimelineInOutPointerHandle = ({ dragging, type, atFrame }) => { const timelineWidth = useContext62(TimelineWidthContext); if (timelineWidth === null) { return null; } return /* @__PURE__ */ jsx186(InnerTimelineInOutPointerHandle, { atFrame, dragging, timelineWidth, type }); }; // src/components/Timeline/TimelineDragHandler.tsx import { jsx as jsx187, jsxs as jsxs90 } from "react/jsx-runtime"; var inner = { overflowY: "auto", overflowX: "hidden" }; var container38 = { userSelect: "none", WebkitUserSelect: "none", position: "absolute", height: "100%", top: 0 }; var style10 = { width: "100%", height: "100%", userSelect: "none", WebkitUserSelect: "none" }; var getClientXWithScroll = (x) => { return x + scrollableRef.current?.scrollLeft; }; var TimelineDragHandler = () => { const video = Internals46.useUnsafeVideoConfig(); const { zoom: zoomMap } = useContext63(TimelineZoomCtx); const { canvasContent } = useContext63(Internals46.CompositionManager); const containerStyle3 = useMemo100(() => { if (!canvasContent || canvasContent.type !== "composition") { return {}; } const zoom = zoomMap[canvasContent.compositionId] ?? TIMELINE_MIN_ZOOM; return { ...container38, width: 100 * zoom + "%" }; }, [canvasContent, zoomMap]); if (!canvasContent || canvasContent.type !== "composition") { return null; } return /* @__PURE__ */ jsx187("div", { ref: sliderAreaRef, style: containerStyle3, children: video ? /* @__PURE__ */ jsx187(Inner2, {}) : null }); }; var Inner2 = () => { const videoConfig = useVideoConfig4(); const size4 = PlayerInternals16.useElementSize(scrollableRef, { triggerOnWindowResize: true, shouldApplyCssTransforms: true }); const { isHighestContext } = useZIndex(); const setFrame = Internals46.useTimelineSetFrame(); const [inOutDragging, setInOutDragging] = useState65({ dragging: false }); const timelineWidth = useContext63(TimelineWidthContext); const get = useCallback94((frame2) => { if (timelineWidth === null) { throw new Error("timeline width is not yet determined"); } return getXPositionOfItemInTimelineImperatively(frame2, videoConfig.durationInFrames, timelineWidth); }, [timelineWidth, videoConfig.durationInFrames]); const width = scrollableRef.current?.scrollWidth ?? 0; const left3 = size4?.left ?? 0; const { inFrame, outFrame } = useTimelineInOutFramePosition(); const { setInAndOutFrames } = useTimelineSetInOutFramePosition(); const [dragging, setDragging] = useState65({ dragging: false }); const { playing, play, pause, seek } = PlayerInternals16.usePlayer(); const scroller = useRef35(null); const stopInterval = () => { if (scroller.current) { clearInterval(scroller.current); scroller.current = null; } }; const onPointerDown = useCallback94((e) => { if (e.button !== 0) { return; } if (!isHighestContext) { return; } stopInterval(); if (!videoConfig) { return; } if (e.target === inPointerHandle.current) { if (inFrame === null) { throw new Error("expected outframe"); } const inMarker = get(inFrame); const outMarker = outFrame === null ? Infinity : get(outFrame - 1); setInOutDragging({ dragging: "in", initialOffset: getClientXWithScroll(e.clientX), boundaries: [-Infinity, outMarker - inMarker] }); return; } if (e.target === outPointerHandle.current) { if (outFrame === null) { throw new Error("expected outframe"); } const outMarker = get(outFrame); const inMarker = inFrame === null ? -Infinity : get(inFrame + 1); setInOutDragging({ dragging: "out", initialOffset: getClientXWithScroll(e.clientX), boundaries: [inMarker - outMarker, Infinity] }); return; } if (e.button !== 0) { return; } const frame2 = getFrameFromX({ clientX: getClientXWithScroll(e.clientX) - left3, durationInFrames: videoConfig.durationInFrames, width, extrapolate: "clamp" }); seek(frame2); setDragging({ dragging: true, wasPlaying: playing }); pause(); }, [ isHighestContext, videoConfig, left3, width, seek, playing, pause, inFrame, get, outFrame ]); const onPointerMoveScrubbing = useCallback94((e) => { if (!videoConfig) { return; } if (!dragging.dragging) { return; } const isRightOfArea = e.clientX >= scrollableRef.current?.clientWidth + left3 - TIMELINE_PADDING; const isLeftOfArea = e.clientX <= left3; const frame2 = getFrameFromX({ clientX: getClientXWithScroll(e.clientX) - left3, durationInFrames: videoConfig.durationInFrames, width, extrapolate: "clamp" }); if (isLeftOfArea && canScrollTimelineIntoDirection().canScrollLeft) { if (scroller.current) { return; } const scrollEvery = () => { if (!canScrollTimelineIntoDirection().canScrollLeft) { stopInterval(); return; } const nextFrame = getFrameWhileScrollingLeft({ durationInFrames: videoConfig.durationInFrames, width }); const scrollPos = getScrollPositionForCursorOnLeftEdge({ nextFrame, durationInFrames: videoConfig.durationInFrames }); redrawTimelineSliderFast.current?.draw(nextFrame); seek(nextFrame); scrollToTimelineXOffset(scrollPos); }; scrollEvery(); scroller.current = setInterval(() => { scrollEvery(); }, 100); } else if (isRightOfArea && canScrollTimelineIntoDirection().canScrollRight) { if (scroller.current) { return; } const scrollEvery = () => { if (!canScrollTimelineIntoDirection().canScrollRight) { stopInterval(); return; } const nextFrame = getFrameWhileScrollingRight({ durationInFrames: videoConfig.durationInFrames, width }); const scrollPos = getScrollPositionForCursorOnRightEdge({ nextFrame, durationInFrames: videoConfig.durationInFrames }); redrawTimelineSliderFast.current?.draw(nextFrame); seek(nextFrame); scrollToTimelineXOffset(scrollPos); }; scrollEvery(); scroller.current = setInterval(() => { scrollEvery(); }, 100); } else { stopInterval(); seek(frame2); } }, [videoConfig, dragging.dragging, left3, width, seek]); const onPointerMoveInOut = useCallback94((e) => { if (!videoConfig) { return; } if (!inOutDragging.dragging) { return; } const offset = Math.max(inOutDragging.boundaries[0], Math.min(inOutDragging.boundaries[1], getClientXWithScroll(e.clientX) - inOutDragging.initialOffset)); if (inOutDragging.dragging === "in") { if (!inPointerHandle.current) { throw new Error("in pointer handle"); } if (!inMarkerAreaRef.current) { throw new Error("expected inMarkerAreaRef"); } if (!inFrame) { throw new Error("expected inframes"); } inPointerHandle.current.style.transform = `translateX(${get(inFrame) + offset}px)`; inMarkerAreaRef.current.style.width = String(get(inFrame) + offset) + "px"; } if (inOutDragging.dragging === "out") { if (!outPointerHandle.current) { throw new Error("in pointer handle"); } if (!outMarkerAreaRef.current) { throw new Error("in outMarkerAreaRef"); } if (!outFrame) { throw new Error("expected outframes"); } outPointerHandle.current.style.transform = `translateX(${get(outFrame) + offset}px)`; outMarkerAreaRef.current.style.left = String(get(outFrame) + offset) + "px"; outMarkerAreaRef.current.style.width = String(width - get(outFrame) - offset) + "px"; } }, [get, inFrame, inOutDragging, outFrame, videoConfig, width]); const onPointerUpScrubbing = useCallback94((e) => { stopInterval(); if (!videoConfig) { return; } if (!dragging.dragging) { return; } setDragging({ dragging: false }); const frame2 = getFrameFromX({ clientX: getClientXWithScroll(e.clientX) - left3, durationInFrames: videoConfig.durationInFrames, width, extrapolate: "clamp" }); setFrame((c) => { const newObj = { ...c, [videoConfig.id]: frame2 }; Internals46.persistCurrentFrame(newObj); return newObj; }); if (dragging.wasPlaying) { play(); } }, [dragging, left3, play, videoConfig, setFrame, width]); const onPointerUpInOut = useCallback94((e) => { if (!videoConfig) { return; } if (!inOutDragging.dragging) { return; } setInOutDragging({ dragging: false }); const frame2 = getFrameFromX({ clientX: getClientXWithScroll(e.clientX) - left3, durationInFrames: videoConfig.durationInFrames, width, extrapolate: "extend" }); if (inOutDragging.dragging === "in") { if (frame2 < 1) { return setInAndOutFrames((prev) => ({ ...prev, [videoConfig.id]: { ...prev[videoConfig.id] ?? defaultInOutValue, inFrame: null } })); } const maxFrame = outFrame === null ? Infinity : outFrame - 1; setInAndOutFrames((prev) => ({ ...prev, [videoConfig.id]: { ...prev[videoConfig.id] ?? defaultInOutValue, inFrame: Math.min(maxFrame, frame2) } })); } else { if (frame2 > videoConfig.durationInFrames - 2) { return setInAndOutFrames((prev) => ({ ...prev, [videoConfig.id]: { ...prev[videoConfig.id] ?? defaultInOutValue, outFrame: null } })); } const minFrame = inFrame === null ? -Infinity : inFrame + 1; setInAndOutFrames((prev) => ({ ...prev, [videoConfig.id]: { ...prev[videoConfig.id] ?? defaultInOutValue, outFrame: Math.max(minFrame, frame2) } })); } }, [ inFrame, inOutDragging.dragging, left3, outFrame, setInAndOutFrames, videoConfig, width ]); useEffect63(() => { if (!dragging.dragging) { return; } window.addEventListener("pointermove", onPointerMoveScrubbing); window.addEventListener("pointerup", onPointerUpScrubbing); return () => { window.removeEventListener("pointermove", onPointerMoveScrubbing); window.removeEventListener("pointerup", onPointerUpScrubbing); }; }, [dragging.dragging, onPointerMoveScrubbing, onPointerUpScrubbing]); useEffect63(() => { if (inOutDragging.dragging === false) { return; } window.addEventListener("pointermove", onPointerMoveInOut); window.addEventListener("pointerup", onPointerUpInOut); return () => { window.removeEventListener("pointermove", onPointerMoveInOut); window.removeEventListener("pointerup", onPointerUpInOut); }; }, [inOutDragging.dragging, onPointerMoveInOut, onPointerUpInOut]); const inContextMenu = useMemo100(() => { return [ { id: "hide-in", keyHint: null, label: "Clear In marker", leftItem: null, onClick: (_, e) => { e?.stopPropagation(); e?.preventDefault(); setInAndOutFrames((prev) => ({ ...prev, [videoConfig.id]: { ...prev[videoConfig.id] ?? defaultInOutValue, inFrame: null } })); }, quickSwitcherLabel: null, subMenu: null, type: "item", value: "hide-in" } ]; }, [setInAndOutFrames, videoConfig.id]); const outContextMenu = useMemo100(() => { return [ { id: "hide-out", keyHint: null, label: "Clear Out marker", leftItem: null, onClick: (_, e) => { e?.stopPropagation(); e?.preventDefault(); setInAndOutFrames((prev) => ({ ...prev, [videoConfig.id]: { ...prev[videoConfig.id] ?? defaultInOutValue, outFrame: null } })); }, quickSwitcherLabel: null, subMenu: null, type: "item", value: "hide-out" } ]; }, [setInAndOutFrames, videoConfig.id]); return /* @__PURE__ */ jsxs90("div", { style: style10, onPointerDown, children: [ /* @__PURE__ */ jsx187("div", { style: inner, className: VERTICAL_SCROLLBAR_CLASSNAME }), inFrame !== null && /* @__PURE__ */ jsx187(ContextMenu, { values: inContextMenu, children: /* @__PURE__ */ jsx187(TimelineInOutPointerHandle, { type: "in", atFrame: inFrame, dragging: inOutDragging.dragging === "in" }) }), outFrame !== null && /* @__PURE__ */ jsx187(ContextMenu, { values: outContextMenu, children: /* @__PURE__ */ jsx187(TimelineInOutPointerHandle, { type: "out", dragging: inOutDragging.dragging === "out", atFrame: outFrame }) }) ] }); }; // src/components/Timeline/TimelineList.tsx import { PlayerInternals as PlayerInternals18 } from "@remotion/player"; import { useRef as useRef38 } from "react"; // src/components/Timeline/TimelineListItem.tsx import { useCallback as useCallback97, useContext as useContext65, useMemo as useMemo102 } from "react"; import { Internals as Internals47 } from "remotion"; // src/components/Timeline/TimelineLayerEye.tsx import { useCallback as useCallback95 } from "react"; import { jsx as jsx188 } from "react/jsx-runtime"; var eyeIcon = { width: 12, color: "currentColor", pointerEvents: "none" }; var speakerIcon = { ...eyeIcon, height: 10, marginLeft: -1 }; var container39 = { height: 16, width: 16, borderRadius: 2, backgroundColor: "rgba(0, 0, 0, 0.4)", display: "inline-flex", justifyContent: "center", alignItems: "center", marginRight: 6, flexShrink: 0 }; var layerPointedDown = null; var TimelineLayerEye = ({ onInvoked, hidden, type }) => { const renderAction = useCallback95((color) => { if (hidden) { return null; } if (type === "speaker") { return /* @__PURE__ */ jsx188("svg", { viewBox: "0 0 10 14", fill: "none", style: speakerIcon, children: /* @__PURE__ */ jsx188("path", { d: "M9.40938 0.0869018C9.76875 0.249402 10 0.605652 10 0.999402V12.9994C10 13.3932 9.76875 13.7494 9.40938 13.9119C9.05 14.0744 8.62813 14.0088 8.33438 13.7463L4.11875 9.9994H2C0.896875 9.9994 0 9.10253 0 7.9994V5.9994C0 4.89628 0.896875 3.9994 2 3.9994H4.11875L8.33438 0.252527C8.62813 -0.0099732 9.05 -0.0724732 9.40938 0.0869018Z", fill: color }) }); } return /* @__PURE__ */ jsx188("svg", { style: eyeIcon, viewBox: "0 0 24 16", fill: "none", children: /* @__PURE__ */ jsx188("path", { d: "M24 7.551C24 7.551 19.748 16 12.015 16C4.835 16 0 7.551 0 7.551C0 7.551 4.446 0 12.015 0C19.709 0 24 7.551 24 7.551ZM17 8C17 5.243 14.757 3 12 3C9.243 3 7 5.243 7 8C7 10.757 9.243 13 12 13C14.757 13 17 10.757 17 8Z", fill: color }) }); }, [hidden, type]); const onPointerDown = useCallback95((e) => { if (e.button !== 0) { return; } layerPointedDown = hidden ? "enable" : "disable"; onInvoked(layerPointedDown); window.addEventListener("pointerup", () => { layerPointedDown = null; }, { once: true }); }, [hidden, onInvoked]); const onPointerEnter = useCallback95(() => { if (layerPointedDown) { onInvoked(layerPointedDown); } }, [onInvoked]); return /* @__PURE__ */ jsx188("div", { style: container39, onPointerEnter, onPointerDown, children: renderAction(LIGHT_COLOR) }); }; // src/components/Timeline/TimelineStack/index.tsx import { SOURCE_MAP_ENDPOINT } from "@remotion/studio-shared"; import { useCallback as useCallback96, useContext as useContext64, useEffect as useEffect64, useMemo as useMemo101, useState as useState66 } from "react"; import { SourceMapConsumer as SourceMapConsumer3 } from "source-map"; import { jsx as jsx189, jsxs as jsxs91, Fragment as Fragment28 } from "react/jsx-runtime"; var publicPath = window.remotion_publicPath === "/" ? "" : window.remotion_publicPath; var withoutSlashInTheEnd = publicPath.endsWith("/") ? publicPath.slice(0, -1) : publicPath; SourceMapConsumer3.initialize({ "lib/mappings.wasm": withoutSlashInTheEnd + SOURCE_MAP_ENDPOINT }); var TimelineStack = ({ isCompact, sequence }) => { const [originalLocation, setOriginalLocation] = useState66(null); const [stackHovered, setStackHovered] = useState66(false); const [titleHovered, setTitleHovered] = useState66(false); const [opening, setOpening] = useState66(false); const selectAsset = useSelectAsset(); const connectionStatus = useContext64(StudioServerConnectionCtx).previewServerState.type; const assetPath = useMemo101(() => { if (sequence.type !== "video" && sequence.type !== "audio") { return null; } const isStatic = sequence.src.startsWith(window.remotion_staticBase); if (!isStatic) { return null; } const relativePath = sequence.src.replace(window.remotion_staticBase + "/", ""); return relativePath; }, [sequence]); const navigateToAsset = useCallback96((asset) => { selectAsset(asset); pushUrl(`/assets/${asset}`); }, [selectAsset]); const openEditor = useCallback96(async (location2) => { if (!window.remotion_editorName) { return; } setOpening(true); try { await openOriginalPositionInEditor(location2); } catch (err) { showNotification(err.message, 2000); } finally { setOpening(false); } }, []); const canOpenInEditor = window.remotion_editorName && connectionStatus === "connected" && originalLocation; const canOpenInGitHub = window.remotion_gitSource && originalLocation; const titleHoverable = isCompact && (canOpenInEditor || canOpenInGitHub) || assetPath; const stackHoverable = !isCompact && (canOpenInEditor || canOpenInGitHub); const onClickTitle = useCallback96(() => { if (!titleHoverable) { return null; } if (assetPath) { navigateToAsset(assetPath); return; } if (!originalLocation) { return; } if (canOpenInEditor) { openEditor(originalLocation); return; } if (canOpenInGitHub) { window.open(getGitRefUrl(window.remotion_gitSource, originalLocation), "_blank"); } }, [ assetPath, canOpenInEditor, canOpenInGitHub, navigateToAsset, openEditor, originalLocation, titleHoverable ]); const onClickStack = useCallback96(() => { if (!originalLocation) { return; } if (canOpenInEditor) { openEditor(originalLocation); return; } if (canOpenInGitHub) { window.open(getGitRefUrl(window.remotion_gitSource, originalLocation), "_blank"); } }, [canOpenInEditor, canOpenInGitHub, openEditor, originalLocation]); useEffect64(() => { if (!sequence.stack) { return; } getOriginalLocationFromStack(sequence.stack, "sequence").then((frame2) => { setOriginalLocation(frame2); }).catch((err) => { console.error("Could not get original location of Sequence", err); }); }, [sequence.stack]); const onStackPointerEnter = useCallback96(() => { setStackHovered(true); }, []); const onStackPointerLeave = useCallback96(() => { setStackHovered(false); }, []); const onTitlePointerEnter = useCallback96(() => { setTitleHovered(true); }, []); const onTitlePointerLeave = useCallback96(() => { setTitleHovered(false); }, []); const style11 = useMemo101(() => { return { fontSize: 12, color: opening ? VERY_LIGHT_TEXT : stackHovered && stackHoverable ? LIGHT_TEXT : VERY_LIGHT_TEXT, marginLeft: 10, cursor: stackHoverable ? "pointer" : undefined, display: "flex", flexDirection: "row", alignItems: "center", whiteSpace: "nowrap", textOverflow: "ellipsis", overflow: "hidden", flexShrink: 1e5 }; }, [opening, stackHovered, stackHoverable]); const titleStyle2 = useMemo101(() => { const hoverEffect = titleHovered && titleHoverable; return { fontSize: 12, whiteSpace: "nowrap", textOverflow: "ellipsis", overflow: "hidden", lineHeight: 1, color: opening && isCompact ? VERY_LIGHT_TEXT : LIGHT_COLOR, userSelect: "none", WebkitUserSelect: "none", borderBottom: hoverEffect ? "1px solid #fff" : "none", cursor: hoverEffect ? "pointer" : undefined }; }, [titleHoverable, isCompact, opening, titleHovered]); const text = sequence.displayName.length > 1000 ? sequence.displayName.slice(0, 1000) + "..." : sequence.displayName; return /* @__PURE__ */ jsxs91(Fragment28, { children: [ /* @__PURE__ */ jsx189("div", { onPointerEnter: onTitlePointerEnter, onPointerLeave: onTitlePointerLeave, title: originalLocation ? getOriginalSourceAttribution(originalLocation) : text || "", style: titleStyle2, onClick: onClickTitle, children: text || "" }), isCompact || !originalLocation ? null : /* @__PURE__ */ jsx189("div", { onPointerEnter: onStackPointerEnter, onPointerLeave: onStackPointerLeave, onClick: onClickStack, style: style11, children: getOriginalSourceAttribution(originalLocation) }), opening ? /* @__PURE__ */ jsxs91(Fragment28, { children: [ /* @__PURE__ */ jsx189(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx189(Spinner, { duration: 0.5, size: 12 }) ] }) : null ] }); }; // src/components/Timeline/TimelineListItem.tsx import { jsx as jsx190, jsxs as jsxs92 } from "react/jsx-runtime"; var SPACING = 5; var space = { width: SPACING, flexShrink: 0 }; var TimelineListItem = ({ nestedDepth, sequence, isCompact }) => { const { hidden, setHidden } = useContext65(Internals47.SequenceVisibilityToggleContext); const padder = useMemo102(() => { return { width: Number(SPACING * 1.5) * nestedDepth, flexShrink: 0 }; }, [nestedDepth]); const isItemHidden = useMemo102(() => { return hidden[sequence.id] ?? false; }, [hidden, sequence.id]); const onToggleVisibility = useCallback97((type) => { setHidden((prev) => { return { ...prev, [sequence.id]: type !== "enable" }; }); }, [sequence.id, setHidden]); const outer2 = useMemo102(() => { return { height: getTimelineLayerHeight(sequence.type === "video" ? "video" : "other") + TIMELINE_ITEM_BORDER_BOTTOM, color: "white", fontFamily: "Arial, Helvetica, sans-serif", display: "flex", flexDirection: "row", alignItems: "center", wordBreak: "break-all", textAlign: "left", paddingLeft: SPACING, borderBottom: `1px solid ${TIMELINE_TRACK_SEPARATOR}` }; }, [sequence.type]); return /* @__PURE__ */ jsxs92("div", { style: outer2, children: [ /* @__PURE__ */ jsx190(TimelineLayerEye, { type: sequence.type === "audio" ? "speaker" : "eye", hidden: isItemHidden, onInvoked: onToggleVisibility }), /* @__PURE__ */ jsx190("div", { style: padder }), sequence.parent && nestedDepth > 0 ? /* @__PURE__ */ jsx190("div", { style: space }) : null, /* @__PURE__ */ jsx190(TimelineStack, { sequence, isCompact }) ] }); }; // src/components/Timeline/TimelineTimeIndicators.tsx import { useContext as useContext66, useEffect as useEffect66, useMemo as useMemo103, useRef as useRef37 } from "react"; import { Internals as Internals49 } from "remotion"; // src/components/TimeValue.tsx import { PlayerInternals as PlayerInternals17 } from "@remotion/player"; import { useCallback as useCallback98, useEffect as useEffect65, useImperativeHandle as useImperativeHandle13, useRef as useRef36 } from "react"; import { Internals as Internals48, useCurrentFrame } from "remotion"; import { jsx as jsx191, jsxs as jsxs93 } from "react/jsx-runtime"; var text = { color: "white", display: "flex", flexDirection: "row", alignItems: "center", fontVariantNumeric: "tabular-nums", lineHeight: 1, width: "100%", userSelect: "none", WebkitUserSelect: "none" }; var time = { display: "inline-block", fontSize: 16, lineHeight: 1, fontFamily: "monospace" }; var frameStyle = { color: LIGHT_TEXT, fontWeight: 500, lineHeight: 1, fontSize: 16, fontFamily: "monospace", paddingRight: 10 }; var TimeValue = () => { const frame2 = useCurrentFrame(); const config = Internals48.useUnsafeVideoConfig(); const isStill = useIsStill(); const { seek, play, pause, toggle } = PlayerInternals17.usePlayer(); const keybindings = useKeybinding(); const ref = useRef36(null); const onTextChange = useCallback98((newVal) => { seek(parseInt(newVal, 10)); }, [seek]); const onValueChange = useCallback98((val) => { seek(val); }, [seek]); useImperativeHandle13(Internals48.timeValueRef, () => ({ goToFrame: () => { ref.current?.click(); }, seek, play, pause, toggle }), [seek, play, pause, toggle]); useEffect65(() => { const gKey = keybindings.registerKeybinding({ event: "keypress", key: "g", callback: () => { ref.current?.click(); }, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); return () => { gKey.unregister(); }; }, [keybindings]); if (!config) { return null; } if (isStill) { return null; } return /* @__PURE__ */ jsxs93("div", { style: text, children: [ /* @__PURE__ */ jsx191("div", { style: time, children: renderFrame(frame2, config.fps) }), /* @__PURE__ */ jsx191(Spacing, { x: 2 }), /* @__PURE__ */ jsx191(Flex, {}), /* @__PURE__ */ jsx191(InputDragger, { ref, value: frame2, onTextChange, onValueChange, rightAlign: true, status: "ok", style: frameStyle }) ] }); }; // src/components/Timeline/TimelineTimeIndicators.tsx import { jsx as jsx192 } from "react/jsx-runtime"; var TIMELINE_TIME_INDICATOR_HEIGHT = 39; var container40 = { height: TIMELINE_TIME_INDICATOR_HEIGHT - 4, boxShadow: `0 0 4px ${TIMELINE_BACKGROUND}`, position: "absolute", backgroundColor: TIMELINE_BACKGROUND, top: 0 }; var tick = { width: 1, backgroundColor: "rgba(255, 255, 255, 0.15)", height: 20, position: "absolute" }; var secondTick = { ...tick, height: 15 }; var tickLabel = { fontSize: 12, marginLeft: 8, marginTop: 7, color: LIGHT_TEXT }; var timeValue = { height: TIMELINE_TIME_INDICATOR_HEIGHT, position: "absolute", top: 0, width: "100%", paddingLeft: 10, display: "flex", alignItems: "center", backgroundColor: BACKGROUND, borderBottom: `${TIMELINE_ITEM_BORDER_BOTTOM}px solid ${TIMELINE_TRACK_SEPARATOR}` }; var TimelineTimePlaceholders = () => { return /* @__PURE__ */ jsx192("div", { style: timeValue, children: /* @__PURE__ */ jsx192(TimeValue, {}) }); }; var TimelineTimePadding = () => { return /* @__PURE__ */ jsx192("div", { style: { height: TIMELINE_TIME_INDICATOR_HEIGHT } }); }; var TimelineTimeIndicators = () => { const sliderTrack = useContext66(TimelineWidthContext); const video = Internals49.useVideo(); if (sliderTrack === null) { return null; } if (video === null) { return null; } return /* @__PURE__ */ jsx192(Inner3, { durationInFrames: video.durationInFrames, fps: video.fps, windowWidth: sliderTrack }); }; var Inner3 = ({ windowWidth, durationInFrames, fps }) => { const ref = useRef37(null); useEffect66(() => { 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); }; }, []); const style11 = useMemo103(() => { return { ...container40, width: windowWidth - SPLITTER_HANDLE_SIZE / 2, overflow: "hidden", marginLeft: SPLITTER_HANDLE_SIZE / 2, pointerEvents: "none" }; }, [windowWidth]); const ticks = useMemo103(() => { const frameInterval = getFrameIncrementFromWidth(durationInFrames, windowWidth); const MIN_SPACING_BETWEEN_TICKS_PX = 5; const seconds = Math.floor(durationInFrames / fps); const secondMarkerEveryNth = Math.ceil(MIN_SPACING_BETWEEN_TICKS_PX * fps / (frameInterval * fps)); const frameMarkerEveryNth = Math.ceil(MIN_SPACING_BETWEEN_TICKS_PX / frameInterval); const secondTicks = new Array(seconds).fill(true).map((_, index) => { return { frame: index * fps, style: { ...secondTick, left: frameInterval * index * fps + TIMELINE_PADDING - SPLITTER_HANDLE_SIZE / 2 }, showTime: index > 0 }; }).filter((_, idx) => idx % secondMarkerEveryNth === 0); const frameTicks = new Array(durationInFrames).fill(true).map((_, index) => { return { frame: index, style: { ...tick, left: frameInterval * index + TIMELINE_PADDING - SPLITTER_HANDLE_SIZE / 2, height: index % fps === 0 ? 10 : index / frameMarkerEveryNth % 2 === 0 ? 5 : 2 }, showTime: false }; }).filter((_, idx) => idx % frameMarkerEveryNth === 0); const hasTicks = []; return [...secondTicks, ...frameTicks].filter((t) => { const alreadyUsed = hasTicks.find((ht) => ht === t.frame) !== undefined; hasTicks.push(t.frame); return !alreadyUsed; }); }, [durationInFrames, fps, windowWidth]); return /* @__PURE__ */ jsx192("div", { ref, style: style11, children: ticks.map((t) => { return /* @__PURE__ */ jsx192("div", { style: t.style, children: t.showTime ? /* @__PURE__ */ jsx192("div", { style: tickLabel, children: renderFrame(t.frame, fps) }) : null }, t.frame); }) }); }; // src/components/Timeline/TimelineList.tsx import { jsx as jsx193, jsxs as jsxs94 } from "react/jsx-runtime"; var container41 = { flex: 1, background: BACKGROUND }; var TimelineList = ({ timeline }) => { const ref = useRef38(null); const size4 = PlayerInternals18.useElementSize(ref, { shouldApplyCssTransforms: false, triggerOnWindowResize: false }); const isCompact = size4 ? size4.width < 250 : false; return /* @__PURE__ */ jsxs94("div", { ref, style: container41, children: [ /* @__PURE__ */ jsx193(TimelineTimePadding, {}), timeline.map((track) => { return /* @__PURE__ */ jsx193("div", { children: /* @__PURE__ */ jsx193(TimelineListItem, { nestedDepth: track.depth, sequence: track.sequence, isCompact }, track.sequence.id) }, track.sequence.id); }) ] }); }; // src/components/Timeline/TimelinePlayCursorSyncer.tsx import { useContext as useContext67, useEffect as useEffect67 } from "react"; import { Internals as Internals50 } from "remotion"; var lastTimelinePositionWhileScrolling = null; var TimelinePlayCursorSyncer = () => { const video = Internals50.useVideo(); const timelineContext = useContext67(Internals50.TimelineContext); const timelinePosition = Internals50.Timeline.useTimelinePosition(); const { canvasContent } = useContext67(Internals50.CompositionManager); const { zoom: zoomMap } = useContext67(TimelineZoomCtx); const compositionId = canvasContent && canvasContent.type === "composition" ? canvasContent.compositionId : null; const zoom = compositionId ? zoomMap[compositionId] ?? TIMELINE_MIN_ZOOM : null; if (zoom && video) { setCurrentFrame(timelinePosition); setCurrentZoom(zoom); setCurrentDuration(video.durationInFrames); setCurrentFps(video.fps); } const playing = timelineContext.playing ?? false; useEffect67(() => { if (!video) { return; } if (!playing) { return; } ensureFrameIsInViewport({ direction: timelineContext.playbackRate > 0 ? "page-right" : "page-left", durationInFrames: video.durationInFrames, frame: timelinePosition }); }, [playing, timelineContext, timelinePosition, video]); useEffect67(() => { const { current } = scrollableRef; if (!current) { return; } if (playing) { lastTimelinePositionWhileScrolling = { scrollLeft: current.scrollLeft, frame: getCurrentFrame(), zoomLevel: getCurrentZoom(), durationInFrames: getCurrentDuration() }; } else if (lastTimelinePositionWhileScrolling !== null) { if (isCursorInViewport({ frame: getCurrentFrame(), durationInFrames: getCurrentDuration() })) { return; } if (lastTimelinePositionWhileScrolling.zoomLevel === getCurrentZoom() && lastTimelinePositionWhileScrolling.durationInFrames === getCurrentDuration()) { current.scrollLeft = lastTimelinePositionWhileScrolling.scrollLeft; } else { ensureFrameIsInViewport({ direction: "center", durationInFrames: getCurrentDuration(), frame: lastTimelinePositionWhileScrolling.frame }); } } }, [playing]); return null; }; // src/components/Timeline/TimelineScrollable.tsx import { useMemo as useMemo104 } from "react"; import { jsx as jsx194 } from "react/jsx-runtime"; var outer2 = { width: "100%", height: "100%", overflowX: "auto", overflowY: "hidden", position: "relative", backgroundColor: TIMELINE_BACKGROUND }; var TimelineScrollable = ({ children }) => { const containerStyle3 = useMemo104(() => { return { width: "100%", minHeight: "100%" }; }, []); return /* @__PURE__ */ jsx194("div", { ref: scrollableRef, style: outer2, className: HORIZONTAL_SCROLLBAR_CLASSNAME, children: /* @__PURE__ */ jsx194("div", { style: containerStyle3, children }) }); }; // src/components/Timeline/TimelineTracks.tsx import { useMemo as useMemo108 } from "react"; // src/components/Timeline/TimelineSequence.tsx import { useContext as useContext68, useMemo as useMemo107 } from "react"; import { Internals as Internals52, useCurrentFrame as useCurrentFrame2 } from "remotion"; // src/helpers/get-timeline-sequence-layout.ts var SEQUENCE_BORDER_WIDTH = 1; var getWidthOfTrack = ({ durationInFrames, lastFrame, windowWidth, spatialDuration, nonNegativeMarginLeft }) => { const fullWidth9 = windowWidth - TIMELINE_PADDING * 2; const base = durationInFrames === Infinity || lastFrame === 0 ? fullWidth9 : spatialDuration / lastFrame * fullWidth9; return base - SEQUENCE_BORDER_WIDTH + nonNegativeMarginLeft; }; var getTimelineSequenceLayout = ({ durationInFrames, startFrom, maxMediaDuration, startFromMedia, video, windowWidth, premountDisplay, postmountDisplay }) => { const maxMediaSequenceDuration = (maxMediaDuration ?? Infinity) - startFromMedia; const lastFrame = (video.durationInFrames ?? 1) - 1; let spatialDuration = Math.min(maxMediaSequenceDuration, durationInFrames - 1, lastFrame - startFrom); let naturalSpatialDuration = Math.min(maxMediaSequenceDuration, durationInFrames - 1); const shouldAddHalfAFrameAtEnd = startFrom + durationInFrames < lastFrame; const shouldAddHalfAFrameAtStart = startFrom > 0; if (shouldAddHalfAFrameAtEnd) { spatialDuration += 0.5; naturalSpatialDuration += 0.5; } if (shouldAddHalfAFrameAtStart) { spatialDuration += 0.5; naturalSpatialDuration += 0.5; } const startFromWithOffset = shouldAddHalfAFrameAtStart ? startFrom - 0.5 : startFrom; const marginLeft = lastFrame === 0 ? 0 : startFromWithOffset / lastFrame * (windowWidth - TIMELINE_PADDING * 2); const nonNegativeMarginLeft = Math.min(marginLeft, 0); const width = getWidthOfTrack({ durationInFrames, lastFrame, nonNegativeMarginLeft, spatialDuration, windowWidth }); const naturalWidth = getWidthOfTrack({ durationInFrames, lastFrame, nonNegativeMarginLeft, spatialDuration: naturalSpatialDuration, windowWidth }); const premountWidth = premountDisplay ? getWidthOfTrack({ durationInFrames: premountDisplay, lastFrame, nonNegativeMarginLeft, spatialDuration: premountDisplay, windowWidth }) : null; const postmountWidth = postmountDisplay ? getWidthOfTrack({ durationInFrames: postmountDisplay, lastFrame, nonNegativeMarginLeft, spatialDuration: postmountDisplay, windowWidth }) : null; return { marginLeft: Math.max(marginLeft, 0) - (premountWidth ?? 0), width: width + (premountWidth ?? 0) + (postmountWidth ?? 0), naturalWidth: naturalWidth + (premountWidth ?? 0) + (postmountWidth ?? 0), premountWidth, postmountWidth }; }; // src/helpers/use-max-media-duration.ts import { getVideoMetadata as getVideoMetadata2 } from "@remotion/media-utils"; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/misc.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ function assert(x) { if (!x) { throw new Error("Assertion failed."); } } var normalizeRotation = (rotation) => { const mappedRotation = (rotation % 360 + 360) % 360; if (mappedRotation === 0 || mappedRotation === 90 || mappedRotation === 180 || mappedRotation === 270) { return mappedRotation; } else { throw new Error(`Invalid rotation ${rotation}.`); } }; var last = (arr) => { return arr && arr[arr.length - 1]; }; class Bitstream { constructor(bytes) { this.bytes = bytes; this.pos = 0; } seekToByte(byteOffset) { this.pos = 8 * byteOffset; } readBit() { const byteIndex = Math.floor(this.pos / 8); const byte = this.bytes[byteIndex] ?? 0; const bitIndex = 7 - (this.pos & 7); const bit = (byte & 1 << bitIndex) >> bitIndex; this.pos++; return bit; } readBits(n) { if (n === 1) { return this.readBit(); } let result = 0; for (let i = 0;i < n; i++) { result <<= 1; result |= this.readBit(); } return result; } writeBits(n, value) { const end = this.pos + n; for (let i = this.pos;i < end; i++) { const byteIndex = Math.floor(i / 8); let byte = this.bytes[byteIndex]; const bitIndex = 7 - (i & 7); byte &= ~(1 << bitIndex); byte |= (value & 1 << end - i - 1) >> end - i - 1 << bitIndex; this.bytes[byteIndex] = byte; } this.pos = end; } readAlignedByte() { if (this.pos % 8 !== 0) { throw new Error("Bitstream is not byte-aligned."); } const byteIndex = this.pos / 8; const byte = this.bytes[byteIndex] ?? 0; this.pos += 8; return byte; } skipBits(n) { this.pos += n; } getBitsLeft() { return this.bytes.length * 8 - this.pos; } clone() { const clone = new Bitstream(this.bytes); clone.pos = this.pos; return clone; } } var readExpGolomb = (bitstream) => { let leadingZeroBits = 0; while (bitstream.readBits(1) === 0 && leadingZeroBits < 32) { leadingZeroBits++; } if (leadingZeroBits >= 32) { throw new Error("Invalid exponential-Golomb code."); } const result = (1 << leadingZeroBits) - 1 + bitstream.readBits(leadingZeroBits); return result; }; var readSignedExpGolomb = (bitstream) => { const codeNum = readExpGolomb(bitstream); return (codeNum & 1) === 0 ? -(codeNum >> 1) : codeNum + 1 >> 1; }; var toUint8Array = (source) => { if (source.constructor === Uint8Array) { return source; } else if (ArrayBuffer.isView(source)) { return new Uint8Array(source.buffer, source.byteOffset, source.byteLength); } else { return new Uint8Array(source); } }; var toDataView = (source) => { if (source.constructor === DataView) { return source; } else if (ArrayBuffer.isView(source)) { return new DataView(source.buffer, source.byteOffset, source.byteLength); } else { return new DataView(source); } }; var textDecoder = /* @__PURE__ */ new TextDecoder; var invertObject = (object) => { return Object.fromEntries(Object.entries(object).map(([key4, value]) => [value, key4])); }; var COLOR_PRIMARIES_MAP = { bt709: 1, bt470bg: 5, smpte170m: 6, bt2020: 9, smpte432: 12 }; var COLOR_PRIMARIES_MAP_INVERSE = /* @__PURE__ */ invertObject(COLOR_PRIMARIES_MAP); var TRANSFER_CHARACTERISTICS_MAP = { bt709: 1, smpte170m: 6, linear: 8, "iec61966-2-1": 13, pq: 16, hlg: 18 }; var TRANSFER_CHARACTERISTICS_MAP_INVERSE = /* @__PURE__ */ invertObject(TRANSFER_CHARACTERISTICS_MAP); var MATRIX_COEFFICIENTS_MAP = { rgb: 0, bt709: 1, bt470bg: 5, smpte170m: 6, "bt2020-ncl": 9 }; var MATRIX_COEFFICIENTS_MAP_INVERSE = /* @__PURE__ */ invertObject(MATRIX_COEFFICIENTS_MAP); var isAllowSharedBufferSource = (x) => { return x instanceof ArrayBuffer || typeof SharedArrayBuffer !== "undefined" && x instanceof SharedArrayBuffer || ArrayBuffer.isView(x); }; class AsyncMutex { constructor() { this.currentPromise = Promise.resolve(); this.pending = 0; } async acquire() { let resolver; const nextPromise = new Promise((resolve) => { let resolved = false; resolver = () => { if (resolved) { return; } resolve(); this.pending--; resolved = true; }; }); const currentPromiseAlias = this.currentPromise; this.currentPromise = nextPromise; this.pending++; await currentPromiseAlias; return resolver; } } var bytesToHexString = (bytes) => { return [...bytes].map((x) => x.toString(16).padStart(2, "0")).join(""); }; var reverseBitsU32 = (x) => { x = x >> 1 & 1431655765 | (x & 1431655765) << 1; x = x >> 2 & 858993459 | (x & 858993459) << 2; x = x >> 4 & 252645135 | (x & 252645135) << 4; x = x >> 8 & 16711935 | (x & 16711935) << 8; x = x >> 16 & 65535 | (x & 65535) << 16; return x >>> 0; }; var binarySearchExact = (arr, key4, valueGetter) => { let low = 0; let high = arr.length - 1; let ans = -1; while (low <= high) { const mid = low + high >> 1; const midVal = valueGetter(arr[mid]); if (midVal === key4) { ans = mid; high = mid - 1; } else if (midVal < key4) { low = mid + 1; } else { high = mid - 1; } } return ans; }; var binarySearchLessOrEqual = (arr, key4, valueGetter) => { let low = 0; let high = arr.length - 1; let ans = -1; while (low <= high) { const mid = low + (high - low + 1) / 2 | 0; const midVal = valueGetter(arr[mid]); if (midVal <= key4) { ans = mid; low = mid + 1; } else { high = mid - 1; } } return ans; }; var insertSorted = (arr, item2, valueGetter) => { const insertionIndex = binarySearchLessOrEqual(arr, valueGetter(item2), valueGetter); arr.splice(insertionIndex + 1, 0, item2); }; var promiseWithResolvers = () => { let resolve; let reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; }; var findLast = (arr, predicate) => { for (let i = arr.length - 1;i >= 0; i--) { if (predicate(arr[i])) { return arr[i]; } } return; }; var findLastIndex = (arr, predicate) => { for (let i = arr.length - 1;i >= 0; i--) { if (predicate(arr[i])) { return i; } } return -1; }; var toAsyncIterator = async function* (source) { if (Symbol.iterator in source) { yield* source[Symbol.iterator](); } else { yield* source[Symbol.asyncIterator](); } }; var validateAnyIterable = (iterable) => { if (!(Symbol.iterator in iterable) && !(Symbol.asyncIterator in iterable)) { throw new TypeError("Argument must be an iterable or async iterable."); } }; var assertNever = (x) => { throw new Error(`Unexpected value: ${x}`); }; var getUint24 = (view, byteOffset, littleEndian) => { const byte1 = view.getUint8(byteOffset); const byte2 = view.getUint8(byteOffset + 1); const byte3 = view.getUint8(byteOffset + 2); if (littleEndian) { return byte1 | byte2 << 8 | byte3 << 16; } else { return byte1 << 16 | byte2 << 8 | byte3; } }; var setUint24 = (view, byteOffset, value, littleEndian) => { value = value >>> 0; value = value & 16777215; if (littleEndian) { view.setUint8(byteOffset, value & 255); view.setUint8(byteOffset + 1, value >>> 8 & 255); view.setUint8(byteOffset + 2, value >>> 16 & 255); } else { view.setUint8(byteOffset, value >>> 16 & 255); view.setUint8(byteOffset + 1, value >>> 8 & 255); view.setUint8(byteOffset + 2, value & 255); } }; var clamp = (value, min, max) => { return Math.max(min, Math.min(max, value)); }; var UNDETERMINED_LANGUAGE = "und"; var roundIfAlmostInteger = (value) => { const rounded = Math.round(value); if (Math.abs(value / rounded - 1) < 10 * Number.EPSILON) { return rounded; } else { return value; } }; var roundToMultiple = (value, multiple) => { return Math.round(value / multiple) * multiple; }; var ilog = (x) => { let ret = 0; while (x) { ret++; x >>= 1; } return ret; }; var ISO_639_2_REGEX = /^[a-z]{3}$/; var isIso639Dash2LanguageCode = (x) => { return ISO_639_2_REGEX.test(x); }; var SECOND_TO_MICROSECOND_FACTOR = 1e6 * (1 + Number.EPSILON); var mergeRequestInit = (init1, init2) => { const merged = { ...init1, ...init2 }; if (init1.headers || init2.headers) { const headers1 = init1.headers ? normalizeHeaders(init1.headers) : {}; const headers2 = init2.headers ? normalizeHeaders(init2.headers) : {}; const mergedHeaders = { ...headers1 }; Object.entries(headers2).forEach(([key22, value2]) => { const existingKey = Object.keys(mergedHeaders).find((key1) => key1.toLowerCase() === key22.toLowerCase()); if (existingKey) { delete mergedHeaders[existingKey]; } mergedHeaders[key22] = value2; }); merged.headers = mergedHeaders; } return merged; }; var normalizeHeaders = (headers) => { if (headers instanceof Headers) { const result = {}; headers.forEach((value, key4) => { result[key4] = value; }); return result; } if (Array.isArray(headers)) { const result = {}; headers.forEach(([key4, value]) => { result[key4] = value; }); return result; } return headers; }; var retriedFetch = async (fetchFn, url, requestInit, getRetryDelay, shouldStop) => { let attempts = 0; while (true) { try { return await fetchFn(url, requestInit); } catch (error) { if (shouldStop()) { throw error; } attempts++; const retryDelayInSeconds = getRetryDelay(attempts, error, url); if (retryDelayInSeconds === null) { throw error; } console.error("Retrying failed fetch. Error:", error); if (!Number.isFinite(retryDelayInSeconds) || retryDelayInSeconds < 0) { throw new TypeError("Retry delay must be a non-negative finite number."); } if (retryDelayInSeconds > 0) { await new Promise((resolve) => setTimeout(resolve, 1000 * retryDelayInSeconds)); } if (shouldStop()) { throw error; } } } }; class CallSerializer { constructor() { this.currentPromise = Promise.resolve(); } call(fn) { return this.currentPromise = this.currentPromise.then(fn); } } var isWebKitCache = null; var isWebKit = () => { if (isWebKitCache !== null) { return isWebKitCache; } return isWebKitCache = !!(typeof navigator !== "undefined" && (navigator.vendor?.match(/apple/i) || /AppleWebKit/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) || /\b(iPad|iPhone|iPod)\b/.test(navigator.userAgent))); }; var isFirefoxCache = null; var isFirefox = () => { if (isFirefoxCache !== null) { return isFirefoxCache; } return isFirefoxCache = typeof navigator !== "undefined" && navigator.userAgent?.includes("Firefox"); }; var isChromiumCache = null; var isChromium = () => { if (isChromiumCache !== null) { return isChromiumCache; } return isChromiumCache = !!(typeof navigator !== "undefined" && (navigator.vendor?.includes("Google Inc") || /Chrome/.test(navigator.userAgent))); }; var chromiumVersionCache = null; var getChromiumVersion = () => { if (chromiumVersionCache !== null) { return chromiumVersionCache; } if (typeof navigator === "undefined") { return null; } const match = /\bChrome\/(\d+)/.exec(navigator.userAgent); if (!match) { return null; } return chromiumVersionCache = Number(match[1]); }; var coalesceIndex = (a, b) => { return a !== -1 ? a : b; }; var closedIntervalsOverlap = (startA, endA, startB, endB) => { return startA <= endB && startB <= endA; }; var base64ToBytes = (base64) => { const decoded = atob(base64); const bytes = new Uint8Array(decoded.length); for (let i = 0;i < decoded.length; i++) { bytes[i] = decoded.charCodeAt(i); } return bytes; }; var polyfillSymbolDispose = () => { Symbol.dispose ??= Symbol("Symbol.dispose"); }; var isNumber = (x) => { return typeof x === "number" && !Number.isNaN(x); }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/metadata.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ class RichImageData { constructor(data, mimeType) { this.data = data; this.mimeType = mimeType; if (!(data instanceof Uint8Array)) { throw new TypeError("data must be a Uint8Array."); } if (typeof mimeType !== "string") { throw new TypeError("mimeType must be a string."); } } } class AttachedFile { constructor(data, mimeType, name, description) { this.data = data; this.mimeType = mimeType; this.name = name; this.description = description; if (!(data instanceof Uint8Array)) { throw new TypeError("data must be a Uint8Array."); } if (mimeType !== undefined && typeof mimeType !== "string") { throw new TypeError("mimeType, when provided, must be a string."); } if (name !== undefined && typeof name !== "string") { throw new TypeError("name, when provided, must be a string."); } if (description !== undefined && typeof description !== "string") { throw new TypeError("description, when provided, must be a string."); } } } var DEFAULT_TRACK_DISPOSITION = { default: true, forced: false, original: false, commentary: false, hearingImpaired: false, visuallyImpaired: false }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/codec.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var PCM_AUDIO_CODECS = [ "pcm-s16", "pcm-s16be", "pcm-s24", "pcm-s24be", "pcm-s32", "pcm-s32be", "pcm-f32", "pcm-f32be", "pcm-f64", "pcm-f64be", "pcm-u8", "pcm-s8", "ulaw", "alaw" ]; var NON_PCM_AUDIO_CODECS = [ "aac", "opus", "mp3", "vorbis", "flac" ]; var AUDIO_CODECS = [ ...NON_PCM_AUDIO_CODECS, ...PCM_AUDIO_CODECS ]; var AVC_LEVEL_TABLE = [ { maxMacroblocks: 99, maxBitrate: 64000, maxDpbMbs: 396, level: 10 }, { maxMacroblocks: 396, maxBitrate: 192000, maxDpbMbs: 900, level: 11 }, { maxMacroblocks: 396, maxBitrate: 384000, maxDpbMbs: 2376, level: 12 }, { maxMacroblocks: 396, maxBitrate: 768000, maxDpbMbs: 2376, level: 13 }, { maxMacroblocks: 396, maxBitrate: 2000000, maxDpbMbs: 2376, level: 20 }, { maxMacroblocks: 792, maxBitrate: 4000000, maxDpbMbs: 4752, level: 21 }, { maxMacroblocks: 1620, maxBitrate: 4000000, maxDpbMbs: 8100, level: 22 }, { maxMacroblocks: 1620, maxBitrate: 1e7, maxDpbMbs: 8100, level: 30 }, { maxMacroblocks: 3600, maxBitrate: 14000000, maxDpbMbs: 18000, level: 31 }, { maxMacroblocks: 5120, maxBitrate: 20000000, maxDpbMbs: 20480, level: 32 }, { maxMacroblocks: 8192, maxBitrate: 20000000, maxDpbMbs: 32768, level: 40 }, { maxMacroblocks: 8192, maxBitrate: 50000000, maxDpbMbs: 32768, level: 41 }, { maxMacroblocks: 8704, maxBitrate: 50000000, maxDpbMbs: 34816, level: 42 }, { maxMacroblocks: 22080, maxBitrate: 135000000, maxDpbMbs: 110400, level: 50 }, { maxMacroblocks: 36864, maxBitrate: 240000000, maxDpbMbs: 184320, level: 51 }, { maxMacroblocks: 36864, maxBitrate: 240000000, maxDpbMbs: 184320, level: 52 }, { maxMacroblocks: 139264, maxBitrate: 240000000, maxDpbMbs: 696320, level: 60 }, { maxMacroblocks: 139264, maxBitrate: 480000000, maxDpbMbs: 696320, level: 61 }, { maxMacroblocks: 139264, maxBitrate: 800000000, maxDpbMbs: 696320, level: 62 } ]; var VP9_LEVEL_TABLE = [ { maxPictureSize: 36864, maxBitrate: 200000, level: 10 }, { maxPictureSize: 73728, maxBitrate: 800000, level: 11 }, { maxPictureSize: 122880, maxBitrate: 1800000, level: 20 }, { maxPictureSize: 245760, maxBitrate: 3600000, level: 21 }, { maxPictureSize: 552960, maxBitrate: 7200000, level: 30 }, { maxPictureSize: 983040, maxBitrate: 12000000, level: 31 }, { maxPictureSize: 2228224, maxBitrate: 18000000, level: 40 }, { maxPictureSize: 2228224, maxBitrate: 30000000, level: 41 }, { maxPictureSize: 8912896, maxBitrate: 60000000, level: 50 }, { maxPictureSize: 8912896, maxBitrate: 120000000, level: 51 }, { maxPictureSize: 8912896, maxBitrate: 180000000, level: 52 }, { maxPictureSize: 35651584, maxBitrate: 180000000, level: 60 }, { maxPictureSize: 35651584, maxBitrate: 240000000, level: 61 }, { maxPictureSize: 35651584, maxBitrate: 480000000, level: 62 } ]; var VP9_DEFAULT_SUFFIX = ".01.01.01.01.00"; var AV1_DEFAULT_SUFFIX = ".0.110.01.01.01.0"; var extractVideoCodecString = (trackInfo) => { const { codec, codecDescription, colorSpace, avcCodecInfo, hevcCodecInfo, vp9CodecInfo, av1CodecInfo } = trackInfo; if (codec === "avc") { assert(trackInfo.avcType !== null); if (avcCodecInfo) { const bytes = new Uint8Array([ avcCodecInfo.avcProfileIndication, avcCodecInfo.profileCompatibility, avcCodecInfo.avcLevelIndication ]); return `avc${trackInfo.avcType}.${bytesToHexString(bytes)}`; } if (!codecDescription || codecDescription.byteLength < 4) { throw new TypeError("AVC decoder description is not provided or is not at least 4 bytes long."); } return `avc${trackInfo.avcType}.${bytesToHexString(codecDescription.subarray(1, 4))}`; } else if (codec === "hevc") { let generalProfileSpace; let generalProfileIdc; let compatibilityFlags; let generalTierFlag; let generalLevelIdc; let constraintFlags; if (hevcCodecInfo) { generalProfileSpace = hevcCodecInfo.generalProfileSpace; generalProfileIdc = hevcCodecInfo.generalProfileIdc; compatibilityFlags = reverseBitsU32(hevcCodecInfo.generalProfileCompatibilityFlags); generalTierFlag = hevcCodecInfo.generalTierFlag; generalLevelIdc = hevcCodecInfo.generalLevelIdc; constraintFlags = [...hevcCodecInfo.generalConstraintIndicatorFlags]; } else { if (!codecDescription || codecDescription.byteLength < 23) { throw new TypeError("HEVC decoder description is not provided or is not at least 23 bytes long."); } const view = toDataView(codecDescription); const profileByte = view.getUint8(1); generalProfileSpace = profileByte >> 6 & 3; generalProfileIdc = profileByte & 31; compatibilityFlags = reverseBitsU32(view.getUint32(2)); generalTierFlag = profileByte >> 5 & 1; generalLevelIdc = view.getUint8(12); constraintFlags = []; for (let i = 0;i < 6; i++) { constraintFlags.push(view.getUint8(6 + i)); } } let codecString = "hev1."; codecString += ["", "A", "B", "C"][generalProfileSpace] + generalProfileIdc; codecString += "."; codecString += compatibilityFlags.toString(16).toUpperCase(); codecString += "."; codecString += generalTierFlag === 0 ? "L" : "H"; codecString += generalLevelIdc; while (constraintFlags.length > 0 && constraintFlags[constraintFlags.length - 1] === 0) { constraintFlags.pop(); } if (constraintFlags.length > 0) { codecString += "."; codecString += constraintFlags.map((x) => x.toString(16).toUpperCase()).join("."); } return codecString; } else if (codec === "vp8") { return "vp8"; } else if (codec === "vp9") { if (!vp9CodecInfo) { const pictureSize = trackInfo.width * trackInfo.height; let level2 = last(VP9_LEVEL_TABLE).level; for (const entry of VP9_LEVEL_TABLE) { if (pictureSize <= entry.maxPictureSize) { level2 = entry.level; break; } } return `vp09.00.${level2.toString().padStart(2, "0")}.08`; } const profile = vp9CodecInfo.profile.toString().padStart(2, "0"); const level = vp9CodecInfo.level.toString().padStart(2, "0"); const bitDepth = vp9CodecInfo.bitDepth.toString().padStart(2, "0"); const chromaSubsampling = vp9CodecInfo.chromaSubsampling.toString().padStart(2, "0"); const colourPrimaries = vp9CodecInfo.colourPrimaries.toString().padStart(2, "0"); const transferCharacteristics = vp9CodecInfo.transferCharacteristics.toString().padStart(2, "0"); const matrixCoefficients = vp9CodecInfo.matrixCoefficients.toString().padStart(2, "0"); const videoFullRangeFlag = vp9CodecInfo.videoFullRangeFlag.toString().padStart(2, "0"); let string = `vp09.${profile}.${level}.${bitDepth}.${chromaSubsampling}`; string += `.${colourPrimaries}.${transferCharacteristics}.${matrixCoefficients}.${videoFullRangeFlag}`; if (string.endsWith(VP9_DEFAULT_SUFFIX)) { string = string.slice(0, -VP9_DEFAULT_SUFFIX.length); } return string; } else if (codec === "av1") { if (!av1CodecInfo) { const pictureSize = trackInfo.width * trackInfo.height; let level2 = last(VP9_LEVEL_TABLE).level; for (const entry of VP9_LEVEL_TABLE) { if (pictureSize <= entry.maxPictureSize) { level2 = entry.level; break; } } return `av01.0.${level2.toString().padStart(2, "0")}M.08`; } const profile = av1CodecInfo.profile; const level = av1CodecInfo.level.toString().padStart(2, "0"); const tier = av1CodecInfo.tier ? "H" : "M"; const bitDepth = av1CodecInfo.bitDepth.toString().padStart(2, "0"); const monochrome = av1CodecInfo.monochrome ? "1" : "0"; const chromaSubsampling = 100 * av1CodecInfo.chromaSubsamplingX + 10 * av1CodecInfo.chromaSubsamplingY + 1 * (av1CodecInfo.chromaSubsamplingX && av1CodecInfo.chromaSubsamplingY ? av1CodecInfo.chromaSamplePosition : 0); const colorPrimaries = colorSpace?.primaries ? COLOR_PRIMARIES_MAP[colorSpace.primaries] : 1; const transferCharacteristics = colorSpace?.transfer ? TRANSFER_CHARACTERISTICS_MAP[colorSpace.transfer] : 1; const matrixCoefficients = colorSpace?.matrix ? MATRIX_COEFFICIENTS_MAP[colorSpace.matrix] : 1; const videoFullRangeFlag = colorSpace?.fullRange ? 1 : 0; let string = `av01.${profile}.${level}${tier}.${bitDepth}`; string += `.${monochrome}.${chromaSubsampling.toString().padStart(3, "0")}`; string += `.${colorPrimaries.toString().padStart(2, "0")}`; string += `.${transferCharacteristics.toString().padStart(2, "0")}`; string += `.${matrixCoefficients.toString().padStart(2, "0")}`; string += `.${videoFullRangeFlag}`; if (string.endsWith(AV1_DEFAULT_SUFFIX)) { string = string.slice(0, -AV1_DEFAULT_SUFFIX.length); } return string; } throw new TypeError(`Unhandled codec '${codec}'.`); }; var extractAudioCodecString = (trackInfo) => { const { codec, codecDescription, aacCodecInfo } = trackInfo; if (codec === "aac") { if (!aacCodecInfo) { throw new TypeError("AAC codec info must be provided."); } if (aacCodecInfo.isMpeg2) { return "mp4a.67"; } else { let objectType; if (aacCodecInfo.objectType !== null) { objectType = aacCodecInfo.objectType; } else { const audioSpecificConfig = parseAacAudioSpecificConfig(codecDescription); objectType = audioSpecificConfig.objectType; } return `mp4a.40.${objectType}`; } } else if (codec === "mp3") { return "mp3"; } else if (codec === "opus") { return "opus"; } else if (codec === "vorbis") { return "vorbis"; } else if (codec === "flac") { return "flac"; } else if (codec && PCM_AUDIO_CODECS.includes(codec)) { return codec; } throw new TypeError(`Unhandled codec '${codec}'.`); }; var aacFrequencyTable = [ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 ]; var aacChannelMap = [-1, 1, 2, 3, 4, 5, 6, 8]; var parseAacAudioSpecificConfig = (bytes) => { if (!bytes || bytes.byteLength < 2) { throw new TypeError("AAC description must be at least 2 bytes long."); } const bitstream = new Bitstream(bytes); let objectType = bitstream.readBits(5); if (objectType === 31) { objectType = 32 + bitstream.readBits(6); } const frequencyIndex = bitstream.readBits(4); let sampleRate = null; if (frequencyIndex === 15) { sampleRate = bitstream.readBits(24); } else { if (frequencyIndex < aacFrequencyTable.length) { sampleRate = aacFrequencyTable[frequencyIndex]; } } const channelConfiguration = bitstream.readBits(4); let numberOfChannels = null; if (channelConfiguration >= 1 && channelConfiguration <= 7) { numberOfChannels = aacChannelMap[channelConfiguration]; } return { objectType, frequencyIndex, sampleRate, channelConfiguration, numberOfChannels }; }; var OPUS_SAMPLE_RATE = 48000; var PCM_CODEC_REGEX = /^pcm-([usf])(\d+)+(be)?$/; var parsePcmCodec = (codec) => { assert(PCM_AUDIO_CODECS.includes(codec)); if (codec === "ulaw") { return { dataType: "ulaw", sampleSize: 1, littleEndian: true, silentValue: 255 }; } else if (codec === "alaw") { return { dataType: "alaw", sampleSize: 1, littleEndian: true, silentValue: 213 }; } const match = PCM_CODEC_REGEX.exec(codec); assert(match); let dataType; if (match[1] === "u") { dataType = "unsigned"; } else if (match[1] === "s") { dataType = "signed"; } else { dataType = "float"; } const sampleSize = Number(match[2]) / 8; const littleEndian = match[3] !== "be"; const silentValue = codec === "pcm-u8" ? 2 ** 7 : 0; return { dataType, sampleSize, littleEndian, silentValue }; }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/codec-data.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var AvcNalUnitType; (function(AvcNalUnitType2) { AvcNalUnitType2[AvcNalUnitType2["NON_IDR_SLICE"] = 1] = "NON_IDR_SLICE"; AvcNalUnitType2[AvcNalUnitType2["SLICE_DPA"] = 2] = "SLICE_DPA"; AvcNalUnitType2[AvcNalUnitType2["SLICE_DPB"] = 3] = "SLICE_DPB"; AvcNalUnitType2[AvcNalUnitType2["SLICE_DPC"] = 4] = "SLICE_DPC"; AvcNalUnitType2[AvcNalUnitType2["IDR"] = 5] = "IDR"; AvcNalUnitType2[AvcNalUnitType2["SEI"] = 6] = "SEI"; AvcNalUnitType2[AvcNalUnitType2["SPS"] = 7] = "SPS"; AvcNalUnitType2[AvcNalUnitType2["PPS"] = 8] = "PPS"; AvcNalUnitType2[AvcNalUnitType2["AUD"] = 9] = "AUD"; AvcNalUnitType2[AvcNalUnitType2["SPS_EXT"] = 13] = "SPS_EXT"; })(AvcNalUnitType || (AvcNalUnitType = {})); var HevcNalUnitType; (function(HevcNalUnitType2) { HevcNalUnitType2[HevcNalUnitType2["RASL_N"] = 8] = "RASL_N"; HevcNalUnitType2[HevcNalUnitType2["RASL_R"] = 9] = "RASL_R"; HevcNalUnitType2[HevcNalUnitType2["BLA_W_LP"] = 16] = "BLA_W_LP"; HevcNalUnitType2[HevcNalUnitType2["RSV_IRAP_VCL23"] = 23] = "RSV_IRAP_VCL23"; HevcNalUnitType2[HevcNalUnitType2["VPS_NUT"] = 32] = "VPS_NUT"; HevcNalUnitType2[HevcNalUnitType2["SPS_NUT"] = 33] = "SPS_NUT"; HevcNalUnitType2[HevcNalUnitType2["PPS_NUT"] = 34] = "PPS_NUT"; HevcNalUnitType2[HevcNalUnitType2["AUD_NUT"] = 35] = "AUD_NUT"; HevcNalUnitType2[HevcNalUnitType2["PREFIX_SEI_NUT"] = 39] = "PREFIX_SEI_NUT"; HevcNalUnitType2[HevcNalUnitType2["SUFFIX_SEI_NUT"] = 40] = "SUFFIX_SEI_NUT"; })(HevcNalUnitType || (HevcNalUnitType = {})); var iterateNalUnitsInAnnexB = function* (packetData) { let i = 0; let nalStart = -1; while (i < packetData.length - 2) { const zeroIndex = packetData.indexOf(0, i); if (zeroIndex === -1 || zeroIndex >= packetData.length - 2) { break; } i = zeroIndex; let startCodeLength = 0; if (i + 3 < packetData.length && packetData[i + 1] === 0 && packetData[i + 2] === 0 && packetData[i + 3] === 1) { startCodeLength = 4; } else if (packetData[i + 1] === 0 && packetData[i + 2] === 1) { startCodeLength = 3; } if (startCodeLength === 0) { i++; continue; } if (nalStart !== -1 && i > nalStart) { yield { offset: nalStart, length: i - nalStart }; } nalStart = i + startCodeLength; i = nalStart; } if (nalStart !== -1 && nalStart < packetData.length) { yield { offset: nalStart, length: packetData.length - nalStart }; } }; var iterateNalUnitsInLengthPrefixed = function* (packetData, lengthSize) { let offset = 0; const dataView = new DataView(packetData.buffer, packetData.byteOffset, packetData.byteLength); while (offset + lengthSize <= packetData.length) { let nalUnitLength; if (lengthSize === 1) { nalUnitLength = dataView.getUint8(offset); } else if (lengthSize === 2) { nalUnitLength = dataView.getUint16(offset, false); } else if (lengthSize === 3) { nalUnitLength = getUint24(dataView, offset, false); } else { assert(lengthSize === 4); nalUnitLength = dataView.getUint32(offset, false); } offset += lengthSize; yield { offset, length: nalUnitLength }; offset += nalUnitLength; } }; var iterateAvcNalUnits = (packetData, decoderConfig) => { if (decoderConfig.description) { const bytes = toUint8Array(decoderConfig.description); const lengthSizeMinusOne = bytes[4] & 3; const lengthSize = lengthSizeMinusOne + 1; return iterateNalUnitsInLengthPrefixed(packetData, lengthSize); } else { return iterateNalUnitsInAnnexB(packetData); } }; var iterateAvcNalUnitsAnnexB = function* (packetData) { yield* iterateNalUnitsInAnnexB(packetData); }; var extractNalUnitTypeForAvc = (byte) => { return byte & 31; }; var removeEmulationPreventionBytes = (data) => { const result = []; const len = data.length; for (let i = 0;i < len; i++) { if (i + 2 < len && data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 3) { result.push(0, 0); i += 2; } else { result.push(data[i]); } } return new Uint8Array(result); }; var ANNEX_B_START_CODE = new Uint8Array([0, 0, 0, 1]); var concatNalUnitsInAnnexB = (nalUnits) => { const totalLength = nalUnits.reduce((a, b) => a + ANNEX_B_START_CODE.byteLength + b.byteLength, 0); const result = new Uint8Array(totalLength); let offset = 0; for (const nalUnit of nalUnits) { result.set(ANNEX_B_START_CODE, offset); offset += ANNEX_B_START_CODE.byteLength; result.set(nalUnit, offset); offset += nalUnit.byteLength; } return result; }; var concatNalUnitsInLengthPrefixed = (nalUnits, lengthSize) => { const totalLength = nalUnits.reduce((a, b) => a + lengthSize + b.byteLength, 0); const result = new Uint8Array(totalLength); let offset = 0; for (const nalUnit of nalUnits) { const dataView = new DataView(result.buffer, result.byteOffset, result.byteLength); switch (lengthSize) { case 1: dataView.setUint8(offset, nalUnit.byteLength); break; case 2: dataView.setUint16(offset, nalUnit.byteLength, false); break; case 3: setUint24(dataView, offset, nalUnit.byteLength, false); break; case 4: dataView.setUint32(offset, nalUnit.byteLength, false); break; } offset += lengthSize; result.set(nalUnit, offset); offset += nalUnit.byteLength; } return result; }; var concatAvcNalUnits = (nalUnits, decoderConfig) => { if (decoderConfig.description) { const bytes = toUint8Array(decoderConfig.description); const lengthSizeMinusOne = bytes[4] & 3; const lengthSize = lengthSizeMinusOne + 1; return concatNalUnitsInLengthPrefixed(nalUnits, lengthSize); } else { return concatNalUnitsInAnnexB(nalUnits); } }; var extractAvcDecoderConfigurationRecord = (packetData) => { try { const spsUnits = []; const ppsUnits = []; const spsExtUnits = []; for (const loc of iterateAvcNalUnitsAnnexB(packetData)) { const nalUnit = packetData.subarray(loc.offset, loc.offset + loc.length); const type = extractNalUnitTypeForAvc(nalUnit[0]); if (type === AvcNalUnitType.SPS) { spsUnits.push(nalUnit); } else if (type === AvcNalUnitType.PPS) { ppsUnits.push(nalUnit); } else if (type === AvcNalUnitType.SPS_EXT) { spsExtUnits.push(nalUnit); } } if (spsUnits.length === 0) { return null; } if (ppsUnits.length === 0) { return null; } const spsData = spsUnits[0]; const spsInfo = parseAvcSps(spsData); assert(spsInfo !== null); const hasExtendedData = spsInfo.profileIdc === 100 || spsInfo.profileIdc === 110 || spsInfo.profileIdc === 122 || spsInfo.profileIdc === 144; return { configurationVersion: 1, avcProfileIndication: spsInfo.profileIdc, profileCompatibility: spsInfo.constraintFlags, avcLevelIndication: spsInfo.levelIdc, lengthSizeMinusOne: 3, sequenceParameterSets: spsUnits, pictureParameterSets: ppsUnits, chromaFormat: hasExtendedData ? spsInfo.chromaFormatIdc : null, bitDepthLumaMinus8: hasExtendedData ? spsInfo.bitDepthLumaMinus8 : null, bitDepthChromaMinus8: hasExtendedData ? spsInfo.bitDepthChromaMinus8 : null, sequenceParameterSetExt: hasExtendedData ? spsExtUnits : null }; } catch (error) { console.error("Error building AVC Decoder Configuration Record:", error); return null; } }; var deserializeAvcDecoderConfigurationRecord = (data) => { try { const view = toDataView(data); let offset = 0; const configurationVersion = view.getUint8(offset++); const avcProfileIndication = view.getUint8(offset++); const profileCompatibility = view.getUint8(offset++); const avcLevelIndication = view.getUint8(offset++); const lengthSizeMinusOne = view.getUint8(offset++) & 3; const numOfSequenceParameterSets = view.getUint8(offset++) & 31; const sequenceParameterSets = []; for (let i = 0;i < numOfSequenceParameterSets; i++) { const length = view.getUint16(offset, false); offset += 2; sequenceParameterSets.push(data.subarray(offset, offset + length)); offset += length; } const numOfPictureParameterSets = view.getUint8(offset++); const pictureParameterSets = []; for (let i = 0;i < numOfPictureParameterSets; i++) { const length = view.getUint16(offset, false); offset += 2; pictureParameterSets.push(data.subarray(offset, offset + length)); offset += length; } const record = { configurationVersion, avcProfileIndication, profileCompatibility, avcLevelIndication, lengthSizeMinusOne, sequenceParameterSets, pictureParameterSets, chromaFormat: null, bitDepthLumaMinus8: null, bitDepthChromaMinus8: null, sequenceParameterSetExt: null }; if ((avcProfileIndication === 100 || avcProfileIndication === 110 || avcProfileIndication === 122 || avcProfileIndication === 144) && offset + 4 <= data.length) { const chromaFormat = view.getUint8(offset++) & 3; const bitDepthLumaMinus8 = view.getUint8(offset++) & 7; const bitDepthChromaMinus8 = view.getUint8(offset++) & 7; const numOfSequenceParameterSetExt = view.getUint8(offset++); record.chromaFormat = chromaFormat; record.bitDepthLumaMinus8 = bitDepthLumaMinus8; record.bitDepthChromaMinus8 = bitDepthChromaMinus8; const sequenceParameterSetExt = []; for (let i = 0;i < numOfSequenceParameterSetExt; i++) { const length = view.getUint16(offset, false); offset += 2; sequenceParameterSetExt.push(data.subarray(offset, offset + length)); offset += length; } record.sequenceParameterSetExt = sequenceParameterSetExt; } return record; } catch (error) { console.error("Error deserializing AVC Decoder Configuration Record:", error); return null; } }; var parseAvcSps = (sps) => { try { const bitstream = new Bitstream(removeEmulationPreventionBytes(sps)); bitstream.skipBits(1); bitstream.skipBits(2); const nalUnitType = bitstream.readBits(5); if (nalUnitType !== 7) { return null; } const profileIdc = bitstream.readAlignedByte(); const constraintFlags = bitstream.readAlignedByte(); const levelIdc = bitstream.readAlignedByte(); readExpGolomb(bitstream); let chromaFormatIdc = 1; let bitDepthLumaMinus8 = 0; let bitDepthChromaMinus8 = 0; let separateColourPlaneFlag = 0; if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) { chromaFormatIdc = readExpGolomb(bitstream); if (chromaFormatIdc === 3) { separateColourPlaneFlag = bitstream.readBits(1); } bitDepthLumaMinus8 = readExpGolomb(bitstream); bitDepthChromaMinus8 = readExpGolomb(bitstream); bitstream.skipBits(1); const seqScalingMatrixPresentFlag = bitstream.readBits(1); if (seqScalingMatrixPresentFlag) { for (let i = 0;i < (chromaFormatIdc !== 3 ? 8 : 12); i++) { const seqScalingListPresentFlag = bitstream.readBits(1); if (seqScalingListPresentFlag) { const sizeOfScalingList = i < 6 ? 16 : 64; let lastScale = 8; let nextScale = 8; for (let j = 0;j < sizeOfScalingList; j++) { if (nextScale !== 0) { const deltaScale = readSignedExpGolomb(bitstream); nextScale = (lastScale + deltaScale + 256) % 256; } lastScale = nextScale === 0 ? lastScale : nextScale; } } } } } readExpGolomb(bitstream); const picOrderCntType = readExpGolomb(bitstream); if (picOrderCntType === 0) { readExpGolomb(bitstream); } else if (picOrderCntType === 1) { bitstream.skipBits(1); readSignedExpGolomb(bitstream); readSignedExpGolomb(bitstream); const numRefFramesInPicOrderCntCycle = readExpGolomb(bitstream); for (let i = 0;i < numRefFramesInPicOrderCntCycle; i++) { readSignedExpGolomb(bitstream); } } readExpGolomb(bitstream); bitstream.skipBits(1); const picWidthInMbsMinus1 = readExpGolomb(bitstream); const picHeightInMapUnitsMinus1 = readExpGolomb(bitstream); const codedWidth = 16 * (picWidthInMbsMinus1 + 1); const codedHeight = 16 * (picHeightInMapUnitsMinus1 + 1); let displayWidth = codedWidth; let displayHeight = codedHeight; const frameMbsOnlyFlag = bitstream.readBits(1); if (!frameMbsOnlyFlag) { bitstream.skipBits(1); } bitstream.skipBits(1); const frameCroppingFlag = bitstream.readBits(1); if (frameCroppingFlag) { const frameCropLeftOffset = readExpGolomb(bitstream); const frameCropRightOffset = readExpGolomb(bitstream); const frameCropTopOffset = readExpGolomb(bitstream); const frameCropBottomOffset = readExpGolomb(bitstream); let cropUnitX; let cropUnitY; const chromaArrayType = separateColourPlaneFlag === 0 ? chromaFormatIdc : 0; if (chromaArrayType === 0) { cropUnitX = 1; cropUnitY = 2 - frameMbsOnlyFlag; } else { const subWidthC = chromaFormatIdc === 3 ? 1 : 2; const subHeightC = chromaFormatIdc === 1 ? 2 : 1; cropUnitX = subWidthC; cropUnitY = subHeightC * (2 - frameMbsOnlyFlag); } displayWidth -= cropUnitX * (frameCropLeftOffset + frameCropRightOffset); displayHeight -= cropUnitY * (frameCropTopOffset + frameCropBottomOffset); } let colourPrimaries = 2; let transferCharacteristics = 2; let matrixCoefficients = 2; let fullRangeFlag = 0; let numReorderFrames = null; let maxDecFrameBuffering = null; const vuiParametersPresentFlag = bitstream.readBits(1); if (vuiParametersPresentFlag) { const aspectRatioInfoPresentFlag = bitstream.readBits(1); if (aspectRatioInfoPresentFlag) { const aspectRatioIdc = bitstream.readBits(8); if (aspectRatioIdc === 255) { bitstream.skipBits(16); bitstream.skipBits(16); } } const overscanInfoPresentFlag = bitstream.readBits(1); if (overscanInfoPresentFlag) { bitstream.skipBits(1); } const videoSignalTypePresentFlag = bitstream.readBits(1); if (videoSignalTypePresentFlag) { bitstream.skipBits(3); fullRangeFlag = bitstream.readBits(1); const colourDescriptionPresentFlag = bitstream.readBits(1); if (colourDescriptionPresentFlag) { colourPrimaries = bitstream.readBits(8); transferCharacteristics = bitstream.readBits(8); matrixCoefficients = bitstream.readBits(8); } } const chromaLocInfoPresentFlag = bitstream.readBits(1); if (chromaLocInfoPresentFlag) { readExpGolomb(bitstream); readExpGolomb(bitstream); } const timingInfoPresentFlag = bitstream.readBits(1); if (timingInfoPresentFlag) { bitstream.skipBits(32); bitstream.skipBits(32); bitstream.skipBits(1); } const nalHrdParametersPresentFlag = bitstream.readBits(1); if (nalHrdParametersPresentFlag) { skipAvcHrdParameters(bitstream); } const vclHrdParametersPresentFlag = bitstream.readBits(1); if (vclHrdParametersPresentFlag) { skipAvcHrdParameters(bitstream); } if (nalHrdParametersPresentFlag || vclHrdParametersPresentFlag) { bitstream.skipBits(1); } bitstream.skipBits(1); const bitstreamRestrictionFlag = bitstream.readBits(1); if (bitstreamRestrictionFlag) { bitstream.skipBits(1); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); numReorderFrames = readExpGolomb(bitstream); maxDecFrameBuffering = readExpGolomb(bitstream); } } if (numReorderFrames === null) { assert(maxDecFrameBuffering === null); const constraintSet3Flag = constraintFlags & 16; if ((profileIdc === 44 || profileIdc === 86 || profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244) && constraintSet3Flag) { numReorderFrames = 0; maxDecFrameBuffering = 0; } else { const picWidthInMbs = picWidthInMbsMinus1 + 1; const picHeightInMapUnits = picHeightInMapUnitsMinus1 + 1; const frameHeightInMbs = (2 - frameMbsOnlyFlag) * picHeightInMapUnits; const levelInfo = AVC_LEVEL_TABLE.find((x) => x.level >= levelIdc) ?? last(AVC_LEVEL_TABLE); const maxDpbFrames = Math.min(Math.floor(levelInfo.maxDpbMbs / (picWidthInMbs * frameHeightInMbs)), 16); numReorderFrames = maxDpbFrames; maxDecFrameBuffering = maxDpbFrames; } } assert(maxDecFrameBuffering !== null); return { profileIdc, constraintFlags, levelIdc, frameMbsOnlyFlag, chromaFormatIdc, bitDepthLumaMinus8, bitDepthChromaMinus8, codedWidth, codedHeight, displayWidth, displayHeight, colourPrimaries, matrixCoefficients, transferCharacteristics, fullRangeFlag, numReorderFrames, maxDecFrameBuffering }; } catch (error) { console.error("Error parsing AVC SPS:", error); return null; } }; var skipAvcHrdParameters = (bitstream) => { const cpb_cnt_minus1 = readExpGolomb(bitstream); bitstream.skipBits(4); bitstream.skipBits(4); for (let i = 0;i <= cpb_cnt_minus1; i++) { readExpGolomb(bitstream); readExpGolomb(bitstream); bitstream.skipBits(1); } bitstream.skipBits(5); bitstream.skipBits(5); bitstream.skipBits(5); bitstream.skipBits(5); }; var iterateHevcNalUnits = (packetData, decoderConfig) => { if (decoderConfig.description) { const bytes = toUint8Array(decoderConfig.description); const lengthSizeMinusOne = bytes[21] & 3; const lengthSize = lengthSizeMinusOne + 1; return iterateNalUnitsInLengthPrefixed(packetData, lengthSize); } else { return iterateNalUnitsInAnnexB(packetData); } }; var iterateHevcNalUnitsAnnexB = function* (packetData) { yield* iterateNalUnitsInAnnexB(packetData); }; var extractNalUnitTypeForHevc = (byte) => { return byte >> 1 & 63; }; var parseHevcSps = (sps) => { try { const bitstream = new Bitstream(removeEmulationPreventionBytes(sps)); bitstream.skipBits(16); bitstream.readBits(4); const spsMaxSubLayersMinus1 = bitstream.readBits(3); const spsTemporalIdNestingFlag = bitstream.readBits(1); const { general_profile_space, general_tier_flag, general_profile_idc, general_profile_compatibility_flags, general_constraint_indicator_flags, general_level_idc } = parseProfileTierLevel(bitstream, spsMaxSubLayersMinus1); readExpGolomb(bitstream); const chromaFormatIdc = readExpGolomb(bitstream); let separateColourPlaneFlag = 0; if (chromaFormatIdc === 3) { separateColourPlaneFlag = bitstream.readBits(1); } const picWidthInLumaSamples = readExpGolomb(bitstream); const picHeightInLumaSamples = readExpGolomb(bitstream); let displayWidth = picWidthInLumaSamples; let displayHeight = picHeightInLumaSamples; if (bitstream.readBits(1)) { const confWinLeftOffset = readExpGolomb(bitstream); const confWinRightOffset = readExpGolomb(bitstream); const confWinTopOffset = readExpGolomb(bitstream); const confWinBottomOffset = readExpGolomb(bitstream); let subWidthC = 1; let subHeightC = 1; const chromaArrayType = separateColourPlaneFlag === 0 ? chromaFormatIdc : 0; if (chromaArrayType === 1) { subWidthC = 2; subHeightC = 2; } else if (chromaArrayType === 2) { subWidthC = 2; subHeightC = 1; } displayWidth -= (confWinLeftOffset + confWinRightOffset) * subWidthC; displayHeight -= (confWinTopOffset + confWinBottomOffset) * subHeightC; } const bitDepthLumaMinus8 = readExpGolomb(bitstream); const bitDepthChromaMinus8 = readExpGolomb(bitstream); readExpGolomb(bitstream); const spsSubLayerOrderingInfoPresentFlag = bitstream.readBits(1); const startI = spsSubLayerOrderingInfoPresentFlag ? 0 : spsMaxSubLayersMinus1; let spsMaxNumReorderPics = 0; for (let i = startI;i <= spsMaxSubLayersMinus1; i++) { readExpGolomb(bitstream); spsMaxNumReorderPics = readExpGolomb(bitstream); readExpGolomb(bitstream); } readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); if (bitstream.readBits(1)) { if (bitstream.readBits(1)) { skipScalingListData(bitstream); } } bitstream.skipBits(1); bitstream.skipBits(1); if (bitstream.readBits(1)) { bitstream.skipBits(4); bitstream.skipBits(4); readExpGolomb(bitstream); readExpGolomb(bitstream); bitstream.skipBits(1); } const numShortTermRefPicSets = readExpGolomb(bitstream); skipAllStRefPicSets(bitstream, numShortTermRefPicSets); if (bitstream.readBits(1)) { const numLongTermRefPicsSps = readExpGolomb(bitstream); for (let i = 0;i < numLongTermRefPicsSps; i++) { readExpGolomb(bitstream); bitstream.skipBits(1); } } bitstream.skipBits(1); bitstream.skipBits(1); let colourPrimaries = 2; let transferCharacteristics = 2; let matrixCoefficients = 2; let fullRangeFlag = 0; let minSpatialSegmentationIdc = 0; if (bitstream.readBits(1)) { const vui = parseHevcVui(bitstream, spsMaxSubLayersMinus1); colourPrimaries = vui.colourPrimaries; transferCharacteristics = vui.transferCharacteristics; matrixCoefficients = vui.matrixCoefficients; fullRangeFlag = vui.fullRangeFlag; minSpatialSegmentationIdc = vui.minSpatialSegmentationIdc; } return { displayWidth, displayHeight, colourPrimaries, transferCharacteristics, matrixCoefficients, fullRangeFlag, maxDecFrameBuffering: spsMaxNumReorderPics + 1, spsMaxSubLayersMinus1, spsTemporalIdNestingFlag, generalProfileSpace: general_profile_space, generalTierFlag: general_tier_flag, generalProfileIdc: general_profile_idc, generalProfileCompatibilityFlags: general_profile_compatibility_flags, generalConstraintIndicatorFlags: general_constraint_indicator_flags, generalLevelIdc: general_level_idc, chromaFormatIdc, bitDepthLumaMinus8, bitDepthChromaMinus8, minSpatialSegmentationIdc }; } catch (error) { console.error("Error parsing HEVC SPS:", error); return null; } }; var extractHevcDecoderConfigurationRecord = (packetData) => { try { const vpsUnits = []; const spsUnits = []; const ppsUnits = []; const seiUnits = []; for (const loc of iterateHevcNalUnitsAnnexB(packetData)) { const nalUnit = packetData.subarray(loc.offset, loc.offset + loc.length); const type = extractNalUnitTypeForHevc(nalUnit[0]); if (type === HevcNalUnitType.VPS_NUT) { vpsUnits.push(nalUnit); } else if (type === HevcNalUnitType.SPS_NUT) { spsUnits.push(nalUnit); } else if (type === HevcNalUnitType.PPS_NUT) { ppsUnits.push(nalUnit); } else if (type === HevcNalUnitType.PREFIX_SEI_NUT || type === HevcNalUnitType.SUFFIX_SEI_NUT) { seiUnits.push(nalUnit); } } if (spsUnits.length === 0 || ppsUnits.length === 0) return null; const spsInfo = parseHevcSps(spsUnits[0]); if (!spsInfo) return null; let parallelismType = 0; if (ppsUnits.length > 0) { const pps = ppsUnits[0]; const ppsBitstream = new Bitstream(removeEmulationPreventionBytes(pps)); ppsBitstream.skipBits(16); readExpGolomb(ppsBitstream); readExpGolomb(ppsBitstream); ppsBitstream.skipBits(1); ppsBitstream.skipBits(1); ppsBitstream.skipBits(3); ppsBitstream.skipBits(1); ppsBitstream.skipBits(1); readExpGolomb(ppsBitstream); readExpGolomb(ppsBitstream); readSignedExpGolomb(ppsBitstream); ppsBitstream.skipBits(1); ppsBitstream.skipBits(1); if (ppsBitstream.readBits(1)) { readExpGolomb(ppsBitstream); } readSignedExpGolomb(ppsBitstream); readSignedExpGolomb(ppsBitstream); ppsBitstream.skipBits(1); ppsBitstream.skipBits(1); ppsBitstream.skipBits(1); ppsBitstream.skipBits(1); const tiles_enabled_flag = ppsBitstream.readBits(1); const entropy_coding_sync_enabled_flag = ppsBitstream.readBits(1); if (!tiles_enabled_flag && !entropy_coding_sync_enabled_flag) parallelismType = 0; else if (tiles_enabled_flag && !entropy_coding_sync_enabled_flag) parallelismType = 2; else if (!tiles_enabled_flag && entropy_coding_sync_enabled_flag) parallelismType = 3; else parallelismType = 0; } const arrays = [ ...vpsUnits.length ? [ { arrayCompleteness: 1, nalUnitType: HevcNalUnitType.VPS_NUT, nalUnits: vpsUnits } ] : [], ...spsUnits.length ? [ { arrayCompleteness: 1, nalUnitType: HevcNalUnitType.SPS_NUT, nalUnits: spsUnits } ] : [], ...ppsUnits.length ? [ { arrayCompleteness: 1, nalUnitType: HevcNalUnitType.PPS_NUT, nalUnits: ppsUnits } ] : [], ...seiUnits.length ? [ { arrayCompleteness: 1, nalUnitType: extractNalUnitTypeForHevc(seiUnits[0][0]), nalUnits: seiUnits } ] : [] ]; const record = { configurationVersion: 1, generalProfileSpace: spsInfo.generalProfileSpace, generalTierFlag: spsInfo.generalTierFlag, generalProfileIdc: spsInfo.generalProfileIdc, generalProfileCompatibilityFlags: spsInfo.generalProfileCompatibilityFlags, generalConstraintIndicatorFlags: spsInfo.generalConstraintIndicatorFlags, generalLevelIdc: spsInfo.generalLevelIdc, minSpatialSegmentationIdc: spsInfo.minSpatialSegmentationIdc, parallelismType, chromaFormatIdc: spsInfo.chromaFormatIdc, bitDepthLumaMinus8: spsInfo.bitDepthLumaMinus8, bitDepthChromaMinus8: spsInfo.bitDepthChromaMinus8, avgFrameRate: 0, constantFrameRate: 0, numTemporalLayers: spsInfo.spsMaxSubLayersMinus1 + 1, temporalIdNested: spsInfo.spsTemporalIdNestingFlag, lengthSizeMinusOne: 3, arrays }; return record; } catch (error) { console.error("Error building HEVC Decoder Configuration Record:", error); return null; } }; var parseProfileTierLevel = (bitstream, maxNumSubLayersMinus1) => { const general_profile_space = bitstream.readBits(2); const general_tier_flag = bitstream.readBits(1); const general_profile_idc = bitstream.readBits(5); let general_profile_compatibility_flags = 0; for (let i = 0;i < 32; i++) { general_profile_compatibility_flags = general_profile_compatibility_flags << 1 | bitstream.readBits(1); } const general_constraint_indicator_flags = new Uint8Array(6); for (let i = 0;i < 6; i++) { general_constraint_indicator_flags[i] = bitstream.readBits(8); } const general_level_idc = bitstream.readBits(8); const sub_layer_profile_present_flag = []; const sub_layer_level_present_flag = []; for (let i = 0;i < maxNumSubLayersMinus1; i++) { sub_layer_profile_present_flag.push(bitstream.readBits(1)); sub_layer_level_present_flag.push(bitstream.readBits(1)); } if (maxNumSubLayersMinus1 > 0) { for (let i = maxNumSubLayersMinus1;i < 8; i++) { bitstream.skipBits(2); } } for (let i = 0;i < maxNumSubLayersMinus1; i++) { if (sub_layer_profile_present_flag[i]) bitstream.skipBits(88); if (sub_layer_level_present_flag[i]) bitstream.skipBits(8); } return { general_profile_space, general_tier_flag, general_profile_idc, general_profile_compatibility_flags, general_constraint_indicator_flags, general_level_idc }; }; var skipScalingListData = (bitstream) => { for (let sizeId = 0;sizeId < 4; sizeId++) { for (let matrixId = 0;matrixId < (sizeId === 3 ? 2 : 6); matrixId++) { const scaling_list_pred_mode_flag = bitstream.readBits(1); if (!scaling_list_pred_mode_flag) { readExpGolomb(bitstream); } else { const coefNum = Math.min(64, 1 << 4 + (sizeId << 1)); if (sizeId > 1) { readSignedExpGolomb(bitstream); } for (let i = 0;i < coefNum; i++) { readSignedExpGolomb(bitstream); } } } } }; var skipAllStRefPicSets = (bitstream, num_short_term_ref_pic_sets) => { const NumDeltaPocs = []; for (let stRpsIdx = 0;stRpsIdx < num_short_term_ref_pic_sets; stRpsIdx++) { NumDeltaPocs[stRpsIdx] = skipStRefPicSet(bitstream, stRpsIdx, num_short_term_ref_pic_sets, NumDeltaPocs); } }; var skipStRefPicSet = (bitstream, stRpsIdx, num_short_term_ref_pic_sets, NumDeltaPocs) => { let NumDeltaPocsThis = 0; let inter_ref_pic_set_prediction_flag = 0; let RefRpsIdx = 0; if (stRpsIdx !== 0) { inter_ref_pic_set_prediction_flag = bitstream.readBits(1); } if (inter_ref_pic_set_prediction_flag) { if (stRpsIdx === num_short_term_ref_pic_sets) { const delta_idx_minus1 = readExpGolomb(bitstream); RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1); } else { RefRpsIdx = stRpsIdx - 1; } bitstream.readBits(1); readExpGolomb(bitstream); const numDelta = NumDeltaPocs[RefRpsIdx] ?? 0; for (let j = 0;j <= numDelta; j++) { const used_by_curr_pic_flag = bitstream.readBits(1); if (!used_by_curr_pic_flag) { bitstream.readBits(1); } } NumDeltaPocsThis = NumDeltaPocs[RefRpsIdx]; } else { const num_negative_pics = readExpGolomb(bitstream); const num_positive_pics = readExpGolomb(bitstream); for (let i = 0;i < num_negative_pics; i++) { readExpGolomb(bitstream); bitstream.readBits(1); } for (let i = 0;i < num_positive_pics; i++) { readExpGolomb(bitstream); bitstream.readBits(1); } NumDeltaPocsThis = num_negative_pics + num_positive_pics; } return NumDeltaPocsThis; }; var parseHevcVui = (bitstream, sps_max_sub_layers_minus1) => { let colourPrimaries = 2; let transferCharacteristics = 2; let matrixCoefficients = 2; let fullRangeFlag = 0; let minSpatialSegmentationIdc = 0; if (bitstream.readBits(1)) { const aspect_ratio_idc = bitstream.readBits(8); if (aspect_ratio_idc === 255) { bitstream.readBits(16); bitstream.readBits(16); } } if (bitstream.readBits(1)) { bitstream.readBits(1); } if (bitstream.readBits(1)) { bitstream.readBits(3); fullRangeFlag = bitstream.readBits(1); if (bitstream.readBits(1)) { colourPrimaries = bitstream.readBits(8); transferCharacteristics = bitstream.readBits(8); matrixCoefficients = bitstream.readBits(8); } } if (bitstream.readBits(1)) { readExpGolomb(bitstream); readExpGolomb(bitstream); } bitstream.readBits(1); bitstream.readBits(1); bitstream.readBits(1); if (bitstream.readBits(1)) { readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); } if (bitstream.readBits(1)) { bitstream.readBits(32); bitstream.readBits(32); if (bitstream.readBits(1)) { readExpGolomb(bitstream); } if (bitstream.readBits(1)) { skipHevcHrdParameters(bitstream, true, sps_max_sub_layers_minus1); } } if (bitstream.readBits(1)) { bitstream.readBits(1); bitstream.readBits(1); bitstream.readBits(1); minSpatialSegmentationIdc = readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); } return { colourPrimaries, transferCharacteristics, matrixCoefficients, fullRangeFlag, minSpatialSegmentationIdc }; }; var skipHevcHrdParameters = (bitstream, commonInfPresentFlag, maxNumSubLayersMinus1) => { let nal_hrd_parameters_present_flag = false; let vcl_hrd_parameters_present_flag = false; let sub_pic_hrd_params_present_flag = false; if (commonInfPresentFlag) { nal_hrd_parameters_present_flag = bitstream.readBits(1) === 1; vcl_hrd_parameters_present_flag = bitstream.readBits(1) === 1; if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) { sub_pic_hrd_params_present_flag = bitstream.readBits(1) === 1; if (sub_pic_hrd_params_present_flag) { bitstream.readBits(8); bitstream.readBits(5); bitstream.readBits(1); bitstream.readBits(5); } bitstream.readBits(4); bitstream.readBits(4); if (sub_pic_hrd_params_present_flag) { bitstream.readBits(4); } bitstream.readBits(5); bitstream.readBits(5); bitstream.readBits(5); } } for (let i = 0;i <= maxNumSubLayersMinus1; i++) { const fixed_pic_rate_general_flag = bitstream.readBits(1) === 1; let fixed_pic_rate_within_cvs_flag = true; if (!fixed_pic_rate_general_flag) { fixed_pic_rate_within_cvs_flag = bitstream.readBits(1) === 1; } let low_delay_hrd_flag = false; if (fixed_pic_rate_within_cvs_flag) { readExpGolomb(bitstream); } else { low_delay_hrd_flag = bitstream.readBits(1) === 1; } let CpbCnt = 1; if (!low_delay_hrd_flag) { const cpb_cnt_minus1 = readExpGolomb(bitstream); CpbCnt = cpb_cnt_minus1 + 1; } if (nal_hrd_parameters_present_flag) { skipSubLayerHrdParameters(bitstream, CpbCnt, sub_pic_hrd_params_present_flag); } if (vcl_hrd_parameters_present_flag) { skipSubLayerHrdParameters(bitstream, CpbCnt, sub_pic_hrd_params_present_flag); } } }; var skipSubLayerHrdParameters = (bitstream, CpbCnt, sub_pic_hrd_params_present_flag) => { for (let i = 0;i < CpbCnt; i++) { readExpGolomb(bitstream); readExpGolomb(bitstream); if (sub_pic_hrd_params_present_flag) { readExpGolomb(bitstream); readExpGolomb(bitstream); } bitstream.readBits(1); } }; var extractVp9CodecInfoFromPacket = (packet) => { const bitstream = new Bitstream(packet); const frameMarker = bitstream.readBits(2); if (frameMarker !== 2) { return null; } const profileLowBit = bitstream.readBits(1); const profileHighBit = bitstream.readBits(1); const profile = (profileHighBit << 1) + profileLowBit; if (profile === 3) { bitstream.skipBits(1); } const showExistingFrame = bitstream.readBits(1); if (showExistingFrame === 1) { return null; } const frameType = bitstream.readBits(1); if (frameType !== 0) { return null; } bitstream.skipBits(2); const syncCode = bitstream.readBits(24); if (syncCode !== 4817730) { return null; } let bitDepth = 8; if (profile >= 2) { const tenOrTwelveBit = bitstream.readBits(1); bitDepth = tenOrTwelveBit ? 12 : 10; } const colorSpace = bitstream.readBits(3); let chromaSubsampling = 0; let videoFullRangeFlag = 0; if (colorSpace !== 7) { const colorRange = bitstream.readBits(1); videoFullRangeFlag = colorRange; if (profile === 1 || profile === 3) { const subsamplingX = bitstream.readBits(1); const subsamplingY = bitstream.readBits(1); chromaSubsampling = !subsamplingX && !subsamplingY ? 3 : subsamplingX && !subsamplingY ? 2 : 1; bitstream.skipBits(1); } else { chromaSubsampling = 1; } } else { chromaSubsampling = 3; videoFullRangeFlag = 1; } const widthMinusOne = bitstream.readBits(16); const heightMinusOne = bitstream.readBits(16); const width = widthMinusOne + 1; const height = heightMinusOne + 1; const pictureSize = width * height; let level = last(VP9_LEVEL_TABLE).level; for (const entry of VP9_LEVEL_TABLE) { if (pictureSize <= entry.maxPictureSize) { level = entry.level; break; } } const matrixCoefficients = colorSpace === 7 ? 0 : colorSpace === 2 ? 1 : colorSpace === 1 ? 6 : 2; const colourPrimaries = colorSpace === 2 ? 1 : colorSpace === 1 ? 6 : 2; const transferCharacteristics = colorSpace === 2 ? 1 : colorSpace === 1 ? 6 : 2; return { profile, level, bitDepth, chromaSubsampling, videoFullRangeFlag, colourPrimaries, transferCharacteristics, matrixCoefficients }; }; var iterateAv1PacketObus = function* (packet) { const bitstream = new Bitstream(packet); const readLeb128 = () => { let value = 0; for (let i = 0;i < 8; i++) { const byte = bitstream.readAlignedByte(); value |= (byte & 127) << i * 7; if (!(byte & 128)) { break; } if (i === 7 && byte & 128) { return null; } } if (value >= 2 ** 32 - 1) { return null; } return value; }; while (bitstream.getBitsLeft() >= 8) { bitstream.skipBits(1); const obuType = bitstream.readBits(4); const obuExtension = bitstream.readBits(1); const obuHasSizeField = bitstream.readBits(1); bitstream.skipBits(1); if (obuExtension) { bitstream.skipBits(8); } let obuSize; if (obuHasSizeField) { const obuSizeValue = readLeb128(); if (obuSizeValue === null) return; obuSize = obuSizeValue; } else { obuSize = Math.floor(bitstream.getBitsLeft() / 8); } assert(bitstream.pos % 8 === 0); yield { type: obuType, data: packet.subarray(bitstream.pos / 8, bitstream.pos / 8 + obuSize) }; bitstream.skipBits(obuSize * 8); } }; var extractAv1CodecInfoFromPacket = (packet) => { for (const { type, data } of iterateAv1PacketObus(packet)) { if (type !== 1) { continue; } const bitstream = new Bitstream(data); const seqProfile = bitstream.readBits(3); const stillPicture = bitstream.readBits(1); const reducedStillPictureHeader = bitstream.readBits(1); let seqLevel = 0; let seqTier = 0; let bufferDelayLengthMinus1 = 0; if (reducedStillPictureHeader) { seqLevel = bitstream.readBits(5); } else { const timingInfoPresentFlag = bitstream.readBits(1); if (timingInfoPresentFlag) { bitstream.skipBits(32); bitstream.skipBits(32); const equalPictureInterval = bitstream.readBits(1); if (equalPictureInterval) { return null; } } const decoderModelInfoPresentFlag = bitstream.readBits(1); if (decoderModelInfoPresentFlag) { bufferDelayLengthMinus1 = bitstream.readBits(5); bitstream.skipBits(32); bitstream.skipBits(5); bitstream.skipBits(5); } const operatingPointsCntMinus1 = bitstream.readBits(5); for (let i = 0;i <= operatingPointsCntMinus1; i++) { bitstream.skipBits(12); const seqLevelIdx = bitstream.readBits(5); if (i === 0) { seqLevel = seqLevelIdx; } if (seqLevelIdx > 7) { const seqTierTemp = bitstream.readBits(1); if (i === 0) { seqTier = seqTierTemp; } } if (decoderModelInfoPresentFlag) { const decoderModelPresentForThisOp = bitstream.readBits(1); if (decoderModelPresentForThisOp) { const n = bufferDelayLengthMinus1 + 1; bitstream.skipBits(n); bitstream.skipBits(n); bitstream.skipBits(1); } } const initialDisplayDelayPresentFlag = bitstream.readBits(1); if (initialDisplayDelayPresentFlag) { bitstream.skipBits(4); } } } const frameWidthBitsMinus1 = bitstream.readBits(4); const frameHeightBitsMinus1 = bitstream.readBits(4); const n1 = frameWidthBitsMinus1 + 1; bitstream.skipBits(n1); const n2 = frameHeightBitsMinus1 + 1; bitstream.skipBits(n2); let frameIdNumbersPresentFlag = 0; if (reducedStillPictureHeader) { frameIdNumbersPresentFlag = 0; } else { frameIdNumbersPresentFlag = bitstream.readBits(1); } if (frameIdNumbersPresentFlag) { bitstream.skipBits(4); bitstream.skipBits(3); } bitstream.skipBits(1); bitstream.skipBits(1); bitstream.skipBits(1); if (!reducedStillPictureHeader) { bitstream.skipBits(1); bitstream.skipBits(1); bitstream.skipBits(1); bitstream.skipBits(1); const enableOrderHint = bitstream.readBits(1); if (enableOrderHint) { bitstream.skipBits(1); bitstream.skipBits(1); } const seqChooseScreenContentTools = bitstream.readBits(1); let seqForceScreenContentTools = 0; if (seqChooseScreenContentTools) { seqForceScreenContentTools = 2; } else { seqForceScreenContentTools = bitstream.readBits(1); } if (seqForceScreenContentTools > 0) { const seqChooseIntegerMv = bitstream.readBits(1); if (!seqChooseIntegerMv) { bitstream.skipBits(1); } } if (enableOrderHint) { bitstream.skipBits(3); } } bitstream.skipBits(1); bitstream.skipBits(1); bitstream.skipBits(1); const highBitdepth = bitstream.readBits(1); let bitDepth = 8; if (seqProfile === 2 && highBitdepth) { const twelveBit = bitstream.readBits(1); bitDepth = twelveBit ? 12 : 10; } else if (seqProfile <= 2) { bitDepth = highBitdepth ? 10 : 8; } let monochrome = 0; if (seqProfile !== 1) { monochrome = bitstream.readBits(1); } let chromaSubsamplingX = 1; let chromaSubsamplingY = 1; let chromaSamplePosition = 0; if (!monochrome) { if (seqProfile === 0) { chromaSubsamplingX = 1; chromaSubsamplingY = 1; } else if (seqProfile === 1) { chromaSubsamplingX = 0; chromaSubsamplingY = 0; } else { if (bitDepth === 12) { chromaSubsamplingX = bitstream.readBits(1); if (chromaSubsamplingX) { chromaSubsamplingY = bitstream.readBits(1); } } } if (chromaSubsamplingX && chromaSubsamplingY) { chromaSamplePosition = bitstream.readBits(2); } } return { profile: seqProfile, level: seqLevel, tier: seqTier, bitDepth, monochrome, chromaSubsamplingX, chromaSubsamplingY, chromaSamplePosition }; } return null; }; var parseOpusIdentificationHeader = (bytes) => { const view = toDataView(bytes); const outputChannelCount = view.getUint8(9); const preSkip = view.getUint16(10, true); const inputSampleRate = view.getUint32(12, true); const outputGain = view.getInt16(16, true); const channelMappingFamily = view.getUint8(18); let channelMappingTable = null; if (channelMappingFamily) { channelMappingTable = bytes.subarray(19, 19 + 2 + outputChannelCount); } return { outputChannelCount, preSkip, inputSampleRate, outputGain, channelMappingFamily, channelMappingTable }; }; var OPUS_FRAME_DURATION_TABLE = [ 480, 960, 1920, 2880, 480, 960, 1920, 2880, 480, 960, 1920, 2880, 480, 960, 480, 960, 120, 240, 480, 960, 120, 240, 480, 960, 120, 240, 480, 960, 120, 240, 480, 960 ]; var parseOpusTocByte = (packet) => { const config = packet[0] >> 3; return { durationInSamples: OPUS_FRAME_DURATION_TABLE[config] }; }; var parseModesFromVorbisSetupPacket = (setupHeader) => { if (setupHeader.length < 7) { throw new Error("Setup header is too short."); } if (setupHeader[0] !== 5) { throw new Error("Wrong packet type in Setup header."); } const signature = String.fromCharCode(...setupHeader.slice(1, 7)); if (signature !== "vorbis") { throw new Error("Invalid packet signature in Setup header."); } const bufSize = setupHeader.length; const revBuffer = new Uint8Array(bufSize); for (let i = 0;i < bufSize; i++) { revBuffer[i] = setupHeader[bufSize - 1 - i]; } const bitstream = new Bitstream(revBuffer); let gotFramingBit = 0; while (bitstream.getBitsLeft() > 97) { if (bitstream.readBits(1) === 1) { gotFramingBit = bitstream.pos; break; } } if (gotFramingBit === 0) { throw new Error("Invalid Setup header: framing bit not found."); } let modeCount = 0; let gotModeHeader = false; let lastModeCount = 0; while (bitstream.getBitsLeft() >= 97) { const tempPos = bitstream.pos; const a = bitstream.readBits(8); const b = bitstream.readBits(16); const c = bitstream.readBits(16); if (a > 63 || b !== 0 || c !== 0) { bitstream.pos = tempPos; break; } bitstream.skipBits(1); modeCount++; if (modeCount > 64) { break; } const bsClone = bitstream.clone(); const candidate = bsClone.readBits(6) + 1; if (candidate === modeCount) { gotModeHeader = true; lastModeCount = modeCount; } } if (!gotModeHeader) { throw new Error("Invalid Setup header: mode header not found."); } if (lastModeCount > 63) { throw new Error(`Unsupported mode count: ${lastModeCount}.`); } const finalModeCount = lastModeCount; bitstream.pos = 0; bitstream.skipBits(gotFramingBit); const modeBlockflags = Array(finalModeCount).fill(0); for (let i = finalModeCount - 1;i >= 0; i--) { bitstream.skipBits(40); modeBlockflags[i] = bitstream.readBits(1); } return { modeBlockflags }; }; var determineVideoPacketType = (codec, decoderConfig, packetData) => { switch (codec) { case "avc": { for (const loc of iterateAvcNalUnits(packetData, decoderConfig)) { const nalTypeByte = packetData[loc.offset]; const type = extractNalUnitTypeForAvc(nalTypeByte); if (type >= AvcNalUnitType.NON_IDR_SLICE && type <= AvcNalUnitType.SLICE_DPC) { return "delta"; } if (type === AvcNalUnitType.IDR) { return "key"; } if (type === AvcNalUnitType.SEI && (!isChromium() || getChromiumVersion() >= 144)) { const nalUnit = packetData.subarray(loc.offset, loc.offset + loc.length); const bytes = removeEmulationPreventionBytes(nalUnit); let pos = 1; do { let payloadType = 0; while (true) { const nextByte = bytes[pos++]; if (nextByte === undefined) break; payloadType += nextByte; if (nextByte < 255) { break; } } let payloadSize = 0; while (true) { const nextByte = bytes[pos++]; if (nextByte === undefined) break; payloadSize += nextByte; if (nextByte < 255) { break; } } const PAYLOAD_TYPE_RECOVERY_POINT = 6; if (payloadType === PAYLOAD_TYPE_RECOVERY_POINT) { const bitstream = new Bitstream(bytes); bitstream.pos = 8 * pos; const recoveryFrameCount = readExpGolomb(bitstream); const exactMatchFlag = bitstream.readBits(1); if (recoveryFrameCount === 0 && exactMatchFlag === 1) { return "key"; } } pos += payloadSize; } while (pos < bytes.length - 1); } } return "delta"; } ; case "hevc": { for (const loc of iterateHevcNalUnits(packetData, decoderConfig)) { const type = extractNalUnitTypeForHevc(packetData[loc.offset]); if (type < HevcNalUnitType.BLA_W_LP) { return "delta"; } if (type <= HevcNalUnitType.RSV_IRAP_VCL23) { return "key"; } } return "delta"; } ; case "vp8": { const frameType = packetData[0] & 1; return frameType === 0 ? "key" : "delta"; } ; case "vp9": { const bitstream = new Bitstream(packetData); if (bitstream.readBits(2) !== 2) { return null; } const profileLowBit = bitstream.readBits(1); const profileHighBit = bitstream.readBits(1); const profile = (profileHighBit << 1) + profileLowBit; if (profile === 3) { bitstream.skipBits(1); } const showExistingFrame = bitstream.readBits(1); if (showExistingFrame) { return null; } const frameType = bitstream.readBits(1); return frameType === 0 ? "key" : "delta"; } ; case "av1": { let reducedStillPictureHeader = false; for (const { type, data } of iterateAv1PacketObus(packetData)) { if (type === 1) { const bitstream = new Bitstream(data); bitstream.skipBits(4); reducedStillPictureHeader = !!bitstream.readBits(1); } else if (type === 3 || type === 6 || type === 7) { if (reducedStillPictureHeader) { return "key"; } const bitstream = new Bitstream(data); const showExistingFrame = bitstream.readBits(1); if (showExistingFrame) { return null; } const frameType = bitstream.readBits(2); return frameType === 0 ? "key" : "delta"; } } return null; } ; default: { assertNever(codec); assert(false); } ; } }; var FlacBlockType; (function(FlacBlockType2) { FlacBlockType2[FlacBlockType2["STREAMINFO"] = 0] = "STREAMINFO"; FlacBlockType2[FlacBlockType2["VORBIS_COMMENT"] = 4] = "VORBIS_COMMENT"; FlacBlockType2[FlacBlockType2["PICTURE"] = 6] = "PICTURE"; })(FlacBlockType || (FlacBlockType = {})); var readVorbisComments = (bytes, metadataTags) => { const commentView = toDataView(bytes); let commentPos = 0; const vendorStringLength = commentView.getUint32(commentPos, true); commentPos += 4; const vendorString = textDecoder.decode(bytes.subarray(commentPos, commentPos + vendorStringLength)); commentPos += vendorStringLength; if (vendorStringLength > 0) { metadataTags.raw ??= {}; metadataTags.raw["vendor"] ??= vendorString; } const listLength = commentView.getUint32(commentPos, true); commentPos += 4; for (let i = 0;i < listLength; i++) { const stringLength = commentView.getUint32(commentPos, true); commentPos += 4; const string = textDecoder.decode(bytes.subarray(commentPos, commentPos + stringLength)); commentPos += stringLength; const separatorIndex = string.indexOf("="); if (separatorIndex === -1) { continue; } const key4 = string.slice(0, separatorIndex).toUpperCase(); const value = string.slice(separatorIndex + 1); metadataTags.raw ??= {}; metadataTags.raw[key4] ??= value; switch (key4) { case "TITLE": { metadataTags.title ??= value; } ; break; case "DESCRIPTION": { metadataTags.description ??= value; } ; break; case "ARTIST": { metadataTags.artist ??= value; } ; break; case "ALBUM": { metadataTags.album ??= value; } ; break; case "ALBUMARTIST": { metadataTags.albumArtist ??= value; } ; break; case "COMMENT": { metadataTags.comment ??= value; } ; break; case "LYRICS": { metadataTags.lyrics ??= value; } ; break; case "TRACKNUMBER": { const parts = value.split("/"); const trackNum = Number.parseInt(parts[0], 10); const tracksTotal = parts[1] && Number.parseInt(parts[1], 10); if (Number.isInteger(trackNum) && trackNum > 0) { metadataTags.trackNumber ??= trackNum; } if (tracksTotal && Number.isInteger(tracksTotal) && tracksTotal > 0) { metadataTags.tracksTotal ??= tracksTotal; } } ; break; case "TRACKTOTAL": { const tracksTotal = Number.parseInt(value, 10); if (Number.isInteger(tracksTotal) && tracksTotal > 0) { metadataTags.tracksTotal ??= tracksTotal; } } ; break; case "DISCNUMBER": { const parts = value.split("/"); const discNum = Number.parseInt(parts[0], 10); const discsTotal = parts[1] && Number.parseInt(parts[1], 10); if (Number.isInteger(discNum) && discNum > 0) { metadataTags.discNumber ??= discNum; } if (discsTotal && Number.isInteger(discsTotal) && discsTotal > 0) { metadataTags.discsTotal ??= discsTotal; } } ; break; case "DISCTOTAL": { const discsTotal = Number.parseInt(value, 10); if (Number.isInteger(discsTotal) && discsTotal > 0) { metadataTags.discsTotal ??= discsTotal; } } ; break; case "DATE": { const date = new Date(value); if (!Number.isNaN(date.getTime())) { metadataTags.date ??= date; } } ; break; case "GENRE": { metadataTags.genre ??= value; } ; break; case "METADATA_BLOCK_PICTURE": { const decoded = base64ToBytes(value); const view = toDataView(decoded); const pictureType = view.getUint32(0, false); const mediaTypeLength = view.getUint32(4, false); const mediaType = String.fromCharCode(...decoded.subarray(8, 8 + mediaTypeLength)); const descriptionLength = view.getUint32(8 + mediaTypeLength, false); const description = textDecoder.decode(decoded.subarray(12 + mediaTypeLength, 12 + mediaTypeLength + descriptionLength)); const dataLength = view.getUint32(mediaTypeLength + descriptionLength + 28); const data = decoded.subarray(mediaTypeLength + descriptionLength + 32, mediaTypeLength + descriptionLength + 32 + dataLength); metadataTags.images ??= []; metadataTags.images.push({ data, mimeType: mediaType, kind: pictureType === 3 ? "coverFront" : pictureType === 4 ? "coverBack" : "unknown", name: undefined, description: description || undefined }); } ; break; } } }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/demuxer.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ class Demuxer { constructor(input2) { this.input = input2; } } // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/custom-coder.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var customVideoDecoders = []; var customAudioDecoders = []; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/packet.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var PLACEHOLDER_DATA = /* @__PURE__ */ new Uint8Array(0); class EncodedPacket { constructor(data, type, timestamp, duration, sequenceNumber = -1, byteLength, sideData) { this.data = data; this.type = type; this.timestamp = timestamp; this.duration = duration; this.sequenceNumber = sequenceNumber; if (data === PLACEHOLDER_DATA && byteLength === undefined) { throw new Error("Internal error: byteLength must be explicitly provided when constructing metadata-only packets."); } if (byteLength === undefined) { byteLength = data.byteLength; } if (!(data instanceof Uint8Array)) { throw new TypeError("data must be a Uint8Array."); } if (type !== "key" && type !== "delta") { throw new TypeError('type must be either "key" or "delta".'); } if (!Number.isFinite(timestamp)) { throw new TypeError("timestamp must be a number."); } if (!Number.isFinite(duration) || duration < 0) { throw new TypeError("duration must be a non-negative number."); } if (!Number.isFinite(sequenceNumber)) { throw new TypeError("sequenceNumber must be a number."); } if (!Number.isInteger(byteLength) || byteLength < 0) { throw new TypeError("byteLength must be a non-negative integer."); } if (sideData !== undefined && (typeof sideData !== "object" || !sideData)) { throw new TypeError("sideData, when provided, must be an object."); } if (sideData?.alpha !== undefined && !(sideData.alpha instanceof Uint8Array)) { throw new TypeError("sideData.alpha, when provided, must be a Uint8Array."); } if (sideData?.alphaByteLength !== undefined && (!Number.isInteger(sideData.alphaByteLength) || sideData.alphaByteLength < 0)) { throw new TypeError("sideData.alphaByteLength, when provided, must be a non-negative integer."); } this.byteLength = byteLength; this.sideData = sideData ?? {}; if (this.sideData.alpha && this.sideData.alphaByteLength === undefined) { this.sideData.alphaByteLength = this.sideData.alpha.byteLength; } } get isMetadataOnly() { return this.data === PLACEHOLDER_DATA; } get microsecondTimestamp() { return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp); } get microsecondDuration() { return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration); } toEncodedVideoChunk() { if (this.isMetadataOnly) { throw new TypeError("Metadata-only packets cannot be converted to a video chunk."); } if (typeof EncodedVideoChunk === "undefined") { throw new Error("Your browser does not support EncodedVideoChunk."); } return new EncodedVideoChunk({ data: this.data, type: this.type, timestamp: this.microsecondTimestamp, duration: this.microsecondDuration }); } alphaToEncodedVideoChunk(type = this.type) { if (!this.sideData.alpha) { throw new TypeError("This packet does not contain alpha side data."); } if (this.isMetadataOnly) { throw new TypeError("Metadata-only packets cannot be converted to a video chunk."); } if (typeof EncodedVideoChunk === "undefined") { throw new Error("Your browser does not support EncodedVideoChunk."); } return new EncodedVideoChunk({ data: this.sideData.alpha, type, timestamp: this.microsecondTimestamp, duration: this.microsecondDuration }); } toEncodedAudioChunk() { if (this.isMetadataOnly) { throw new TypeError("Metadata-only packets cannot be converted to an audio chunk."); } if (typeof EncodedAudioChunk === "undefined") { throw new Error("Your browser does not support EncodedAudioChunk."); } return new EncodedAudioChunk({ data: this.data, type: this.type, timestamp: this.microsecondTimestamp, duration: this.microsecondDuration }); } static fromEncodedChunk(chunk, sideData) { if (!(chunk instanceof EncodedVideoChunk || chunk instanceof EncodedAudioChunk)) { throw new TypeError("chunk must be an EncodedVideoChunk or EncodedAudioChunk."); } const data = new Uint8Array(chunk.byteLength); chunk.copyTo(data); return new EncodedPacket(data, chunk.type, chunk.timestamp / 1e6, (chunk.duration ?? 0) / 1e6, undefined, undefined, sideData); } clone(options) { if (options !== undefined && (typeof options !== "object" || options === null)) { throw new TypeError("options, when provided, must be an object."); } if (options?.data !== undefined && !(options.data instanceof Uint8Array)) { throw new TypeError("options.data, when provided, must be a Uint8Array."); } if (options?.type !== undefined && options.type !== "key" && options.type !== "delta") { throw new TypeError('options.type, when provided, must be either "key" or "delta".'); } if (options?.timestamp !== undefined && !Number.isFinite(options.timestamp)) { throw new TypeError("options.timestamp, when provided, must be a number."); } if (options?.duration !== undefined && !Number.isFinite(options.duration)) { throw new TypeError("options.duration, when provided, must be a number."); } if (options?.sequenceNumber !== undefined && !Number.isFinite(options.sequenceNumber)) { throw new TypeError("options.sequenceNumber, when provided, must be a number."); } if (options?.sideData !== undefined && (typeof options.sideData !== "object" || options.sideData === null)) { throw new TypeError("options.sideData, when provided, must be an object."); } return new EncodedPacket(options?.data ?? this.data, options?.type ?? this.type, options?.timestamp ?? this.timestamp, options?.duration ?? this.duration, options?.sequenceNumber ?? this.sequenceNumber, this.byteLength, options?.sideData ?? this.sideData); } } // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/sample.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ polyfillSymbolDispose(); var lastVideoGcErrorLog = -Infinity; var lastAudioGcErrorLog = -Infinity; var finalizationRegistry = null; if (typeof FinalizationRegistry !== "undefined") { finalizationRegistry = new FinalizationRegistry((value) => { const now = Date.now(); if (value.type === "video") { if (now - lastVideoGcErrorLog >= 1000) { console.error(`A VideoSample was garbage collected without first being closed. For proper resource management,` + ` make sure to call close() on all your VideoSamples as soon as you're done using them.`); lastVideoGcErrorLog = now; } if (typeof VideoFrame !== "undefined" && value.data instanceof VideoFrame) { value.data.close(); } } else { if (now - lastAudioGcErrorLog >= 1000) { console.error(`An AudioSample was garbage collected without first being closed. For proper resource management,` + ` make sure to call close() on all your AudioSamples as soon as you're done using them.`); lastAudioGcErrorLog = now; } if (typeof AudioData !== "undefined" && value.data instanceof AudioData) { value.data.close(); } } }); } var VIDEO_SAMPLE_PIXEL_FORMATS = [ "I420", "I420P10", "I420P12", "I420A", "I420AP10", "I420AP12", "I422", "I422P10", "I422P12", "I422A", "I422AP10", "I422AP12", "I444", "I444P10", "I444P12", "I444A", "I444AP10", "I444AP12", "NV12", "RGBA", "RGBX", "BGRA", "BGRX" ]; var VIDEO_SAMPLE_PIXEL_FORMATS_SET = new Set(VIDEO_SAMPLE_PIXEL_FORMATS); class VideoSample { get displayWidth() { return this.rotation % 180 === 0 ? this.codedWidth : this.codedHeight; } get displayHeight() { return this.rotation % 180 === 0 ? this.codedHeight : this.codedWidth; } get microsecondTimestamp() { return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp); } get microsecondDuration() { return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration); } get hasAlpha() { return this.format && this.format.includes("A"); } constructor(data, init) { this._closed = false; if (data instanceof ArrayBuffer || typeof SharedArrayBuffer !== "undefined" && data instanceof SharedArrayBuffer || ArrayBuffer.isView(data)) { if (!init || typeof init !== "object") { throw new TypeError("init must be an object."); } if (init.format === undefined || !VIDEO_SAMPLE_PIXEL_FORMATS_SET.has(init.format)) { throw new TypeError("init.format must be one of: " + VIDEO_SAMPLE_PIXEL_FORMATS.join(", ")); } if (!Number.isInteger(init.codedWidth) || init.codedWidth <= 0) { throw new TypeError("init.codedWidth must be a positive integer."); } if (!Number.isInteger(init.codedHeight) || init.codedHeight <= 0) { throw new TypeError("init.codedHeight must be a positive integer."); } if (init.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) { throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270."); } if (!Number.isFinite(init.timestamp)) { throw new TypeError("init.timestamp must be a number."); } if (init.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) { throw new TypeError("init.duration, when provided, must be a non-negative number."); } this._data = toUint8Array(data).slice(); this._layout = init.layout ?? createDefaultPlaneLayout(init.format, init.codedWidth, init.codedHeight); this.format = init.format; this.codedWidth = init.codedWidth; this.codedHeight = init.codedHeight; this.rotation = init.rotation ?? 0; this.timestamp = init.timestamp; this.duration = init.duration ?? 0; this.colorSpace = new VideoSampleColorSpace(init.colorSpace); } else if (typeof VideoFrame !== "undefined" && data instanceof VideoFrame) { if (init?.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) { throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270."); } if (init?.timestamp !== undefined && !Number.isFinite(init?.timestamp)) { throw new TypeError("init.timestamp, when provided, must be a number."); } if (init?.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) { throw new TypeError("init.duration, when provided, must be a non-negative number."); } this._data = data; this._layout = null; this.format = data.format; this.codedWidth = data.displayWidth; this.codedHeight = data.displayHeight; this.rotation = init?.rotation ?? 0; this.timestamp = init?.timestamp ?? data.timestamp / 1e6; this.duration = init?.duration ?? (data.duration ?? 0) / 1e6; this.colorSpace = new VideoSampleColorSpace(data.colorSpace); } else if (typeof HTMLImageElement !== "undefined" && data instanceof HTMLImageElement || typeof SVGImageElement !== "undefined" && data instanceof SVGImageElement || typeof ImageBitmap !== "undefined" && data instanceof ImageBitmap || typeof HTMLVideoElement !== "undefined" && data instanceof HTMLVideoElement || typeof HTMLCanvasElement !== "undefined" && data instanceof HTMLCanvasElement || typeof OffscreenCanvas !== "undefined" && data instanceof OffscreenCanvas) { if (!init || typeof init !== "object") { throw new TypeError("init must be an object."); } if (init.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) { throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270."); } if (!Number.isFinite(init.timestamp)) { throw new TypeError("init.timestamp must be a number."); } if (init.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) { throw new TypeError("init.duration, when provided, must be a non-negative number."); } if (typeof VideoFrame !== "undefined") { return new VideoSample(new VideoFrame(data, { timestamp: Math.trunc(init.timestamp * SECOND_TO_MICROSECOND_FACTOR), duration: Math.trunc((init.duration ?? 0) * SECOND_TO_MICROSECOND_FACTOR) || undefined }), init); } let width = 0; let height = 0; if ("naturalWidth" in data) { width = data.naturalWidth; height = data.naturalHeight; } else if ("videoWidth" in data) { width = data.videoWidth; height = data.videoHeight; } else if ("width" in data) { width = Number(data.width); height = Number(data.height); } if (!width || !height) { throw new TypeError("Could not determine dimensions."); } const canvas = new OffscreenCanvas(width, height); const context = canvas.getContext("2d", { alpha: isFirefox(), willReadFrequently: true }); assert(context); context.drawImage(data, 0, 0); this._data = canvas; this._layout = null; this.format = "RGBX"; this.codedWidth = width; this.codedHeight = height; this.rotation = init.rotation ?? 0; this.timestamp = init.timestamp; this.duration = init.duration ?? 0; this.colorSpace = new VideoSampleColorSpace({ matrix: "rgb", primaries: "bt709", transfer: "iec61966-2-1", fullRange: true }); } else { throw new TypeError("Invalid data type: Must be a BufferSource or CanvasImageSource."); } finalizationRegistry?.register(this, { type: "video", data: this._data }, this); } clone() { if (this._closed) { throw new Error("VideoSample is closed."); } assert(this._data !== null); if (isVideoFrame(this._data)) { return new VideoSample(this._data.clone(), { timestamp: this.timestamp, duration: this.duration, rotation: this.rotation }); } else if (this._data instanceof Uint8Array) { assert(this._layout); return new VideoSample(this._data, { format: this.format, layout: this._layout, codedWidth: this.codedWidth, codedHeight: this.codedHeight, timestamp: this.timestamp, duration: this.duration, colorSpace: this.colorSpace, rotation: this.rotation }); } else { return new VideoSample(this._data, { format: this.format, codedWidth: this.codedWidth, codedHeight: this.codedHeight, timestamp: this.timestamp, duration: this.duration, colorSpace: this.colorSpace, rotation: this.rotation }); } } close() { if (this._closed) { return; } finalizationRegistry?.unregister(this); if (isVideoFrame(this._data)) { this._data.close(); } else { this._data = null; } this._closed = true; } allocationSize(options = {}) { validateVideoFrameCopyToOptions(options); if (this._closed) { throw new Error("VideoSample is closed."); } if (this.format === null) { throw new Error("Cannot get allocation size when format is null. Sorry!"); } assert(this._data !== null); if (!isVideoFrame(this._data)) { if (options.colorSpace || options.format && options.format !== this.format || options.layout || options.rect) { const videoFrame = this.toVideoFrame(); const size4 = videoFrame.allocationSize(options); videoFrame.close(); return size4; } } if (isVideoFrame(this._data)) { return this._data.allocationSize(options); } else if (this._data instanceof Uint8Array) { return this._data.byteLength; } else { return this.codedWidth * this.codedHeight * 4; } } async copyTo(destination, options = {}) { if (!isAllowSharedBufferSource(destination)) { throw new TypeError("destination must be an ArrayBuffer or an ArrayBuffer view."); } validateVideoFrameCopyToOptions(options); if (this._closed) { throw new Error("VideoSample is closed."); } if (this.format === null) { throw new Error("Cannot copy video sample data when format is null. Sorry!"); } assert(this._data !== null); if (!isVideoFrame(this._data)) { if (options.colorSpace || options.format && options.format !== this.format || options.layout || options.rect) { const videoFrame = this.toVideoFrame(); const layout = await videoFrame.copyTo(destination, options); videoFrame.close(); return layout; } } if (isVideoFrame(this._data)) { return this._data.copyTo(destination, options); } else if (this._data instanceof Uint8Array) { assert(this._layout); const dest = toUint8Array(destination); dest.set(this._data); return this._layout; } else { const canvas = this._data; const context = canvas.getContext("2d"); assert(context); const imageData = context.getImageData(0, 0, this.codedWidth, this.codedHeight); const dest = toUint8Array(destination); dest.set(imageData.data); return [{ offset: 0, stride: 4 * this.codedWidth }]; } } toVideoFrame() { if (this._closed) { throw new Error("VideoSample is closed."); } assert(this._data !== null); if (isVideoFrame(this._data)) { return new VideoFrame(this._data, { timestamp: this.microsecondTimestamp, duration: this.microsecondDuration || undefined }); } else if (this._data instanceof Uint8Array) { return new VideoFrame(this._data, { format: this.format, codedWidth: this.codedWidth, codedHeight: this.codedHeight, timestamp: this.microsecondTimestamp, duration: this.microsecondDuration || undefined, colorSpace: this.colorSpace }); } else { return new VideoFrame(this._data, { timestamp: this.microsecondTimestamp, duration: this.microsecondDuration || undefined }); } } draw(context, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) { let sx = 0; let sy = 0; let sWidth = this.displayWidth; let sHeight = this.displayHeight; let dx = 0; let dy = 0; let dWidth = this.displayWidth; let dHeight = this.displayHeight; if (arg5 !== undefined) { sx = arg1; sy = arg2; sWidth = arg3; sHeight = arg4; dx = arg5; dy = arg6; if (arg7 !== undefined) { dWidth = arg7; dHeight = arg8; } else { dWidth = sWidth; dHeight = sHeight; } } else { dx = arg1; dy = arg2; if (arg3 !== undefined) { dWidth = arg3; dHeight = arg4; } } if (!(typeof CanvasRenderingContext2D !== "undefined" && context instanceof CanvasRenderingContext2D || typeof OffscreenCanvasRenderingContext2D !== "undefined" && context instanceof OffscreenCanvasRenderingContext2D)) { throw new TypeError("context must be a CanvasRenderingContext2D or OffscreenCanvasRenderingContext2D."); } if (!Number.isFinite(sx)) { throw new TypeError("sx must be a number."); } if (!Number.isFinite(sy)) { throw new TypeError("sy must be a number."); } if (!Number.isFinite(sWidth) || sWidth < 0) { throw new TypeError("sWidth must be a non-negative number."); } if (!Number.isFinite(sHeight) || sHeight < 0) { throw new TypeError("sHeight must be a non-negative number."); } if (!Number.isFinite(dx)) { throw new TypeError("dx must be a number."); } if (!Number.isFinite(dy)) { throw new TypeError("dy must be a number."); } if (!Number.isFinite(dWidth) || dWidth < 0) { throw new TypeError("dWidth must be a non-negative number."); } if (!Number.isFinite(dHeight) || dHeight < 0) { throw new TypeError("dHeight must be a non-negative number."); } if (this._closed) { throw new Error("VideoSample is closed."); } ({ sx, sy, sWidth, sHeight } = this._rotateSourceRegion(sx, sy, sWidth, sHeight, this.rotation)); const source = this.toCanvasImageSource(); context.save(); const centerX = dx + dWidth / 2; const centerY = dy + dHeight / 2; context.translate(centerX, centerY); context.rotate(this.rotation * Math.PI / 180); const aspectRatioChange = this.rotation % 180 === 0 ? 1 : dWidth / dHeight; context.scale(1 / aspectRatioChange, aspectRatioChange); context.drawImage(source, sx, sy, sWidth, sHeight, -dWidth / 2, -dHeight / 2, dWidth, dHeight); context.restore(); } drawWithFit(context, options) { if (!(typeof CanvasRenderingContext2D !== "undefined" && context instanceof CanvasRenderingContext2D || typeof OffscreenCanvasRenderingContext2D !== "undefined" && context instanceof OffscreenCanvasRenderingContext2D)) { throw new TypeError("context must be a CanvasRenderingContext2D or OffscreenCanvasRenderingContext2D."); } if (!options || typeof options !== "object") { throw new TypeError("options must be an object."); } if (!["fill", "contain", "cover"].includes(options.fit)) { throw new TypeError("options.fit must be 'fill', 'contain', or 'cover'."); } if (options.rotation !== undefined && ![0, 90, 180, 270].includes(options.rotation)) { throw new TypeError("options.rotation, when provided, must be 0, 90, 180, or 270."); } if (options.crop !== undefined) { validateCropRectangle(options.crop, "options."); } const canvasWidth = context.canvas.width; const canvasHeight = context.canvas.height; const rotation = options.rotation ?? this.rotation; const [rotatedWidth, rotatedHeight] = rotation % 180 === 0 ? [this.codedWidth, this.codedHeight] : [this.codedHeight, this.codedWidth]; if (options.crop) { clampCropRectangle(options.crop, rotatedWidth, rotatedHeight); } let dx; let dy; let newWidth; let newHeight; const { sx, sy, sWidth, sHeight } = this._rotateSourceRegion(options.crop?.left ?? 0, options.crop?.top ?? 0, options.crop?.width ?? rotatedWidth, options.crop?.height ?? rotatedHeight, rotation); if (options.fit === "fill") { dx = 0; dy = 0; newWidth = canvasWidth; newHeight = canvasHeight; } else { const [sampleWidth, sampleHeight] = options.crop ? [options.crop.width, options.crop.height] : [rotatedWidth, rotatedHeight]; const scale = options.fit === "contain" ? Math.min(canvasWidth / sampleWidth, canvasHeight / sampleHeight) : Math.max(canvasWidth / sampleWidth, canvasHeight / sampleHeight); newWidth = sampleWidth * scale; newHeight = sampleHeight * scale; dx = (canvasWidth - newWidth) / 2; dy = (canvasHeight - newHeight) / 2; } context.save(); const aspectRatioChange = rotation % 180 === 0 ? 1 : newWidth / newHeight; context.translate(canvasWidth / 2, canvasHeight / 2); context.rotate(rotation * Math.PI / 180); context.scale(1 / aspectRatioChange, aspectRatioChange); context.translate(-canvasWidth / 2, -canvasHeight / 2); context.drawImage(this.toCanvasImageSource(), sx, sy, sWidth, sHeight, dx, dy, newWidth, newHeight); context.restore(); } _rotateSourceRegion(sx, sy, sWidth, sHeight, rotation) { if (rotation === 90) { [sx, sy, sWidth, sHeight] = [ sy, this.codedHeight - sx - sWidth, sHeight, sWidth ]; } else if (rotation === 180) { [sx, sy] = [ this.codedWidth - sx - sWidth, this.codedHeight - sy - sHeight ]; } else if (rotation === 270) { [sx, sy, sWidth, sHeight] = [ this.codedWidth - sy - sHeight, sx, sHeight, sWidth ]; } return { sx, sy, sWidth, sHeight }; } toCanvasImageSource() { if (this._closed) { throw new Error("VideoSample is closed."); } assert(this._data !== null); if (this._data instanceof Uint8Array) { const videoFrame = this.toVideoFrame(); queueMicrotask(() => videoFrame.close()); return videoFrame; } else { return this._data; } } setRotation(newRotation) { if (![0, 90, 180, 270].includes(newRotation)) { throw new TypeError("newRotation must be 0, 90, 180, or 270."); } this.rotation = newRotation; } setTimestamp(newTimestamp) { if (!Number.isFinite(newTimestamp)) { throw new TypeError("newTimestamp must be a number."); } this.timestamp = newTimestamp; } setDuration(newDuration) { if (!Number.isFinite(newDuration) || newDuration < 0) { throw new TypeError("newDuration must be a non-negative number."); } this.duration = newDuration; } [Symbol.dispose]() { this.close(); } } class VideoSampleColorSpace { constructor(init) { this.primaries = init?.primaries ?? null; this.transfer = init?.transfer ?? null; this.matrix = init?.matrix ?? null; this.fullRange = init?.fullRange ?? null; } toJSON() { return { primaries: this.primaries, transfer: this.transfer, matrix: this.matrix, fullRange: this.fullRange }; } } var isVideoFrame = (x) => { return typeof VideoFrame !== "undefined" && x instanceof VideoFrame; }; var clampCropRectangle = (crop, outerWidth, outerHeight) => { crop.left = Math.min(crop.left, outerWidth); crop.top = Math.min(crop.top, outerHeight); crop.width = Math.min(crop.width, outerWidth - crop.left); crop.height = Math.min(crop.height, outerHeight - crop.top); assert(crop.width >= 0); assert(crop.height >= 0); }; var validateCropRectangle = (crop, prefix) => { if (!crop || typeof crop !== "object") { throw new TypeError(prefix + "crop, when provided, must be an object."); } if (!Number.isInteger(crop.left) || crop.left < 0) { throw new TypeError(prefix + "crop.left must be a non-negative integer."); } if (!Number.isInteger(crop.top) || crop.top < 0) { throw new TypeError(prefix + "crop.top must be a non-negative integer."); } if (!Number.isInteger(crop.width) || crop.width < 0) { throw new TypeError(prefix + "crop.width must be a non-negative integer."); } if (!Number.isInteger(crop.height) || crop.height < 0) { throw new TypeError(prefix + "crop.height must be a non-negative integer."); } }; var validateVideoFrameCopyToOptions = (options) => { if (!options || typeof options !== "object") { throw new TypeError("options must be an object."); } if (options.colorSpace !== undefined && !["display-p3", "srgb"].includes(options.colorSpace)) { throw new TypeError("options.colorSpace, when provided, must be 'display-p3' or 'srgb'."); } if (options.format !== undefined && typeof options.format !== "string") { throw new TypeError("options.format, when provided, must be a string."); } if (options.layout !== undefined) { if (!Array.isArray(options.layout)) { throw new TypeError("options.layout, when provided, must be an array."); } for (const plane of options.layout) { if (!plane || typeof plane !== "object") { throw new TypeError("Each entry in options.layout must be an object."); } if (!Number.isInteger(plane.offset) || plane.offset < 0) { throw new TypeError("plane.offset must be a non-negative integer."); } if (!Number.isInteger(plane.stride) || plane.stride < 0) { throw new TypeError("plane.stride must be a non-negative integer."); } } } if (options.rect !== undefined) { if (!options.rect || typeof options.rect !== "object") { throw new TypeError("options.rect, when provided, must be an object."); } if (options.rect.x !== undefined && (!Number.isInteger(options.rect.x) || options.rect.x < 0)) { throw new TypeError("options.rect.x, when provided, must be a non-negative integer."); } if (options.rect.y !== undefined && (!Number.isInteger(options.rect.y) || options.rect.y < 0)) { throw new TypeError("options.rect.y, when provided, must be a non-negative integer."); } if (options.rect.width !== undefined && (!Number.isInteger(options.rect.width) || options.rect.width < 0)) { throw new TypeError("options.rect.width, when provided, must be a non-negative integer."); } if (options.rect.height !== undefined && (!Number.isInteger(options.rect.height) || options.rect.height < 0)) { throw new TypeError("options.rect.height, when provided, must be a non-negative integer."); } } }; var createDefaultPlaneLayout = (format, codedWidth, codedHeight) => { const planes = getPlaneConfigs(format); const layouts = []; let currentOffset = 0; for (const plane of planes) { const planeWidth = Math.ceil(codedWidth / plane.widthDivisor); const planeHeight = Math.ceil(codedHeight / plane.heightDivisor); const stride = planeWidth * plane.sampleBytes; const planeSize = stride * planeHeight; layouts.push({ offset: currentOffset, stride }); currentOffset += planeSize; } return layouts; }; var getPlaneConfigs = (format) => { const yuv = (yBytes, uvBytes, subX, subY, hasAlpha) => { const configs = [ { sampleBytes: yBytes, widthDivisor: 1, heightDivisor: 1 }, { sampleBytes: uvBytes, widthDivisor: subX, heightDivisor: subY }, { sampleBytes: uvBytes, widthDivisor: subX, heightDivisor: subY } ]; if (hasAlpha) { configs.push({ sampleBytes: yBytes, widthDivisor: 1, heightDivisor: 1 }); } return configs; }; switch (format) { case "I420": return yuv(1, 1, 2, 2, false); case "I420P10": case "I420P12": return yuv(2, 2, 2, 2, false); case "I420A": return yuv(1, 1, 2, 2, true); case "I420AP10": case "I420AP12": return yuv(2, 2, 2, 2, true); case "I422": return yuv(1, 1, 2, 1, false); case "I422P10": case "I422P12": return yuv(2, 2, 2, 1, false); case "I422A": return yuv(1, 1, 2, 1, true); case "I422AP10": case "I422AP12": return yuv(2, 2, 2, 1, true); case "I444": return yuv(1, 1, 1, 1, false); case "I444P10": case "I444P12": return yuv(2, 2, 1, 1, false); case "I444A": return yuv(1, 1, 1, 1, true); case "I444AP10": case "I444AP12": return yuv(2, 2, 1, 1, true); case "NV12": return [ { sampleBytes: 1, widthDivisor: 1, heightDivisor: 1 }, { sampleBytes: 2, widthDivisor: 2, heightDivisor: 2 } ]; case "RGBA": case "RGBX": case "BGRA": case "BGRX": return [ { sampleBytes: 4, widthDivisor: 1, heightDivisor: 1 } ]; default: assertNever(format); assert(false); } }; var AUDIO_SAMPLE_FORMATS = new Set(["f32", "f32-planar", "s16", "s16-planar", "s32", "s32-planar", "u8", "u8-planar"]); // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/media-sink.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var validatePacketRetrievalOptions = (options) => { if (!options || typeof options !== "object") { throw new TypeError("options must be an object."); } if (options.metadataOnly !== undefined && typeof options.metadataOnly !== "boolean") { throw new TypeError("options.metadataOnly, when defined, must be a boolean."); } if (options.verifyKeyPackets !== undefined && typeof options.verifyKeyPackets !== "boolean") { throw new TypeError("options.verifyKeyPackets, when defined, must be a boolean."); } if (options.verifyKeyPackets && options.metadataOnly) { throw new TypeError("options.verifyKeyPackets and options.metadataOnly cannot be enabled together."); } }; var validateTimestamp = (timestamp) => { if (!isNumber(timestamp)) { throw new TypeError("timestamp must be a number."); } }; var maybeFixPacketType = (track, promise, options) => { if (options.verifyKeyPackets) { return promise.then(async (packet) => { if (!packet || packet.type === "delta") { return packet; } const determinedType = await track.determinePacketType(packet); if (determinedType) { packet.type = determinedType; } return packet; }); } else { return promise; } }; class EncodedPacketSink { constructor(track) { if (!(track instanceof InputTrack)) { throw new TypeError("track must be an InputTrack."); } this._track = track; } getFirstPacket(options = {}) { validatePacketRetrievalOptions(options); if (this._track.input._disposed) { throw new InputDisposedError; } return maybeFixPacketType(this._track, this._track._backing.getFirstPacket(options), options); } getPacket(timestamp, options = {}) { validateTimestamp(timestamp); validatePacketRetrievalOptions(options); if (this._track.input._disposed) { throw new InputDisposedError; } return maybeFixPacketType(this._track, this._track._backing.getPacket(timestamp, options), options); } getNextPacket(packet, options = {}) { if (!(packet instanceof EncodedPacket)) { throw new TypeError("packet must be an EncodedPacket."); } validatePacketRetrievalOptions(options); if (this._track.input._disposed) { throw new InputDisposedError; } return maybeFixPacketType(this._track, this._track._backing.getNextPacket(packet, options), options); } async getKeyPacket(timestamp, options = {}) { validateTimestamp(timestamp); validatePacketRetrievalOptions(options); if (this._track.input._disposed) { throw new InputDisposedError; } if (!options.verifyKeyPackets) { return this._track._backing.getKeyPacket(timestamp, options); } const packet = await this._track._backing.getKeyPacket(timestamp, options); if (!packet) { return packet; } assert(packet.type === "key"); const determinedType = await this._track.determinePacketType(packet); if (determinedType === "delta") { return this.getKeyPacket(packet.timestamp - 1 / this._track.timeResolution, options); } return packet; } async getNextKeyPacket(packet, options = {}) { if (!(packet instanceof EncodedPacket)) { throw new TypeError("packet must be an EncodedPacket."); } validatePacketRetrievalOptions(options); if (this._track.input._disposed) { throw new InputDisposedError; } if (!options.verifyKeyPackets) { return this._track._backing.getNextKeyPacket(packet, options); } const nextPacket = await this._track._backing.getNextKeyPacket(packet, options); if (!nextPacket) { return nextPacket; } assert(nextPacket.type === "key"); const determinedType = await this._track.determinePacketType(nextPacket); if (determinedType === "delta") { return this.getNextKeyPacket(nextPacket, options); } return nextPacket; } packets(startPacket, endPacket, options = {}) { if (startPacket !== undefined && !(startPacket instanceof EncodedPacket)) { throw new TypeError("startPacket must be an EncodedPacket."); } if (startPacket !== undefined && startPacket.isMetadataOnly && !options?.metadataOnly) { throw new TypeError("startPacket can only be metadata-only if options.metadataOnly is enabled."); } if (endPacket !== undefined && !(endPacket instanceof EncodedPacket)) { throw new TypeError("endPacket must be an EncodedPacket."); } validatePacketRetrievalOptions(options); if (this._track.input._disposed) { throw new InputDisposedError; } const packetQueue = []; let { promise: queueNotEmpty, resolve: onQueueNotEmpty } = promiseWithResolvers(); let { promise: queueDequeue, resolve: onQueueDequeue } = promiseWithResolvers(); let ended = false; let terminated = false; let outOfBandError = null; const timestamps = []; const maxQueueSize = () => Math.max(2, timestamps.length); (async () => { let packet = startPacket ?? await this.getFirstPacket(options); while (packet && !terminated && !this._track.input._disposed) { if (endPacket && packet.sequenceNumber >= endPacket?.sequenceNumber) { break; } if (packetQueue.length > maxQueueSize()) { ({ promise: queueDequeue, resolve: onQueueDequeue } = promiseWithResolvers()); await queueDequeue; continue; } packetQueue.push(packet); onQueueNotEmpty(); ({ promise: queueNotEmpty, resolve: onQueueNotEmpty } = promiseWithResolvers()); packet = await this.getNextPacket(packet, options); } ended = true; onQueueNotEmpty(); })().catch((error) => { if (!outOfBandError) { outOfBandError = error; onQueueNotEmpty(); } }); const track = this._track; return { async next() { while (true) { if (track.input._disposed) { throw new InputDisposedError; } else if (terminated) { return { value: undefined, done: true }; } else if (outOfBandError) { throw outOfBandError; } else if (packetQueue.length > 0) { const value = packetQueue.shift(); const now = performance.now(); timestamps.push(now); while (timestamps.length > 0 && now - timestamps[0] >= 1000) { timestamps.shift(); } onQueueDequeue(); return { value, done: false }; } else if (ended) { return { value: undefined, done: true }; } else { await queueNotEmpty; } } }, async return() { terminated = true; onQueueDequeue(); onQueueNotEmpty(); return { value: undefined, done: true }; }, async throw(error) { throw error; }, [Symbol.asyncIterator]() { return this; } }; } } class DecoderWrapper { constructor(onSample, onError) { this.onSample = onSample; this.onError = onError; } } class BaseMediaSampleSink { mediaSamplesInRange(startTimestamp = 0, endTimestamp = Infinity) { validateTimestamp(startTimestamp); validateTimestamp(endTimestamp); const sampleQueue = []; let firstSampleQueued = false; let lastSample = null; let { promise: queueNotEmpty, resolve: onQueueNotEmpty } = promiseWithResolvers(); let { promise: queueDequeue, resolve: onQueueDequeue } = promiseWithResolvers(); let decoderIsFlushed = false; let ended = false; let terminated = false; let outOfBandError = null; (async () => { const decoder = await this._createDecoder((sample) => { onQueueDequeue(); if (sample.timestamp >= endTimestamp) { ended = true; } if (ended) { sample.close(); return; } if (lastSample) { if (sample.timestamp > startTimestamp) { sampleQueue.push(lastSample); firstSampleQueued = true; } else { lastSample.close(); } } if (sample.timestamp >= startTimestamp) { sampleQueue.push(sample); firstSampleQueued = true; } lastSample = firstSampleQueued ? null : sample; if (sampleQueue.length > 0) { onQueueNotEmpty(); ({ promise: queueNotEmpty, resolve: onQueueNotEmpty } = promiseWithResolvers()); } }, (error) => { if (!outOfBandError) { outOfBandError = error; onQueueNotEmpty(); } }); const packetSink = this._createPacketSink(); const keyPacket = await packetSink.getKeyPacket(startTimestamp, { verifyKeyPackets: true }) ?? await packetSink.getFirstPacket(); let currentPacket = keyPacket; const endPacket = undefined; const packets = packetSink.packets(keyPacket ?? undefined, endPacket); await packets.next(); while (currentPacket && !ended && !this._track.input._disposed) { const maxQueueSize = computeMaxQueueSize(sampleQueue.length); if (sampleQueue.length + decoder.getDecodeQueueSize() > maxQueueSize) { ({ promise: queueDequeue, resolve: onQueueDequeue } = promiseWithResolvers()); await queueDequeue; continue; } decoder.decode(currentPacket); const packetResult = await packets.next(); if (packetResult.done) { break; } currentPacket = packetResult.value; } await packets.return(); if (!terminated && !this._track.input._disposed) { await decoder.flush(); } decoder.close(); if (!firstSampleQueued && lastSample) { sampleQueue.push(lastSample); } decoderIsFlushed = true; onQueueNotEmpty(); })().catch((error) => { if (!outOfBandError) { outOfBandError = error; onQueueNotEmpty(); } }); const track = this._track; const closeSamples = () => { lastSample?.close(); for (const sample of sampleQueue) { sample.close(); } }; return { async next() { while (true) { if (track.input._disposed) { closeSamples(); throw new InputDisposedError; } else if (terminated) { return { value: undefined, done: true }; } else if (outOfBandError) { closeSamples(); throw outOfBandError; } else if (sampleQueue.length > 0) { const value = sampleQueue.shift(); onQueueDequeue(); return { value, done: false }; } else if (!decoderIsFlushed) { await queueNotEmpty; } else { return { value: undefined, done: true }; } } }, async return() { terminated = true; ended = true; onQueueDequeue(); onQueueNotEmpty(); closeSamples(); return { value: undefined, done: true }; }, async throw(error) { throw error; }, [Symbol.asyncIterator]() { return this; } }; } mediaSamplesAtTimestamps(timestamps) { validateAnyIterable(timestamps); const timestampIterator = toAsyncIterator(timestamps); const timestampsOfInterest = []; const sampleQueue = []; let { promise: queueNotEmpty, resolve: onQueueNotEmpty } = promiseWithResolvers(); let { promise: queueDequeue, resolve: onQueueDequeue } = promiseWithResolvers(); let decoderIsFlushed = false; let terminated = false; let outOfBandError = null; const pushToQueue = (sample) => { sampleQueue.push(sample); onQueueNotEmpty(); ({ promise: queueNotEmpty, resolve: onQueueNotEmpty } = promiseWithResolvers()); }; (async () => { const decoder = await this._createDecoder((sample) => { onQueueDequeue(); if (terminated) { sample.close(); return; } let sampleUses = 0; while (timestampsOfInterest.length > 0 && sample.timestamp - timestampsOfInterest[0] > -0.0000000001) { sampleUses++; timestampsOfInterest.shift(); } if (sampleUses > 0) { for (let i = 0;i < sampleUses; i++) { pushToQueue(i < sampleUses - 1 ? sample.clone() : sample); } } else { sample.close(); } }, (error) => { if (!outOfBandError) { outOfBandError = error; onQueueNotEmpty(); } }); const packetSink = this._createPacketSink(); let lastPacket = null; let lastKeyPacket = null; let maxSequenceNumber = -1; const decodePackets = async () => { assert(lastKeyPacket); let currentPacket = lastKeyPacket; decoder.decode(currentPacket); while (currentPacket.sequenceNumber < maxSequenceNumber) { const maxQueueSize = computeMaxQueueSize(sampleQueue.length); while (sampleQueue.length + decoder.getDecodeQueueSize() > maxQueueSize && !terminated) { ({ promise: queueDequeue, resolve: onQueueDequeue } = promiseWithResolvers()); await queueDequeue; } if (terminated) { break; } const nextPacket = await packetSink.getNextPacket(currentPacket); assert(nextPacket); decoder.decode(nextPacket); currentPacket = nextPacket; } maxSequenceNumber = -1; }; const flushDecoder = async () => { await decoder.flush(); for (let i = 0;i < timestampsOfInterest.length; i++) { pushToQueue(null); } timestampsOfInterest.length = 0; }; for await (const timestamp of timestampIterator) { validateTimestamp(timestamp); if (terminated || this._track.input._disposed) { break; } const targetPacket = await packetSink.getPacket(timestamp); const keyPacket = targetPacket && await packetSink.getKeyPacket(timestamp, { verifyKeyPackets: true }); if (!keyPacket) { if (maxSequenceNumber !== -1) { await decodePackets(); await flushDecoder(); } pushToQueue(null); lastPacket = null; continue; } if (lastPacket && (keyPacket.sequenceNumber !== lastKeyPacket.sequenceNumber || targetPacket.timestamp < lastPacket.timestamp)) { await decodePackets(); await flushDecoder(); } timestampsOfInterest.push(targetPacket.timestamp); maxSequenceNumber = Math.max(targetPacket.sequenceNumber, maxSequenceNumber); lastPacket = targetPacket; lastKeyPacket = keyPacket; } if (!terminated && !this._track.input._disposed) { if (maxSequenceNumber !== -1) { await decodePackets(); } await flushDecoder(); } decoder.close(); decoderIsFlushed = true; onQueueNotEmpty(); })().catch((error) => { if (!outOfBandError) { outOfBandError = error; onQueueNotEmpty(); } }); const track = this._track; const closeSamples = () => { for (const sample of sampleQueue) { sample?.close(); } }; return { async next() { while (true) { if (track.input._disposed) { closeSamples(); throw new InputDisposedError; } else if (terminated) { return { value: undefined, done: true }; } else if (outOfBandError) { closeSamples(); throw outOfBandError; } else if (sampleQueue.length > 0) { const value = sampleQueue.shift(); assert(value !== undefined); onQueueDequeue(); return { value, done: false }; } else if (!decoderIsFlushed) { await queueNotEmpty; } else { return { value: undefined, done: true }; } } }, async return() { terminated = true; onQueueDequeue(); onQueueNotEmpty(); closeSamples(); return { value: undefined, done: true }; }, async throw(error) { throw error; }, [Symbol.asyncIterator]() { return this; } }; } } var computeMaxQueueSize = (decodedSampleQueueSize) => { return decodedSampleQueueSize === 0 ? 40 : 8; }; class VideoDecoderWrapper extends DecoderWrapper { constructor(onSample, onError, codec, decoderConfig, rotation, timeResolution) { super(onSample, onError); this.codec = codec; this.decoderConfig = decoderConfig; this.rotation = rotation; this.timeResolution = timeResolution; this.decoder = null; this.customDecoder = null; this.customDecoderCallSerializer = new CallSerializer; this.customDecoderQueueSize = 0; this.inputTimestamps = []; this.sampleQueue = []; this.currentPacketIndex = 0; this.raslSkipped = false; this.alphaDecoder = null; this.alphaHadKeyframe = false; this.colorQueue = []; this.alphaQueue = []; this.merger = null; this.mergerCreationFailed = false; this.decodedAlphaChunkCount = 0; this.alphaDecoderQueueSize = 0; this.nullAlphaFrameQueue = []; this.currentAlphaPacketIndex = 0; this.alphaRaslSkipped = false; const MatchingCustomDecoder = customVideoDecoders.find((x) => x.supports(codec, decoderConfig)); if (MatchingCustomDecoder) { this.customDecoder = new MatchingCustomDecoder; this.customDecoder.codec = codec; this.customDecoder.config = decoderConfig; this.customDecoder.onSample = (sample) => { if (!(sample instanceof VideoSample)) { throw new TypeError("The argument passed to onSample must be a VideoSample."); } this.finalizeAndEmitSample(sample); }; this.customDecoderCallSerializer.call(() => this.customDecoder.init()); } else { const colorHandler = (frame2) => { if (this.alphaQueue.length > 0) { const alphaFrame = this.alphaQueue.shift(); assert(alphaFrame !== undefined); this.mergeAlpha(frame2, alphaFrame); } else { this.colorQueue.push(frame2); } }; if (codec === "avc" && this.decoderConfig.description && isChromium()) { const record = deserializeAvcDecoderConfigurationRecord(toUint8Array(this.decoderConfig.description)); if (record && record.sequenceParameterSets.length > 0) { const sps = parseAvcSps(record.sequenceParameterSets[0]); if (sps && sps.frameMbsOnlyFlag === 0) { this.decoderConfig = { ...this.decoderConfig, hardwareAcceleration: "prefer-software" }; } } } const stack2 = new Error("Decoding error").stack; this.decoder = new VideoDecoder({ output: (frame2) => { try { colorHandler(frame2); } catch (error) { this.onError(error); } }, error: (error) => { error.stack = stack2; this.onError(error); } }); this.decoder.configure(this.decoderConfig); } } getDecodeQueueSize() { if (this.customDecoder) { return this.customDecoderQueueSize; } else { assert(this.decoder); return Math.max(this.decoder.decodeQueueSize, this.alphaDecoder?.decodeQueueSize ?? 0); } } decode(packet) { if (this.codec === "hevc" && this.currentPacketIndex > 0 && !this.raslSkipped) { if (this.hasHevcRaslPicture(packet.data)) { return; } this.raslSkipped = true; } if (this.customDecoder) { this.customDecoderQueueSize++; this.customDecoderCallSerializer.call(() => this.customDecoder.decode(packet)).then(() => this.customDecoderQueueSize--); } else { assert(this.decoder); if (!isWebKit()) { insertSorted(this.inputTimestamps, packet.timestamp, (x) => x); } if (isChromium() && this.currentPacketIndex === 0 && this.codec === "avc") { const filteredNalUnits = []; for (const loc of iterateAvcNalUnits(packet.data, this.decoderConfig)) { const type = extractNalUnitTypeForAvc(packet.data[loc.offset]); if (!(type >= 20 && type <= 31)) { filteredNalUnits.push(packet.data.subarray(loc.offset, loc.offset + loc.length)); } } const newData = concatAvcNalUnits(filteredNalUnits, this.decoderConfig); packet = new EncodedPacket(newData, packet.type, packet.timestamp, packet.duration); } this.decoder.decode(packet.toEncodedVideoChunk()); this.decodeAlphaData(packet); } this.currentPacketIndex++; } decodeAlphaData(packet) { if (!packet.sideData.alpha || this.mergerCreationFailed) { this.pushNullAlphaFrame(); return; } if (!this.merger) { try { this.merger = new ColorAlphaMerger; } catch (error) { console.error("Due to an error, only color data will be decoded.", error); this.mergerCreationFailed = true; this.decodeAlphaData(packet); return; } } if (!this.alphaDecoder) { const alphaHandler = (frame2) => { this.alphaDecoderQueueSize--; if (this.colorQueue.length > 0) { const colorFrame = this.colorQueue.shift(); assert(colorFrame !== undefined); this.mergeAlpha(colorFrame, frame2); } else { this.alphaQueue.push(frame2); } this.decodedAlphaChunkCount++; while (this.nullAlphaFrameQueue.length > 0 && this.nullAlphaFrameQueue[0] === this.decodedAlphaChunkCount) { this.nullAlphaFrameQueue.shift(); if (this.colorQueue.length > 0) { const colorFrame = this.colorQueue.shift(); assert(colorFrame !== undefined); this.mergeAlpha(colorFrame, null); } else { this.alphaQueue.push(null); } } }; const stack2 = new Error("Decoding error").stack; this.alphaDecoder = new VideoDecoder({ output: (frame2) => { try { alphaHandler(frame2); } catch (error) { this.onError(error); } }, error: (error) => { error.stack = stack2; this.onError(error); } }); this.alphaDecoder.configure(this.decoderConfig); } const type = determineVideoPacketType(this.codec, this.decoderConfig, packet.sideData.alpha); if (!this.alphaHadKeyframe) { this.alphaHadKeyframe = type === "key"; } if (this.alphaHadKeyframe) { if (this.codec === "hevc" && this.currentAlphaPacketIndex > 0 && !this.alphaRaslSkipped) { if (this.hasHevcRaslPicture(packet.sideData.alpha)) { this.pushNullAlphaFrame(); return; } this.alphaRaslSkipped = true; } this.currentAlphaPacketIndex++; this.alphaDecoder.decode(packet.alphaToEncodedVideoChunk(type ?? packet.type)); this.alphaDecoderQueueSize++; } else { this.pushNullAlphaFrame(); } } pushNullAlphaFrame() { if (this.alphaDecoderQueueSize === 0) { this.alphaQueue.push(null); } else { this.nullAlphaFrameQueue.push(this.decodedAlphaChunkCount + this.alphaDecoderQueueSize); } } hasHevcRaslPicture(packetData) { for (const loc of iterateHevcNalUnits(packetData, this.decoderConfig)) { const type = extractNalUnitTypeForHevc(packetData[loc.offset]); if (type === HevcNalUnitType.RASL_N || type === HevcNalUnitType.RASL_R) { return true; } } return false; } sampleHandler(sample) { if (isWebKit()) { if (this.sampleQueue.length > 0 && sample.timestamp >= last(this.sampleQueue).timestamp) { for (const sample2 of this.sampleQueue) { this.finalizeAndEmitSample(sample2); } this.sampleQueue.length = 0; } insertSorted(this.sampleQueue, sample, (x) => x.timestamp); } else { const timestamp = this.inputTimestamps.shift(); assert(timestamp !== undefined); sample.setTimestamp(timestamp); this.finalizeAndEmitSample(sample); } } finalizeAndEmitSample(sample) { sample.setTimestamp(Math.round(sample.timestamp * this.timeResolution) / this.timeResolution); sample.setDuration(Math.round(sample.duration * this.timeResolution) / this.timeResolution); sample.setRotation(this.rotation); this.onSample(sample); } mergeAlpha(color, alpha) { if (!alpha) { const finalSample2 = new VideoSample(color); this.sampleHandler(finalSample2); return; } assert(this.merger); this.merger.update(color, alpha); color.close(); alpha.close(); const finalFrame = new VideoFrame(this.merger.canvas, { timestamp: color.timestamp, duration: color.duration ?? undefined }); const finalSample = new VideoSample(finalFrame); this.sampleHandler(finalSample); } async flush() { if (this.customDecoder) { await this.customDecoderCallSerializer.call(() => this.customDecoder.flush()); } else { assert(this.decoder); await Promise.all([ this.decoder.flush(), this.alphaDecoder?.flush() ]); this.colorQueue.forEach((x) => x.close()); this.colorQueue.length = 0; this.alphaQueue.forEach((x) => x?.close()); this.alphaQueue.length = 0; this.alphaHadKeyframe = false; this.decodedAlphaChunkCount = 0; this.alphaDecoderQueueSize = 0; this.nullAlphaFrameQueue.length = 0; this.currentAlphaPacketIndex = 0; this.alphaRaslSkipped = false; } if (isWebKit()) { for (const sample of this.sampleQueue) { this.finalizeAndEmitSample(sample); } this.sampleQueue.length = 0; } this.currentPacketIndex = 0; this.raslSkipped = false; } close() { if (this.customDecoder) { this.customDecoderCallSerializer.call(() => this.customDecoder.close()); } else { assert(this.decoder); this.decoder.close(); this.alphaDecoder?.close(); this.colorQueue.forEach((x) => x.close()); this.colorQueue.length = 0; this.alphaQueue.forEach((x) => x?.close()); this.alphaQueue.length = 0; this.merger?.close(); } for (const sample of this.sampleQueue) { sample.close(); } this.sampleQueue.length = 0; } } class ColorAlphaMerger { constructor() { if (typeof OffscreenCanvas !== "undefined") { this.canvas = new OffscreenCanvas(300, 150); } else { this.canvas = document.createElement("canvas"); } const gl = this.canvas.getContext("webgl2", { premultipliedAlpha: false }); if (!gl) { throw new Error("Couldn't acquire WebGL 2 context."); } this.gl = gl; this.program = this.createProgram(); this.vao = this.createVAO(); this.colorTexture = this.createTexture(); this.alphaTexture = this.createTexture(); this.gl.useProgram(this.program); this.gl.uniform1i(this.gl.getUniformLocation(this.program, "u_colorTexture"), 0); this.gl.uniform1i(this.gl.getUniformLocation(this.program, "u_alphaTexture"), 1); } createProgram() { const vertexShader = this.createShader(this.gl.VERTEX_SHADER, `#version 300 es in vec2 a_position; in vec2 a_texCoord; out vec2 v_texCoord; void main() { gl_Position = vec4(a_position, 0.0, 1.0); v_texCoord = a_texCoord; } `); const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, `#version 300 es precision highp float; uniform sampler2D u_colorTexture; uniform sampler2D u_alphaTexture; in vec2 v_texCoord; out vec4 fragColor; void main() { vec3 color = texture(u_colorTexture, v_texCoord).rgb; float alpha = texture(u_alphaTexture, v_texCoord).r; fragColor = vec4(color, alpha); } `); const program = this.gl.createProgram(); this.gl.attachShader(program, vertexShader); this.gl.attachShader(program, fragmentShader); this.gl.linkProgram(program); return program; } createShader(type, source) { const shader = this.gl.createShader(type); this.gl.shaderSource(shader, source); this.gl.compileShader(shader); return shader; } createVAO() { const vao = this.gl.createVertexArray(); this.gl.bindVertexArray(vao); const vertices = new Float32Array([ -1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0, 1, 1, 1, 0 ]); const buffer = this.gl.createBuffer(); this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer); this.gl.bufferData(this.gl.ARRAY_BUFFER, vertices, this.gl.STATIC_DRAW); const positionLocation = this.gl.getAttribLocation(this.program, "a_position"); const texCoordLocation = this.gl.getAttribLocation(this.program, "a_texCoord"); this.gl.enableVertexAttribArray(positionLocation); this.gl.vertexAttribPointer(positionLocation, 2, this.gl.FLOAT, false, 16, 0); this.gl.enableVertexAttribArray(texCoordLocation); this.gl.vertexAttribPointer(texCoordLocation, 2, this.gl.FLOAT, false, 16, 8); return vao; } createTexture() { const texture = this.gl.createTexture(); this.gl.bindTexture(this.gl.TEXTURE_2D, texture); this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE); this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE); this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR); this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); return texture; } update(color, alpha) { if (color.displayWidth !== this.canvas.width || color.displayHeight !== this.canvas.height) { this.canvas.width = color.displayWidth; this.canvas.height = color.displayHeight; } this.gl.activeTexture(this.gl.TEXTURE0); this.gl.bindTexture(this.gl.TEXTURE_2D, this.colorTexture); this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, color); this.gl.activeTexture(this.gl.TEXTURE1); this.gl.bindTexture(this.gl.TEXTURE_2D, this.alphaTexture); this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, alpha); this.gl.viewport(0, 0, this.canvas.width, this.canvas.height); this.gl.clear(this.gl.COLOR_BUFFER_BIT); this.gl.bindVertexArray(this.vao); this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4); } close() { this.gl.getExtension("WEBGL_lose_context")?.loseContext(); this.gl = null; } } class VideoSampleSink extends BaseMediaSampleSink { constructor(videoTrack) { if (!(videoTrack instanceof InputVideoTrack)) { throw new TypeError("videoTrack must be an InputVideoTrack."); } super(); this._track = videoTrack; } async _createDecoder(onSample, onError) { if (!await this._track.canDecode()) { throw new Error("This video track cannot be decoded by this browser. Make sure to check decodability before using" + " a track."); } const codec = this._track.codec; const rotation = this._track.rotation; const decoderConfig = await this._track.getDecoderConfig(); const timeResolution = this._track.timeResolution; assert(codec && decoderConfig); return new VideoDecoderWrapper(onSample, onError, codec, decoderConfig, rotation, timeResolution); } _createPacketSink() { return new EncodedPacketSink(this._track); } async getSample(timestamp) { validateTimestamp(timestamp); for await (const sample of this.mediaSamplesAtTimestamps([timestamp])) { return sample; } throw new Error("Internal error: Iterator returned nothing."); } samples(startTimestamp = 0, endTimestamp = Infinity) { return this.mediaSamplesInRange(startTimestamp, endTimestamp); } samplesAtTimestamps(timestamps) { return this.mediaSamplesAtTimestamps(timestamps); } } // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/input-track.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ class InputTrack { constructor(input2, backing) { this.input = input2; this._backing = backing; } isVideoTrack() { return this instanceof InputVideoTrack; } isAudioTrack() { return this instanceof InputAudioTrack; } get id() { return this._backing.getId(); } get internalCodecId() { return this._backing.getInternalCodecId(); } get languageCode() { return this._backing.getLanguageCode(); } get name() { return this._backing.getName(); } get timeResolution() { return this._backing.getTimeResolution(); } get disposition() { return this._backing.getDisposition(); } getFirstTimestamp() { return this._backing.getFirstTimestamp(); } computeDuration() { return this._backing.computeDuration(); } async computePacketStats(targetPacketCount = Infinity) { const sink = new EncodedPacketSink(this); let startTimestamp = Infinity; let endTimestamp = -Infinity; let packetCount = 0; let totalPacketBytes = 0; for await (const packet of sink.packets(undefined, undefined, { metadataOnly: true })) { if (packetCount >= targetPacketCount && packet.timestamp >= endTimestamp) { break; } startTimestamp = Math.min(startTimestamp, packet.timestamp); endTimestamp = Math.max(endTimestamp, packet.timestamp + packet.duration); packetCount++; totalPacketBytes += packet.byteLength; } return { packetCount, averagePacketRate: packetCount ? Number((packetCount / (endTimestamp - startTimestamp)).toPrecision(16)) : 0, averageBitrate: packetCount ? Number((8 * totalPacketBytes / (endTimestamp - startTimestamp)).toPrecision(16)) : 0 }; } } class InputVideoTrack extends InputTrack { constructor(input2, backing) { super(input2, backing); this._backing = backing; } get type() { return "video"; } get codec() { return this._backing.getCodec(); } get codedWidth() { return this._backing.getCodedWidth(); } get codedHeight() { return this._backing.getCodedHeight(); } get rotation() { return this._backing.getRotation(); } get displayWidth() { const rotation = this._backing.getRotation(); return rotation % 180 === 0 ? this._backing.getCodedWidth() : this._backing.getCodedHeight(); } get displayHeight() { const rotation = this._backing.getRotation(); return rotation % 180 === 0 ? this._backing.getCodedHeight() : this._backing.getCodedWidth(); } getColorSpace() { return this._backing.getColorSpace(); } async hasHighDynamicRange() { const colorSpace = await this._backing.getColorSpace(); return colorSpace.primaries === "bt2020" || colorSpace.primaries === "smpte432" || colorSpace.transfer === "pg" || colorSpace.transfer === "hlg" || colorSpace.matrix === "bt2020-ncl"; } canBeTransparent() { return this._backing.canBeTransparent(); } getDecoderConfig() { return this._backing.getDecoderConfig(); } async getCodecParameterString() { const decoderConfig = await this._backing.getDecoderConfig(); return decoderConfig?.codec ?? null; } async canDecode() { try { const decoderConfig = await this._backing.getDecoderConfig(); if (!decoderConfig) { return false; } const codec = this._backing.getCodec(); assert(codec !== null); if (customVideoDecoders.some((x) => x.supports(codec, decoderConfig))) { return true; } if (typeof VideoDecoder === "undefined") { return false; } const support = await VideoDecoder.isConfigSupported(decoderConfig); return support.supported === true; } catch (error) { console.error("Error during decodability check:", error); return false; } } async determinePacketType(packet) { if (!(packet instanceof EncodedPacket)) { throw new TypeError("packet must be an EncodedPacket."); } if (packet.isMetadataOnly) { throw new TypeError("packet must not be metadata-only to determine its type."); } if (this.codec === null) { return null; } const decoderConfig = await this.getDecoderConfig(); assert(decoderConfig); return determineVideoPacketType(this.codec, decoderConfig, packet.data); } } class InputAudioTrack extends InputTrack { constructor(input2, backing) { super(input2, backing); this._backing = backing; } get type() { return "audio"; } get codec() { return this._backing.getCodec(); } get numberOfChannels() { return this._backing.getNumberOfChannels(); } get sampleRate() { return this._backing.getSampleRate(); } getDecoderConfig() { return this._backing.getDecoderConfig(); } async getCodecParameterString() { const decoderConfig = await this._backing.getDecoderConfig(); return decoderConfig?.codec ?? null; } async canDecode() { try { const decoderConfig = await this._backing.getDecoderConfig(); if (!decoderConfig) { return false; } const codec = this._backing.getCodec(); assert(codec !== null); if (customAudioDecoders.some((x) => x.supports(codec, decoderConfig))) { return true; } if (decoderConfig.codec.startsWith("pcm-")) { return true; } else { if (typeof AudioDecoder === "undefined") { return false; } const support = await AudioDecoder.isConfigSupported(decoderConfig); return support.supported === true; } } catch (error) { console.error("Error during decodability check:", error); return false; } } async determinePacketType(packet) { if (!(packet instanceof EncodedPacket)) { throw new TypeError("packet must be an EncodedPacket."); } if (this.codec === null) { return null; } return "key"; } } // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-misc.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var buildIsobmffMimeType = (info) => { const base = info.hasVideo ? "video/" : info.hasAudio ? "audio/" : "application/"; let string = base + (info.isQuickTime ? "quicktime" : "mp4"); if (info.codecStrings.length > 0) { const uniqueCodecMimeTypes = [...new Set(info.codecStrings)]; string += `; codecs="${uniqueCodecMimeTypes.join(", ")}"`; } return string; }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-reader.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var MIN_BOX_HEADER_SIZE = 8; var MAX_BOX_HEADER_SIZE = 16; var readBoxHeader = (slice) => { let totalSize = readU32Be(slice); const name = readAscii(slice, 4); let headerSize = 8; const hasLargeSize = totalSize === 1; if (hasLargeSize) { totalSize = readU64Be(slice); headerSize = 16; } const contentSize = totalSize - headerSize; if (contentSize < 0) { return null; } return { name, totalSize, headerSize, contentSize }; }; var readFixed_16_16 = (slice) => { return readI32Be(slice) / 65536; }; var readFixed_2_30 = (slice) => { return readI32Be(slice) / 1073741824; }; var readIsomVariableInteger = (slice) => { let result = 0; for (let i = 0;i < 4; i++) { result <<= 7; const nextByte = readU8(slice); result |= nextByte & 127; if ((nextByte & 128) === 0) { break; } } return result; }; var readMetadataStringShort = (slice) => { let stringLength = readU16Be(slice); slice.skip(2); stringLength = Math.min(stringLength, slice.remainingLength); return textDecoder.decode(readBytes(slice, stringLength)); }; var readDataBox = (slice) => { const header2 = readBoxHeader(slice); if (!header2 || header2.name !== "data") { return null; } if (slice.remainingLength < 8) { return null; } const typeIndicator = readU32Be(slice); slice.skip(4); const data = readBytes(slice, header2.contentSize - 8); switch (typeIndicator) { case 1: return textDecoder.decode(data); case 2: return new TextDecoder("utf-16be").decode(data); case 13: return new RichImageData(data, "image/jpeg"); case 14: return new RichImageData(data, "image/png"); case 27: return new RichImageData(data, "image/bmp"); default: return data; } }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-demuxer.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ class IsobmffDemuxer extends Demuxer { constructor(input2) { super(input2); this.moovSlice = null; this.currentTrack = null; this.tracks = []; this.metadataPromise = null; this.movieTimescale = -1; this.movieDurationInTimescale = -1; this.isQuickTime = false; this.metadataTags = {}; this.currentMetadataKeys = null; this.isFragmented = false; this.fragmentTrackDefaults = []; this.currentFragment = null; this.lastReadFragment = null; this.reader = input2._reader; } async computeDuration() { const tracks = await this.getTracks(); const trackDurations = await Promise.all(tracks.map((x) => x.computeDuration())); return Math.max(0, ...trackDurations); } async getTracks() { await this.readMetadata(); return this.tracks.map((track) => track.inputTrack); } async getMimeType() { await this.readMetadata(); const codecStrings = await Promise.all(this.tracks.map((x) => x.inputTrack.getCodecParameterString())); return buildIsobmffMimeType({ isQuickTime: this.isQuickTime, hasVideo: this.tracks.some((x) => x.info?.type === "video"), hasAudio: this.tracks.some((x) => x.info?.type === "audio"), codecStrings: codecStrings.filter(Boolean) }); } async getMetadataTags() { await this.readMetadata(); return this.metadataTags; } readMetadata() { return this.metadataPromise ??= (async () => { let currentPos = 0; while (true) { let slice = this.reader.requestSliceRange(currentPos, MIN_BOX_HEADER_SIZE, MAX_BOX_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) break; const startPos = currentPos; const boxInfo = readBoxHeader(slice); if (!boxInfo) { break; } if (boxInfo.name === "ftyp") { const majorBrand = readAscii(slice, 4); this.isQuickTime = majorBrand === "qt "; } else if (boxInfo.name === "moov") { let moovSlice = this.reader.requestSlice(slice.filePos, boxInfo.contentSize); if (moovSlice instanceof Promise) moovSlice = await moovSlice; if (!moovSlice) break; this.moovSlice = moovSlice; this.readContiguousBoxes(this.moovSlice); this.tracks.sort((a, b) => Number(b.disposition.default) - Number(a.disposition.default)); for (const track of this.tracks) { const previousSegmentDurationsInSeconds = track.editListPreviousSegmentDurations / this.movieTimescale; track.editListOffset -= Math.round(previousSegmentDurationsInSeconds * track.timescale); } break; } currentPos = startPos + boxInfo.totalSize; } if (this.isFragmented && this.reader.fileSize !== null) { let lastWordSlice = this.reader.requestSlice(this.reader.fileSize - 4, 4); if (lastWordSlice instanceof Promise) lastWordSlice = await lastWordSlice; assert(lastWordSlice); const lastWord = readU32Be(lastWordSlice); const potentialMfraPos = this.reader.fileSize - lastWord; if (potentialMfraPos >= 0 && potentialMfraPos <= this.reader.fileSize - MAX_BOX_HEADER_SIZE) { let mfraHeaderSlice = this.reader.requestSliceRange(potentialMfraPos, MIN_BOX_HEADER_SIZE, MAX_BOX_HEADER_SIZE); if (mfraHeaderSlice instanceof Promise) mfraHeaderSlice = await mfraHeaderSlice; if (mfraHeaderSlice) { const boxInfo = readBoxHeader(mfraHeaderSlice); if (boxInfo && boxInfo.name === "mfra") { let mfraSlice = this.reader.requestSlice(mfraHeaderSlice.filePos, boxInfo.contentSize); if (mfraSlice instanceof Promise) mfraSlice = await mfraSlice; if (mfraSlice) { this.readContiguousBoxes(mfraSlice); } } } } } })(); } getSampleTableForTrack(internalTrack) { if (internalTrack.sampleTable) { return internalTrack.sampleTable; } const sampleTable = { sampleTimingEntries: [], sampleCompositionTimeOffsets: [], sampleSizes: [], keySampleIndices: null, chunkOffsets: [], sampleToChunk: [], presentationTimestamps: null, presentationTimestampIndexMap: null }; internalTrack.sampleTable = sampleTable; assert(this.moovSlice); const stblContainerSlice = this.moovSlice.slice(internalTrack.sampleTableByteOffset); this.currentTrack = internalTrack; this.traverseBox(stblContainerSlice); this.currentTrack = null; const isPcmCodec = internalTrack.info?.type === "audio" && internalTrack.info.codec && PCM_AUDIO_CODECS.includes(internalTrack.info.codec); if (isPcmCodec && sampleTable.sampleCompositionTimeOffsets.length === 0) { assert(internalTrack.info?.type === "audio"); const pcmInfo = parsePcmCodec(internalTrack.info.codec); const newSampleTimingEntries = []; const newSampleSizes = []; for (let i = 0;i < sampleTable.sampleToChunk.length; i++) { const chunkEntry = sampleTable.sampleToChunk[i]; const nextEntry = sampleTable.sampleToChunk[i + 1]; const chunkCount = (nextEntry ? nextEntry.startChunkIndex : sampleTable.chunkOffsets.length) - chunkEntry.startChunkIndex; for (let j = 0;j < chunkCount; j++) { const startSampleIndex = chunkEntry.startSampleIndex + j * chunkEntry.samplesPerChunk; const endSampleIndex = startSampleIndex + chunkEntry.samplesPerChunk; const startTimingEntryIndex = binarySearchLessOrEqual(sampleTable.sampleTimingEntries, startSampleIndex, (x) => x.startIndex); const startTimingEntry = sampleTable.sampleTimingEntries[startTimingEntryIndex]; const endTimingEntryIndex = binarySearchLessOrEqual(sampleTable.sampleTimingEntries, endSampleIndex, (x) => x.startIndex); const endTimingEntry = sampleTable.sampleTimingEntries[endTimingEntryIndex]; const firstSampleTimestamp = startTimingEntry.startDecodeTimestamp + (startSampleIndex - startTimingEntry.startIndex) * startTimingEntry.delta; const lastSampleTimestamp = endTimingEntry.startDecodeTimestamp + (endSampleIndex - endTimingEntry.startIndex) * endTimingEntry.delta; const delta = lastSampleTimestamp - firstSampleTimestamp; const lastSampleTimingEntry = last(newSampleTimingEntries); if (lastSampleTimingEntry && lastSampleTimingEntry.delta === delta) { lastSampleTimingEntry.count++; } else { newSampleTimingEntries.push({ startIndex: chunkEntry.startChunkIndex + j, startDecodeTimestamp: firstSampleTimestamp, count: 1, delta }); } const chunkSize = chunkEntry.samplesPerChunk * pcmInfo.sampleSize * internalTrack.info.numberOfChannels; newSampleSizes.push(chunkSize); } chunkEntry.startSampleIndex = chunkEntry.startChunkIndex; chunkEntry.samplesPerChunk = 1; } sampleTable.sampleTimingEntries = newSampleTimingEntries; sampleTable.sampleSizes = newSampleSizes; } if (sampleTable.sampleCompositionTimeOffsets.length > 0) { sampleTable.presentationTimestamps = []; for (const entry of sampleTable.sampleTimingEntries) { for (let i = 0;i < entry.count; i++) { sampleTable.presentationTimestamps.push({ presentationTimestamp: entry.startDecodeTimestamp + i * entry.delta, sampleIndex: entry.startIndex + i }); } } for (const entry of sampleTable.sampleCompositionTimeOffsets) { for (let i = 0;i < entry.count; i++) { const sampleIndex = entry.startIndex + i; const sample = sampleTable.presentationTimestamps[sampleIndex]; if (!sample) { continue; } sample.presentationTimestamp += entry.offset; } } sampleTable.presentationTimestamps.sort((a, b) => a.presentationTimestamp - b.presentationTimestamp); sampleTable.presentationTimestampIndexMap = Array(sampleTable.presentationTimestamps.length).fill(-1); for (let i = 0;i < sampleTable.presentationTimestamps.length; i++) { sampleTable.presentationTimestampIndexMap[sampleTable.presentationTimestamps[i].sampleIndex] = i; } } else {} return sampleTable; } async readFragment(startPos) { if (this.lastReadFragment?.moofOffset === startPos) { return this.lastReadFragment; } let headerSlice = this.reader.requestSliceRange(startPos, MIN_BOX_HEADER_SIZE, MAX_BOX_HEADER_SIZE); if (headerSlice instanceof Promise) headerSlice = await headerSlice; assert(headerSlice); const moofBoxInfo = readBoxHeader(headerSlice); assert(moofBoxInfo?.name === "moof"); let entireSlice = this.reader.requestSlice(startPos, moofBoxInfo.totalSize); if (entireSlice instanceof Promise) entireSlice = await entireSlice; assert(entireSlice); this.traverseBox(entireSlice); const fragment = this.lastReadFragment; assert(fragment && fragment.moofOffset === startPos); for (const [, trackData] of fragment.trackData) { const track = trackData.track; const { fragmentPositionCache } = track; if (!trackData.startTimestampIsFinal) { const lookupEntry = track.fragmentLookupTable.find((x) => x.moofOffset === fragment.moofOffset); if (lookupEntry) { offsetFragmentTrackDataByTimestamp(trackData, lookupEntry.timestamp); } else { const lastCacheIndex = binarySearchLessOrEqual(fragmentPositionCache, fragment.moofOffset - 1, (x) => x.moofOffset); if (lastCacheIndex !== -1) { const lastCache = fragmentPositionCache[lastCacheIndex]; offsetFragmentTrackDataByTimestamp(trackData, lastCache.endTimestamp); } else {} } trackData.startTimestampIsFinal = true; } const insertionIndex = binarySearchLessOrEqual(fragmentPositionCache, trackData.startTimestamp, (x) => x.startTimestamp); if (insertionIndex === -1 || fragmentPositionCache[insertionIndex].moofOffset !== fragment.moofOffset) { fragmentPositionCache.splice(insertionIndex + 1, 0, { moofOffset: fragment.moofOffset, startTimestamp: trackData.startTimestamp, endTimestamp: trackData.endTimestamp }); } } return fragment; } readContiguousBoxes(slice) { const startIndex = slice.filePos; while (slice.filePos - startIndex <= slice.length - MIN_BOX_HEADER_SIZE) { const foundBox = this.traverseBox(slice); if (!foundBox) { break; } } } *iterateContiguousBoxes(slice) { const startIndex = slice.filePos; while (slice.filePos - startIndex <= slice.length - MIN_BOX_HEADER_SIZE) { const startPos = slice.filePos; const boxInfo = readBoxHeader(slice); if (!boxInfo) { break; } yield { boxInfo, slice }; slice.filePos = startPos + boxInfo.totalSize; } } traverseBox(slice) { const startPos = slice.filePos; const boxInfo = readBoxHeader(slice); if (!boxInfo) { return false; } const contentStartPos = slice.filePos; const boxEndPos = startPos + boxInfo.totalSize; switch (boxInfo.name) { case "mdia": case "minf": case "dinf": case "mfra": case "edts": { this.readContiguousBoxes(slice.slice(contentStartPos, boxInfo.contentSize)); } ; break; case "mvhd": { const version = readU8(slice); slice.skip(3); if (version === 1) { slice.skip(8 + 8); this.movieTimescale = readU32Be(slice); this.movieDurationInTimescale = readU64Be(slice); } else { slice.skip(4 + 4); this.movieTimescale = readU32Be(slice); this.movieDurationInTimescale = readU32Be(slice); } } ; break; case "trak": { const track = { id: -1, demuxer: this, inputTrack: null, disposition: { ...DEFAULT_TRACK_DISPOSITION }, info: null, timescale: -1, durationInMovieTimescale: -1, durationInMediaTimescale: -1, rotation: 0, internalCodecId: null, name: null, languageCode: UNDETERMINED_LANGUAGE, sampleTableByteOffset: -1, sampleTable: null, fragmentLookupTable: [], currentFragmentState: null, fragmentPositionCache: [], editListPreviousSegmentDurations: 0, editListOffset: 0 }; this.currentTrack = track; this.readContiguousBoxes(slice.slice(contentStartPos, boxInfo.contentSize)); if (track.id !== -1 && track.timescale !== -1 && track.info !== null) { if (track.info.type === "video" && track.info.width !== -1) { const videoTrack = track; track.inputTrack = new InputVideoTrack(this.input, new IsobmffVideoTrackBacking(videoTrack)); this.tracks.push(track); } else if (track.info.type === "audio" && track.info.numberOfChannels !== -1) { const audioTrack = track; track.inputTrack = new InputAudioTrack(this.input, new IsobmffAudioTrackBacking(audioTrack)); this.tracks.push(track); } } this.currentTrack = null; } ; break; case "tkhd": { const track = this.currentTrack; if (!track) { break; } const version = readU8(slice); const flags = readU24Be(slice); const trackEnabled = !!(flags & 1); track.disposition.default = trackEnabled; if (version === 0) { slice.skip(8); track.id = readU32Be(slice); slice.skip(4); track.durationInMovieTimescale = readU32Be(slice); } else if (version === 1) { slice.skip(16); track.id = readU32Be(slice); slice.skip(4); track.durationInMovieTimescale = readU64Be(slice); } else { throw new Error(`Incorrect track header version ${version}.`); } slice.skip(2 * 4 + 2 + 2 + 2 + 2); const matrix = [ readFixed_16_16(slice), readFixed_16_16(slice), readFixed_2_30(slice), readFixed_16_16(slice), readFixed_16_16(slice), readFixed_2_30(slice), readFixed_16_16(slice), readFixed_16_16(slice), readFixed_2_30(slice) ]; const rotation = normalizeRotation(roundToMultiple(extractRotationFromMatrix(matrix), 90)); assert(rotation === 0 || rotation === 90 || rotation === 180 || rotation === 270); track.rotation = rotation; } ; break; case "elst": { const track = this.currentTrack; if (!track) { break; } const version = readU8(slice); slice.skip(3); let relevantEntryFound = false; let previousSegmentDurations = 0; const entryCount = readU32Be(slice); for (let i = 0;i < entryCount; i++) { const segmentDuration = version === 1 ? readU64Be(slice) : readU32Be(slice); const mediaTime = version === 1 ? readI64Be(slice) : readI32Be(slice); const mediaRate = readFixed_16_16(slice); if (segmentDuration === 0) { continue; } if (relevantEntryFound) { console.warn("Unsupported edit list: multiple edits are not currently supported. Only using first edit."); break; } if (mediaTime === -1) { previousSegmentDurations += segmentDuration; continue; } if (mediaRate !== 1) { console.warn("Unsupported edit list entry: media rate must be 1."); break; } track.editListPreviousSegmentDurations = previousSegmentDurations; track.editListOffset = mediaTime; relevantEntryFound = true; } } ; break; case "mdhd": { const track = this.currentTrack; if (!track) { break; } const version = readU8(slice); slice.skip(3); if (version === 0) { slice.skip(8); track.timescale = readU32Be(slice); track.durationInMediaTimescale = readU32Be(slice); } else if (version === 1) { slice.skip(16); track.timescale = readU32Be(slice); track.durationInMediaTimescale = readU64Be(slice); } let language = readU16Be(slice); if (language > 0) { track.languageCode = ""; for (let i = 0;i < 3; i++) { track.languageCode = String.fromCharCode(96 + (language & 31)) + track.languageCode; language >>= 5; } if (!isIso639Dash2LanguageCode(track.languageCode)) { track.languageCode = UNDETERMINED_LANGUAGE; } } } ; break; case "hdlr": { const track = this.currentTrack; if (!track) { break; } slice.skip(8); const handlerType = readAscii(slice, 4); if (handlerType === "vide") { track.info = { type: "video", width: -1, height: -1, codec: null, codecDescription: null, colorSpace: null, avcType: null, avcCodecInfo: null, hevcCodecInfo: null, vp9CodecInfo: null, av1CodecInfo: null }; } else if (handlerType === "soun") { track.info = { type: "audio", numberOfChannels: -1, sampleRate: -1, codec: null, codecDescription: null, aacCodecInfo: null }; } } ; break; case "stbl": { const track = this.currentTrack; if (!track) { break; } track.sampleTableByteOffset = startPos; this.readContiguousBoxes(slice.slice(contentStartPos, boxInfo.contentSize)); } ; break; case "stsd": { const track = this.currentTrack; if (!track) { break; } if (track.info === null || track.sampleTable) { break; } const stsdVersion = readU8(slice); slice.skip(3); const entries = readU32Be(slice); for (let i = 0;i < entries; i++) { const sampleBoxStartPos = slice.filePos; const sampleBoxInfo = readBoxHeader(slice); if (!sampleBoxInfo) { break; } track.internalCodecId = sampleBoxInfo.name; const lowercaseBoxName = sampleBoxInfo.name.toLowerCase(); if (track.info.type === "video") { if (lowercaseBoxName === "avc1" || lowercaseBoxName === "avc3") { track.info.codec = "avc"; track.info.avcType = lowercaseBoxName === "avc1" ? 1 : 3; } else if (lowercaseBoxName === "hvc1" || lowercaseBoxName === "hev1") { track.info.codec = "hevc"; } else if (lowercaseBoxName === "vp08") { track.info.codec = "vp8"; } else if (lowercaseBoxName === "vp09") { track.info.codec = "vp9"; } else if (lowercaseBoxName === "av01") { track.info.codec = "av1"; } else { console.warn(`Unsupported video codec (sample entry type '${sampleBoxInfo.name}').`); } slice.skip(6 * 1 + 2 + 2 + 2 + 3 * 4); track.info.width = readU16Be(slice); track.info.height = readU16Be(slice); slice.skip(4 + 4 + 4 + 2 + 32 + 2 + 2); this.readContiguousBoxes(slice.slice(slice.filePos, sampleBoxStartPos + sampleBoxInfo.totalSize - slice.filePos)); } else { if (lowercaseBoxName === "mp4a") {} else if (lowercaseBoxName === "opus") { track.info.codec = "opus"; } else if (lowercaseBoxName === "flac") { track.info.codec = "flac"; } else if (lowercaseBoxName === "twos" || lowercaseBoxName === "sowt" || lowercaseBoxName === "raw " || lowercaseBoxName === "in24" || lowercaseBoxName === "in32" || lowercaseBoxName === "fl32" || lowercaseBoxName === "fl64" || lowercaseBoxName === "lpcm" || lowercaseBoxName === "ipcm" || lowercaseBoxName === "fpcm") {} else if (lowercaseBoxName === "ulaw") { track.info.codec = "ulaw"; } else if (lowercaseBoxName === "alaw") { track.info.codec = "alaw"; } else { console.warn(`Unsupported audio codec (sample entry type '${sampleBoxInfo.name}').`); } slice.skip(6 * 1 + 2); const version = readU16Be(slice); slice.skip(3 * 2); let channelCount = readU16Be(slice); let sampleSize = readU16Be(slice); slice.skip(2 * 2); let sampleRate = readU32Be(slice) / 65536; if (stsdVersion === 0 && version > 0) { if (version === 1) { slice.skip(4); sampleSize = 8 * readU32Be(slice); slice.skip(2 * 4); } else if (version === 2) { slice.skip(4); sampleRate = readF64Be(slice); channelCount = readU32Be(slice); slice.skip(4); sampleSize = readU32Be(slice); const flags = readU32Be(slice); slice.skip(2 * 4); if (lowercaseBoxName === "lpcm") { const bytesPerSample = sampleSize + 7 >> 3; const isFloat = Boolean(flags & 1); const isBigEndian = Boolean(flags & 2); const sFlags = flags & 4 ? -1 : 0; if (sampleSize > 0 && sampleSize <= 64) { if (isFloat) { if (sampleSize === 32) { track.info.codec = isBigEndian ? "pcm-f32be" : "pcm-f32"; } } else { if (sFlags & 1 << bytesPerSample - 1) { if (bytesPerSample === 1) { track.info.codec = "pcm-s8"; } else if (bytesPerSample === 2) { track.info.codec = isBigEndian ? "pcm-s16be" : "pcm-s16"; } else if (bytesPerSample === 3) { track.info.codec = isBigEndian ? "pcm-s24be" : "pcm-s24"; } else if (bytesPerSample === 4) { track.info.codec = isBigEndian ? "pcm-s32be" : "pcm-s32"; } } else { if (bytesPerSample === 1) { track.info.codec = "pcm-u8"; } } } } if (track.info.codec === null) { console.warn("Unsupported PCM format."); } } } } if (track.info.codec === "opus") { sampleRate = OPUS_SAMPLE_RATE; } track.info.numberOfChannels = channelCount; track.info.sampleRate = sampleRate; if (lowercaseBoxName === "twos") { if (sampleSize === 8) { track.info.codec = "pcm-s8"; } else if (sampleSize === 16) { track.info.codec = "pcm-s16be"; } else { console.warn(`Unsupported sample size ${sampleSize} for codec 'twos'.`); track.info.codec = null; } } else if (lowercaseBoxName === "sowt") { if (sampleSize === 8) { track.info.codec = "pcm-s8"; } else if (sampleSize === 16) { track.info.codec = "pcm-s16"; } else { console.warn(`Unsupported sample size ${sampleSize} for codec 'sowt'.`); track.info.codec = null; } } else if (lowercaseBoxName === "raw ") { track.info.codec = "pcm-u8"; } else if (lowercaseBoxName === "in24") { track.info.codec = "pcm-s24be"; } else if (lowercaseBoxName === "in32") { track.info.codec = "pcm-s32be"; } else if (lowercaseBoxName === "fl32") { track.info.codec = "pcm-f32be"; } else if (lowercaseBoxName === "fl64") { track.info.codec = "pcm-f64be"; } else if (lowercaseBoxName === "ipcm") { track.info.codec = "pcm-s16be"; } else if (lowercaseBoxName === "fpcm") { track.info.codec = "pcm-f32be"; } this.readContiguousBoxes(slice.slice(slice.filePos, sampleBoxStartPos + sampleBoxInfo.totalSize - slice.filePos)); } } } ; break; case "avcC": { const track = this.currentTrack; if (!track) { break; } assert(track.info); track.info.codecDescription = readBytes(slice, boxInfo.contentSize); } ; break; case "hvcC": { const track = this.currentTrack; if (!track) { break; } assert(track.info); track.info.codecDescription = readBytes(slice, boxInfo.contentSize); } ; break; case "vpcC": { const track = this.currentTrack; if (!track) { break; } assert(track.info?.type === "video"); slice.skip(4); const profile = readU8(slice); const level = readU8(slice); const thirdByte = readU8(slice); const bitDepth = thirdByte >> 4; const chromaSubsampling = thirdByte >> 1 & 7; const videoFullRangeFlag = thirdByte & 1; const colourPrimaries = readU8(slice); const transferCharacteristics = readU8(slice); const matrixCoefficients = readU8(slice); track.info.vp9CodecInfo = { profile, level, bitDepth, chromaSubsampling, videoFullRangeFlag, colourPrimaries, transferCharacteristics, matrixCoefficients }; } ; break; case "av1C": { const track = this.currentTrack; if (!track) { break; } assert(track.info?.type === "video"); slice.skip(1); const secondByte = readU8(slice); const profile = secondByte >> 5; const level = secondByte & 31; const thirdByte = readU8(slice); const tier = thirdByte >> 7; const highBitDepth = thirdByte >> 6 & 1; const twelveBit = thirdByte >> 5 & 1; const monochrome = thirdByte >> 4 & 1; const chromaSubsamplingX = thirdByte >> 3 & 1; const chromaSubsamplingY = thirdByte >> 2 & 1; const chromaSamplePosition = thirdByte & 3; const bitDepth = profile === 2 && highBitDepth ? twelveBit ? 12 : 10 : highBitDepth ? 10 : 8; track.info.av1CodecInfo = { profile, level, tier, bitDepth, monochrome, chromaSubsamplingX, chromaSubsamplingY, chromaSamplePosition }; } ; break; case "colr": { const track = this.currentTrack; if (!track) { break; } assert(track.info?.type === "video"); const colourType = readAscii(slice, 4); if (colourType !== "nclx") { break; } const colourPrimaries = readU16Be(slice); const transferCharacteristics = readU16Be(slice); const matrixCoefficients = readU16Be(slice); const fullRangeFlag = Boolean(readU8(slice) & 128); track.info.colorSpace = { primaries: COLOR_PRIMARIES_MAP_INVERSE[colourPrimaries], transfer: TRANSFER_CHARACTERISTICS_MAP_INVERSE[transferCharacteristics], matrix: MATRIX_COEFFICIENTS_MAP_INVERSE[matrixCoefficients], fullRange: fullRangeFlag }; } ; break; case "wave": { this.readContiguousBoxes(slice.slice(contentStartPos, boxInfo.contentSize)); } ; break; case "esds": { const track = this.currentTrack; if (!track) { break; } assert(track.info?.type === "audio"); slice.skip(4); const tag = readU8(slice); assert(tag === 3); readIsomVariableInteger(slice); slice.skip(2); const mixed = readU8(slice); const streamDependenceFlag = (mixed & 128) !== 0; const urlFlag = (mixed & 64) !== 0; const ocrStreamFlag = (mixed & 32) !== 0; if (streamDependenceFlag) { slice.skip(2); } if (urlFlag) { const urlLength = readU8(slice); slice.skip(urlLength); } if (ocrStreamFlag) { slice.skip(2); } const decoderConfigTag = readU8(slice); assert(decoderConfigTag === 4); const decoderConfigDescriptorLength = readIsomVariableInteger(slice); const payloadStart = slice.filePos; const objectTypeIndication = readU8(slice); if (objectTypeIndication === 64 || objectTypeIndication === 103) { track.info.codec = "aac"; track.info.aacCodecInfo = { isMpeg2: objectTypeIndication === 103, objectType: null }; } else if (objectTypeIndication === 105 || objectTypeIndication === 107) { track.info.codec = "mp3"; } else if (objectTypeIndication === 221) { track.info.codec = "vorbis"; } else { console.warn(`Unsupported audio codec (objectTypeIndication ${objectTypeIndication}) - discarding track.`); } slice.skip(1 + 3 + 4 + 4); if (decoderConfigDescriptorLength > slice.filePos - payloadStart) { const decoderSpecificInfoTag = readU8(slice); assert(decoderSpecificInfoTag === 5); const decoderSpecificInfoLength = readIsomVariableInteger(slice); track.info.codecDescription = readBytes(slice, decoderSpecificInfoLength); if (track.info.codec === "aac") { const audioSpecificConfig = parseAacAudioSpecificConfig(track.info.codecDescription); if (audioSpecificConfig.numberOfChannels !== null) { track.info.numberOfChannels = audioSpecificConfig.numberOfChannels; } if (audioSpecificConfig.sampleRate !== null) { track.info.sampleRate = audioSpecificConfig.sampleRate; } } } } ; break; case "enda": { const track = this.currentTrack; if (!track) { break; } assert(track.info?.type === "audio"); const littleEndian = readU16Be(slice) & 255; if (littleEndian) { if (track.info.codec === "pcm-s16be") { track.info.codec = "pcm-s16"; } else if (track.info.codec === "pcm-s24be") { track.info.codec = "pcm-s24"; } else if (track.info.codec === "pcm-s32be") { track.info.codec = "pcm-s32"; } else if (track.info.codec === "pcm-f32be") { track.info.codec = "pcm-f32"; } else if (track.info.codec === "pcm-f64be") { track.info.codec = "pcm-f64"; } } } ; break; case "pcmC": { const track = this.currentTrack; if (!track) { break; } assert(track.info?.type === "audio"); slice.skip(1 + 3); const formatFlags = readU8(slice); const isLittleEndian = Boolean(formatFlags & 1); const pcmSampleSize = readU8(slice); if (track.info.codec === "pcm-s16be") { if (isLittleEndian) { if (pcmSampleSize === 16) { track.info.codec = "pcm-s16"; } else if (pcmSampleSize === 24) { track.info.codec = "pcm-s24"; } else if (pcmSampleSize === 32) { track.info.codec = "pcm-s32"; } else { console.warn(`Invalid ipcm sample size ${pcmSampleSize}.`); track.info.codec = null; } } else { if (pcmSampleSize === 16) { track.info.codec = "pcm-s16be"; } else if (pcmSampleSize === 24) { track.info.codec = "pcm-s24be"; } else if (pcmSampleSize === 32) { track.info.codec = "pcm-s32be"; } else { console.warn(`Invalid ipcm sample size ${pcmSampleSize}.`); track.info.codec = null; } } } else if (track.info.codec === "pcm-f32be") { if (isLittleEndian) { if (pcmSampleSize === 32) { track.info.codec = "pcm-f32"; } else if (pcmSampleSize === 64) { track.info.codec = "pcm-f64"; } else { console.warn(`Invalid fpcm sample size ${pcmSampleSize}.`); track.info.codec = null; } } else { if (pcmSampleSize === 32) { track.info.codec = "pcm-f32be"; } else if (pcmSampleSize === 64) { track.info.codec = "pcm-f64be"; } else { console.warn(`Invalid fpcm sample size ${pcmSampleSize}.`); track.info.codec = null; } } } break; } ; case "dOps": { const track = this.currentTrack; if (!track) { break; } assert(track.info?.type === "audio"); slice.skip(1); const outputChannelCount = readU8(slice); const preSkip = readU16Be(slice); const inputSampleRate = readU32Be(slice); const outputGain = readI16Be(slice); const channelMappingFamily = readU8(slice); let channelMappingTable; if (channelMappingFamily !== 0) { channelMappingTable = readBytes(slice, 2 + outputChannelCount); } else { channelMappingTable = new Uint8Array(0); } const description = new Uint8Array(8 + 1 + 1 + 2 + 4 + 2 + 1 + channelMappingTable.byteLength); const view = new DataView(description.buffer); view.setUint32(0, 1332770163, false); view.setUint32(4, 1214603620, false); view.setUint8(8, 1); view.setUint8(9, outputChannelCount); view.setUint16(10, preSkip, true); view.setUint32(12, inputSampleRate, true); view.setInt16(16, outputGain, true); view.setUint8(18, channelMappingFamily); description.set(channelMappingTable, 19); track.info.codecDescription = description; track.info.numberOfChannels = outputChannelCount; } ; break; case "dfLa": { const track = this.currentTrack; if (!track) { break; } assert(track.info?.type === "audio"); slice.skip(4); const BLOCK_TYPE_MASK = 127; const LAST_METADATA_BLOCK_FLAG_MASK = 128; const startPos2 = slice.filePos; while (slice.filePos < boxEndPos) { const flagAndType = readU8(slice); const metadataBlockLength = readU24Be(slice); const type = flagAndType & BLOCK_TYPE_MASK; if (type === FlacBlockType.STREAMINFO) { slice.skip(10); const word = readU32Be(slice); const sampleRate = word >>> 12; const numberOfChannels = (word >> 9 & 7) + 1; track.info.sampleRate = sampleRate; track.info.numberOfChannels = numberOfChannels; slice.skip(20); } else { slice.skip(metadataBlockLength); } if (flagAndType & LAST_METADATA_BLOCK_FLAG_MASK) { break; } } const endPos = slice.filePos; slice.filePos = startPos2; const bytes = readBytes(slice, endPos - startPos2); const description = new Uint8Array(4 + bytes.byteLength); const view = new DataView(description.buffer); view.setUint32(0, 1716281667, false); description.set(bytes, 4); track.info.codecDescription = description; } ; break; case "stts": { const track = this.currentTrack; if (!track) { break; } if (!track.sampleTable) { break; } slice.skip(4); const entryCount = readU32Be(slice); let currentIndex = 0; let currentTimestamp = 0; for (let i = 0;i < entryCount; i++) { const sampleCount = readU32Be(slice); const sampleDelta = readU32Be(slice); track.sampleTable.sampleTimingEntries.push({ startIndex: currentIndex, startDecodeTimestamp: currentTimestamp, count: sampleCount, delta: sampleDelta }); currentIndex += sampleCount; currentTimestamp += sampleCount * sampleDelta; } } ; break; case "ctts": { const track = this.currentTrack; if (!track) { break; } if (!track.sampleTable) { break; } slice.skip(1 + 3); const entryCount = readU32Be(slice); let sampleIndex = 0; for (let i = 0;i < entryCount; i++) { const sampleCount = readU32Be(slice); const sampleOffset = readI32Be(slice); track.sampleTable.sampleCompositionTimeOffsets.push({ startIndex: sampleIndex, count: sampleCount, offset: sampleOffset }); sampleIndex += sampleCount; } } ; break; case "stsz": { const track = this.currentTrack; if (!track) { break; } if (!track.sampleTable) { break; } slice.skip(4); const sampleSize = readU32Be(slice); const sampleCount = readU32Be(slice); if (sampleSize === 0) { for (let i = 0;i < sampleCount; i++) { const sampleSize2 = readU32Be(slice); track.sampleTable.sampleSizes.push(sampleSize2); } } else { track.sampleTable.sampleSizes.push(sampleSize); } } ; break; case "stz2": { const track = this.currentTrack; if (!track) { break; } if (!track.sampleTable) { break; } slice.skip(4); slice.skip(3); const fieldSize = readU8(slice); const sampleCount = readU32Be(slice); const bytes = readBytes(slice, Math.ceil(sampleCount * fieldSize / 8)); const bitstream = new Bitstream(bytes); for (let i = 0;i < sampleCount; i++) { const sampleSize = bitstream.readBits(fieldSize); track.sampleTable.sampleSizes.push(sampleSize); } } ; break; case "stss": { const track = this.currentTrack; if (!track) { break; } if (!track.sampleTable) { break; } slice.skip(4); track.sampleTable.keySampleIndices = []; const entryCount = readU32Be(slice); for (let i = 0;i < entryCount; i++) { const sampleIndex = readU32Be(slice) - 1; track.sampleTable.keySampleIndices.push(sampleIndex); } if (track.sampleTable.keySampleIndices[0] !== 0) { track.sampleTable.keySampleIndices.unshift(0); } } ; break; case "stsc": { const track = this.currentTrack; if (!track) { break; } if (!track.sampleTable) { break; } slice.skip(4); const entryCount = readU32Be(slice); for (let i = 0;i < entryCount; i++) { const startChunkIndex = readU32Be(slice) - 1; const samplesPerChunk = readU32Be(slice); const sampleDescriptionIndex = readU32Be(slice); track.sampleTable.sampleToChunk.push({ startSampleIndex: -1, startChunkIndex, samplesPerChunk, sampleDescriptionIndex }); } let startSampleIndex = 0; for (let i = 0;i < track.sampleTable.sampleToChunk.length; i++) { track.sampleTable.sampleToChunk[i].startSampleIndex = startSampleIndex; if (i < track.sampleTable.sampleToChunk.length - 1) { const nextChunk = track.sampleTable.sampleToChunk[i + 1]; const chunkCount = nextChunk.startChunkIndex - track.sampleTable.sampleToChunk[i].startChunkIndex; startSampleIndex += chunkCount * track.sampleTable.sampleToChunk[i].samplesPerChunk; } } } ; break; case "stco": { const track = this.currentTrack; if (!track) { break; } if (!track.sampleTable) { break; } slice.skip(4); const entryCount = readU32Be(slice); for (let i = 0;i < entryCount; i++) { const chunkOffset = readU32Be(slice); track.sampleTable.chunkOffsets.push(chunkOffset); } } ; break; case "co64": { const track = this.currentTrack; if (!track) { break; } if (!track.sampleTable) { break; } slice.skip(4); const entryCount = readU32Be(slice); for (let i = 0;i < entryCount; i++) { const chunkOffset = readU64Be(slice); track.sampleTable.chunkOffsets.push(chunkOffset); } } ; break; case "mvex": { this.isFragmented = true; this.readContiguousBoxes(slice.slice(contentStartPos, boxInfo.contentSize)); } ; break; case "mehd": { const version = readU8(slice); slice.skip(3); const fragmentDuration = version === 1 ? readU64Be(slice) : readU32Be(slice); this.movieDurationInTimescale = fragmentDuration; } ; break; case "trex": { slice.skip(4); const trackId = readU32Be(slice); const defaultSampleDescriptionIndex = readU32Be(slice); const defaultSampleDuration = readU32Be(slice); const defaultSampleSize = readU32Be(slice); const defaultSampleFlags = readU32Be(slice); this.fragmentTrackDefaults.push({ trackId, defaultSampleDescriptionIndex, defaultSampleDuration, defaultSampleSize, defaultSampleFlags }); } ; break; case "tfra": { const version = readU8(slice); slice.skip(3); const trackId = readU32Be(slice); const track = this.tracks.find((x) => x.id === trackId); if (!track) { break; } const word = readU32Be(slice); const lengthSizeOfTrafNum = (word & 48) >> 4; const lengthSizeOfTrunNum = (word & 12) >> 2; const lengthSizeOfSampleNum = word & 3; const functions = [readU8, readU16Be, readU24Be, readU32Be]; const readTrafNum = functions[lengthSizeOfTrafNum]; const readTrunNum = functions[lengthSizeOfTrunNum]; const readSampleNum = functions[lengthSizeOfSampleNum]; const numberOfEntries = readU32Be(slice); for (let i = 0;i < numberOfEntries; i++) { const time2 = version === 1 ? readU64Be(slice) : readU32Be(slice); const moofOffset = version === 1 ? readU64Be(slice) : readU32Be(slice); readTrafNum(slice); readTrunNum(slice); readSampleNum(slice); track.fragmentLookupTable.push({ timestamp: time2, moofOffset }); } track.fragmentLookupTable.sort((a, b) => a.timestamp - b.timestamp); for (let i = 0;i < track.fragmentLookupTable.length - 1; i++) { const entry1 = track.fragmentLookupTable[i]; const entry2 = track.fragmentLookupTable[i + 1]; if (entry1.timestamp === entry2.timestamp) { track.fragmentLookupTable.splice(i + 1, 1); i--; } } } ; break; case "moof": { this.currentFragment = { moofOffset: startPos, moofSize: boxInfo.totalSize, implicitBaseDataOffset: startPos, trackData: new Map }; this.readContiguousBoxes(slice.slice(contentStartPos, boxInfo.contentSize)); this.lastReadFragment = this.currentFragment; this.currentFragment = null; } ; break; case "traf": { assert(this.currentFragment); this.readContiguousBoxes(slice.slice(contentStartPos, boxInfo.contentSize)); if (this.currentTrack) { const trackData = this.currentFragment.trackData.get(this.currentTrack.id); if (trackData) { const { currentFragmentState } = this.currentTrack; assert(currentFragmentState); if (currentFragmentState.startTimestamp !== null) { offsetFragmentTrackDataByTimestamp(trackData, currentFragmentState.startTimestamp); trackData.startTimestampIsFinal = true; } } this.currentTrack.currentFragmentState = null; this.currentTrack = null; } } ; break; case "tfhd": { assert(this.currentFragment); slice.skip(1); const flags = readU24Be(slice); const baseDataOffsetPresent = Boolean(flags & 1); const sampleDescriptionIndexPresent = Boolean(flags & 2); const defaultSampleDurationPresent = Boolean(flags & 8); const defaultSampleSizePresent = Boolean(flags & 16); const defaultSampleFlagsPresent = Boolean(flags & 32); const durationIsEmpty = Boolean(flags & 65536); const defaultBaseIsMoof = Boolean(flags & 131072); const trackId = readU32Be(slice); const track = this.tracks.find((x) => x.id === trackId); if (!track) { break; } const defaults = this.fragmentTrackDefaults.find((x) => x.trackId === trackId); this.currentTrack = track; track.currentFragmentState = { baseDataOffset: this.currentFragment.implicitBaseDataOffset, sampleDescriptionIndex: defaults?.defaultSampleDescriptionIndex ?? null, defaultSampleDuration: defaults?.defaultSampleDuration ?? null, defaultSampleSize: defaults?.defaultSampleSize ?? null, defaultSampleFlags: defaults?.defaultSampleFlags ?? null, startTimestamp: null }; if (baseDataOffsetPresent) { track.currentFragmentState.baseDataOffset = readU64Be(slice); } else if (defaultBaseIsMoof) { track.currentFragmentState.baseDataOffset = this.currentFragment.moofOffset; } if (sampleDescriptionIndexPresent) { track.currentFragmentState.sampleDescriptionIndex = readU32Be(slice); } if (defaultSampleDurationPresent) { track.currentFragmentState.defaultSampleDuration = readU32Be(slice); } if (defaultSampleSizePresent) { track.currentFragmentState.defaultSampleSize = readU32Be(slice); } if (defaultSampleFlagsPresent) { track.currentFragmentState.defaultSampleFlags = readU32Be(slice); } if (durationIsEmpty) { track.currentFragmentState.defaultSampleDuration = 0; } } ; break; case "tfdt": { const track = this.currentTrack; if (!track) { break; } assert(track.currentFragmentState); const version = readU8(slice); slice.skip(3); const baseMediaDecodeTime = version === 0 ? readU32Be(slice) : readU64Be(slice); track.currentFragmentState.startTimestamp = baseMediaDecodeTime; } ; break; case "trun": { const track = this.currentTrack; if (!track) { break; } assert(this.currentFragment); assert(track.currentFragmentState); if (this.currentFragment.trackData.has(track.id)) { console.warn("Can't have two trun boxes for the same track in one fragment. Ignoring..."); break; } const version = readU8(slice); const flags = readU24Be(slice); const dataOffsetPresent = Boolean(flags & 1); const firstSampleFlagsPresent = Boolean(flags & 4); const sampleDurationPresent = Boolean(flags & 256); const sampleSizePresent = Boolean(flags & 512); const sampleFlagsPresent = Boolean(flags & 1024); const sampleCompositionTimeOffsetsPresent = Boolean(flags & 2048); const sampleCount = readU32Be(slice); let dataOffset = track.currentFragmentState.baseDataOffset; if (dataOffsetPresent) { dataOffset += readI32Be(slice); } let firstSampleFlags = null; if (firstSampleFlagsPresent) { firstSampleFlags = readU32Be(slice); } let currentOffset = dataOffset; if (sampleCount === 0) { this.currentFragment.implicitBaseDataOffset = currentOffset; break; } let currentTimestamp = 0; const trackData = { track, startTimestamp: 0, endTimestamp: 0, firstKeyFrameTimestamp: null, samples: [], presentationTimestamps: [], startTimestampIsFinal: false }; this.currentFragment.trackData.set(track.id, trackData); for (let i = 0;i < sampleCount; i++) { let sampleDuration; if (sampleDurationPresent) { sampleDuration = readU32Be(slice); } else { assert(track.currentFragmentState.defaultSampleDuration !== null); sampleDuration = track.currentFragmentState.defaultSampleDuration; } let sampleSize; if (sampleSizePresent) { sampleSize = readU32Be(slice); } else { assert(track.currentFragmentState.defaultSampleSize !== null); sampleSize = track.currentFragmentState.defaultSampleSize; } let sampleFlags; if (sampleFlagsPresent) { sampleFlags = readU32Be(slice); } else { assert(track.currentFragmentState.defaultSampleFlags !== null); sampleFlags = track.currentFragmentState.defaultSampleFlags; } if (i === 0 && firstSampleFlags !== null) { sampleFlags = firstSampleFlags; } let sampleCompositionTimeOffset = 0; if (sampleCompositionTimeOffsetsPresent) { if (version === 0) { sampleCompositionTimeOffset = readU32Be(slice); } else { sampleCompositionTimeOffset = readI32Be(slice); } } const isKeyFrame = !(sampleFlags & 65536); trackData.samples.push({ presentationTimestamp: currentTimestamp + sampleCompositionTimeOffset, duration: sampleDuration, byteOffset: currentOffset, byteSize: sampleSize, isKeyFrame }); currentOffset += sampleSize; currentTimestamp += sampleDuration; } trackData.presentationTimestamps = trackData.samples.map((x, i) => ({ presentationTimestamp: x.presentationTimestamp, sampleIndex: i })).sort((a, b) => a.presentationTimestamp - b.presentationTimestamp); for (let i = 0;i < trackData.presentationTimestamps.length; i++) { const currentEntry = trackData.presentationTimestamps[i]; const currentSample = trackData.samples[currentEntry.sampleIndex]; if (trackData.firstKeyFrameTimestamp === null && currentSample.isKeyFrame) { trackData.firstKeyFrameTimestamp = currentSample.presentationTimestamp; } if (i < trackData.presentationTimestamps.length - 1) { const nextEntry = trackData.presentationTimestamps[i + 1]; currentSample.duration = nextEntry.presentationTimestamp - currentEntry.presentationTimestamp; } } const firstSample = trackData.samples[trackData.presentationTimestamps[0].sampleIndex]; const lastSample = trackData.samples[last(trackData.presentationTimestamps).sampleIndex]; trackData.startTimestamp = firstSample.presentationTimestamp; trackData.endTimestamp = lastSample.presentationTimestamp + lastSample.duration; this.currentFragment.implicitBaseDataOffset = currentOffset; } ; break; case "udta": { const iterator = this.iterateContiguousBoxes(slice.slice(contentStartPos, boxInfo.contentSize)); for (const { boxInfo: boxInfo2, slice: slice2 } of iterator) { if (boxInfo2.name !== "meta" && !this.currentTrack) { const startPos2 = slice2.filePos; this.metadataTags.raw ??= {}; if (boxInfo2.name[0] === "©") { this.metadataTags.raw[boxInfo2.name] ??= readMetadataStringShort(slice2); } else { this.metadataTags.raw[boxInfo2.name] ??= readBytes(slice2, boxInfo2.contentSize); } slice2.filePos = startPos2; } switch (boxInfo2.name) { case "meta": { slice2.skip(-boxInfo2.headerSize); this.traverseBox(slice2); } ; break; case "©nam": case "name": { if (this.currentTrack) { this.currentTrack.name = textDecoder.decode(readBytes(slice2, boxInfo2.contentSize)); } else { this.metadataTags.title ??= readMetadataStringShort(slice2); } } ; break; case "©des": { if (!this.currentTrack) { this.metadataTags.description ??= readMetadataStringShort(slice2); } } ; break; case "©ART": { if (!this.currentTrack) { this.metadataTags.artist ??= readMetadataStringShort(slice2); } } ; break; case "©alb": { if (!this.currentTrack) { this.metadataTags.album ??= readMetadataStringShort(slice2); } } ; break; case "albr": { if (!this.currentTrack) { this.metadataTags.albumArtist ??= readMetadataStringShort(slice2); } } ; break; case "©gen": { if (!this.currentTrack) { this.metadataTags.genre ??= readMetadataStringShort(slice2); } } ; break; case "©day": { if (!this.currentTrack) { const date = new Date(readMetadataStringShort(slice2)); if (!Number.isNaN(date.getTime())) { this.metadataTags.date ??= date; } } } ; break; case "©cmt": { if (!this.currentTrack) { this.metadataTags.comment ??= readMetadataStringShort(slice2); } } ; break; case "©lyr": { if (!this.currentTrack) { this.metadataTags.lyrics ??= readMetadataStringShort(slice2); } } ; break; } } } ; break; case "meta": { if (this.currentTrack) { break; } const word = readU32Be(slice); const isQuickTime = word !== 0; this.currentMetadataKeys = new Map; if (isQuickTime) { this.readContiguousBoxes(slice.slice(contentStartPos, boxInfo.contentSize)); } else { this.readContiguousBoxes(slice.slice(contentStartPos + 4, boxInfo.contentSize - 4)); } this.currentMetadataKeys = null; } ; break; case "keys": { if (!this.currentMetadataKeys) { break; } slice.skip(4); const entryCount = readU32Be(slice); for (let i = 0;i < entryCount; i++) { const keySize = readU32Be(slice); slice.skip(4); const keyName = textDecoder.decode(readBytes(slice, keySize - 8)); this.currentMetadataKeys.set(i + 1, keyName); } } ; break; case "ilst": { if (!this.currentMetadataKeys) { break; } const iterator = this.iterateContiguousBoxes(slice.slice(contentStartPos, boxInfo.contentSize)); for (const { boxInfo: boxInfo2, slice: slice2 } of iterator) { let metadataKey = boxInfo2.name; const nameAsNumber = (metadataKey.charCodeAt(0) << 24) + (metadataKey.charCodeAt(1) << 16) + (metadataKey.charCodeAt(2) << 8) + metadataKey.charCodeAt(3); if (this.currentMetadataKeys.has(nameAsNumber)) { metadataKey = this.currentMetadataKeys.get(nameAsNumber); } const data = readDataBox(slice2); this.metadataTags.raw ??= {}; this.metadataTags.raw[metadataKey] ??= data; switch (metadataKey) { case "©nam": case "titl": case "com.apple.quicktime.title": case "title": { if (typeof data === "string") { this.metadataTags.title ??= data; } } ; break; case "©des": case "desc": case "dscp": case "com.apple.quicktime.description": case "description": { if (typeof data === "string") { this.metadataTags.description ??= data; } } ; break; case "©ART": case "com.apple.quicktime.artist": case "artist": { if (typeof data === "string") { this.metadataTags.artist ??= data; } } ; break; case "©alb": case "albm": case "com.apple.quicktime.album": case "album": { if (typeof data === "string") { this.metadataTags.album ??= data; } } ; break; case "aART": case "album_artist": { if (typeof data === "string") { this.metadataTags.albumArtist ??= data; } } ; break; case "©cmt": case "com.apple.quicktime.comment": case "comment": { if (typeof data === "string") { this.metadataTags.comment ??= data; } } ; break; case "©gen": case "gnre": case "com.apple.quicktime.genre": case "genre": { if (typeof data === "string") { this.metadataTags.genre ??= data; } } ; break; case "©lyr": case "lyrics": { if (typeof data === "string") { this.metadataTags.lyrics ??= data; } } ; break; case "©day": case "rldt": case "com.apple.quicktime.creationdate": case "date": { if (typeof data === "string") { const date = new Date(data); if (!Number.isNaN(date.getTime())) { this.metadataTags.date ??= date; } } } ; break; case "covr": case "com.apple.quicktime.artwork": { if (data instanceof RichImageData) { this.metadataTags.images ??= []; this.metadataTags.images.push({ data: data.data, kind: "coverFront", mimeType: data.mimeType }); } else if (data instanceof Uint8Array) { this.metadataTags.images ??= []; this.metadataTags.images.push({ data, kind: "coverFront", mimeType: "image/*" }); } } ; break; case "track": { if (typeof data === "string") { const parts = data.split("/"); const trackNum = Number.parseInt(parts[0], 10); const tracksTotal = parts[1] && Number.parseInt(parts[1], 10); if (Number.isInteger(trackNum) && trackNum > 0) { this.metadataTags.trackNumber ??= trackNum; } if (tracksTotal && Number.isInteger(tracksTotal) && tracksTotal > 0) { this.metadataTags.tracksTotal ??= tracksTotal; } } } ; break; case "trkn": { if (data instanceof Uint8Array && data.length >= 6) { const view = toDataView(data); const trackNumber = view.getUint16(2, false); const tracksTotal = view.getUint16(4, false); if (trackNumber > 0) { this.metadataTags.trackNumber ??= trackNumber; } if (tracksTotal > 0) { this.metadataTags.tracksTotal ??= tracksTotal; } } } ; break; case "disc": case "disk": { if (data instanceof Uint8Array && data.length >= 6) { const view = toDataView(data); const discNumber = view.getUint16(2, false); const discNumberMax = view.getUint16(4, false); if (discNumber > 0) { this.metadataTags.discNumber ??= discNumber; } if (discNumberMax > 0) { this.metadataTags.discsTotal ??= discNumberMax; } } } ; break; } } } ; break; } slice.filePos = boxEndPos; return true; } } class IsobmffTrackBacking { constructor(internalTrack) { this.internalTrack = internalTrack; this.packetToSampleIndex = new WeakMap; this.packetToFragmentLocation = new WeakMap; } getId() { return this.internalTrack.id; } getCodec() { throw new Error("Not implemented on base class."); } getInternalCodecId() { return this.internalTrack.internalCodecId; } getName() { return this.internalTrack.name; } getLanguageCode() { return this.internalTrack.languageCode; } getTimeResolution() { return this.internalTrack.timescale; } getDisposition() { return this.internalTrack.disposition; } async computeDuration() { const lastPacket = await this.getPacket(Infinity, { metadataOnly: true }); return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0); } async getFirstTimestamp() { const firstPacket = await this.getFirstPacket({ metadataOnly: true }); return firstPacket?.timestamp ?? 0; } async getFirstPacket(options) { const regularPacket = await this.fetchPacketForSampleIndex(0, options); if (regularPacket || !this.internalTrack.demuxer.isFragmented) { return regularPacket; } return this.performFragmentedLookup(null, (fragment) => { const trackData = fragment.trackData.get(this.internalTrack.id); if (trackData) { return { sampleIndex: 0, correctSampleFound: true }; } return { sampleIndex: -1, correctSampleFound: false }; }, -Infinity, Infinity, options); } mapTimestampIntoTimescale(timestamp) { return roundIfAlmostInteger(timestamp * this.internalTrack.timescale) + this.internalTrack.editListOffset; } async getPacket(timestamp, options) { const timestampInTimescale = this.mapTimestampIntoTimescale(timestamp); const sampleTable = this.internalTrack.demuxer.getSampleTableForTrack(this.internalTrack); const sampleIndex = getSampleIndexForTimestamp(sampleTable, timestampInTimescale); const regularPacket = await this.fetchPacketForSampleIndex(sampleIndex, options); if (!sampleTableIsEmpty(sampleTable) || !this.internalTrack.demuxer.isFragmented) { return regularPacket; } return this.performFragmentedLookup(null, (fragment) => { const trackData = fragment.trackData.get(this.internalTrack.id); if (!trackData) { return { sampleIndex: -1, correctSampleFound: false }; } const index = binarySearchLessOrEqual(trackData.presentationTimestamps, timestampInTimescale, (x) => x.presentationTimestamp); const sampleIndex2 = index !== -1 ? trackData.presentationTimestamps[index].sampleIndex : -1; const correctSampleFound = index !== -1 && timestampInTimescale < trackData.endTimestamp; return { sampleIndex: sampleIndex2, correctSampleFound }; }, timestampInTimescale, timestampInTimescale, options); } async getNextPacket(packet, options) { const regularSampleIndex = this.packetToSampleIndex.get(packet); if (regularSampleIndex !== undefined) { return this.fetchPacketForSampleIndex(regularSampleIndex + 1, options); } const locationInFragment = this.packetToFragmentLocation.get(packet); if (locationInFragment === undefined) { throw new Error("Packet was not created from this track."); } return this.performFragmentedLookup(locationInFragment.fragment, (fragment) => { if (fragment === locationInFragment.fragment) { const trackData = fragment.trackData.get(this.internalTrack.id); if (locationInFragment.sampleIndex + 1 < trackData.samples.length) { return { sampleIndex: locationInFragment.sampleIndex + 1, correctSampleFound: true }; } } else { const trackData = fragment.trackData.get(this.internalTrack.id); if (trackData) { return { sampleIndex: 0, correctSampleFound: true }; } } return { sampleIndex: -1, correctSampleFound: false }; }, -Infinity, Infinity, options); } async getKeyPacket(timestamp, options) { const timestampInTimescale = this.mapTimestampIntoTimescale(timestamp); const sampleTable = this.internalTrack.demuxer.getSampleTableForTrack(this.internalTrack); const sampleIndex = getKeyframeSampleIndexForTimestamp(sampleTable, timestampInTimescale); const regularPacket = await this.fetchPacketForSampleIndex(sampleIndex, options); if (!sampleTableIsEmpty(sampleTable) || !this.internalTrack.demuxer.isFragmented) { return regularPacket; } return this.performFragmentedLookup(null, (fragment) => { const trackData = fragment.trackData.get(this.internalTrack.id); if (!trackData) { return { sampleIndex: -1, correctSampleFound: false }; } const index = findLastIndex(trackData.presentationTimestamps, (x) => { const sample = trackData.samples[x.sampleIndex]; return sample.isKeyFrame && x.presentationTimestamp <= timestampInTimescale; }); const sampleIndex2 = index !== -1 ? trackData.presentationTimestamps[index].sampleIndex : -1; const correctSampleFound = index !== -1 && timestampInTimescale < trackData.endTimestamp; return { sampleIndex: sampleIndex2, correctSampleFound }; }, timestampInTimescale, timestampInTimescale, options); } async getNextKeyPacket(packet, options) { const regularSampleIndex = this.packetToSampleIndex.get(packet); if (regularSampleIndex !== undefined) { const sampleTable = this.internalTrack.demuxer.getSampleTableForTrack(this.internalTrack); const nextKeyFrameSampleIndex = getNextKeyframeIndexForSample(sampleTable, regularSampleIndex); return this.fetchPacketForSampleIndex(nextKeyFrameSampleIndex, options); } const locationInFragment = this.packetToFragmentLocation.get(packet); if (locationInFragment === undefined) { throw new Error("Packet was not created from this track."); } return this.performFragmentedLookup(locationInFragment.fragment, (fragment) => { if (fragment === locationInFragment.fragment) { const trackData = fragment.trackData.get(this.internalTrack.id); const nextKeyFrameIndex = trackData.samples.findIndex((x, i) => x.isKeyFrame && i > locationInFragment.sampleIndex); if (nextKeyFrameIndex !== -1) { return { sampleIndex: nextKeyFrameIndex, correctSampleFound: true }; } } else { const trackData = fragment.trackData.get(this.internalTrack.id); if (trackData && trackData.firstKeyFrameTimestamp !== null) { const keyFrameIndex = trackData.samples.findIndex((x) => x.isKeyFrame); assert(keyFrameIndex !== -1); return { sampleIndex: keyFrameIndex, correctSampleFound: true }; } } return { sampleIndex: -1, correctSampleFound: false }; }, -Infinity, Infinity, options); } async fetchPacketForSampleIndex(sampleIndex, options) { if (sampleIndex === -1) { return null; } const sampleTable = this.internalTrack.demuxer.getSampleTableForTrack(this.internalTrack); const sampleInfo = getSampleInfo(sampleTable, sampleIndex); if (!sampleInfo) { return null; } let data; if (options.metadataOnly) { data = PLACEHOLDER_DATA; } else { let slice = this.internalTrack.demuxer.reader.requestSlice(sampleInfo.sampleOffset, sampleInfo.sampleSize); if (slice instanceof Promise) slice = await slice; assert(slice); data = readBytes(slice, sampleInfo.sampleSize); } const timestamp = (sampleInfo.presentationTimestamp - this.internalTrack.editListOffset) / this.internalTrack.timescale; const duration = sampleInfo.duration / this.internalTrack.timescale; const packet = new EncodedPacket(data, sampleInfo.isKeyFrame ? "key" : "delta", timestamp, duration, sampleIndex, sampleInfo.sampleSize); this.packetToSampleIndex.set(packet, sampleIndex); return packet; } async fetchPacketInFragment(fragment, sampleIndex, options) { if (sampleIndex === -1) { return null; } const trackData = fragment.trackData.get(this.internalTrack.id); const fragmentSample = trackData.samples[sampleIndex]; assert(fragmentSample); let data; if (options.metadataOnly) { data = PLACEHOLDER_DATA; } else { let slice = this.internalTrack.demuxer.reader.requestSlice(fragmentSample.byteOffset, fragmentSample.byteSize); if (slice instanceof Promise) slice = await slice; assert(slice); data = readBytes(slice, fragmentSample.byteSize); } const timestamp = (fragmentSample.presentationTimestamp - this.internalTrack.editListOffset) / this.internalTrack.timescale; const duration = fragmentSample.duration / this.internalTrack.timescale; const packet = new EncodedPacket(data, fragmentSample.isKeyFrame ? "key" : "delta", timestamp, duration, fragment.moofOffset + sampleIndex, fragmentSample.byteSize); this.packetToFragmentLocation.set(packet, { fragment, sampleIndex }); return packet; } async performFragmentedLookup(startFragment, getMatchInFragment, searchTimestamp, latestTimestamp, options) { const demuxer = this.internalTrack.demuxer; let currentFragment = null; let bestFragment = null; let bestSampleIndex = -1; if (startFragment) { const { sampleIndex, correctSampleFound } = getMatchInFragment(startFragment); if (correctSampleFound) { return this.fetchPacketInFragment(startFragment, sampleIndex, options); } if (sampleIndex !== -1) { bestFragment = startFragment; bestSampleIndex = sampleIndex; } } const lookupEntryIndex = binarySearchLessOrEqual(this.internalTrack.fragmentLookupTable, searchTimestamp, (x) => x.timestamp); const lookupEntry = lookupEntryIndex !== -1 ? this.internalTrack.fragmentLookupTable[lookupEntryIndex] : null; const positionCacheIndex = binarySearchLessOrEqual(this.internalTrack.fragmentPositionCache, searchTimestamp, (x) => x.startTimestamp); const positionCacheEntry = positionCacheIndex !== -1 ? this.internalTrack.fragmentPositionCache[positionCacheIndex] : null; const lookupEntryPosition = Math.max(lookupEntry?.moofOffset ?? 0, positionCacheEntry?.moofOffset ?? 0) || null; let currentPos; if (!startFragment) { currentPos = lookupEntryPosition ?? 0; } else { if (lookupEntryPosition === null || startFragment.moofOffset >= lookupEntryPosition) { currentPos = startFragment.moofOffset + startFragment.moofSize; currentFragment = startFragment; } else { currentPos = lookupEntryPosition; } } while (true) { if (currentFragment) { const trackData = currentFragment.trackData.get(this.internalTrack.id); if (trackData && trackData.startTimestamp > latestTimestamp) { break; } } let slice = demuxer.reader.requestSliceRange(currentPos, MIN_BOX_HEADER_SIZE, MAX_BOX_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) break; const boxStartPos = currentPos; const boxInfo = readBoxHeader(slice); if (!boxInfo) { break; } if (boxInfo.name === "moof") { currentFragment = await demuxer.readFragment(boxStartPos); const { sampleIndex, correctSampleFound } = getMatchInFragment(currentFragment); if (correctSampleFound) { return this.fetchPacketInFragment(currentFragment, sampleIndex, options); } if (sampleIndex !== -1) { bestFragment = currentFragment; bestSampleIndex = sampleIndex; } } currentPos = boxStartPos + boxInfo.totalSize; } if (lookupEntry && (!bestFragment || bestFragment.moofOffset < lookupEntry.moofOffset)) { const previousLookupEntry = this.internalTrack.fragmentLookupTable[lookupEntryIndex - 1]; assert(!previousLookupEntry || previousLookupEntry.timestamp < lookupEntry.timestamp); const newSearchTimestamp = previousLookupEntry?.timestamp ?? -Infinity; return this.performFragmentedLookup(null, getMatchInFragment, newSearchTimestamp, latestTimestamp, options); } if (bestFragment) { return this.fetchPacketInFragment(bestFragment, bestSampleIndex, options); } return null; } } class IsobmffVideoTrackBacking extends IsobmffTrackBacking { constructor(internalTrack) { super(internalTrack); this.decoderConfigPromise = null; this.internalTrack = internalTrack; } getCodec() { return this.internalTrack.info.codec; } getCodedWidth() { return this.internalTrack.info.width; } getCodedHeight() { return this.internalTrack.info.height; } getRotation() { return this.internalTrack.rotation; } async getColorSpace() { return { primaries: this.internalTrack.info.colorSpace?.primaries, transfer: this.internalTrack.info.colorSpace?.transfer, matrix: this.internalTrack.info.colorSpace?.matrix, fullRange: this.internalTrack.info.colorSpace?.fullRange }; } async canBeTransparent() { return false; } async getDecoderConfig() { if (!this.internalTrack.info.codec) { return null; } return this.decoderConfigPromise ??= (async () => { if (this.internalTrack.info.codec === "vp9" && !this.internalTrack.info.vp9CodecInfo) { const firstPacket = await this.getFirstPacket({}); this.internalTrack.info.vp9CodecInfo = firstPacket && extractVp9CodecInfoFromPacket(firstPacket.data); } else if (this.internalTrack.info.codec === "av1" && !this.internalTrack.info.av1CodecInfo) { const firstPacket = await this.getFirstPacket({}); this.internalTrack.info.av1CodecInfo = firstPacket && extractAv1CodecInfoFromPacket(firstPacket.data); } return { codec: extractVideoCodecString(this.internalTrack.info), codedWidth: this.internalTrack.info.width, codedHeight: this.internalTrack.info.height, description: this.internalTrack.info.codecDescription ?? undefined, colorSpace: this.internalTrack.info.colorSpace ?? undefined }; })(); } } class IsobmffAudioTrackBacking extends IsobmffTrackBacking { constructor(internalTrack) { super(internalTrack); this.decoderConfig = null; this.internalTrack = internalTrack; } getCodec() { return this.internalTrack.info.codec; } getNumberOfChannels() { return this.internalTrack.info.numberOfChannels; } getSampleRate() { return this.internalTrack.info.sampleRate; } async getDecoderConfig() { if (!this.internalTrack.info.codec) { return null; } return this.decoderConfig ??= { codec: extractAudioCodecString(this.internalTrack.info), numberOfChannels: this.internalTrack.info.numberOfChannels, sampleRate: this.internalTrack.info.sampleRate, description: this.internalTrack.info.codecDescription ?? undefined }; } } var getSampleIndexForTimestamp = (sampleTable, timescaleUnits) => { if (sampleTable.presentationTimestamps) { const index = binarySearchLessOrEqual(sampleTable.presentationTimestamps, timescaleUnits, (x) => x.presentationTimestamp); if (index === -1) { return -1; } return sampleTable.presentationTimestamps[index].sampleIndex; } else { const index = binarySearchLessOrEqual(sampleTable.sampleTimingEntries, timescaleUnits, (x) => x.startDecodeTimestamp); if (index === -1) { return -1; } const entry = sampleTable.sampleTimingEntries[index]; return entry.startIndex + Math.min(Math.floor((timescaleUnits - entry.startDecodeTimestamp) / entry.delta), entry.count - 1); } }; var getKeyframeSampleIndexForTimestamp = (sampleTable, timescaleUnits) => { if (!sampleTable.keySampleIndices) { return getSampleIndexForTimestamp(sampleTable, timescaleUnits); } if (sampleTable.presentationTimestamps) { const index = binarySearchLessOrEqual(sampleTable.presentationTimestamps, timescaleUnits, (x) => x.presentationTimestamp); if (index === -1) { return -1; } for (let i = index;i >= 0; i--) { const sampleIndex = sampleTable.presentationTimestamps[i].sampleIndex; const isKeyFrame = binarySearchExact(sampleTable.keySampleIndices, sampleIndex, (x) => x) !== -1; if (isKeyFrame) { return sampleIndex; } } return -1; } else { const sampleIndex = getSampleIndexForTimestamp(sampleTable, timescaleUnits); const index = binarySearchLessOrEqual(sampleTable.keySampleIndices, sampleIndex, (x) => x); return sampleTable.keySampleIndices[index] ?? -1; } }; var getSampleInfo = (sampleTable, sampleIndex) => { const timingEntryIndex = binarySearchLessOrEqual(sampleTable.sampleTimingEntries, sampleIndex, (x) => x.startIndex); const timingEntry = sampleTable.sampleTimingEntries[timingEntryIndex]; if (!timingEntry || timingEntry.startIndex + timingEntry.count <= sampleIndex) { return null; } const decodeTimestamp = timingEntry.startDecodeTimestamp + (sampleIndex - timingEntry.startIndex) * timingEntry.delta; let presentationTimestamp = decodeTimestamp; const offsetEntryIndex = binarySearchLessOrEqual(sampleTable.sampleCompositionTimeOffsets, sampleIndex, (x) => x.startIndex); const offsetEntry = sampleTable.sampleCompositionTimeOffsets[offsetEntryIndex]; if (offsetEntry && sampleIndex - offsetEntry.startIndex < offsetEntry.count) { presentationTimestamp += offsetEntry.offset; } const sampleSize = sampleTable.sampleSizes[Math.min(sampleIndex, sampleTable.sampleSizes.length - 1)]; const chunkEntryIndex = binarySearchLessOrEqual(sampleTable.sampleToChunk, sampleIndex, (x) => x.startSampleIndex); const chunkEntry = sampleTable.sampleToChunk[chunkEntryIndex]; assert(chunkEntry); const chunkIndex = chunkEntry.startChunkIndex + Math.floor((sampleIndex - chunkEntry.startSampleIndex) / chunkEntry.samplesPerChunk); const chunkOffset = sampleTable.chunkOffsets[chunkIndex]; const startSampleIndexOfChunk = chunkEntry.startSampleIndex + (chunkIndex - chunkEntry.startChunkIndex) * chunkEntry.samplesPerChunk; let chunkSize = 0; let sampleOffset = chunkOffset; if (sampleTable.sampleSizes.length === 1) { sampleOffset += sampleSize * (sampleIndex - startSampleIndexOfChunk); chunkSize += sampleSize * chunkEntry.samplesPerChunk; } else { for (let i = startSampleIndexOfChunk;i < startSampleIndexOfChunk + chunkEntry.samplesPerChunk; i++) { const sampleSize2 = sampleTable.sampleSizes[i]; if (i < sampleIndex) { sampleOffset += sampleSize2; } chunkSize += sampleSize2; } } let duration = timingEntry.delta; if (sampleTable.presentationTimestamps) { const presentationIndex = sampleTable.presentationTimestampIndexMap[sampleIndex]; assert(presentationIndex !== undefined); if (presentationIndex < sampleTable.presentationTimestamps.length - 1) { const nextEntry = sampleTable.presentationTimestamps[presentationIndex + 1]; const nextPresentationTimestamp = nextEntry.presentationTimestamp; duration = nextPresentationTimestamp - presentationTimestamp; } } return { presentationTimestamp, duration, sampleOffset, sampleSize, chunkOffset, chunkSize, isKeyFrame: sampleTable.keySampleIndices ? binarySearchExact(sampleTable.keySampleIndices, sampleIndex, (x) => x) !== -1 : true }; }; var getNextKeyframeIndexForSample = (sampleTable, sampleIndex) => { if (!sampleTable.keySampleIndices) { return sampleIndex + 1; } const index = binarySearchLessOrEqual(sampleTable.keySampleIndices, sampleIndex, (x) => x); return sampleTable.keySampleIndices[index + 1] ?? -1; }; var offsetFragmentTrackDataByTimestamp = (trackData, timestamp) => { trackData.startTimestamp += timestamp; trackData.endTimestamp += timestamp; for (const sample of trackData.samples) { sample.presentationTimestamp += timestamp; } for (const entry of trackData.presentationTimestamps) { entry.presentationTimestamp += timestamp; } }; var extractRotationFromMatrix = (matrix) => { const [m11, , , m21] = matrix; const scaleX = Math.hypot(m11, m21); const cosTheta = m11 / scaleX; const sinTheta = m21 / scaleX; const result = -Math.atan2(sinTheta, cosTheta) * (180 / Math.PI); if (!Number.isFinite(result)) { return 0; } return result; }; var sampleTableIsEmpty = (sampleTable) => { return sampleTable.sampleSizes.length === 0; }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/matroska/ebml.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var EBMLId; (function(EBMLId2) { EBMLId2[EBMLId2["EBML"] = 440786851] = "EBML"; EBMLId2[EBMLId2["EBMLVersion"] = 17030] = "EBMLVersion"; EBMLId2[EBMLId2["EBMLReadVersion"] = 17143] = "EBMLReadVersion"; EBMLId2[EBMLId2["EBMLMaxIDLength"] = 17138] = "EBMLMaxIDLength"; EBMLId2[EBMLId2["EBMLMaxSizeLength"] = 17139] = "EBMLMaxSizeLength"; EBMLId2[EBMLId2["DocType"] = 17026] = "DocType"; EBMLId2[EBMLId2["DocTypeVersion"] = 17031] = "DocTypeVersion"; EBMLId2[EBMLId2["DocTypeReadVersion"] = 17029] = "DocTypeReadVersion"; EBMLId2[EBMLId2["Void"] = 236] = "Void"; EBMLId2[EBMLId2["Segment"] = 408125543] = "Segment"; EBMLId2[EBMLId2["SeekHead"] = 290298740] = "SeekHead"; EBMLId2[EBMLId2["Seek"] = 19899] = "Seek"; EBMLId2[EBMLId2["SeekID"] = 21419] = "SeekID"; EBMLId2[EBMLId2["SeekPosition"] = 21420] = "SeekPosition"; EBMLId2[EBMLId2["Duration"] = 17545] = "Duration"; EBMLId2[EBMLId2["Info"] = 357149030] = "Info"; EBMLId2[EBMLId2["TimestampScale"] = 2807729] = "TimestampScale"; EBMLId2[EBMLId2["MuxingApp"] = 19840] = "MuxingApp"; EBMLId2[EBMLId2["WritingApp"] = 22337] = "WritingApp"; EBMLId2[EBMLId2["Tracks"] = 374648427] = "Tracks"; EBMLId2[EBMLId2["TrackEntry"] = 174] = "TrackEntry"; EBMLId2[EBMLId2["TrackNumber"] = 215] = "TrackNumber"; EBMLId2[EBMLId2["TrackUID"] = 29637] = "TrackUID"; EBMLId2[EBMLId2["TrackType"] = 131] = "TrackType"; EBMLId2[EBMLId2["FlagEnabled"] = 185] = "FlagEnabled"; EBMLId2[EBMLId2["FlagDefault"] = 136] = "FlagDefault"; EBMLId2[EBMLId2["FlagForced"] = 21930] = "FlagForced"; EBMLId2[EBMLId2["FlagOriginal"] = 21934] = "FlagOriginal"; EBMLId2[EBMLId2["FlagHearingImpaired"] = 21931] = "FlagHearingImpaired"; EBMLId2[EBMLId2["FlagVisualImpaired"] = 21932] = "FlagVisualImpaired"; EBMLId2[EBMLId2["FlagCommentary"] = 21935] = "FlagCommentary"; EBMLId2[EBMLId2["FlagLacing"] = 156] = "FlagLacing"; EBMLId2[EBMLId2["Name"] = 21358] = "Name"; EBMLId2[EBMLId2["Language"] = 2274716] = "Language"; EBMLId2[EBMLId2["LanguageBCP47"] = 2274717] = "LanguageBCP47"; EBMLId2[EBMLId2["CodecID"] = 134] = "CodecID"; EBMLId2[EBMLId2["CodecPrivate"] = 25506] = "CodecPrivate"; EBMLId2[EBMLId2["CodecDelay"] = 22186] = "CodecDelay"; EBMLId2[EBMLId2["SeekPreRoll"] = 22203] = "SeekPreRoll"; EBMLId2[EBMLId2["DefaultDuration"] = 2352003] = "DefaultDuration"; EBMLId2[EBMLId2["Video"] = 224] = "Video"; EBMLId2[EBMLId2["PixelWidth"] = 176] = "PixelWidth"; EBMLId2[EBMLId2["PixelHeight"] = 186] = "PixelHeight"; EBMLId2[EBMLId2["AlphaMode"] = 21440] = "AlphaMode"; EBMLId2[EBMLId2["Audio"] = 225] = "Audio"; EBMLId2[EBMLId2["SamplingFrequency"] = 181] = "SamplingFrequency"; EBMLId2[EBMLId2["Channels"] = 159] = "Channels"; EBMLId2[EBMLId2["BitDepth"] = 25188] = "BitDepth"; EBMLId2[EBMLId2["SimpleBlock"] = 163] = "SimpleBlock"; EBMLId2[EBMLId2["BlockGroup"] = 160] = "BlockGroup"; EBMLId2[EBMLId2["Block"] = 161] = "Block"; EBMLId2[EBMLId2["BlockAdditions"] = 30113] = "BlockAdditions"; EBMLId2[EBMLId2["BlockMore"] = 166] = "BlockMore"; EBMLId2[EBMLId2["BlockAdditional"] = 165] = "BlockAdditional"; EBMLId2[EBMLId2["BlockAddID"] = 238] = "BlockAddID"; EBMLId2[EBMLId2["BlockDuration"] = 155] = "BlockDuration"; EBMLId2[EBMLId2["ReferenceBlock"] = 251] = "ReferenceBlock"; EBMLId2[EBMLId2["Cluster"] = 524531317] = "Cluster"; EBMLId2[EBMLId2["Timestamp"] = 231] = "Timestamp"; EBMLId2[EBMLId2["Cues"] = 475249515] = "Cues"; EBMLId2[EBMLId2["CuePoint"] = 187] = "CuePoint"; EBMLId2[EBMLId2["CueTime"] = 179] = "CueTime"; EBMLId2[EBMLId2["CueTrackPositions"] = 183] = "CueTrackPositions"; EBMLId2[EBMLId2["CueTrack"] = 247] = "CueTrack"; EBMLId2[EBMLId2["CueClusterPosition"] = 241] = "CueClusterPosition"; EBMLId2[EBMLId2["Colour"] = 21936] = "Colour"; EBMLId2[EBMLId2["MatrixCoefficients"] = 21937] = "MatrixCoefficients"; EBMLId2[EBMLId2["TransferCharacteristics"] = 21946] = "TransferCharacteristics"; EBMLId2[EBMLId2["Primaries"] = 21947] = "Primaries"; EBMLId2[EBMLId2["Range"] = 21945] = "Range"; EBMLId2[EBMLId2["Projection"] = 30320] = "Projection"; EBMLId2[EBMLId2["ProjectionType"] = 30321] = "ProjectionType"; EBMLId2[EBMLId2["ProjectionPoseRoll"] = 30325] = "ProjectionPoseRoll"; EBMLId2[EBMLId2["Attachments"] = 423732329] = "Attachments"; EBMLId2[EBMLId2["AttachedFile"] = 24999] = "AttachedFile"; EBMLId2[EBMLId2["FileDescription"] = 18046] = "FileDescription"; EBMLId2[EBMLId2["FileName"] = 18030] = "FileName"; EBMLId2[EBMLId2["FileMediaType"] = 18016] = "FileMediaType"; EBMLId2[EBMLId2["FileData"] = 18012] = "FileData"; EBMLId2[EBMLId2["FileUID"] = 18094] = "FileUID"; EBMLId2[EBMLId2["Chapters"] = 272869232] = "Chapters"; EBMLId2[EBMLId2["Tags"] = 307544935] = "Tags"; EBMLId2[EBMLId2["Tag"] = 29555] = "Tag"; EBMLId2[EBMLId2["Targets"] = 25536] = "Targets"; EBMLId2[EBMLId2["TargetTypeValue"] = 26826] = "TargetTypeValue"; EBMLId2[EBMLId2["TargetType"] = 25546] = "TargetType"; EBMLId2[EBMLId2["TagTrackUID"] = 25541] = "TagTrackUID"; EBMLId2[EBMLId2["TagEditionUID"] = 25545] = "TagEditionUID"; EBMLId2[EBMLId2["TagChapterUID"] = 25540] = "TagChapterUID"; EBMLId2[EBMLId2["TagAttachmentUID"] = 25542] = "TagAttachmentUID"; EBMLId2[EBMLId2["SimpleTag"] = 26568] = "SimpleTag"; EBMLId2[EBMLId2["TagName"] = 17827] = "TagName"; EBMLId2[EBMLId2["TagLanguage"] = 17530] = "TagLanguage"; EBMLId2[EBMLId2["TagString"] = 17543] = "TagString"; EBMLId2[EBMLId2["TagBinary"] = 17541] = "TagBinary"; EBMLId2[EBMLId2["ContentEncodings"] = 28032] = "ContentEncodings"; EBMLId2[EBMLId2["ContentEncoding"] = 25152] = "ContentEncoding"; EBMLId2[EBMLId2["ContentEncodingOrder"] = 20529] = "ContentEncodingOrder"; EBMLId2[EBMLId2["ContentEncodingScope"] = 20530] = "ContentEncodingScope"; EBMLId2[EBMLId2["ContentCompression"] = 20532] = "ContentCompression"; EBMLId2[EBMLId2["ContentCompAlgo"] = 16980] = "ContentCompAlgo"; EBMLId2[EBMLId2["ContentCompSettings"] = 16981] = "ContentCompSettings"; EBMLId2[EBMLId2["ContentEncryption"] = 20533] = "ContentEncryption"; })(EBMLId || (EBMLId = {})); var LEVEL_0_EBML_IDS = [ EBMLId.EBML, EBMLId.Segment ]; var LEVEL_1_EBML_IDS = [ EBMLId.SeekHead, EBMLId.Info, EBMLId.Cluster, EBMLId.Tracks, EBMLId.Cues, EBMLId.Attachments, EBMLId.Chapters, EBMLId.Tags ]; var LEVEL_0_AND_1_EBML_IDS = [ ...LEVEL_0_EBML_IDS, ...LEVEL_1_EBML_IDS ]; var MAX_VAR_INT_SIZE = 8; var MIN_HEADER_SIZE = 2; var MAX_HEADER_SIZE = 2 * MAX_VAR_INT_SIZE; var readVarIntSize = (slice) => { if (slice.remainingLength < 1) { return null; } const firstByte = readU8(slice); slice.skip(-1); if (firstByte === 0) { return null; } let width = 1; let mask = 128; while ((firstByte & mask) === 0) { width++; mask >>= 1; } if (slice.remainingLength < width) { return null; } return width; }; var readVarInt = (slice) => { if (slice.remainingLength < 1) { return null; } const firstByte = readU8(slice); if (firstByte === 0) { return null; } let width = 1; let mask = 1 << 7; while ((firstByte & mask) === 0) { width++; mask >>= 1; } if (slice.remainingLength < width - 1) { return null; } let value = firstByte & mask - 1; for (let i = 1;i < width; i++) { value *= 1 << 8; value += readU8(slice); } return value; }; var readUnsignedInt = (slice, width) => { if (width < 1 || width > 8) { throw new Error("Bad unsigned int size " + width); } let value = 0; for (let i = 0;i < width; i++) { value *= 1 << 8; value += readU8(slice); } return value; }; var readUnsignedBigInt = (slice, width) => { if (width < 1) { throw new Error("Bad unsigned int size " + width); } let value = 0n; for (let i = 0;i < width; i++) { value <<= 8n; value += BigInt(readU8(slice)); } return value; }; var readElementId = (slice) => { const size4 = readVarIntSize(slice); if (size4 === null) { return null; } if (slice.remainingLength < size4) { return null; } const id = readUnsignedInt(slice, size4); return id; }; var readElementSize = (slice) => { if (slice.remainingLength < 1) { return null; } const firstByte = readU8(slice); if (firstByte === 255) { return; } slice.skip(-1); const size4 = readVarInt(slice); if (size4 === null) { return null; } if (size4 === 72057594037927940) { return; } return size4; }; var readElementHeader = (slice) => { assert(slice.remainingLength >= MIN_HEADER_SIZE); const id = readElementId(slice); if (id === null) { return null; } const size4 = readElementSize(slice); if (size4 === null) { return null; } return { id, size: size4 }; }; var readAsciiString = (slice, length) => { const bytes = readBytes(slice, length); let strLength = 0; while (strLength < length && bytes[strLength] !== 0) { strLength += 1; } return String.fromCharCode(...bytes.subarray(0, strLength)); }; var readUnicodeString = (slice, length) => { const bytes = readBytes(slice, length); let strLength = 0; while (strLength < length && bytes[strLength] !== 0) { strLength += 1; } return textDecoder.decode(bytes.subarray(0, strLength)); }; var readFloat = (slice, width) => { if (width === 0) { return 0; } if (width !== 4 && width !== 8) { throw new Error("Bad float size " + width); } return width === 4 ? readF32Be(slice) : readF64Be(slice); }; var searchForNextElementId = async (reader, startPos, ids, until) => { const idsSet = new Set(ids); let currentPos = startPos; while (until === null || currentPos < until) { let slice = reader.requestSliceRange(currentPos, MIN_HEADER_SIZE, MAX_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) break; const elementHeader = readElementHeader(slice); if (!elementHeader) { break; } if (idsSet.has(elementHeader.id)) { return { pos: currentPos, found: true }; } assertDefinedSize(elementHeader.size); currentPos = slice.filePos + elementHeader.size; } return { pos: until !== null && until > currentPos ? until : currentPos, found: false }; }; var resync = async (reader, startPos, ids, until) => { const CHUNK_SIZE = 2 ** 16; const idsSet = new Set(ids); let currentPos = startPos; while (currentPos < until) { let slice = reader.requestSliceRange(currentPos, 0, Math.min(CHUNK_SIZE, until - currentPos)); if (slice instanceof Promise) slice = await slice; if (!slice) break; if (slice.length < MAX_VAR_INT_SIZE) break; for (let i = 0;i < slice.length - MAX_VAR_INT_SIZE; i++) { slice.filePos = currentPos; const elementId = readElementId(slice); if (elementId !== null && idsSet.has(elementId)) { return currentPos; } currentPos++; } } return null; }; var CODEC_STRING_MAP = { avc: "V_MPEG4/ISO/AVC", hevc: "V_MPEGH/ISO/HEVC", vp8: "V_VP8", vp9: "V_VP9", av1: "V_AV1", aac: "A_AAC", mp3: "A_MPEG/L3", opus: "A_OPUS", vorbis: "A_VORBIS", flac: "A_FLAC", "pcm-u8": "A_PCM/INT/LIT", "pcm-s16": "A_PCM/INT/LIT", "pcm-s16be": "A_PCM/INT/BIG", "pcm-s24": "A_PCM/INT/LIT", "pcm-s24be": "A_PCM/INT/BIG", "pcm-s32": "A_PCM/INT/LIT", "pcm-s32be": "A_PCM/INT/BIG", "pcm-f32": "A_PCM/FLOAT/IEEE", "pcm-f64": "A_PCM/FLOAT/IEEE", webvtt: "S_TEXT/WEBVTT" }; function assertDefinedSize(size4) { if (size4 === undefined) { throw new Error("Undefined element size is used in a place where it is not supported."); } } // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/matroska/matroska-misc.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var buildMatroskaMimeType = (info) => { const base = info.hasVideo ? "video/" : info.hasAudio ? "audio/" : "application/"; let string = base + (info.isWebM ? "webm" : "x-matroska"); if (info.codecStrings.length > 0) { const uniqueCodecMimeTypes = [...new Set(info.codecStrings.filter(Boolean))]; string += `; codecs="${uniqueCodecMimeTypes.join(", ")}"`; } return string; }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/matroska/matroska-demuxer.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var BlockLacing; (function(BlockLacing2) { BlockLacing2[BlockLacing2["None"] = 0] = "None"; BlockLacing2[BlockLacing2["Xiph"] = 1] = "Xiph"; BlockLacing2[BlockLacing2["FixedSize"] = 2] = "FixedSize"; BlockLacing2[BlockLacing2["Ebml"] = 3] = "Ebml"; })(BlockLacing || (BlockLacing = {})); var ContentEncodingScope; (function(ContentEncodingScope2) { ContentEncodingScope2[ContentEncodingScope2["Block"] = 1] = "Block"; ContentEncodingScope2[ContentEncodingScope2["Private"] = 2] = "Private"; ContentEncodingScope2[ContentEncodingScope2["Next"] = 4] = "Next"; })(ContentEncodingScope || (ContentEncodingScope = {})); var ContentCompAlgo; (function(ContentCompAlgo2) { ContentCompAlgo2[ContentCompAlgo2["Zlib"] = 0] = "Zlib"; ContentCompAlgo2[ContentCompAlgo2["Bzlib"] = 1] = "Bzlib"; ContentCompAlgo2[ContentCompAlgo2["lzo1x"] = 2] = "lzo1x"; ContentCompAlgo2[ContentCompAlgo2["HeaderStripping"] = 3] = "HeaderStripping"; })(ContentCompAlgo || (ContentCompAlgo = {})); var METADATA_ELEMENTS = [ { id: EBMLId.SeekHead, flag: "seekHeadSeen" }, { id: EBMLId.Info, flag: "infoSeen" }, { id: EBMLId.Tracks, flag: "tracksSeen" }, { id: EBMLId.Cues, flag: "cuesSeen" } ]; var MAX_RESYNC_LENGTH = 10 * 2 ** 20; class MatroskaDemuxer extends Demuxer { constructor(input2) { super(input2); this.readMetadataPromise = null; this.segments = []; this.currentSegment = null; this.currentTrack = null; this.currentCluster = null; this.currentBlock = null; this.currentBlockAdditional = null; this.currentCueTime = null; this.currentDecodingInstruction = null; this.currentTagTargetIsMovie = true; this.currentSimpleTagName = null; this.currentAttachedFile = null; this.isWebM = false; this.reader = input2._reader; } async computeDuration() { const tracks = await this.getTracks(); const trackDurations = await Promise.all(tracks.map((x) => x.computeDuration())); return Math.max(0, ...trackDurations); } async getTracks() { await this.readMetadata(); return this.segments.flatMap((segment) => segment.tracks.map((track) => track.inputTrack)); } async getMimeType() { await this.readMetadata(); const tracks = await this.getTracks(); const codecStrings = await Promise.all(tracks.map((x) => x.getCodecParameterString())); return buildMatroskaMimeType({ isWebM: this.isWebM, hasVideo: this.segments.some((segment) => segment.tracks.some((x) => x.info?.type === "video")), hasAudio: this.segments.some((segment) => segment.tracks.some((x) => x.info?.type === "audio")), codecStrings: codecStrings.filter(Boolean) }); } async getMetadataTags() { await this.readMetadata(); for (const segment of this.segments) { if (!segment.metadataTagsCollected) { if (this.reader.fileSize !== null) { await this.loadSegmentMetadata(segment); } else {} segment.metadataTagsCollected = true; } } let metadataTags = {}; for (const segment of this.segments) { metadataTags = { ...metadataTags, ...segment.metadataTags }; } return metadataTags; } readMetadata() { return this.readMetadataPromise ??= (async () => { let currentPos = 0; while (true) { let slice = this.reader.requestSliceRange(currentPos, MIN_HEADER_SIZE, MAX_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) break; const header2 = readElementHeader(slice); if (!header2) { break; } const id = header2.id; let size4 = header2.size; const dataStartPos = slice.filePos; if (id === EBMLId.EBML) { assertDefinedSize(size4); let slice2 = this.reader.requestSlice(dataStartPos, size4); if (slice2 instanceof Promise) slice2 = await slice2; if (!slice2) break; this.readContiguousElements(slice2); } else if (id === EBMLId.Segment) { await this.readSegment(dataStartPos, size4); if (size4 === undefined) { break; } if (this.reader.fileSize === null) { break; } } else if (id === EBMLId.Cluster) { if (this.reader.fileSize === null) { break; } if (size4 === undefined) { const nextElementPos = await searchForNextElementId(this.reader, dataStartPos, LEVEL_0_AND_1_EBML_IDS, this.reader.fileSize); size4 = nextElementPos.pos - dataStartPos; } const lastSegment = last(this.segments); if (lastSegment) { lastSegment.elementEndPos = dataStartPos + size4; } } assertDefinedSize(size4); currentPos = dataStartPos + size4; } })(); } async readSegment(segmentDataStart, dataSize) { this.currentSegment = { seekHeadSeen: false, infoSeen: false, tracksSeen: false, cuesSeen: false, tagsSeen: false, attachmentsSeen: false, timestampScale: -1, timestampFactor: -1, duration: -1, seekEntries: [], tracks: [], cuePoints: [], dataStartPos: segmentDataStart, elementEndPos: dataSize === undefined ? null : segmentDataStart + dataSize, clusterSeekStartPos: segmentDataStart, lastReadCluster: null, metadataTags: {}, metadataTagsCollected: false }; this.segments.push(this.currentSegment); let currentPos = segmentDataStart; while (this.currentSegment.elementEndPos === null || currentPos < this.currentSegment.elementEndPos) { let slice = this.reader.requestSliceRange(currentPos, MIN_HEADER_SIZE, MAX_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) break; const elementStartPos = currentPos; const header2 = readElementHeader(slice); if (!header2 || !LEVEL_1_EBML_IDS.includes(header2.id) && header2.id !== EBMLId.Void) { const nextPos = await resync(this.reader, elementStartPos, LEVEL_1_EBML_IDS, Math.min(this.currentSegment.elementEndPos ?? Infinity, elementStartPos + MAX_RESYNC_LENGTH)); if (nextPos) { currentPos = nextPos; continue; } else { break; } } const { id, size: size4 } = header2; const dataStartPos = slice.filePos; const metadataElementIndex = METADATA_ELEMENTS.findIndex((x) => x.id === id); if (metadataElementIndex !== -1) { const field = METADATA_ELEMENTS[metadataElementIndex].flag; this.currentSegment[field] = true; assertDefinedSize(size4); let slice2 = this.reader.requestSlice(dataStartPos, size4); if (slice2 instanceof Promise) slice2 = await slice2; if (slice2) { this.readContiguousElements(slice2); } } else if (id === EBMLId.Tags || id === EBMLId.Attachments) { if (id === EBMLId.Tags) { this.currentSegment.tagsSeen = true; } else { this.currentSegment.attachmentsSeen = true; } assertDefinedSize(size4); let slice2 = this.reader.requestSlice(dataStartPos, size4); if (slice2 instanceof Promise) slice2 = await slice2; if (slice2) { this.readContiguousElements(slice2); } } else if (id === EBMLId.Cluster) { this.currentSegment.clusterSeekStartPos = elementStartPos; break; } if (size4 === undefined) { break; } else { currentPos = dataStartPos + size4; } } this.currentSegment.seekEntries.sort((a, b) => a.segmentPosition - b.segmentPosition); if (this.reader.fileSize !== null) { for (const seekEntry of this.currentSegment.seekEntries) { const target = METADATA_ELEMENTS.find((x) => x.id === seekEntry.id); if (!target) { continue; } if (this.currentSegment[target.flag]) continue; let slice = this.reader.requestSliceRange(segmentDataStart + seekEntry.segmentPosition, MIN_HEADER_SIZE, MAX_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) continue; const header2 = readElementHeader(slice); if (!header2) continue; const { id, size: size4 } = header2; if (id !== target.id) continue; assertDefinedSize(size4); this.currentSegment[target.flag] = true; let dataSlice = this.reader.requestSlice(slice.filePos, size4); if (dataSlice instanceof Promise) dataSlice = await dataSlice; if (!dataSlice) continue; this.readContiguousElements(dataSlice); } } if (this.currentSegment.timestampScale === -1) { this.currentSegment.timestampScale = 1e6; this.currentSegment.timestampFactor = 1e9 / 1e6; } for (const track of this.currentSegment.tracks) { if (track.defaultDurationNs !== null) { track.defaultDuration = this.currentSegment.timestampFactor * track.defaultDurationNs / 1e9; } } this.currentSegment.tracks.sort((a, b) => Number(b.disposition.default) - Number(a.disposition.default)); const idToTrack = new Map(this.currentSegment.tracks.map((x) => [x.id, x])); for (const cuePoint of this.currentSegment.cuePoints) { const track = idToTrack.get(cuePoint.trackId); if (track) { track.cuePoints.push(cuePoint); } } for (const track of this.currentSegment.tracks) { track.cuePoints.sort((a, b) => a.time - b.time); for (let i = 0;i < track.cuePoints.length - 1; i++) { const cuePoint1 = track.cuePoints[i]; const cuePoint2 = track.cuePoints[i + 1]; if (cuePoint1.time === cuePoint2.time) { track.cuePoints.splice(i + 1, 1); i--; } } } let trackWithMostCuePoints = null; let maxCuePointCount = -Infinity; for (const track of this.currentSegment.tracks) { if (track.cuePoints.length > maxCuePointCount) { maxCuePointCount = track.cuePoints.length; trackWithMostCuePoints = track; } } for (const track of this.currentSegment.tracks) { if (track.cuePoints.length === 0) { track.cuePoints = trackWithMostCuePoints.cuePoints; } } this.currentSegment = null; } async readCluster(startPos, segment) { if (segment.lastReadCluster?.elementStartPos === startPos) { return segment.lastReadCluster; } let headerSlice = this.reader.requestSliceRange(startPos, MIN_HEADER_SIZE, MAX_HEADER_SIZE); if (headerSlice instanceof Promise) headerSlice = await headerSlice; assert(headerSlice); const elementStartPos = startPos; const elementHeader = readElementHeader(headerSlice); assert(elementHeader); const id = elementHeader.id; assert(id === EBMLId.Cluster); let size4 = elementHeader.size; const dataStartPos = headerSlice.filePos; if (size4 === undefined) { const nextElementPos = await searchForNextElementId(this.reader, dataStartPos, LEVEL_0_AND_1_EBML_IDS, segment.elementEndPos); size4 = nextElementPos.pos - dataStartPos; } let dataSlice = this.reader.requestSlice(dataStartPos, size4); if (dataSlice instanceof Promise) dataSlice = await dataSlice; const cluster = { segment, elementStartPos, elementEndPos: dataStartPos + size4, dataStartPos, timestamp: -1, trackData: new Map }; this.currentCluster = cluster; if (dataSlice) { const endPos = this.readContiguousElements(dataSlice, LEVEL_0_AND_1_EBML_IDS); cluster.elementEndPos = endPos; } for (const [, trackData] of cluster.trackData) { const track = trackData.track; assert(trackData.blocks.length > 0); let hasLacedBlocks = false; for (let i = 0;i < trackData.blocks.length; i++) { const block = trackData.blocks[i]; block.timestamp += cluster.timestamp; hasLacedBlocks ||= block.lacing !== BlockLacing.None; } trackData.presentationTimestamps = trackData.blocks.map((block, i) => ({ timestamp: block.timestamp, blockIndex: i })).sort((a, b) => a.timestamp - b.timestamp); for (let i = 0;i < trackData.presentationTimestamps.length; i++) { const currentEntry = trackData.presentationTimestamps[i]; const currentBlock = trackData.blocks[currentEntry.blockIndex]; if (trackData.firstKeyFrameTimestamp === null && currentBlock.isKeyFrame) { trackData.firstKeyFrameTimestamp = currentBlock.timestamp; } if (i < trackData.presentationTimestamps.length - 1) { const nextEntry = trackData.presentationTimestamps[i + 1]; currentBlock.duration = nextEntry.timestamp - currentBlock.timestamp; } else if (currentBlock.duration === 0) { if (track.defaultDuration != null) { if (currentBlock.lacing === BlockLacing.None) { currentBlock.duration = track.defaultDuration; } else {} } } } if (hasLacedBlocks) { this.expandLacedBlocks(trackData.blocks, track); trackData.presentationTimestamps = trackData.blocks.map((block, i) => ({ timestamp: block.timestamp, blockIndex: i })).sort((a, b) => a.timestamp - b.timestamp); } const firstBlock = trackData.blocks[trackData.presentationTimestamps[0].blockIndex]; const lastBlock = trackData.blocks[last(trackData.presentationTimestamps).blockIndex]; trackData.startTimestamp = firstBlock.timestamp; trackData.endTimestamp = lastBlock.timestamp + lastBlock.duration; const insertionIndex = binarySearchLessOrEqual(track.clusterPositionCache, trackData.startTimestamp, (x) => x.startTimestamp); if (insertionIndex === -1 || track.clusterPositionCache[insertionIndex].elementStartPos !== elementStartPos) { track.clusterPositionCache.splice(insertionIndex + 1, 0, { elementStartPos: cluster.elementStartPos, startTimestamp: trackData.startTimestamp }); } } segment.lastReadCluster = cluster; return cluster; } getTrackDataInCluster(cluster, trackNumber) { let trackData = cluster.trackData.get(trackNumber); if (!trackData) { const track = cluster.segment.tracks.find((x) => x.id === trackNumber); if (!track) { return null; } trackData = { track, startTimestamp: 0, endTimestamp: 0, firstKeyFrameTimestamp: null, blocks: [], presentationTimestamps: [] }; cluster.trackData.set(trackNumber, trackData); } return trackData; } expandLacedBlocks(blocks, track) { for (let blockIndex = 0;blockIndex < blocks.length; blockIndex++) { const originalBlock = blocks[blockIndex]; if (originalBlock.lacing === BlockLacing.None) { continue; } if (!originalBlock.decoded) { originalBlock.data = this.decodeBlockData(track, originalBlock.data); originalBlock.decoded = true; } const slice = FileSlice.tempFromBytes(originalBlock.data); const frameSizes = []; const frameCount = readU8(slice) + 1; switch (originalBlock.lacing) { case BlockLacing.Xiph: { let totalUsedSize = 0; for (let i = 0;i < frameCount - 1; i++) { let frameSize = 0; while (slice.bufferPos < slice.length) { const value = readU8(slice); frameSize += value; if (value < 255) { frameSizes.push(frameSize); totalUsedSize += frameSize; break; } } } frameSizes.push(slice.length - (slice.bufferPos + totalUsedSize)); } ; break; case BlockLacing.FixedSize: { const totalDataSize = slice.length - 1; const frameSize = Math.floor(totalDataSize / frameCount); for (let i = 0;i < frameCount; i++) { frameSizes.push(frameSize); } } ; break; case BlockLacing.Ebml: { const firstResult = readVarInt(slice); assert(firstResult !== null); let currentSize = firstResult; frameSizes.push(currentSize); let totalUsedSize = currentSize; for (let i = 1;i < frameCount - 1; i++) { const startPos = slice.bufferPos; const diffResult = readVarInt(slice); assert(diffResult !== null); const unsignedDiff = diffResult; const width = slice.bufferPos - startPos; const bias = (1 << width * 7 - 1) - 1; const diff = unsignedDiff - bias; currentSize += diff; frameSizes.push(currentSize); totalUsedSize += currentSize; } frameSizes.push(slice.length - (slice.bufferPos + totalUsedSize)); } ; break; default: assert(false); } assert(frameSizes.length === frameCount); blocks.splice(blockIndex, 1); const blockDuration = originalBlock.duration || frameCount * (track.defaultDuration ?? 0); for (let i = 0;i < frameCount; i++) { const frameSize = frameSizes[i]; const frameData = readBytes(slice, frameSize); const frameTimestamp = originalBlock.timestamp + blockDuration * i / frameCount; const frameDuration = blockDuration / frameCount; blocks.splice(blockIndex + i, 0, { timestamp: frameTimestamp, duration: frameDuration, isKeyFrame: originalBlock.isKeyFrame, data: frameData, lacing: BlockLacing.None, decoded: true, mainAdditional: originalBlock.mainAdditional }); } blockIndex += frameCount; blockIndex--; } } async loadSegmentMetadata(segment) { for (const seekEntry of segment.seekEntries) { if (seekEntry.id === EBMLId.Tags && !segment.tagsSeen) {} else if (seekEntry.id === EBMLId.Attachments && !segment.attachmentsSeen) {} else { continue; } let slice = this.reader.requestSliceRange(segment.dataStartPos + seekEntry.segmentPosition, MIN_HEADER_SIZE, MAX_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) continue; const header2 = readElementHeader(slice); if (!header2 || header2.id !== seekEntry.id) continue; const { size: size4 } = header2; assertDefinedSize(size4); assert(!this.currentSegment); this.currentSegment = segment; let dataSlice = this.reader.requestSlice(slice.filePos, size4); if (dataSlice instanceof Promise) dataSlice = await dataSlice; if (dataSlice) { this.readContiguousElements(dataSlice); } this.currentSegment = null; if (seekEntry.id === EBMLId.Tags) { segment.tagsSeen = true; } else if (seekEntry.id === EBMLId.Attachments) { segment.attachmentsSeen = true; } } } readContiguousElements(slice, stopIds) { while (slice.remainingLength >= MIN_HEADER_SIZE) { const startPos = slice.filePos; const foundElement = this.traverseElement(slice, stopIds); if (!foundElement) { return startPos; } } return slice.filePos; } traverseElement(slice, stopIds) { const header2 = readElementHeader(slice); if (!header2) { return false; } if (stopIds && stopIds.includes(header2.id)) { return false; } const { id, size: size4 } = header2; const dataStartPos = slice.filePos; assertDefinedSize(size4); switch (id) { case EBMLId.DocType: { this.isWebM = readAsciiString(slice, size4) === "webm"; } ; break; case EBMLId.Seek: { if (!this.currentSegment) break; const seekEntry = { id: -1, segmentPosition: -1 }; this.currentSegment.seekEntries.push(seekEntry); this.readContiguousElements(slice.slice(dataStartPos, size4)); if (seekEntry.id === -1 || seekEntry.segmentPosition === -1) { this.currentSegment.seekEntries.pop(); } } ; break; case EBMLId.SeekID: { const lastSeekEntry = this.currentSegment?.seekEntries[this.currentSegment.seekEntries.length - 1]; if (!lastSeekEntry) break; lastSeekEntry.id = readUnsignedInt(slice, size4); } ; break; case EBMLId.SeekPosition: { const lastSeekEntry = this.currentSegment?.seekEntries[this.currentSegment.seekEntries.length - 1]; if (!lastSeekEntry) break; lastSeekEntry.segmentPosition = readUnsignedInt(slice, size4); } ; break; case EBMLId.TimestampScale: { if (!this.currentSegment) break; this.currentSegment.timestampScale = readUnsignedInt(slice, size4); this.currentSegment.timestampFactor = 1e9 / this.currentSegment.timestampScale; } ; break; case EBMLId.Duration: { if (!this.currentSegment) break; this.currentSegment.duration = readFloat(slice, size4); } ; break; case EBMLId.TrackEntry: { if (!this.currentSegment) break; this.currentTrack = { id: -1, segment: this.currentSegment, demuxer: this, clusterPositionCache: [], cuePoints: [], disposition: { ...DEFAULT_TRACK_DISPOSITION }, inputTrack: null, codecId: null, codecPrivate: null, defaultDuration: null, defaultDurationNs: null, name: null, languageCode: UNDETERMINED_LANGUAGE, decodingInstructions: [], info: null }; this.readContiguousElements(slice.slice(dataStartPos, size4)); if (!this.currentTrack) { break; } if (this.currentTrack.decodingInstructions.some((instruction) => { return instruction.data?.type !== "decompress" || instruction.scope !== ContentEncodingScope.Block || instruction.data.algorithm !== ContentCompAlgo.HeaderStripping; })) { console.warn(`Track #${this.currentTrack.id} has an unsupported content encoding; dropping.`); this.currentTrack = null; } if (this.currentTrack && this.currentTrack.id !== -1 && this.currentTrack.codecId && this.currentTrack.info) { const slashIndex = this.currentTrack.codecId.indexOf("/"); const codecIdWithoutSuffix = slashIndex === -1 ? this.currentTrack.codecId : this.currentTrack.codecId.slice(0, slashIndex); if (this.currentTrack.info.type === "video" && this.currentTrack.info.width !== -1 && this.currentTrack.info.height !== -1) { if (this.currentTrack.codecId === CODEC_STRING_MAP.avc) { this.currentTrack.info.codec = "avc"; this.currentTrack.info.codecDescription = this.currentTrack.codecPrivate; } else if (this.currentTrack.codecId === CODEC_STRING_MAP.hevc) { this.currentTrack.info.codec = "hevc"; this.currentTrack.info.codecDescription = this.currentTrack.codecPrivate; } else if (codecIdWithoutSuffix === CODEC_STRING_MAP.vp8) { this.currentTrack.info.codec = "vp8"; } else if (codecIdWithoutSuffix === CODEC_STRING_MAP.vp9) { this.currentTrack.info.codec = "vp9"; } else if (codecIdWithoutSuffix === CODEC_STRING_MAP.av1) { this.currentTrack.info.codec = "av1"; } const videoTrack = this.currentTrack; const inputTrack = new InputVideoTrack(this.input, new MatroskaVideoTrackBacking(videoTrack)); this.currentTrack.inputTrack = inputTrack; this.currentSegment.tracks.push(this.currentTrack); } else if (this.currentTrack.info.type === "audio" && this.currentTrack.info.numberOfChannels !== -1 && this.currentTrack.info.sampleRate !== -1) { if (codecIdWithoutSuffix === CODEC_STRING_MAP.aac) { this.currentTrack.info.codec = "aac"; this.currentTrack.info.aacCodecInfo = { isMpeg2: this.currentTrack.codecId.includes("MPEG2"), objectType: null }; this.currentTrack.info.codecDescription = this.currentTrack.codecPrivate; } else if (this.currentTrack.codecId === CODEC_STRING_MAP.mp3) { this.currentTrack.info.codec = "mp3"; } else if (codecIdWithoutSuffix === CODEC_STRING_MAP.opus) { this.currentTrack.info.codec = "opus"; this.currentTrack.info.codecDescription = this.currentTrack.codecPrivate; this.currentTrack.info.sampleRate = OPUS_SAMPLE_RATE; } else if (codecIdWithoutSuffix === CODEC_STRING_MAP.vorbis) { this.currentTrack.info.codec = "vorbis"; this.currentTrack.info.codecDescription = this.currentTrack.codecPrivate; } else if (codecIdWithoutSuffix === CODEC_STRING_MAP.flac) { this.currentTrack.info.codec = "flac"; this.currentTrack.info.codecDescription = this.currentTrack.codecPrivate; } else if (this.currentTrack.codecId === "A_PCM/INT/LIT") { if (this.currentTrack.info.bitDepth === 8) { this.currentTrack.info.codec = "pcm-u8"; } else if (this.currentTrack.info.bitDepth === 16) { this.currentTrack.info.codec = "pcm-s16"; } else if (this.currentTrack.info.bitDepth === 24) { this.currentTrack.info.codec = "pcm-s24"; } else if (this.currentTrack.info.bitDepth === 32) { this.currentTrack.info.codec = "pcm-s32"; } } else if (this.currentTrack.codecId === "A_PCM/INT/BIG") { if (this.currentTrack.info.bitDepth === 8) { this.currentTrack.info.codec = "pcm-u8"; } else if (this.currentTrack.info.bitDepth === 16) { this.currentTrack.info.codec = "pcm-s16be"; } else if (this.currentTrack.info.bitDepth === 24) { this.currentTrack.info.codec = "pcm-s24be"; } else if (this.currentTrack.info.bitDepth === 32) { this.currentTrack.info.codec = "pcm-s32be"; } } else if (this.currentTrack.codecId === "A_PCM/FLOAT/IEEE") { if (this.currentTrack.info.bitDepth === 32) { this.currentTrack.info.codec = "pcm-f32"; } else if (this.currentTrack.info.bitDepth === 64) { this.currentTrack.info.codec = "pcm-f64"; } } const audioTrack = this.currentTrack; const inputTrack = new InputAudioTrack(this.input, new MatroskaAudioTrackBacking(audioTrack)); this.currentTrack.inputTrack = inputTrack; this.currentSegment.tracks.push(this.currentTrack); } } this.currentTrack = null; } ; break; case EBMLId.TrackNumber: { if (!this.currentTrack) break; this.currentTrack.id = readUnsignedInt(slice, size4); } ; break; case EBMLId.TrackType: { if (!this.currentTrack) break; const type = readUnsignedInt(slice, size4); if (type === 1) { this.currentTrack.info = { type: "video", width: -1, height: -1, rotation: 0, codec: null, codecDescription: null, colorSpace: null, alphaMode: false }; } else if (type === 2) { this.currentTrack.info = { type: "audio", numberOfChannels: -1, sampleRate: -1, bitDepth: -1, codec: null, codecDescription: null, aacCodecInfo: null }; } } ; break; case EBMLId.FlagEnabled: { if (!this.currentTrack) break; const enabled = readUnsignedInt(slice, size4); if (!enabled) { this.currentTrack = null; } } ; break; case EBMLId.FlagDefault: { if (!this.currentTrack) break; this.currentTrack.disposition.default = !!readUnsignedInt(slice, size4); } ; break; case EBMLId.FlagForced: { if (!this.currentTrack) break; this.currentTrack.disposition.forced = !!readUnsignedInt(slice, size4); } ; break; case EBMLId.FlagOriginal: { if (!this.currentTrack) break; this.currentTrack.disposition.original = !!readUnsignedInt(slice, size4); } ; break; case EBMLId.FlagHearingImpaired: { if (!this.currentTrack) break; this.currentTrack.disposition.hearingImpaired = !!readUnsignedInt(slice, size4); } ; break; case EBMLId.FlagVisualImpaired: { if (!this.currentTrack) break; this.currentTrack.disposition.visuallyImpaired = !!readUnsignedInt(slice, size4); } ; break; case EBMLId.FlagCommentary: { if (!this.currentTrack) break; this.currentTrack.disposition.commentary = !!readUnsignedInt(slice, size4); } ; break; case EBMLId.CodecID: { if (!this.currentTrack) break; this.currentTrack.codecId = readAsciiString(slice, size4); } ; break; case EBMLId.CodecPrivate: { if (!this.currentTrack) break; this.currentTrack.codecPrivate = readBytes(slice, size4); } ; break; case EBMLId.DefaultDuration: { if (!this.currentTrack) break; this.currentTrack.defaultDurationNs = readUnsignedInt(slice, size4); } ; break; case EBMLId.Name: { if (!this.currentTrack) break; this.currentTrack.name = readUnicodeString(slice, size4); } ; break; case EBMLId.Language: { if (!this.currentTrack) break; if (this.currentTrack.languageCode !== UNDETERMINED_LANGUAGE) { break; } this.currentTrack.languageCode = readAsciiString(slice, size4); if (!isIso639Dash2LanguageCode(this.currentTrack.languageCode)) { this.currentTrack.languageCode = UNDETERMINED_LANGUAGE; } } ; break; case EBMLId.LanguageBCP47: { if (!this.currentTrack) break; const bcp47 = readAsciiString(slice, size4); const languageSubtag = bcp47.split("-")[0]; if (languageSubtag) { this.currentTrack.languageCode = languageSubtag; } else { this.currentTrack.languageCode = UNDETERMINED_LANGUAGE; } } ; break; case EBMLId.Video: { if (this.currentTrack?.info?.type !== "video") break; this.readContiguousElements(slice.slice(dataStartPos, size4)); } ; break; case EBMLId.PixelWidth: { if (this.currentTrack?.info?.type !== "video") break; this.currentTrack.info.width = readUnsignedInt(slice, size4); } ; break; case EBMLId.PixelHeight: { if (this.currentTrack?.info?.type !== "video") break; this.currentTrack.info.height = readUnsignedInt(slice, size4); } ; break; case EBMLId.AlphaMode: { if (this.currentTrack?.info?.type !== "video") break; this.currentTrack.info.alphaMode = readUnsignedInt(slice, size4) === 1; } ; break; case EBMLId.Colour: { if (this.currentTrack?.info?.type !== "video") break; this.currentTrack.info.colorSpace = {}; this.readContiguousElements(slice.slice(dataStartPos, size4)); } ; break; case EBMLId.MatrixCoefficients: { if (this.currentTrack?.info?.type !== "video" || !this.currentTrack.info.colorSpace) break; const matrixCoefficients = readUnsignedInt(slice, size4); const mapped = MATRIX_COEFFICIENTS_MAP_INVERSE[matrixCoefficients] ?? null; this.currentTrack.info.colorSpace.matrix = mapped; } ; break; case EBMLId.Range: { if (this.currentTrack?.info?.type !== "video" || !this.currentTrack.info.colorSpace) break; this.currentTrack.info.colorSpace.fullRange = readUnsignedInt(slice, size4) === 2; } ; break; case EBMLId.TransferCharacteristics: { if (this.currentTrack?.info?.type !== "video" || !this.currentTrack.info.colorSpace) break; const transferCharacteristics = readUnsignedInt(slice, size4); const mapped = TRANSFER_CHARACTERISTICS_MAP_INVERSE[transferCharacteristics] ?? null; this.currentTrack.info.colorSpace.transfer = mapped; } ; break; case EBMLId.Primaries: { if (this.currentTrack?.info?.type !== "video" || !this.currentTrack.info.colorSpace) break; const primaries = readUnsignedInt(slice, size4); const mapped = COLOR_PRIMARIES_MAP_INVERSE[primaries] ?? null; this.currentTrack.info.colorSpace.primaries = mapped; } ; break; case EBMLId.Projection: { if (this.currentTrack?.info?.type !== "video") break; this.readContiguousElements(slice.slice(dataStartPos, size4)); } ; break; case EBMLId.ProjectionPoseRoll: { if (this.currentTrack?.info?.type !== "video") break; const rotation = readFloat(slice, size4); const flippedRotation = -rotation; try { this.currentTrack.info.rotation = normalizeRotation(flippedRotation); } catch {} } ; break; case EBMLId.Audio: { if (this.currentTrack?.info?.type !== "audio") break; this.readContiguousElements(slice.slice(dataStartPos, size4)); } ; break; case EBMLId.SamplingFrequency: { if (this.currentTrack?.info?.type !== "audio") break; this.currentTrack.info.sampleRate = readFloat(slice, size4); } ; break; case EBMLId.Channels: { if (this.currentTrack?.info?.type !== "audio") break; this.currentTrack.info.numberOfChannels = readUnsignedInt(slice, size4); } ; break; case EBMLId.BitDepth: { if (this.currentTrack?.info?.type !== "audio") break; this.currentTrack.info.bitDepth = readUnsignedInt(slice, size4); } ; break; case EBMLId.CuePoint: { if (!this.currentSegment) break; this.readContiguousElements(slice.slice(dataStartPos, size4)); this.currentCueTime = null; } ; break; case EBMLId.CueTime: { this.currentCueTime = readUnsignedInt(slice, size4); } ; break; case EBMLId.CueTrackPositions: { if (this.currentCueTime === null) break; assert(this.currentSegment); const cuePoint = { time: this.currentCueTime, trackId: -1, clusterPosition: -1 }; this.currentSegment.cuePoints.push(cuePoint); this.readContiguousElements(slice.slice(dataStartPos, size4)); if (cuePoint.trackId === -1 || cuePoint.clusterPosition === -1) { this.currentSegment.cuePoints.pop(); } } ; break; case EBMLId.CueTrack: { const lastCuePoint = this.currentSegment?.cuePoints[this.currentSegment.cuePoints.length - 1]; if (!lastCuePoint) break; lastCuePoint.trackId = readUnsignedInt(slice, size4); } ; break; case EBMLId.CueClusterPosition: { const lastCuePoint = this.currentSegment?.cuePoints[this.currentSegment.cuePoints.length - 1]; if (!lastCuePoint) break; assert(this.currentSegment); lastCuePoint.clusterPosition = this.currentSegment.dataStartPos + readUnsignedInt(slice, size4); } ; break; case EBMLId.Timestamp: { if (!this.currentCluster) break; this.currentCluster.timestamp = readUnsignedInt(slice, size4); } ; break; case EBMLId.SimpleBlock: { if (!this.currentCluster) break; const trackNumber = readVarInt(slice); if (trackNumber === null) break; const trackData = this.getTrackDataInCluster(this.currentCluster, trackNumber); if (!trackData) break; const relativeTimestamp = readI16Be(slice); const flags = readU8(slice); const lacing = flags >> 1 & 3; let isKeyFrame = !!(flags & 128); if (trackData.track.info?.type === "audio" && trackData.track.info.codec) { isKeyFrame = true; } const blockData = readBytes(slice, size4 - (slice.filePos - dataStartPos)); const hasDecodingInstructions = trackData.track.decodingInstructions.length > 0; trackData.blocks.push({ timestamp: relativeTimestamp, duration: 0, isKeyFrame, data: blockData, lacing, decoded: !hasDecodingInstructions, mainAdditional: null }); } ; break; case EBMLId.BlockGroup: { if (!this.currentCluster) break; this.readContiguousElements(slice.slice(dataStartPos, size4)); this.currentBlock = null; } ; break; case EBMLId.Block: { if (!this.currentCluster) break; const trackNumber = readVarInt(slice); if (trackNumber === null) break; const trackData = this.getTrackDataInCluster(this.currentCluster, trackNumber); if (!trackData) break; const relativeTimestamp = readI16Be(slice); const flags = readU8(slice); const lacing = flags >> 1 & 3; const blockData = readBytes(slice, size4 - (slice.filePos - dataStartPos)); const hasDecodingInstructions = trackData.track.decodingInstructions.length > 0; this.currentBlock = { timestamp: relativeTimestamp, duration: 0, isKeyFrame: true, data: blockData, lacing, decoded: !hasDecodingInstructions, mainAdditional: null }; trackData.blocks.push(this.currentBlock); } ; break; case EBMLId.BlockAdditions: { this.readContiguousElements(slice.slice(dataStartPos, size4)); } ; break; case EBMLId.BlockMore: { if (!this.currentBlock) break; this.currentBlockAdditional = { addId: 1, data: null }; this.readContiguousElements(slice.slice(dataStartPos, size4)); if (this.currentBlockAdditional.data && this.currentBlockAdditional.addId === 1) { this.currentBlock.mainAdditional = this.currentBlockAdditional.data; } this.currentBlockAdditional = null; } ; break; case EBMLId.BlockAdditional: { if (!this.currentBlockAdditional) break; this.currentBlockAdditional.data = readBytes(slice, size4); } ; break; case EBMLId.BlockAddID: { if (!this.currentBlockAdditional) break; this.currentBlockAdditional.addId = readUnsignedInt(slice, size4); } ; break; case EBMLId.BlockDuration: { if (!this.currentBlock) break; this.currentBlock.duration = readUnsignedInt(slice, size4); } ; break; case EBMLId.ReferenceBlock: { if (!this.currentBlock) break; this.currentBlock.isKeyFrame = false; } ; break; case EBMLId.Tag: { this.currentTagTargetIsMovie = true; this.readContiguousElements(slice.slice(dataStartPos, size4)); } ; break; case EBMLId.Targets: { this.readContiguousElements(slice.slice(dataStartPos, size4)); } ; break; case EBMLId.TargetTypeValue: { const targetTypeValue = readUnsignedInt(slice, size4); if (targetTypeValue !== 50) { this.currentTagTargetIsMovie = false; } } ; break; case EBMLId.TagTrackUID: case EBMLId.TagEditionUID: case EBMLId.TagChapterUID: case EBMLId.TagAttachmentUID: { this.currentTagTargetIsMovie = false; } ; break; case EBMLId.SimpleTag: { if (!this.currentTagTargetIsMovie) break; this.currentSimpleTagName = null; this.readContiguousElements(slice.slice(dataStartPos, size4)); } ; break; case EBMLId.TagName: { this.currentSimpleTagName = readUnicodeString(slice, size4); } ; break; case EBMLId.TagString: { if (!this.currentSimpleTagName) break; const value = readUnicodeString(slice, size4); this.processTagValue(this.currentSimpleTagName, value); } ; break; case EBMLId.TagBinary: { if (!this.currentSimpleTagName) break; const value = readBytes(slice, size4); this.processTagValue(this.currentSimpleTagName, value); } ; break; case EBMLId.AttachedFile: { if (!this.currentSegment) break; this.currentAttachedFile = { fileUid: null, fileName: null, fileMediaType: null, fileData: null, fileDescription: null }; this.readContiguousElements(slice.slice(dataStartPos, size4)); const tags = this.currentSegment.metadataTags; if (this.currentAttachedFile.fileUid && this.currentAttachedFile.fileData) { tags.raw ??= {}; tags.raw[this.currentAttachedFile.fileUid.toString()] = new AttachedFile(this.currentAttachedFile.fileData, this.currentAttachedFile.fileMediaType ?? undefined, this.currentAttachedFile.fileName ?? undefined, this.currentAttachedFile.fileDescription ?? undefined); } if (this.currentAttachedFile.fileMediaType?.startsWith("image/") && this.currentAttachedFile.fileData) { const fileName = this.currentAttachedFile.fileName; let kind = "unknown"; if (fileName) { const lowerName = fileName.toLowerCase(); if (lowerName.startsWith("cover.")) { kind = "coverFront"; } else if (lowerName.startsWith("back.")) { kind = "coverBack"; } } tags.images ??= []; tags.images.push({ data: this.currentAttachedFile.fileData, mimeType: this.currentAttachedFile.fileMediaType, kind, name: this.currentAttachedFile.fileName ?? undefined, description: this.currentAttachedFile.fileDescription ?? undefined }); } this.currentAttachedFile = null; } ; break; case EBMLId.FileUID: { if (!this.currentAttachedFile) break; this.currentAttachedFile.fileUid = readUnsignedBigInt(slice, size4); } ; break; case EBMLId.FileName: { if (!this.currentAttachedFile) break; this.currentAttachedFile.fileName = readUnicodeString(slice, size4); } ; break; case EBMLId.FileMediaType: { if (!this.currentAttachedFile) break; this.currentAttachedFile.fileMediaType = readAsciiString(slice, size4); } ; break; case EBMLId.FileData: { if (!this.currentAttachedFile) break; this.currentAttachedFile.fileData = readBytes(slice, size4); } ; break; case EBMLId.FileDescription: { if (!this.currentAttachedFile) break; this.currentAttachedFile.fileDescription = readUnicodeString(slice, size4); } ; break; case EBMLId.ContentEncodings: { if (!this.currentTrack) break; this.readContiguousElements(slice.slice(dataStartPos, size4)); this.currentTrack.decodingInstructions.sort((a, b) => b.order - a.order); } ; break; case EBMLId.ContentEncoding: { this.currentDecodingInstruction = { order: 0, scope: ContentEncodingScope.Block, data: null }; this.readContiguousElements(slice.slice(dataStartPos, size4)); if (this.currentDecodingInstruction.data) { this.currentTrack.decodingInstructions.push(this.currentDecodingInstruction); } this.currentDecodingInstruction = null; } ; break; case EBMLId.ContentEncodingOrder: { if (!this.currentDecodingInstruction) break; this.currentDecodingInstruction.order = readUnsignedInt(slice, size4); } ; break; case EBMLId.ContentEncodingScope: { if (!this.currentDecodingInstruction) break; this.currentDecodingInstruction.scope = readUnsignedInt(slice, size4); } ; break; case EBMLId.ContentCompression: { if (!this.currentDecodingInstruction) break; this.currentDecodingInstruction.data = { type: "decompress", algorithm: ContentCompAlgo.Zlib, settings: null }; this.readContiguousElements(slice.slice(dataStartPos, size4)); } ; break; case EBMLId.ContentCompAlgo: { if (this.currentDecodingInstruction?.data?.type !== "decompress") break; this.currentDecodingInstruction.data.algorithm = readUnsignedInt(slice, size4); } ; break; case EBMLId.ContentCompSettings: { if (this.currentDecodingInstruction?.data?.type !== "decompress") break; this.currentDecodingInstruction.data.settings = readBytes(slice, size4); } ; break; case EBMLId.ContentEncryption: { if (!this.currentDecodingInstruction) break; this.currentDecodingInstruction.data = { type: "decrypt" }; } ; break; } slice.filePos = dataStartPos + size4; return true; } decodeBlockData(track, rawData) { assert(track.decodingInstructions.length > 0); let currentData = rawData; for (const instruction of track.decodingInstructions) { assert(instruction.data); switch (instruction.data.type) { case "decompress": { switch (instruction.data.algorithm) { case ContentCompAlgo.HeaderStripping: { if (instruction.data.settings && instruction.data.settings.length > 0) { const prefix = instruction.data.settings; const newData = new Uint8Array(prefix.length + currentData.length); newData.set(prefix, 0); newData.set(currentData, prefix.length); currentData = newData; } } ; break; default: {} ; } } ; break; default: {} ; } } return currentData; } processTagValue(name, value) { if (!this.currentSegment?.metadataTags) return; const metadataTags = this.currentSegment.metadataTags; metadataTags.raw ??= {}; metadataTags.raw[name] ??= value; if (typeof value === "string") { switch (name.toLowerCase()) { case "title": { metadataTags.title ??= value; } ; break; case "description": { metadataTags.description ??= value; } ; break; case "artist": { metadataTags.artist ??= value; } ; break; case "album": { metadataTags.album ??= value; } ; break; case "album_artist": { metadataTags.albumArtist ??= value; } ; break; case "genre": { metadataTags.genre ??= value; } ; break; case "comment": { metadataTags.comment ??= value; } ; break; case "lyrics": { metadataTags.lyrics ??= value; } ; break; case "date": { const date = new Date(value); if (!Number.isNaN(date.getTime())) { metadataTags.date ??= date; } } ; break; case "track_number": case "part_number": { const parts = value.split("/"); const trackNum = Number.parseInt(parts[0], 10); const tracksTotal = parts[1] && Number.parseInt(parts[1], 10); if (Number.isInteger(trackNum) && trackNum > 0) { metadataTags.trackNumber ??= trackNum; } if (tracksTotal && Number.isInteger(tracksTotal) && tracksTotal > 0) { metadataTags.tracksTotal ??= tracksTotal; } } ; break; case "disc_number": case "disc": { const discParts = value.split("/"); const discNum = Number.parseInt(discParts[0], 10); const discsTotal = discParts[1] && Number.parseInt(discParts[1], 10); if (Number.isInteger(discNum) && discNum > 0) { metadataTags.discNumber ??= discNum; } if (discsTotal && Number.isInteger(discsTotal) && discsTotal > 0) { metadataTags.discsTotal ??= discsTotal; } } ; break; } } } } class MatroskaTrackBacking { constructor(internalTrack) { this.internalTrack = internalTrack; this.packetToClusterLocation = new WeakMap; } getId() { return this.internalTrack.id; } getCodec() { throw new Error("Not implemented on base class."); } getInternalCodecId() { return this.internalTrack.codecId; } async computeDuration() { const lastPacket = await this.getPacket(Infinity, { metadataOnly: true }); return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0); } getName() { return this.internalTrack.name; } getLanguageCode() { return this.internalTrack.languageCode; } async getFirstTimestamp() { const firstPacket = await this.getFirstPacket({ metadataOnly: true }); return firstPacket?.timestamp ?? 0; } getTimeResolution() { return this.internalTrack.segment.timestampFactor; } getDisposition() { return this.internalTrack.disposition; } async getFirstPacket(options) { return this.performClusterLookup(null, (cluster) => { const trackData = cluster.trackData.get(this.internalTrack.id); if (trackData) { return { blockIndex: 0, correctBlockFound: true }; } return { blockIndex: -1, correctBlockFound: false }; }, -Infinity, Infinity, options); } intoTimescale(timestamp) { return roundIfAlmostInteger(timestamp * this.internalTrack.segment.timestampFactor); } async getPacket(timestamp, options) { const timestampInTimescale = this.intoTimescale(timestamp); return this.performClusterLookup(null, (cluster) => { const trackData = cluster.trackData.get(this.internalTrack.id); if (!trackData) { return { blockIndex: -1, correctBlockFound: false }; } const index = binarySearchLessOrEqual(trackData.presentationTimestamps, timestampInTimescale, (x) => x.timestamp); const blockIndex = index !== -1 ? trackData.presentationTimestamps[index].blockIndex : -1; const correctBlockFound = index !== -1 && timestampInTimescale < trackData.endTimestamp; return { blockIndex, correctBlockFound }; }, timestampInTimescale, timestampInTimescale, options); } async getNextPacket(packet, options) { const locationInCluster = this.packetToClusterLocation.get(packet); if (locationInCluster === undefined) { throw new Error("Packet was not created from this track."); } return this.performClusterLookup(locationInCluster.cluster, (cluster) => { if (cluster === locationInCluster.cluster) { const trackData = cluster.trackData.get(this.internalTrack.id); if (locationInCluster.blockIndex + 1 < trackData.blocks.length) { return { blockIndex: locationInCluster.blockIndex + 1, correctBlockFound: true }; } } else { const trackData = cluster.trackData.get(this.internalTrack.id); if (trackData) { return { blockIndex: 0, correctBlockFound: true }; } } return { blockIndex: -1, correctBlockFound: false }; }, -Infinity, Infinity, options); } async getKeyPacket(timestamp, options) { const timestampInTimescale = this.intoTimescale(timestamp); return this.performClusterLookup(null, (cluster) => { const trackData = cluster.trackData.get(this.internalTrack.id); if (!trackData) { return { blockIndex: -1, correctBlockFound: false }; } const index = findLastIndex(trackData.presentationTimestamps, (x) => { const block = trackData.blocks[x.blockIndex]; return block.isKeyFrame && x.timestamp <= timestampInTimescale; }); const blockIndex = index !== -1 ? trackData.presentationTimestamps[index].blockIndex : -1; const correctBlockFound = index !== -1 && timestampInTimescale < trackData.endTimestamp; return { blockIndex, correctBlockFound }; }, timestampInTimescale, timestampInTimescale, options); } async getNextKeyPacket(packet, options) { const locationInCluster = this.packetToClusterLocation.get(packet); if (locationInCluster === undefined) { throw new Error("Packet was not created from this track."); } return this.performClusterLookup(locationInCluster.cluster, (cluster) => { if (cluster === locationInCluster.cluster) { const trackData = cluster.trackData.get(this.internalTrack.id); const nextKeyFrameIndex = trackData.blocks.findIndex((x, i) => x.isKeyFrame && i > locationInCluster.blockIndex); if (nextKeyFrameIndex !== -1) { return { blockIndex: nextKeyFrameIndex, correctBlockFound: true }; } } else { const trackData = cluster.trackData.get(this.internalTrack.id); if (trackData && trackData.firstKeyFrameTimestamp !== null) { const keyFrameIndex = trackData.blocks.findIndex((x) => x.isKeyFrame); assert(keyFrameIndex !== -1); return { blockIndex: keyFrameIndex, correctBlockFound: true }; } } return { blockIndex: -1, correctBlockFound: false }; }, -Infinity, Infinity, options); } async fetchPacketInCluster(cluster, blockIndex, options) { if (blockIndex === -1) { return null; } const trackData = cluster.trackData.get(this.internalTrack.id); const block = trackData.blocks[blockIndex]; assert(block); if (!block.decoded) { block.data = this.internalTrack.demuxer.decodeBlockData(this.internalTrack, block.data); block.decoded = true; } const data = options.metadataOnly ? PLACEHOLDER_DATA : block.data; const timestamp = block.timestamp / this.internalTrack.segment.timestampFactor; const duration = block.duration / this.internalTrack.segment.timestampFactor; const sideData = {}; if (block.mainAdditional && this.internalTrack.info?.type === "video" && this.internalTrack.info.alphaMode) { sideData.alpha = options.metadataOnly ? PLACEHOLDER_DATA : block.mainAdditional; sideData.alphaByteLength = block.mainAdditional.byteLength; } const packet = new EncodedPacket(data, block.isKeyFrame ? "key" : "delta", timestamp, duration, cluster.dataStartPos + blockIndex, block.data.byteLength, sideData); this.packetToClusterLocation.set(packet, { cluster, blockIndex }); return packet; } async performClusterLookup(startCluster, getMatchInCluster, searchTimestamp, latestTimestamp, options) { const { demuxer, segment } = this.internalTrack; let currentCluster = null; let bestCluster = null; let bestBlockIndex = -1; if (startCluster) { const { blockIndex, correctBlockFound } = getMatchInCluster(startCluster); if (correctBlockFound) { return this.fetchPacketInCluster(startCluster, blockIndex, options); } if (blockIndex !== -1) { bestCluster = startCluster; bestBlockIndex = blockIndex; } } const cuePointIndex = binarySearchLessOrEqual(this.internalTrack.cuePoints, searchTimestamp, (x) => x.time); const cuePoint = cuePointIndex !== -1 ? this.internalTrack.cuePoints[cuePointIndex] : null; const positionCacheIndex = binarySearchLessOrEqual(this.internalTrack.clusterPositionCache, searchTimestamp, (x) => x.startTimestamp); const positionCacheEntry = positionCacheIndex !== -1 ? this.internalTrack.clusterPositionCache[positionCacheIndex] : null; const lookupEntryPosition = Math.max(cuePoint?.clusterPosition ?? 0, positionCacheEntry?.elementStartPos ?? 0) || null; let currentPos; if (!startCluster) { currentPos = lookupEntryPosition ?? segment.clusterSeekStartPos; } else { if (lookupEntryPosition === null || startCluster.elementStartPos >= lookupEntryPosition) { currentPos = startCluster.elementEndPos; currentCluster = startCluster; } else { currentPos = lookupEntryPosition; } } while (segment.elementEndPos === null || currentPos <= segment.elementEndPos - MIN_HEADER_SIZE) { if (currentCluster) { const trackData = currentCluster.trackData.get(this.internalTrack.id); if (trackData && trackData.startTimestamp > latestTimestamp) { break; } } let slice = demuxer.reader.requestSliceRange(currentPos, MIN_HEADER_SIZE, MAX_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) break; const elementStartPos = currentPos; const elementHeader = readElementHeader(slice); if (!elementHeader || !LEVEL_1_EBML_IDS.includes(elementHeader.id) && elementHeader.id !== EBMLId.Void) { const nextPos = await resync(demuxer.reader, elementStartPos, LEVEL_1_EBML_IDS, Math.min(segment.elementEndPos ?? Infinity, elementStartPos + MAX_RESYNC_LENGTH)); if (nextPos) { currentPos = nextPos; continue; } else { break; } } const id = elementHeader.id; let size4 = elementHeader.size; const dataStartPos = slice.filePos; if (id === EBMLId.Cluster) { currentCluster = await demuxer.readCluster(elementStartPos, segment); size4 = currentCluster.elementEndPos - dataStartPos; const { blockIndex, correctBlockFound } = getMatchInCluster(currentCluster); if (correctBlockFound) { return this.fetchPacketInCluster(currentCluster, blockIndex, options); } if (blockIndex !== -1) { bestCluster = currentCluster; bestBlockIndex = blockIndex; } } if (size4 === undefined) { assert(id !== EBMLId.Cluster); const nextElementPos = await searchForNextElementId(demuxer.reader, dataStartPos, LEVEL_0_AND_1_EBML_IDS, segment.elementEndPos); size4 = nextElementPos.pos - dataStartPos; } const endPos = dataStartPos + size4; if (segment.elementEndPos === null) { let slice2 = demuxer.reader.requestSliceRange(endPos, MIN_HEADER_SIZE, MAX_HEADER_SIZE); if (slice2 instanceof Promise) slice2 = await slice2; if (!slice2) break; const elementId = readElementId(slice2); if (elementId === EBMLId.Segment) { segment.elementEndPos = endPos; break; } } currentPos = endPos; } if (cuePoint && (!bestCluster || bestCluster.elementStartPos < cuePoint.clusterPosition)) { const previousCuePoint = this.internalTrack.cuePoints[cuePointIndex - 1]; assert(!previousCuePoint || previousCuePoint.time < cuePoint.time); const newSearchTimestamp = previousCuePoint?.time ?? -Infinity; return this.performClusterLookup(null, getMatchInCluster, newSearchTimestamp, latestTimestamp, options); } if (bestCluster) { return this.fetchPacketInCluster(bestCluster, bestBlockIndex, options); } return null; } } class MatroskaVideoTrackBacking extends MatroskaTrackBacking { constructor(internalTrack) { super(internalTrack); this.decoderConfigPromise = null; this.internalTrack = internalTrack; } getCodec() { return this.internalTrack.info.codec; } getCodedWidth() { return this.internalTrack.info.width; } getCodedHeight() { return this.internalTrack.info.height; } getRotation() { return this.internalTrack.info.rotation; } async getColorSpace() { return { primaries: this.internalTrack.info.colorSpace?.primaries, transfer: this.internalTrack.info.colorSpace?.transfer, matrix: this.internalTrack.info.colorSpace?.matrix, fullRange: this.internalTrack.info.colorSpace?.fullRange }; } async canBeTransparent() { return this.internalTrack.info.alphaMode; } async getDecoderConfig() { if (!this.internalTrack.info.codec) { return null; } return this.decoderConfigPromise ??= (async () => { let firstPacket = null; const needsPacketForAdditionalInfo = this.internalTrack.info.codec === "vp9" || this.internalTrack.info.codec === "av1" || this.internalTrack.info.codec === "avc" && !this.internalTrack.info.codecDescription || this.internalTrack.info.codec === "hevc" && !this.internalTrack.info.codecDescription; if (needsPacketForAdditionalInfo) { firstPacket = await this.getFirstPacket({}); } return { codec: extractVideoCodecString({ width: this.internalTrack.info.width, height: this.internalTrack.info.height, codec: this.internalTrack.info.codec, codecDescription: this.internalTrack.info.codecDescription, colorSpace: this.internalTrack.info.colorSpace, avcType: 1, avcCodecInfo: this.internalTrack.info.codec === "avc" && firstPacket ? extractAvcDecoderConfigurationRecord(firstPacket.data) : null, hevcCodecInfo: this.internalTrack.info.codec === "hevc" && firstPacket ? extractHevcDecoderConfigurationRecord(firstPacket.data) : null, vp9CodecInfo: this.internalTrack.info.codec === "vp9" && firstPacket ? extractVp9CodecInfoFromPacket(firstPacket.data) : null, av1CodecInfo: this.internalTrack.info.codec === "av1" && firstPacket ? extractAv1CodecInfoFromPacket(firstPacket.data) : null }), codedWidth: this.internalTrack.info.width, codedHeight: this.internalTrack.info.height, description: this.internalTrack.info.codecDescription ?? undefined, colorSpace: this.internalTrack.info.colorSpace ?? undefined }; })(); } } class MatroskaAudioTrackBacking extends MatroskaTrackBacking { constructor(internalTrack) { super(internalTrack); this.decoderConfig = null; this.internalTrack = internalTrack; } getCodec() { return this.internalTrack.info.codec; } getNumberOfChannels() { return this.internalTrack.info.numberOfChannels; } getSampleRate() { return this.internalTrack.info.sampleRate; } async getDecoderConfig() { if (!this.internalTrack.info.codec) { return null; } return this.decoderConfig ??= { codec: extractAudioCodecString({ codec: this.internalTrack.info.codec, codecDescription: this.internalTrack.info.codecDescription, aacCodecInfo: this.internalTrack.info.aacCodecInfo }), numberOfChannels: this.internalTrack.info.numberOfChannels, sampleRate: this.internalTrack.info.sampleRate, description: this.internalTrack.info.codecDescription ?? undefined }; } } // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/shared/mp3-misc.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var FRAME_HEADER_SIZE = 4; var SAMPLING_RATES = [44100, 48000, 32000]; var KILOBIT_RATES = [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1, -1, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1, -1, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1, -1, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1, -1, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 ]; var XING = 1483304551; var INFO = 1231971951; var computeMp3FrameSize = (lowSamplingFrequency, layer, bitrate, sampleRate, padding3) => { if (layer === 0) { return 0; } else if (layer === 1) { return Math.floor(144 * bitrate / (sampleRate << lowSamplingFrequency)) + padding3; } else if (layer === 2) { return Math.floor(144 * bitrate / sampleRate) + padding3; } else { return (Math.floor(12 * bitrate / sampleRate) + padding3) * 4; } }; var getXingOffset = (mpegVersionId, channel) => { return mpegVersionId === 3 ? channel === 3 ? 21 : 36 : channel === 3 ? 13 : 21; }; var readMp3FrameHeader = (word, remainingBytes) => { const firstByte = word >>> 24; const secondByte = word >>> 16 & 255; const thirdByte = word >>> 8 & 255; const fourthByte = word & 255; if (firstByte !== 255 && secondByte !== 255 && thirdByte !== 255 && fourthByte !== 255) { return { header: null, bytesAdvanced: 4 }; } if (firstByte !== 255) { return { header: null, bytesAdvanced: 1 }; } if ((secondByte & 224) !== 224) { return { header: null, bytesAdvanced: 1 }; } let lowSamplingFrequency = 0; let mpeg25 = 0; if (secondByte & 1 << 4) { lowSamplingFrequency = secondByte & 1 << 3 ? 0 : 1; } else { lowSamplingFrequency = 1; mpeg25 = 1; } const mpegVersionId = secondByte >> 3 & 3; const layer = secondByte >> 1 & 3; const bitrateIndex = thirdByte >> 4 & 15; const frequencyIndex = (thirdByte >> 2 & 3) % 3; const padding3 = thirdByte >> 1 & 1; const channel = fourthByte >> 6 & 3; const modeExtension = fourthByte >> 4 & 3; const copyright = fourthByte >> 3 & 1; const original = fourthByte >> 2 & 1; const emphasis = fourthByte & 3; const kilobitRate = KILOBIT_RATES[lowSamplingFrequency * 16 * 4 + layer * 16 + bitrateIndex]; if (kilobitRate === -1) { return { header: null, bytesAdvanced: 1 }; } const bitrate = kilobitRate * 1000; const sampleRate = SAMPLING_RATES[frequencyIndex] >> lowSamplingFrequency + mpeg25; const frameLength = computeMp3FrameSize(lowSamplingFrequency, layer, bitrate, sampleRate, padding3); if (remainingBytes !== null && remainingBytes < frameLength) { return { header: null, bytesAdvanced: 1 }; } let audioSamplesInFrame; if (mpegVersionId === 3) { audioSamplesInFrame = layer === 3 ? 384 : 1152; } else { if (layer === 3) { audioSamplesInFrame = 384; } else if (layer === 2) { audioSamplesInFrame = 1152; } else { audioSamplesInFrame = 576; } } return { header: { totalSize: frameLength, mpegVersionId, layer, bitrate, frequencyIndex, sampleRate, channel, modeExtension, copyright, original, emphasis, audioSamplesInFrame }, bytesAdvanced: 1 }; }; var decodeSynchsafe = (synchsafed) => { let mask = 2130706432; let unsynchsafed = 0; while (mask !== 0) { unsynchsafed >>= 1; unsynchsafed |= synchsafed & mask; mask >>= 8; } return unsynchsafed; }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/id3.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var Id3V2HeaderFlags; (function(Id3V2HeaderFlags2) { Id3V2HeaderFlags2[Id3V2HeaderFlags2["Unsynchronisation"] = 128] = "Unsynchronisation"; Id3V2HeaderFlags2[Id3V2HeaderFlags2["ExtendedHeader"] = 64] = "ExtendedHeader"; Id3V2HeaderFlags2[Id3V2HeaderFlags2["ExperimentalIndicator"] = 32] = "ExperimentalIndicator"; Id3V2HeaderFlags2[Id3V2HeaderFlags2["Footer"] = 16] = "Footer"; })(Id3V2HeaderFlags || (Id3V2HeaderFlags = {})); var Id3V2TextEncoding; (function(Id3V2TextEncoding2) { Id3V2TextEncoding2[Id3V2TextEncoding2["ISO_8859_1"] = 0] = "ISO_8859_1"; Id3V2TextEncoding2[Id3V2TextEncoding2["UTF_16_WITH_BOM"] = 1] = "UTF_16_WITH_BOM"; Id3V2TextEncoding2[Id3V2TextEncoding2["UTF_16_BE_NO_BOM"] = 2] = "UTF_16_BE_NO_BOM"; Id3V2TextEncoding2[Id3V2TextEncoding2["UTF_8"] = 3] = "UTF_8"; })(Id3V2TextEncoding || (Id3V2TextEncoding = {})); var ID3_V1_TAG_SIZE = 128; var ID3_V2_HEADER_SIZE = 10; var ID3_V1_GENRES = [ "Blues", "Classic rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-hop", "Jazz", "Metal", "New age", "Oldies", "Other", "Pop", "Rhythm and blues", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", "Death metal", "Pranks", "Soundtrack", "Euro-techno", "Ambient", "Trip-hop", "Vocal", "Jazz & funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound clip", "Gospel", "Noise", "Alternative rock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental pop", "Instrumental rock", "Ethnic", "Gothic", "Darkwave", "Techno-industrial", "Electronic", "Pop-folk", "Eurodance", "Dream", "Southern rock", "Comedy", "Cult", "Gangsta", "Top 40", "Christian rap", "Pop/funk", "Jungle music", "Native US", "Cabaret", "New wave", "Psychedelic", "Rave", "Showtunes", "Trailer", "Lo-fi", "Tribal", "Acid punk", "Acid jazz", "Polka", "Retro", "Musical", "Rock 'n' roll", "Hard rock", "Folk", "Folk rock", "National folk", "Swing", "Fast fusion", "Bebop", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic rock", "Progressive rock", "Psychedelic rock", "Symphonic rock", "Slow rock", "Big band", "Chorus", "Easy listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera", "Chamber music", "Sonata", "Symphony", "Booty bass", "Primus", "Porn groove", "Satire", "Slow jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk rock", "Drum solo", "A cappella", "Euro-house", "Dance hall", "Goa music", "Drum & bass", "Club-house", "Hardcore techno", "Terror", "Indie", "Britpop", "Negerpunk", "Polsk punk", "Beat", "Christian gangsta rap", "Heavy metal", "Black metal", "Crossover", "Contemporary Christian", "Christian rock", "Merengue", "Salsa", "Thrash metal", "Anime", "Jpop", "Synthpop", "Christmas", "Art rock", "Baroque", "Bhangra", "Big beat", "Breakbeat", "Chillout", "Downtempo", "Dub", "EBM", "Eclectic", "Electro", "Electroclash", "Emo", "Experimental", "Garage", "Global", "IDM", "Illbient", "Industro-Goth", "Jam Band", "Krautrock", "Leftfield", "Lounge", "Math rock", "New romantic", "Nu-breakz", "Post-punk", "Post-rock", "Psytrance", "Shoegaze", "Space rock", "Trop rock", "World music", "Neoclassical", "Audiobook", "Audio theatre", "Neue Deutsche Welle", "Podcast", "Indie rock", "G-Funk", "Dubstep", "Garage rock", "Psybient" ]; var parseId3V1Tag = (slice, tags) => { const startPos = slice.filePos; tags.raw ??= {}; tags.raw["TAG"] ??= readBytes(slice, ID3_V1_TAG_SIZE - 3); slice.filePos = startPos; const title4 = readId3V1String(slice, 30); if (title4) tags.title ??= title4; const artist = readId3V1String(slice, 30); if (artist) tags.artist ??= artist; const album = readId3V1String(slice, 30); if (album) tags.album ??= album; const yearText = readId3V1String(slice, 4); const year = Number.parseInt(yearText, 10); if (Number.isInteger(year) && year > 0) { tags.date ??= new Date(year, 0, 1); } const commentBytes = readBytes(slice, 30); let comment; if (commentBytes[28] === 0 && commentBytes[29] !== 0) { const trackNum = commentBytes[29]; if (trackNum > 0) { tags.trackNumber ??= trackNum; } slice.skip(-30); comment = readId3V1String(slice, 28); slice.skip(2); } else { slice.skip(-30); comment = readId3V1String(slice, 30); } if (comment) tags.comment ??= comment; const genreIndex = readU8(slice); if (genreIndex < ID3_V1_GENRES.length) { tags.genre ??= ID3_V1_GENRES[genreIndex]; } }; var readId3V1String = (slice, length) => { const bytes = readBytes(slice, length); const endIndex = coalesceIndex(bytes.indexOf(0), bytes.length); const relevantBytes = bytes.subarray(0, endIndex); let str = ""; for (let i = 0;i < relevantBytes.length; i++) { str += String.fromCharCode(relevantBytes[i]); } return str.trimEnd(); }; var readId3V2Header = (slice) => { const startPos = slice.filePos; const tag = readAscii(slice, 3); const majorVersion = readU8(slice); const revision = readU8(slice); const flags = readU8(slice); const sizeRaw = readU32Be(slice); if (tag !== "ID3" || majorVersion === 255 || revision === 255 || (sizeRaw & 2155905152) !== 0) { slice.filePos = startPos; return null; } const size4 = decodeSynchsafe(sizeRaw); return { majorVersion, revision, flags, size: size4 }; }; var parseId3V2Tag = (slice, header2, tags) => { if (![2, 3, 4].includes(header2.majorVersion)) { console.warn(`Unsupported ID3v2 major version: ${header2.majorVersion}`); return; } const bytes = readBytes(slice, header2.size); const reader = new Id3V2Reader(header2, bytes); if (header2.flags & Id3V2HeaderFlags.Footer) { reader.removeFooter(); } if (header2.flags & Id3V2HeaderFlags.Unsynchronisation && header2.majorVersion === 3) { reader.ununsynchronizeAll(); } if (header2.flags & Id3V2HeaderFlags.ExtendedHeader) { const extendedHeaderSize = reader.readU32(); if (header2.majorVersion === 3) { reader.pos += extendedHeaderSize; } else { reader.pos += extendedHeaderSize - 4; } } while (reader.pos <= reader.bytes.length - reader.frameHeaderSize()) { const frame2 = reader.readId3V2Frame(); if (!frame2) { break; } const frameStartPos = reader.pos; const frameEndPos = reader.pos + frame2.size; let frameEncrypted = false; let frameCompressed = false; let frameUnsynchronized = false; if (header2.majorVersion === 3) { frameEncrypted = !!(frame2.flags & 1 << 6); frameCompressed = !!(frame2.flags & 1 << 7); } else if (header2.majorVersion === 4) { frameEncrypted = !!(frame2.flags & 1 << 2); frameCompressed = !!(frame2.flags & 1 << 3); frameUnsynchronized = !!(frame2.flags & 1 << 1) || !!(header2.flags & Id3V2HeaderFlags.Unsynchronisation); } if (frameEncrypted) { console.warn(`Skipping encrypted ID3v2 frame ${frame2.id}`); reader.pos = frameEndPos; continue; } if (frameCompressed) { console.warn(`Skipping compressed ID3v2 frame ${frame2.id}`); reader.pos = frameEndPos; continue; } if (frameUnsynchronized) { reader.ununsynchronizeRegion(reader.pos, frameEndPos); } tags.raw ??= {}; if (frame2.id[0] === "T") { tags.raw[frame2.id] ??= reader.readId3V2EncodingAndText(frameEndPos); } else { tags.raw[frame2.id] ??= reader.readBytes(frame2.size); } reader.pos = frameStartPos; switch (frame2.id) { case "TIT2": case "TT2": { tags.title ??= reader.readId3V2EncodingAndText(frameEndPos); } ; break; case "TIT3": case "TT3": { tags.description ??= reader.readId3V2EncodingAndText(frameEndPos); } ; break; case "TPE1": case "TP1": { tags.artist ??= reader.readId3V2EncodingAndText(frameEndPos); } ; break; case "TALB": case "TAL": { tags.album ??= reader.readId3V2EncodingAndText(frameEndPos); } ; break; case "TPE2": case "TP2": { tags.albumArtist ??= reader.readId3V2EncodingAndText(frameEndPos); } ; break; case "TRCK": case "TRK": { const trackText = reader.readId3V2EncodingAndText(frameEndPos); const parts = trackText.split("/"); const trackNum = Number.parseInt(parts[0], 10); const tracksTotal = parts[1] && Number.parseInt(parts[1], 10); if (Number.isInteger(trackNum) && trackNum > 0) { tags.trackNumber ??= trackNum; } if (tracksTotal && Number.isInteger(tracksTotal) && tracksTotal > 0) { tags.tracksTotal ??= tracksTotal; } } ; break; case "TPOS": case "TPA": { const discText = reader.readId3V2EncodingAndText(frameEndPos); const parts = discText.split("/"); const discNum = Number.parseInt(parts[0], 10); const discsTotal = parts[1] && Number.parseInt(parts[1], 10); if (Number.isInteger(discNum) && discNum > 0) { tags.discNumber ??= discNum; } if (discsTotal && Number.isInteger(discsTotal) && discsTotal > 0) { tags.discsTotal ??= discsTotal; } } ; break; case "TCON": case "TCO": { const genreText = reader.readId3V2EncodingAndText(frameEndPos); let match = /^\((\d+)\)/.exec(genreText); if (match) { const genreNumber = Number.parseInt(match[1]); if (ID3_V1_GENRES[genreNumber] !== undefined) { tags.genre ??= ID3_V1_GENRES[genreNumber]; break; } } match = /^\d+$/.exec(genreText); if (match) { const genreNumber = Number.parseInt(match[0]); if (ID3_V1_GENRES[genreNumber] !== undefined) { tags.genre ??= ID3_V1_GENRES[genreNumber]; break; } } tags.genre ??= genreText; } ; break; case "TDRC": case "TDAT": { const dateText = reader.readId3V2EncodingAndText(frameEndPos); const date = new Date(dateText); if (!Number.isNaN(date.getTime())) { tags.date ??= date; } } ; break; case "TYER": case "TYE": { const yearText = reader.readId3V2EncodingAndText(frameEndPos); const year = Number.parseInt(yearText, 10); if (Number.isInteger(year)) { tags.date ??= new Date(year, 0, 1); } } ; break; case "USLT": case "ULT": { const encoding = reader.readU8(); reader.pos += 3; reader.readId3V2Text(encoding, frameEndPos); tags.lyrics ??= reader.readId3V2Text(encoding, frameEndPos); } ; break; case "COMM": case "COM": { const encoding = reader.readU8(); reader.pos += 3; reader.readId3V2Text(encoding, frameEndPos); tags.comment ??= reader.readId3V2Text(encoding, frameEndPos); } ; break; case "APIC": case "PIC": { const encoding = reader.readId3V2TextEncoding(); let mimeType; if (header2.majorVersion === 2) { const imageFormat = reader.readAscii(3); mimeType = imageFormat === "PNG" ? "image/png" : imageFormat === "JPG" ? "image/jpeg" : "image/*"; } else { mimeType = reader.readId3V2Text(encoding, frameEndPos); } const pictureType = reader.readU8(); const description = reader.readId3V2Text(encoding, frameEndPos).trimEnd(); const imageDataSize = frameEndPos - reader.pos; if (imageDataSize >= 0) { const imageData = reader.readBytes(imageDataSize); if (!tags.images) tags.images = []; tags.images.push({ data: imageData, mimeType, kind: pictureType === 3 ? "coverFront" : pictureType === 4 ? "coverBack" : "unknown", description }); } } ; break; default: { reader.pos += frame2.size; } ; break; } reader.pos = frameEndPos; } }; class Id3V2Reader { constructor(header2, bytes) { this.header = header2; this.bytes = bytes; this.pos = 0; this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); } frameHeaderSize() { return this.header.majorVersion === 2 ? 6 : 10; } ununsynchronizeAll() { const newBytes = []; for (let i = 0;i < this.bytes.length; i++) { const value1 = this.bytes[i]; newBytes.push(value1); if (value1 === 255 && i !== this.bytes.length - 1) { const value2 = this.bytes[i]; if (value2 === 0) { i++; } } } this.bytes = new Uint8Array(newBytes); this.view = new DataView(this.bytes.buffer); } ununsynchronizeRegion(start, end) { const newBytes = []; for (let i = start;i < end; i++) { const value1 = this.bytes[i]; newBytes.push(value1); if (value1 === 255 && i !== end - 1) { const value2 = this.bytes[i + 1]; if (value2 === 0) { i++; } } } const before = this.bytes.subarray(0, start); const after = this.bytes.subarray(end); this.bytes = new Uint8Array(before.length + newBytes.length + after.length); this.bytes.set(before, 0); this.bytes.set(newBytes, before.length); this.bytes.set(after, before.length + newBytes.length); this.view = new DataView(this.bytes.buffer); } removeFooter() { this.bytes = this.bytes.subarray(0, this.bytes.length - ID3_V2_HEADER_SIZE); this.view = new DataView(this.bytes.buffer); } readBytes(length) { const slice = this.bytes.subarray(this.pos, this.pos + length); this.pos += length; return slice; } readU8() { const value = this.view.getUint8(this.pos); this.pos += 1; return value; } readU16() { const value = this.view.getUint16(this.pos, false); this.pos += 2; return value; } readU24() { const high = this.view.getUint16(this.pos, false); const low = this.view.getUint8(this.pos + 1); this.pos += 3; return high * 256 + low; } readU32() { const value = this.view.getUint32(this.pos, false); this.pos += 4; return value; } readAscii(length) { let str = ""; for (let i = 0;i < length; i++) { str += String.fromCharCode(this.view.getUint8(this.pos + i)); } this.pos += length; return str; } readId3V2Frame() { if (this.header.majorVersion === 2) { const id = this.readAscii(3); if (id === "\x00\x00\x00") { return null; } const size4 = this.readU24(); return { id, size: size4, flags: 0 }; } else { const id = this.readAscii(4); if (id === "\x00\x00\x00\x00") { return null; } const sizeRaw = this.readU32(); let size4 = this.header.majorVersion === 4 ? decodeSynchsafe(sizeRaw) : sizeRaw; const flags = this.readU16(); const headerEndPos = this.pos; const isSizeValid = (size5) => { const nextPos = this.pos + size5; if (nextPos > this.bytes.length) { return false; } if (nextPos <= this.bytes.length - this.frameHeaderSize()) { this.pos += size5; const nextId = this.readAscii(4); if (nextId !== "\x00\x00\x00\x00" && !/[0-9A-Z]{4}/.test(nextId)) { return false; } } return true; }; if (!isSizeValid(size4)) { const otherSize = this.header.majorVersion === 4 ? sizeRaw : decodeSynchsafe(sizeRaw); if (isSizeValid(otherSize)) { size4 = otherSize; } } this.pos = headerEndPos; return { id, size: size4, flags }; } } readId3V2TextEncoding() { const number = this.readU8(); if (number > 3) { throw new Error(`Unsupported text encoding: ${number}`); } return number; } readId3V2Text(encoding, until) { const startPos = this.pos; const data = this.readBytes(until - this.pos); switch (encoding) { case Id3V2TextEncoding.ISO_8859_1: { let str = ""; for (let i = 0;i < data.length; i++) { const value = data[i]; if (value === 0) { this.pos = startPos + i + 1; break; } str += String.fromCharCode(value); } return str; } case Id3V2TextEncoding.UTF_16_WITH_BOM: { if (data[0] === 255 && data[1] === 254) { const decoder = new TextDecoder("utf-16le"); const endIndex = coalesceIndex(data.findIndex((x, i) => x === 0 && data[i + 1] === 0 && i % 2 === 0), data.length); this.pos = startPos + Math.min(endIndex + 2, data.length); return decoder.decode(data.subarray(2, endIndex)); } else if (data[0] === 254 && data[1] === 255) { const decoder = new TextDecoder("utf-16be"); const endIndex = coalesceIndex(data.findIndex((x, i) => x === 0 && data[i + 1] === 0 && i % 2 === 0), data.length); this.pos = startPos + Math.min(endIndex + 2, data.length); return decoder.decode(data.subarray(2, endIndex)); } else { const endIndex = coalesceIndex(data.findIndex((x) => x === 0), data.length); this.pos = startPos + Math.min(endIndex + 1, data.length); return textDecoder.decode(data.subarray(0, endIndex)); } } case Id3V2TextEncoding.UTF_16_BE_NO_BOM: { const decoder = new TextDecoder("utf-16be"); const endIndex = coalesceIndex(data.findIndex((x, i) => x === 0 && data[i + 1] === 0 && i % 2 === 0), data.length); this.pos = startPos + Math.min(endIndex + 2, data.length); return decoder.decode(data.subarray(0, endIndex)); } case Id3V2TextEncoding.UTF_8: { const endIndex = coalesceIndex(data.findIndex((x) => x === 0), data.length); this.pos = startPos + Math.min(endIndex + 1, data.length); return textDecoder.decode(data.subarray(0, endIndex)); } } } readId3V2EncodingAndText(until) { if (this.pos >= until) { return ""; } const encoding = this.readId3V2TextEncoding(); return this.readId3V2Text(encoding, until); } } // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/mp3/mp3-reader.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var readNextMp3FrameHeader = async (reader, startPos, until) => { let currentPos = startPos; while (until === null || currentPos < until) { let slice = reader.requestSlice(currentPos, FRAME_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) break; const word = readU32Be(slice); const result = readMp3FrameHeader(word, reader.fileSize !== null ? reader.fileSize - currentPos : null); if (result.header) { return { header: result.header, startPos: currentPos }; } currentPos += result.bytesAdvanced; } return null; }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/mp3/mp3-demuxer.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ class Mp3Demuxer extends Demuxer { constructor(input2) { super(input2); this.metadataPromise = null; this.firstFrameHeader = null; this.loadedSamples = []; this.metadataTags = null; this.tracks = []; this.readingMutex = new AsyncMutex; this.lastSampleLoaded = false; this.lastLoadedPos = 0; this.nextTimestampInSamples = 0; this.reader = input2._reader; } async readMetadata() { return this.metadataPromise ??= (async () => { while (!this.firstFrameHeader && !this.lastSampleLoaded) { await this.advanceReader(); } if (!this.firstFrameHeader) { throw new Error("No valid MP3 frame found."); } this.tracks = [new InputAudioTrack(this.input, new Mp3AudioTrackBacking(this))]; })(); } async advanceReader() { if (this.lastLoadedPos === 0) { while (true) { let slice2 = this.reader.requestSlice(this.lastLoadedPos, ID3_V2_HEADER_SIZE); if (slice2 instanceof Promise) slice2 = await slice2; if (!slice2) { this.lastSampleLoaded = true; return; } const id3V2Header = readId3V2Header(slice2); if (!id3V2Header) { break; } this.lastLoadedPos = slice2.filePos + id3V2Header.size; } } const result = await readNextMp3FrameHeader(this.reader, this.lastLoadedPos, this.reader.fileSize); if (!result) { this.lastSampleLoaded = true; return; } const header2 = result.header; this.lastLoadedPos = result.startPos + header2.totalSize - 1; const xingOffset = getXingOffset(header2.mpegVersionId, header2.channel); let slice = this.reader.requestSlice(result.startPos + xingOffset, 4); if (slice instanceof Promise) slice = await slice; if (slice) { const word = readU32Be(slice); const isXing = word === XING || word === INFO; if (isXing) { return; } } if (!this.firstFrameHeader) { this.firstFrameHeader = header2; } if (header2.sampleRate !== this.firstFrameHeader.sampleRate) { console.warn(`MP3 changed sample rate mid-file: ${this.firstFrameHeader.sampleRate} Hz to ${header2.sampleRate} Hz.` + ` Might be a bug, so please report this file.`); } const sampleDuration = header2.audioSamplesInFrame / this.firstFrameHeader.sampleRate; const sample = { timestamp: this.nextTimestampInSamples / this.firstFrameHeader.sampleRate, duration: sampleDuration, dataStart: result.startPos, dataSize: header2.totalSize }; this.loadedSamples.push(sample); this.nextTimestampInSamples += header2.audioSamplesInFrame; return; } async getMimeType() { return "audio/mpeg"; } async getTracks() { await this.readMetadata(); return this.tracks; } async computeDuration() { await this.readMetadata(); const track = this.tracks[0]; assert(track); return track.computeDuration(); } async getMetadataTags() { const release = await this.readingMutex.acquire(); try { await this.readMetadata(); if (this.metadataTags) { return this.metadataTags; } this.metadataTags = {}; let currentPos = 0; let id3V2HeaderFound = false; while (true) { let headerSlice = this.reader.requestSlice(currentPos, ID3_V2_HEADER_SIZE); if (headerSlice instanceof Promise) headerSlice = await headerSlice; if (!headerSlice) break; const id3V2Header = readId3V2Header(headerSlice); if (!id3V2Header) { break; } id3V2HeaderFound = true; let contentSlice = this.reader.requestSlice(headerSlice.filePos, id3V2Header.size); if (contentSlice instanceof Promise) contentSlice = await contentSlice; if (!contentSlice) break; parseId3V2Tag(contentSlice, id3V2Header, this.metadataTags); currentPos = headerSlice.filePos + id3V2Header.size; } if (!id3V2HeaderFound && this.reader.fileSize !== null && this.reader.fileSize >= ID3_V1_TAG_SIZE) { let slice = this.reader.requestSlice(this.reader.fileSize - ID3_V1_TAG_SIZE, ID3_V1_TAG_SIZE); if (slice instanceof Promise) slice = await slice; assert(slice); const tag = readAscii(slice, 3); if (tag === "TAG") { parseId3V1Tag(slice, this.metadataTags); } } return this.metadataTags; } finally { release(); } } } class Mp3AudioTrackBacking { constructor(demuxer) { this.demuxer = demuxer; } getId() { return 1; } async getFirstTimestamp() { return 0; } getTimeResolution() { assert(this.demuxer.firstFrameHeader); return this.demuxer.firstFrameHeader.sampleRate / this.demuxer.firstFrameHeader.audioSamplesInFrame; } async computeDuration() { const lastPacket = await this.getPacket(Infinity, { metadataOnly: true }); return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0); } getName() { return null; } getLanguageCode() { return UNDETERMINED_LANGUAGE; } getCodec() { return "mp3"; } getInternalCodecId() { return null; } getNumberOfChannels() { assert(this.demuxer.firstFrameHeader); return this.demuxer.firstFrameHeader.channel === 3 ? 1 : 2; } getSampleRate() { assert(this.demuxer.firstFrameHeader); return this.demuxer.firstFrameHeader.sampleRate; } getDisposition() { return { ...DEFAULT_TRACK_DISPOSITION }; } async getDecoderConfig() { assert(this.demuxer.firstFrameHeader); return { codec: "mp3", numberOfChannels: this.demuxer.firstFrameHeader.channel === 3 ? 1 : 2, sampleRate: this.demuxer.firstFrameHeader.sampleRate }; } async getPacketAtIndex(sampleIndex, options) { if (sampleIndex === -1) { return null; } const rawSample = this.demuxer.loadedSamples[sampleIndex]; if (!rawSample) { return null; } let data; if (options.metadataOnly) { data = PLACEHOLDER_DATA; } else { let slice = this.demuxer.reader.requestSlice(rawSample.dataStart, rawSample.dataSize); if (slice instanceof Promise) slice = await slice; if (!slice) { return null; } data = readBytes(slice, rawSample.dataSize); } return new EncodedPacket(data, "key", rawSample.timestamp, rawSample.duration, sampleIndex, rawSample.dataSize); } getFirstPacket(options) { return this.getPacketAtIndex(0, options); } async getNextPacket(packet, options) { const release = await this.demuxer.readingMutex.acquire(); try { const sampleIndex = binarySearchExact(this.demuxer.loadedSamples, packet.timestamp, (x) => x.timestamp); if (sampleIndex === -1) { throw new Error("Packet was not created from this track."); } const nextIndex = sampleIndex + 1; while (nextIndex >= this.demuxer.loadedSamples.length && !this.demuxer.lastSampleLoaded) { await this.demuxer.advanceReader(); } return this.getPacketAtIndex(nextIndex, options); } finally { release(); } } async getPacket(timestamp, options) { const release = await this.demuxer.readingMutex.acquire(); try { while (true) { const index = binarySearchLessOrEqual(this.demuxer.loadedSamples, timestamp, (x) => x.timestamp); if (index === -1 && this.demuxer.loadedSamples.length > 0) { return null; } if (this.demuxer.lastSampleLoaded) { return this.getPacketAtIndex(index, options); } if (index >= 0 && index + 1 < this.demuxer.loadedSamples.length) { return this.getPacketAtIndex(index, options); } await this.demuxer.advanceReader(); } } finally { release(); } } getKeyPacket(timestamp, options) { return this.getPacket(timestamp, options); } getNextKeyPacket(packet, options) { return this.getNextPacket(packet, options); } } // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/ogg/ogg-misc.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var OGGS = 1399285583; var OGG_CRC_POLYNOMIAL = 79764919; var OGG_CRC_TABLE = new Uint32Array(256); for (let n = 0;n < 256; n++) { let crc = n << 24; for (let k = 0;k < 8; k++) { crc = crc & 2147483648 ? crc << 1 ^ OGG_CRC_POLYNOMIAL : crc << 1; } OGG_CRC_TABLE[n] = crc >>> 0 & 4294967295; } var computeOggPageCrc = (bytes) => { const view = toDataView(bytes); const originalChecksum = view.getUint32(22, true); view.setUint32(22, 0, true); let crc = 0; for (let i = 0;i < bytes.length; i++) { const byte = bytes[i]; crc = (crc << 8 ^ OGG_CRC_TABLE[crc >>> 24 ^ byte]) >>> 0; } view.setUint32(22, originalChecksum, true); return crc; }; var extractSampleMetadata = (data, codecInfo, vorbisLastBlocksize) => { let durationInSamples = 0; let currentBlocksize = null; if (data.length > 0) { if (codecInfo.codec === "vorbis") { assert(codecInfo.vorbisInfo); const vorbisModeCount = codecInfo.vorbisInfo.modeBlockflags.length; const bitCount = ilog(vorbisModeCount - 1); const modeMask = (1 << bitCount) - 1 << 1; const modeNumber = (data[0] & modeMask) >> 1; if (modeNumber >= codecInfo.vorbisInfo.modeBlockflags.length) { throw new Error("Invalid mode number."); } let prevBlocksize = vorbisLastBlocksize; const blockflag = codecInfo.vorbisInfo.modeBlockflags[modeNumber]; currentBlocksize = codecInfo.vorbisInfo.blocksizes[blockflag]; if (blockflag === 1) { const prevMask = (modeMask | 1) + 1; const flag = data[0] & prevMask ? 1 : 0; prevBlocksize = codecInfo.vorbisInfo.blocksizes[flag]; } durationInSamples = prevBlocksize !== null ? prevBlocksize + currentBlocksize >> 2 : 0; } else if (codecInfo.codec === "opus") { const toc = parseOpusTocByte(data); durationInSamples = toc.durationInSamples; } } return { durationInSamples, vorbisBlockSize: currentBlocksize }; }; var buildOggMimeType = (info) => { let string = "audio/ogg"; if (info.codecStrings) { const uniqueCodecMimeTypes = [...new Set(info.codecStrings)]; string += `; codecs="${uniqueCodecMimeTypes.join(", ")}"`; } return string; }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/ogg/ogg-reader.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var MIN_PAGE_HEADER_SIZE = 27; var MAX_PAGE_HEADER_SIZE = 27 + 255; var MAX_PAGE_SIZE = MAX_PAGE_HEADER_SIZE + 255 * 255; var readPageHeader = (slice) => { const startPos = slice.filePos; const capturePattern = readU32Le(slice); if (capturePattern !== OGGS) { return null; } slice.skip(1); const headerType = readU8(slice); const granulePosition = readI64Le(slice); const serialNumber = readU32Le(slice); const sequenceNumber = readU32Le(slice); const checksum = readU32Le(slice); const numberPageSegments = readU8(slice); const lacingValues = new Uint8Array(numberPageSegments); for (let i = 0;i < numberPageSegments; i++) { lacingValues[i] = readU8(slice); } const headerSize = 27 + numberPageSegments; const dataSize = lacingValues.reduce((a, b) => a + b, 0); const totalSize = headerSize + dataSize; return { headerStartPos: startPos, totalSize, dataStartPos: startPos + headerSize, dataSize, headerType, granulePosition, serialNumber, sequenceNumber, checksum, lacingValues }; }; var findNextPageHeader = (slice, until) => { while (slice.filePos < until - (4 - 1)) { const word = readU32Le(slice); const firstByte = word & 255; const secondByte = word >>> 8 & 255; const thirdByte = word >>> 16 & 255; const fourthByte = word >>> 24 & 255; const O = 79; if (firstByte !== O && secondByte !== O && thirdByte !== O && fourthByte !== O) { continue; } slice.skip(-4); if (word === OGGS) { return true; } slice.skip(1); } return false; }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/ogg/ogg-demuxer.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ class OggDemuxer extends Demuxer { constructor(input2) { super(input2); this.metadataPromise = null; this.bitstreams = []; this.tracks = []; this.metadataTags = {}; this.reader = input2._reader; } async readMetadata() { return this.metadataPromise ??= (async () => { let currentPos = 0; while (true) { let slice = this.reader.requestSliceRange(currentPos, MIN_PAGE_HEADER_SIZE, MAX_PAGE_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) break; const page = readPageHeader(slice); if (!page) { break; } const isBos = !!(page.headerType & 2); if (!isBos) { break; } this.bitstreams.push({ serialNumber: page.serialNumber, bosPage: page, description: null, numberOfChannels: -1, sampleRate: -1, codecInfo: { codec: null, vorbisInfo: null, opusInfo: null }, lastMetadataPacket: null }); currentPos = page.headerStartPos + page.totalSize; } for (const bitstream of this.bitstreams) { const firstPacket = await this.readPacket(bitstream.bosPage, 0); if (!firstPacket) { continue; } if (firstPacket.data.byteLength >= 7 && firstPacket.data[0] === 1 && firstPacket.data[1] === 118 && firstPacket.data[2] === 111 && firstPacket.data[3] === 114 && firstPacket.data[4] === 98 && firstPacket.data[5] === 105 && firstPacket.data[6] === 115) { await this.readVorbisMetadata(firstPacket, bitstream); } else if (firstPacket.data.byteLength >= 8 && firstPacket.data[0] === 79 && firstPacket.data[1] === 112 && firstPacket.data[2] === 117 && firstPacket.data[3] === 115 && firstPacket.data[4] === 72 && firstPacket.data[5] === 101 && firstPacket.data[6] === 97 && firstPacket.data[7] === 100) { await this.readOpusMetadata(firstPacket, bitstream); } if (bitstream.codecInfo.codec !== null) { this.tracks.push(new InputAudioTrack(this.input, new OggAudioTrackBacking(bitstream, this))); } } })(); } async readVorbisMetadata(firstPacket, bitstream) { let nextPacketPosition = await this.findNextPacketStart(firstPacket); if (!nextPacketPosition) { return; } const secondPacket = await this.readPacket(nextPacketPosition.startPage, nextPacketPosition.startSegmentIndex); if (!secondPacket) { return; } nextPacketPosition = await this.findNextPacketStart(secondPacket); if (!nextPacketPosition) { return; } const thirdPacket = await this.readPacket(nextPacketPosition.startPage, nextPacketPosition.startSegmentIndex); if (!thirdPacket) { return; } if (secondPacket.data[0] !== 3 || thirdPacket.data[0] !== 5) { return; } const lacingValues = []; const addBytesToSegmentTable = (bytes) => { while (true) { lacingValues.push(Math.min(255, bytes)); if (bytes < 255) { break; } bytes -= 255; } }; addBytesToSegmentTable(firstPacket.data.length); addBytesToSegmentTable(secondPacket.data.length); const description = new Uint8Array(1 + lacingValues.length + firstPacket.data.length + secondPacket.data.length + thirdPacket.data.length); description[0] = 2; description.set(lacingValues, 1); description.set(firstPacket.data, 1 + lacingValues.length); description.set(secondPacket.data, 1 + lacingValues.length + firstPacket.data.length); description.set(thirdPacket.data, 1 + lacingValues.length + firstPacket.data.length + secondPacket.data.length); bitstream.codecInfo.codec = "vorbis"; bitstream.description = description; bitstream.lastMetadataPacket = thirdPacket; const view = toDataView(firstPacket.data); bitstream.numberOfChannels = view.getUint8(11); bitstream.sampleRate = view.getUint32(12, true); const blockSizeByte = view.getUint8(28); bitstream.codecInfo.vorbisInfo = { blocksizes: [ 1 << (blockSizeByte & 15), 1 << (blockSizeByte >> 4) ], modeBlockflags: parseModesFromVorbisSetupPacket(thirdPacket.data).modeBlockflags }; readVorbisComments(secondPacket.data.subarray(7), this.metadataTags); } async readOpusMetadata(firstPacket, bitstream) { const nextPacketPosition = await this.findNextPacketStart(firstPacket); if (!nextPacketPosition) { return; } const secondPacket = await this.readPacket(nextPacketPosition.startPage, nextPacketPosition.startSegmentIndex); if (!secondPacket) { return; } bitstream.codecInfo.codec = "opus"; bitstream.description = firstPacket.data; bitstream.lastMetadataPacket = secondPacket; const header2 = parseOpusIdentificationHeader(firstPacket.data); bitstream.numberOfChannels = header2.outputChannelCount; bitstream.sampleRate = OPUS_SAMPLE_RATE; bitstream.codecInfo.opusInfo = { preSkip: header2.preSkip }; readVorbisComments(secondPacket.data.subarray(8), this.metadataTags); } async readPacket(startPage, startSegmentIndex) { assert(startSegmentIndex < startPage.lacingValues.length); let startDataOffset = 0; for (let i = 0;i < startSegmentIndex; i++) { startDataOffset += startPage.lacingValues[i]; } let currentPage = startPage; let currentDataOffset = startDataOffset; let currentSegmentIndex = startSegmentIndex; const chunks = []; outer: while (true) { let pageSlice = this.reader.requestSlice(currentPage.dataStartPos, currentPage.dataSize); if (pageSlice instanceof Promise) pageSlice = await pageSlice; assert(pageSlice); const pageData = readBytes(pageSlice, currentPage.dataSize); while (true) { if (currentSegmentIndex === currentPage.lacingValues.length) { chunks.push(pageData.subarray(startDataOffset, currentDataOffset)); break; } const lacingValue = currentPage.lacingValues[currentSegmentIndex]; currentDataOffset += lacingValue; if (lacingValue < 255) { chunks.push(pageData.subarray(startDataOffset, currentDataOffset)); break outer; } currentSegmentIndex++; } let currentPos = currentPage.headerStartPos + currentPage.totalSize; while (true) { let headerSlice = this.reader.requestSliceRange(currentPos, MIN_PAGE_HEADER_SIZE, MAX_PAGE_HEADER_SIZE); if (headerSlice instanceof Promise) headerSlice = await headerSlice; if (!headerSlice) { return null; } const nextPage = readPageHeader(headerSlice); if (!nextPage) { return null; } currentPage = nextPage; if (currentPage.serialNumber === startPage.serialNumber) { break; } currentPos = currentPage.headerStartPos + currentPage.totalSize; } startDataOffset = 0; currentDataOffset = 0; currentSegmentIndex = 0; } const totalPacketSize = chunks.reduce((sum, chunk) => sum + chunk.length, 0); if (totalPacketSize === 0) { return null; } const packetData = new Uint8Array(totalPacketSize); let offset = 0; for (let i = 0;i < chunks.length; i++) { const chunk = chunks[i]; packetData.set(chunk, offset); offset += chunk.length; } return { data: packetData, endPage: currentPage, endSegmentIndex: currentSegmentIndex }; } async findNextPacketStart(lastPacket) { if (lastPacket.endSegmentIndex < lastPacket.endPage.lacingValues.length - 1) { return { startPage: lastPacket.endPage, startSegmentIndex: lastPacket.endSegmentIndex + 1 }; } const isEos = !!(lastPacket.endPage.headerType & 4); if (isEos) { return null; } let currentPos = lastPacket.endPage.headerStartPos + lastPacket.endPage.totalSize; while (true) { let slice = this.reader.requestSliceRange(currentPos, MIN_PAGE_HEADER_SIZE, MAX_PAGE_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) { return null; } const nextPage = readPageHeader(slice); if (!nextPage) { return null; } if (nextPage.serialNumber === lastPacket.endPage.serialNumber) { return { startPage: nextPage, startSegmentIndex: 0 }; } currentPos = nextPage.headerStartPos + nextPage.totalSize; } } async getMimeType() { await this.readMetadata(); const codecStrings = await Promise.all(this.tracks.map((x) => x.getCodecParameterString())); return buildOggMimeType({ codecStrings: codecStrings.filter(Boolean) }); } async getTracks() { await this.readMetadata(); return this.tracks; } async computeDuration() { const tracks = await this.getTracks(); const trackDurations = await Promise.all(tracks.map((x) => x.computeDuration())); return Math.max(0, ...trackDurations); } async getMetadataTags() { await this.readMetadata(); return this.metadataTags; } } class OggAudioTrackBacking { constructor(bitstream, demuxer) { this.bitstream = bitstream; this.demuxer = demuxer; this.encodedPacketToMetadata = new WeakMap; this.sequentialScanCache = []; this.sequentialScanMutex = new AsyncMutex; this.internalSampleRate = bitstream.codecInfo.codec === "opus" ? OPUS_SAMPLE_RATE : bitstream.sampleRate; } getId() { return this.bitstream.serialNumber; } getNumberOfChannels() { return this.bitstream.numberOfChannels; } getSampleRate() { return this.bitstream.sampleRate; } getTimeResolution() { return this.bitstream.sampleRate; } getCodec() { return this.bitstream.codecInfo.codec; } getInternalCodecId() { return null; } async getDecoderConfig() { assert(this.bitstream.codecInfo.codec); return { codec: this.bitstream.codecInfo.codec, numberOfChannels: this.bitstream.numberOfChannels, sampleRate: this.bitstream.sampleRate, description: this.bitstream.description ?? undefined }; } getName() { return null; } getLanguageCode() { return UNDETERMINED_LANGUAGE; } getDisposition() { return { ...DEFAULT_TRACK_DISPOSITION }; } async getFirstTimestamp() { return 0; } async computeDuration() { const lastPacket = await this.getPacket(Infinity, { metadataOnly: true }); return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0); } granulePositionToTimestampInSamples(granulePosition) { if (this.bitstream.codecInfo.codec === "opus") { assert(this.bitstream.codecInfo.opusInfo); return granulePosition - this.bitstream.codecInfo.opusInfo.preSkip; } return granulePosition; } createEncodedPacketFromOggPacket(packet, additional, options) { if (!packet) { return null; } const { durationInSamples, vorbisBlockSize } = extractSampleMetadata(packet.data, this.bitstream.codecInfo, additional.vorbisLastBlocksize); const encodedPacket = new EncodedPacket(options.metadataOnly ? PLACEHOLDER_DATA : packet.data, "key", Math.max(0, additional.timestampInSamples) / this.internalSampleRate, durationInSamples / this.internalSampleRate, packet.endPage.headerStartPos + packet.endSegmentIndex, packet.data.byteLength); this.encodedPacketToMetadata.set(encodedPacket, { packet, timestampInSamples: additional.timestampInSamples, durationInSamples, vorbisLastBlockSize: additional.vorbisLastBlocksize, vorbisBlockSize }); return encodedPacket; } async getFirstPacket(options) { assert(this.bitstream.lastMetadataPacket); const packetPosition = await this.demuxer.findNextPacketStart(this.bitstream.lastMetadataPacket); if (!packetPosition) { return null; } let timestampInSamples = 0; if (this.bitstream.codecInfo.codec === "opus") { assert(this.bitstream.codecInfo.opusInfo); timestampInSamples -= this.bitstream.codecInfo.opusInfo.preSkip; } const packet = await this.demuxer.readPacket(packetPosition.startPage, packetPosition.startSegmentIndex); return this.createEncodedPacketFromOggPacket(packet, { timestampInSamples, vorbisLastBlocksize: null }, options); } async getNextPacket(prevPacket, options) { const prevMetadata = this.encodedPacketToMetadata.get(prevPacket); if (!prevMetadata) { throw new Error("Packet was not created from this track."); } const packetPosition = await this.demuxer.findNextPacketStart(prevMetadata.packet); if (!packetPosition) { return null; } const timestampInSamples = prevMetadata.timestampInSamples + prevMetadata.durationInSamples; const packet = await this.demuxer.readPacket(packetPosition.startPage, packetPosition.startSegmentIndex); return this.createEncodedPacketFromOggPacket(packet, { timestampInSamples, vorbisLastBlocksize: prevMetadata.vorbisBlockSize }, options); } async getPacket(timestamp, options) { if (this.demuxer.reader.fileSize === null) { return this.getPacketSequential(timestamp, options); } const timestampInSamples = roundIfAlmostInteger(timestamp * this.internalSampleRate); if (timestampInSamples === 0) { return this.getFirstPacket(options); } if (timestampInSamples < 0) { return null; } assert(this.bitstream.lastMetadataPacket); const startPosition = await this.demuxer.findNextPacketStart(this.bitstream.lastMetadataPacket); if (!startPosition) { return null; } let lowPage = startPosition.startPage; let high = this.demuxer.reader.fileSize; const lowPages = [lowPage]; outer: while (lowPage.headerStartPos + lowPage.totalSize < high) { const low = lowPage.headerStartPos; const mid = Math.floor((low + high) / 2); let searchStartPos = mid; while (true) { const until = Math.min(searchStartPos + MAX_PAGE_SIZE, high - MIN_PAGE_HEADER_SIZE); let searchSlice = this.demuxer.reader.requestSlice(searchStartPos, until - searchStartPos); if (searchSlice instanceof Promise) searchSlice = await searchSlice; assert(searchSlice); const found = findNextPageHeader(searchSlice, until); if (!found) { high = mid + MIN_PAGE_HEADER_SIZE; continue outer; } let headerSlice = this.demuxer.reader.requestSliceRange(searchSlice.filePos, MIN_PAGE_HEADER_SIZE, MAX_PAGE_HEADER_SIZE); if (headerSlice instanceof Promise) headerSlice = await headerSlice; assert(headerSlice); const page = readPageHeader(headerSlice); assert(page); let pageValid = false; if (page.serialNumber === this.bitstream.serialNumber) { pageValid = true; } else { let pageSlice = this.demuxer.reader.requestSlice(page.headerStartPos, page.totalSize); if (pageSlice instanceof Promise) pageSlice = await pageSlice; assert(pageSlice); const bytes = readBytes(pageSlice, page.totalSize); const crc = computeOggPageCrc(bytes); pageValid = crc === page.checksum; } if (!pageValid) { searchStartPos = page.headerStartPos + 4; continue; } if (pageValid && page.serialNumber !== this.bitstream.serialNumber) { searchStartPos = page.headerStartPos + page.totalSize; continue; } const isContinuationPage = page.granulePosition === -1; if (isContinuationPage) { searchStartPos = page.headerStartPos + page.totalSize; continue; } if (this.granulePositionToTimestampInSamples(page.granulePosition) > timestampInSamples) { high = page.headerStartPos; } else { lowPage = page; lowPages.push(page); } continue outer; } } let lowerPage = startPosition.startPage; for (const otherLowPage of lowPages) { if (otherLowPage.granulePosition === lowPage.granulePosition) { break; } if (!lowerPage || otherLowPage.headerStartPos > lowerPage.headerStartPos) { lowerPage = otherLowPage; } } let currentPage = lowerPage; const previousPages = [currentPage]; while (true) { if (currentPage.serialNumber === this.bitstream.serialNumber && currentPage.granulePosition === lowPage.granulePosition) { break; } const nextPos = currentPage.headerStartPos + currentPage.totalSize; let slice = this.demuxer.reader.requestSliceRange(nextPos, MIN_PAGE_HEADER_SIZE, MAX_PAGE_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; assert(slice); const nextPage = readPageHeader(slice); assert(nextPage); currentPage = nextPage; if (currentPage.serialNumber === this.bitstream.serialNumber) { previousPages.push(currentPage); } } assert(currentPage.granulePosition !== -1); let currentSegmentIndex = null; let currentTimestampInSamples; let currentTimestampIsCorrect; let endPage = currentPage; let endSegmentIndex = 0; if (currentPage.headerStartPos === startPosition.startPage.headerStartPos) { currentTimestampInSamples = this.granulePositionToTimestampInSamples(0); currentTimestampIsCorrect = true; currentSegmentIndex = 0; } else { currentTimestampInSamples = 0; currentTimestampIsCorrect = false; for (let i = currentPage.lacingValues.length - 1;i >= 0; i--) { const value = currentPage.lacingValues[i]; if (value < 255) { currentSegmentIndex = i + 1; break; } } if (currentSegmentIndex === null) { throw new Error("Invalid page with granule position: no packets end on this page."); } endSegmentIndex = currentSegmentIndex - 1; const pseudopacket = { data: PLACEHOLDER_DATA, endPage, endSegmentIndex }; const nextPosition = await this.demuxer.findNextPacketStart(pseudopacket); if (nextPosition) { const endPosition = findPreviousPacketEndPosition(previousPages, currentPage, currentSegmentIndex); assert(endPosition); const startPosition2 = findPacketStartPosition(previousPages, endPosition.page, endPosition.segmentIndex); if (startPosition2) { currentPage = startPosition2.page; currentSegmentIndex = startPosition2.segmentIndex; } } else { while (true) { const endPosition = findPreviousPacketEndPosition(previousPages, currentPage, currentSegmentIndex); if (!endPosition) { break; } const startPosition2 = findPacketStartPosition(previousPages, endPosition.page, endPosition.segmentIndex); if (!startPosition2) { break; } currentPage = startPosition2.page; currentSegmentIndex = startPosition2.segmentIndex; if (endPosition.page.headerStartPos !== endPage.headerStartPos) { endPage = endPosition.page; endSegmentIndex = endPosition.segmentIndex; break; } } } } let lastEncodedPacket = null; let lastEncodedPacketMetadata = null; while (currentPage !== null) { assert(currentSegmentIndex !== null); const packet = await this.demuxer.readPacket(currentPage, currentSegmentIndex); if (!packet) { break; } const skipPacket = currentPage.headerStartPos === startPosition.startPage.headerStartPos && currentSegmentIndex < startPosition.startSegmentIndex; if (!skipPacket) { let encodedPacket = this.createEncodedPacketFromOggPacket(packet, { timestampInSamples: currentTimestampInSamples, vorbisLastBlocksize: lastEncodedPacketMetadata?.vorbisBlockSize ?? null }, options); assert(encodedPacket); let encodedPacketMetadata = this.encodedPacketToMetadata.get(encodedPacket); assert(encodedPacketMetadata); if (!currentTimestampIsCorrect && packet.endPage.headerStartPos === endPage.headerStartPos && packet.endSegmentIndex === endSegmentIndex) { currentTimestampInSamples = this.granulePositionToTimestampInSamples(currentPage.granulePosition); currentTimestampIsCorrect = true; encodedPacket = this.createEncodedPacketFromOggPacket(packet, { timestampInSamples: currentTimestampInSamples - encodedPacketMetadata.durationInSamples, vorbisLastBlocksize: lastEncodedPacketMetadata?.vorbisBlockSize ?? null }, options); assert(encodedPacket); encodedPacketMetadata = this.encodedPacketToMetadata.get(encodedPacket); assert(encodedPacketMetadata); } else { currentTimestampInSamples += encodedPacketMetadata.durationInSamples; } lastEncodedPacket = encodedPacket; lastEncodedPacketMetadata = encodedPacketMetadata; if (currentTimestampIsCorrect && (Math.max(currentTimestampInSamples, 0) > timestampInSamples || Math.max(encodedPacketMetadata.timestampInSamples, 0) === timestampInSamples)) { break; } } const nextPosition = await this.demuxer.findNextPacketStart(packet); if (!nextPosition) { break; } currentPage = nextPosition.startPage; currentSegmentIndex = nextPosition.startSegmentIndex; } return lastEncodedPacket; } async getPacketSequential(timestamp, options) { const release = await this.sequentialScanMutex.acquire(); try { const timestampInSamples = roundIfAlmostInteger(timestamp * this.internalSampleRate); timestamp = timestampInSamples / this.internalSampleRate; const index = binarySearchLessOrEqual(this.sequentialScanCache, timestampInSamples, (x) => x.timestampInSamples); let currentPacket; if (index !== -1) { const cacheEntry = this.sequentialScanCache[index]; currentPacket = this.createEncodedPacketFromOggPacket(cacheEntry.packet, { timestampInSamples: cacheEntry.timestampInSamples, vorbisLastBlocksize: cacheEntry.vorbisLastBlockSize }, options); } else { currentPacket = await this.getFirstPacket(options); } let i = 0; while (currentPacket && currentPacket.timestamp < timestamp) { const nextPacket = await this.getNextPacket(currentPacket, options); if (!nextPacket || nextPacket.timestamp > timestamp) { break; } currentPacket = nextPacket; i++; if (i === 100) { i = 0; const metadata = this.encodedPacketToMetadata.get(currentPacket); assert(metadata); if (this.sequentialScanCache.length > 0) { assert(last(this.sequentialScanCache).timestampInSamples <= metadata.timestampInSamples); } this.sequentialScanCache.push(metadata); } } return currentPacket; } finally { release(); } } getKeyPacket(timestamp, options) { return this.getPacket(timestamp, options); } getNextKeyPacket(packet, options) { return this.getNextPacket(packet, options); } } var findPacketStartPosition = (pageList, endPage, endSegmentIndex) => { let page = endPage; let segmentIndex = endSegmentIndex; outer: while (true) { segmentIndex--; for (segmentIndex;segmentIndex >= 0; segmentIndex--) { const lacingValue = page.lacingValues[segmentIndex]; if (lacingValue < 255) { segmentIndex++; break outer; } } assert(segmentIndex === -1); const pageStartsWithFreshPacket = !(page.headerType & 1); if (pageStartsWithFreshPacket) { segmentIndex = 0; break; } const previousPage = findLast(pageList, (x) => x.headerStartPos < page.headerStartPos); if (!previousPage) { return null; } page = previousPage; segmentIndex = page.lacingValues.length; } assert(segmentIndex !== -1); if (segmentIndex === page.lacingValues.length) { const nextPage = pageList[pageList.indexOf(page) + 1]; assert(nextPage); page = nextPage; segmentIndex = 0; } return { page, segmentIndex }; }; var findPreviousPacketEndPosition = (pageList, startPage, startSegmentIndex) => { if (startSegmentIndex > 0) { return { page: startPage, segmentIndex: startSegmentIndex - 1 }; } const previousPage = findLast(pageList, (x) => x.headerStartPos < startPage.headerStartPos); if (!previousPage) { return null; } return { page: previousPage, segmentIndex: previousPage.lacingValues.length - 1 }; }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/wave/wave-demuxer.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var WaveFormat; (function(WaveFormat2) { WaveFormat2[WaveFormat2["PCM"] = 1] = "PCM"; WaveFormat2[WaveFormat2["IEEE_FLOAT"] = 3] = "IEEE_FLOAT"; WaveFormat2[WaveFormat2["ALAW"] = 6] = "ALAW"; WaveFormat2[WaveFormat2["MULAW"] = 7] = "MULAW"; WaveFormat2[WaveFormat2["EXTENSIBLE"] = 65534] = "EXTENSIBLE"; })(WaveFormat || (WaveFormat = {})); class WaveDemuxer extends Demuxer { constructor(input2) { super(input2); this.metadataPromise = null; this.dataStart = -1; this.dataSize = -1; this.audioInfo = null; this.tracks = []; this.lastKnownPacketIndex = 0; this.metadataTags = {}; this.reader = input2._reader; } async readMetadata() { return this.metadataPromise ??= (async () => { let slice = this.reader.requestSlice(0, 12); if (slice instanceof Promise) slice = await slice; assert(slice); const riffType = readAscii(slice, 4); const littleEndian = riffType !== "RIFX"; const isRf64 = riffType === "RF64"; const outerChunkSize = readU32(slice, littleEndian); let totalFileSize = isRf64 ? this.reader.fileSize : Math.min(outerChunkSize + 8, this.reader.fileSize ?? Infinity); const format = readAscii(slice, 4); if (format !== "WAVE") { throw new Error("Invalid WAVE file - wrong format"); } let chunksRead = 0; let dataChunkSize = null; let currentPos = slice.filePos; while (totalFileSize === null || currentPos < totalFileSize) { let slice2 = this.reader.requestSlice(currentPos, 8); if (slice2 instanceof Promise) slice2 = await slice2; if (!slice2) break; const chunkId = readAscii(slice2, 4); const chunkSize = readU32(slice2, littleEndian); const startPos = slice2.filePos; if (isRf64 && chunksRead === 0 && chunkId !== "ds64") { throw new Error('Invalid RF64 file: First chunk must be "ds64".'); } if (chunkId === "fmt ") { await this.parseFmtChunk(startPos, chunkSize, littleEndian); } else if (chunkId === "data") { dataChunkSize ??= chunkSize; this.dataStart = slice2.filePos; this.dataSize = Math.min(dataChunkSize, (totalFileSize ?? Infinity) - this.dataStart); if (this.reader.fileSize === null) { break; } } else if (chunkId === "ds64") { let ds64Slice = this.reader.requestSlice(startPos, chunkSize); if (ds64Slice instanceof Promise) ds64Slice = await ds64Slice; if (!ds64Slice) break; const riffChunkSize = readU64(ds64Slice, littleEndian); dataChunkSize = readU64(ds64Slice, littleEndian); totalFileSize = Math.min(riffChunkSize + 8, this.reader.fileSize ?? Infinity); } else if (chunkId === "LIST") { await this.parseListChunk(startPos, chunkSize, littleEndian); } else if (chunkId === "ID3 " || chunkId === "id3 ") { await this.parseId3Chunk(startPos, chunkSize); } currentPos = startPos + chunkSize + (chunkSize & 1); chunksRead++; } if (!this.audioInfo) { throw new Error('Invalid WAVE file - missing "fmt " chunk'); } if (this.dataStart === -1) { throw new Error('Invalid WAVE file - missing "data" chunk'); } const blockSize = this.audioInfo.blockSizeInBytes; this.dataSize = Math.floor(this.dataSize / blockSize) * blockSize; this.tracks.push(new InputAudioTrack(this.input, new WaveAudioTrackBacking(this))); })(); } async parseFmtChunk(startPos, size4, littleEndian) { let slice = this.reader.requestSlice(startPos, size4); if (slice instanceof Promise) slice = await slice; if (!slice) return; let formatTag = readU16(slice, littleEndian); const numChannels = readU16(slice, littleEndian); const sampleRate = readU32(slice, littleEndian); slice.skip(4); const blockAlign = readU16(slice, littleEndian); let bitsPerSample; if (size4 === 14) { bitsPerSample = 8; } else { bitsPerSample = readU16(slice, littleEndian); } if (size4 >= 18 && formatTag !== 357) { const cbSize = readU16(slice, littleEndian); const remainingSize = size4 - 18; const extensionSize = Math.min(remainingSize, cbSize); if (extensionSize >= 22 && formatTag === WaveFormat.EXTENSIBLE) { slice.skip(2 + 4); const subFormat = readBytes(slice, 16); formatTag = subFormat[0] | subFormat[1] << 8; } } if (formatTag === WaveFormat.MULAW || formatTag === WaveFormat.ALAW) { bitsPerSample = 8; } this.audioInfo = { format: formatTag, numberOfChannels: numChannels, sampleRate, sampleSizeInBytes: Math.ceil(bitsPerSample / 8), blockSizeInBytes: blockAlign }; } async parseListChunk(startPos, size4, littleEndian) { let slice = this.reader.requestSlice(startPos, size4); if (slice instanceof Promise) slice = await slice; if (!slice) return; const infoType = readAscii(slice, 4); if (infoType !== "INFO" && infoType !== "INF0") { return; } let currentPos = slice.filePos; while (currentPos <= startPos + size4 - 8) { slice.filePos = currentPos; const chunkName = readAscii(slice, 4); const chunkSize = readU32(slice, littleEndian); const bytes = readBytes(slice, chunkSize); let stringLength = 0; for (let i = 0;i < bytes.length; i++) { if (bytes[i] === 0) { break; } stringLength++; } const value = String.fromCharCode(...bytes.subarray(0, stringLength)); this.metadataTags.raw ??= {}; this.metadataTags.raw[chunkName] = value; switch (chunkName) { case "INAM": case "TITL": { this.metadataTags.title ??= value; } ; break; case "TIT3": { this.metadataTags.description ??= value; } ; break; case "IART": { this.metadataTags.artist ??= value; } ; break; case "IPRD": { this.metadataTags.album ??= value; } ; break; case "IPRT": case "ITRK": case "TRCK": { const parts = value.split("/"); const trackNum = Number.parseInt(parts[0], 10); const tracksTotal = parts[1] && Number.parseInt(parts[1], 10); if (Number.isInteger(trackNum) && trackNum > 0) { this.metadataTags.trackNumber ??= trackNum; } if (tracksTotal && Number.isInteger(tracksTotal) && tracksTotal > 0) { this.metadataTags.tracksTotal ??= tracksTotal; } } ; break; case "ICRD": case "IDIT": { const date = new Date(value); if (!Number.isNaN(date.getTime())) { this.metadataTags.date ??= date; } } ; break; case "YEAR": { const year = Number.parseInt(value, 10); if (Number.isInteger(year) && year > 0) { this.metadataTags.date ??= new Date(year, 0, 1); } } ; break; case "IGNR": case "GENR": { this.metadataTags.genre ??= value; } ; break; case "ICMT": case "CMNT": case "COMM": { this.metadataTags.comment ??= value; } ; break; } currentPos += 8 + chunkSize + (chunkSize & 1); } } async parseId3Chunk(startPos, size4) { let slice = this.reader.requestSlice(startPos, size4); if (slice instanceof Promise) slice = await slice; if (!slice) return; const id3V2Header = readId3V2Header(slice); if (id3V2Header) { const contentSlice = slice.slice(startPos + 10, id3V2Header.size); parseId3V2Tag(contentSlice, id3V2Header, this.metadataTags); } } getCodec() { assert(this.audioInfo); if (this.audioInfo.format === WaveFormat.MULAW) { return "ulaw"; } if (this.audioInfo.format === WaveFormat.ALAW) { return "alaw"; } if (this.audioInfo.format === WaveFormat.PCM) { if (this.audioInfo.sampleSizeInBytes === 1) { return "pcm-u8"; } else if (this.audioInfo.sampleSizeInBytes === 2) { return "pcm-s16"; } else if (this.audioInfo.sampleSizeInBytes === 3) { return "pcm-s24"; } else if (this.audioInfo.sampleSizeInBytes === 4) { return "pcm-s32"; } } if (this.audioInfo.format === WaveFormat.IEEE_FLOAT) { if (this.audioInfo.sampleSizeInBytes === 4) { return "pcm-f32"; } } return null; } async getMimeType() { return "audio/wav"; } async computeDuration() { await this.readMetadata(); const track = this.tracks[0]; assert(track); return track.computeDuration(); } async getTracks() { await this.readMetadata(); return this.tracks; } async getMetadataTags() { await this.readMetadata(); return this.metadataTags; } } var PACKET_SIZE_IN_FRAMES = 2048; class WaveAudioTrackBacking { constructor(demuxer) { this.demuxer = demuxer; } getId() { return 1; } getCodec() { return this.demuxer.getCodec(); } getInternalCodecId() { assert(this.demuxer.audioInfo); return this.demuxer.audioInfo.format; } async getDecoderConfig() { const codec = this.demuxer.getCodec(); if (!codec) { return null; } assert(this.demuxer.audioInfo); return { codec, numberOfChannels: this.demuxer.audioInfo.numberOfChannels, sampleRate: this.demuxer.audioInfo.sampleRate }; } async computeDuration() { const lastPacket = await this.getPacket(Infinity, { metadataOnly: true }); return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0); } getNumberOfChannels() { assert(this.demuxer.audioInfo); return this.demuxer.audioInfo.numberOfChannels; } getSampleRate() { assert(this.demuxer.audioInfo); return this.demuxer.audioInfo.sampleRate; } getTimeResolution() { assert(this.demuxer.audioInfo); return this.demuxer.audioInfo.sampleRate; } getName() { return null; } getLanguageCode() { return UNDETERMINED_LANGUAGE; } getDisposition() { return { ...DEFAULT_TRACK_DISPOSITION }; } async getFirstTimestamp() { return 0; } async getPacketAtIndex(packetIndex, options) { assert(this.demuxer.audioInfo); const startOffset = packetIndex * PACKET_SIZE_IN_FRAMES * this.demuxer.audioInfo.blockSizeInBytes; if (startOffset >= this.demuxer.dataSize) { return null; } const sizeInBytes = Math.min(PACKET_SIZE_IN_FRAMES * this.demuxer.audioInfo.blockSizeInBytes, this.demuxer.dataSize - startOffset); if (this.demuxer.reader.fileSize === null) { let slice = this.demuxer.reader.requestSlice(this.demuxer.dataStart + startOffset, sizeInBytes); if (slice instanceof Promise) slice = await slice; if (!slice) { return null; } } let data; if (options.metadataOnly) { data = PLACEHOLDER_DATA; } else { let slice = this.demuxer.reader.requestSlice(this.demuxer.dataStart + startOffset, sizeInBytes); if (slice instanceof Promise) slice = await slice; assert(slice); data = readBytes(slice, sizeInBytes); } const timestamp = packetIndex * PACKET_SIZE_IN_FRAMES / this.demuxer.audioInfo.sampleRate; const duration = sizeInBytes / this.demuxer.audioInfo.blockSizeInBytes / this.demuxer.audioInfo.sampleRate; this.demuxer.lastKnownPacketIndex = Math.max(packetIndex, timestamp); return new EncodedPacket(data, "key", timestamp, duration, packetIndex, sizeInBytes); } getFirstPacket(options) { return this.getPacketAtIndex(0, options); } async getPacket(timestamp, options) { assert(this.demuxer.audioInfo); const packetIndex = Math.floor(Math.min(timestamp * this.demuxer.audioInfo.sampleRate / PACKET_SIZE_IN_FRAMES, (this.demuxer.dataSize - 1) / (PACKET_SIZE_IN_FRAMES * this.demuxer.audioInfo.blockSizeInBytes))); const packet = await this.getPacketAtIndex(packetIndex, options); if (packet) { return packet; } if (packetIndex === 0) { return null; } assert(this.demuxer.reader.fileSize === null); let currentPacket = await this.getPacketAtIndex(this.demuxer.lastKnownPacketIndex, options); while (currentPacket) { const nextPacket = await this.getNextPacket(currentPacket, options); if (!nextPacket) { break; } currentPacket = nextPacket; } return currentPacket; } getNextPacket(packet, options) { assert(this.demuxer.audioInfo); const packetIndex = Math.round(packet.timestamp * this.demuxer.audioInfo.sampleRate / PACKET_SIZE_IN_FRAMES); return this.getPacketAtIndex(packetIndex + 1, options); } getKeyPacket(timestamp, options) { return this.getPacket(timestamp, options); } getNextKeyPacket(packet, options) { return this.getNextPacket(packet, options); } } // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/adts/adts-reader.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var MIN_ADTS_FRAME_HEADER_SIZE = 7; var MAX_ADTS_FRAME_HEADER_SIZE = 9; var readAdtsFrameHeader = (slice) => { const startPos = slice.filePos; const bytes = readBytes(slice, 9); const bitstream = new Bitstream(bytes); const syncword = bitstream.readBits(12); if (syncword !== 4095) { return null; } bitstream.skipBits(1); const layer = bitstream.readBits(2); if (layer !== 0) { return null; } const protectionAbsence = bitstream.readBits(1); const objectType = bitstream.readBits(2) + 1; const samplingFrequencyIndex = bitstream.readBits(4); if (samplingFrequencyIndex === 15) { return null; } bitstream.skipBits(1); const channelConfiguration = bitstream.readBits(3); if (channelConfiguration === 0) { throw new Error("ADTS frames with channel configuration 0 are not supported."); } bitstream.skipBits(1); bitstream.skipBits(1); bitstream.skipBits(1); bitstream.skipBits(1); const frameLength = bitstream.readBits(13); bitstream.skipBits(11); const numberOfAacFrames = bitstream.readBits(2) + 1; if (numberOfAacFrames !== 1) { throw new Error("ADTS frames with more than one AAC frame are not supported."); } let crcCheck = null; if (protectionAbsence === 1) { slice.filePos -= 2; } else { crcCheck = bitstream.readBits(16); } return { objectType, samplingFrequencyIndex, channelConfiguration, frameLength, numberOfAacFrames, crcCheck, startPos }; }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/adts/adts-demuxer.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var SAMPLES_PER_AAC_FRAME = 1024; class AdtsDemuxer extends Demuxer { constructor(input2) { super(input2); this.metadataPromise = null; this.firstFrameHeader = null; this.loadedSamples = []; this.tracks = []; this.readingMutex = new AsyncMutex; this.lastSampleLoaded = false; this.lastLoadedPos = 0; this.nextTimestampInSamples = 0; this.reader = input2._reader; } async readMetadata() { return this.metadataPromise ??= (async () => { while (!this.firstFrameHeader && !this.lastSampleLoaded) { await this.advanceReader(); } assert(this.firstFrameHeader); this.tracks = [new InputAudioTrack(this.input, new AdtsAudioTrackBacking(this))]; })(); } async advanceReader() { let slice = this.reader.requestSliceRange(this.lastLoadedPos, MIN_ADTS_FRAME_HEADER_SIZE, MAX_ADTS_FRAME_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) { this.lastSampleLoaded = true; return; } const header2 = readAdtsFrameHeader(slice); if (!header2) { this.lastSampleLoaded = true; return; } if (this.reader.fileSize !== null && header2.startPos + header2.frameLength > this.reader.fileSize) { this.lastSampleLoaded = true; return; } if (!this.firstFrameHeader) { this.firstFrameHeader = header2; } const sampleRate = aacFrequencyTable[header2.samplingFrequencyIndex]; assert(sampleRate !== undefined); const sampleDuration = SAMPLES_PER_AAC_FRAME / sampleRate; const sample = { timestamp: this.nextTimestampInSamples / sampleRate, duration: sampleDuration, dataStart: header2.startPos, dataSize: header2.frameLength }; this.loadedSamples.push(sample); this.nextTimestampInSamples += SAMPLES_PER_AAC_FRAME; this.lastLoadedPos = header2.startPos + header2.frameLength; } async getMimeType() { return "audio/aac"; } async getTracks() { await this.readMetadata(); return this.tracks; } async computeDuration() { await this.readMetadata(); const track = this.tracks[0]; assert(track); return track.computeDuration(); } async getMetadataTags() { return {}; } } class AdtsAudioTrackBacking { constructor(demuxer) { this.demuxer = demuxer; } getId() { return 1; } async getFirstTimestamp() { return 0; } getTimeResolution() { const sampleRate = this.getSampleRate(); return sampleRate / SAMPLES_PER_AAC_FRAME; } async computeDuration() { const lastPacket = await this.getPacket(Infinity, { metadataOnly: true }); return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0); } getName() { return null; } getLanguageCode() { return UNDETERMINED_LANGUAGE; } getCodec() { return "aac"; } getInternalCodecId() { assert(this.demuxer.firstFrameHeader); return this.demuxer.firstFrameHeader.objectType; } getNumberOfChannels() { assert(this.demuxer.firstFrameHeader); const numberOfChannels = aacChannelMap[this.demuxer.firstFrameHeader.channelConfiguration]; assert(numberOfChannels !== undefined); return numberOfChannels; } getSampleRate() { assert(this.demuxer.firstFrameHeader); const sampleRate = aacFrequencyTable[this.demuxer.firstFrameHeader.samplingFrequencyIndex]; assert(sampleRate !== undefined); return sampleRate; } getDisposition() { return { ...DEFAULT_TRACK_DISPOSITION }; } async getDecoderConfig() { assert(this.demuxer.firstFrameHeader); return { codec: `mp4a.40.${this.demuxer.firstFrameHeader.objectType}`, numberOfChannels: this.getNumberOfChannels(), sampleRate: this.getSampleRate() }; } async getPacketAtIndex(sampleIndex, options) { if (sampleIndex === -1) { return null; } const rawSample = this.demuxer.loadedSamples[sampleIndex]; if (!rawSample) { return null; } let data; if (options.metadataOnly) { data = PLACEHOLDER_DATA; } else { let slice = this.demuxer.reader.requestSlice(rawSample.dataStart, rawSample.dataSize); if (slice instanceof Promise) slice = await slice; if (!slice) { return null; } data = readBytes(slice, rawSample.dataSize); } return new EncodedPacket(data, "key", rawSample.timestamp, rawSample.duration, sampleIndex, rawSample.dataSize); } getFirstPacket(options) { return this.getPacketAtIndex(0, options); } async getNextPacket(packet, options) { const release = await this.demuxer.readingMutex.acquire(); try { const sampleIndex = binarySearchExact(this.demuxer.loadedSamples, packet.timestamp, (x) => x.timestamp); if (sampleIndex === -1) { throw new Error("Packet was not created from this track."); } const nextIndex = sampleIndex + 1; while (nextIndex >= this.demuxer.loadedSamples.length && !this.demuxer.lastSampleLoaded) { await this.demuxer.advanceReader(); } return this.getPacketAtIndex(nextIndex, options); } finally { release(); } } async getPacket(timestamp, options) { const release = await this.demuxer.readingMutex.acquire(); try { while (true) { const index = binarySearchLessOrEqual(this.demuxer.loadedSamples, timestamp, (x) => x.timestamp); if (index === -1 && this.demuxer.loadedSamples.length > 0) { return null; } if (this.demuxer.lastSampleLoaded) { return this.getPacketAtIndex(index, options); } if (index >= 0 && index + 1 < this.demuxer.loadedSamples.length) { return this.getPacketAtIndex(index, options); } await this.demuxer.advanceReader(); } } finally { release(); } } getKeyPacket(timestamp, options) { return this.getPacket(timestamp, options); } getNextKeyPacket(packet, options) { return this.getNextPacket(packet, options); } } // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/flac/flac-misc.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var getBlockSizeOrUncommon = (bits) => { if (bits === 0) { return null; } else if (bits === 1) { return 192; } else if (bits >= 2 && bits <= 5) { return 144 * 2 ** bits; } else if (bits === 6) { return "uncommon-u8"; } else if (bits === 7) { return "uncommon-u16"; } else if (bits >= 8 && bits <= 15) { return 2 ** bits; } else { return null; } }; var getSampleRateOrUncommon = (sampleRateBits, streamInfoSampleRate) => { switch (sampleRateBits) { case 0: return streamInfoSampleRate; case 1: return 88200; case 2: return 176400; case 3: return 192000; case 4: return 8000; case 5: return 16000; case 6: return 22050; case 7: return 24000; case 8: return 32000; case 9: return 44100; case 10: return 48000; case 11: return 96000; case 12: return "uncommon-u8"; case 13: return "uncommon-u16"; case 14: return "uncommon-u16-10"; default: return null; } }; var readCodedNumber = (fileSlice) => { let ones = 0; const bitstream1 = new Bitstream(readBytes(fileSlice, 1)); while (bitstream1.readBits(1) === 1) { ones++; } if (ones === 0) { return bitstream1.readBits(7); } const bitArray = []; const extraBytes = ones - 1; const bitstream2 = new Bitstream(readBytes(fileSlice, extraBytes)); const firstByteBits = 8 - ones - 1; for (let i = 0;i < firstByteBits; i++) { bitArray.unshift(bitstream1.readBits(1)); } for (let i = 0;i < extraBytes; i++) { for (let j = 0;j < 8; j++) { const val = bitstream2.readBits(1); if (j < 2) { continue; } bitArray.unshift(val); } } const encoded = bitArray.reduce((acc, bit, index) => { return acc | bit << index; }, 0); return encoded; }; var readBlockSize = (slice, blockSizeBits) => { if (blockSizeBits === "uncommon-u16") { return readU16Be(slice) + 1; } else if (blockSizeBits === "uncommon-u8") { return readU8(slice) + 1; } else if (typeof blockSizeBits === "number") { return blockSizeBits; } else { assertNever(blockSizeBits); assert(false); } }; var readSampleRate = (slice, sampleRateOrUncommon) => { if (sampleRateOrUncommon === "uncommon-u16") { return readU16Be(slice); } if (sampleRateOrUncommon === "uncommon-u16-10") { return readU16Be(slice) * 10; } if (sampleRateOrUncommon === "uncommon-u8") { return readU8(slice); } if (typeof sampleRateOrUncommon === "number") { return sampleRateOrUncommon; } return null; }; var calculateCrc8 = (data) => { const polynomial = 7; let crc = 0; for (const byte of data) { crc ^= byte; for (let i = 0;i < 8; i++) { if ((crc & 128) !== 0) { crc = crc << 1 ^ polynomial; } else { crc <<= 1; } crc &= 255; } } return crc; }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/flac/flac-demuxer.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ class FlacDemuxer extends Demuxer { constructor(input2) { super(input2); this.loadedSamples = []; this.metadataPromise = null; this.track = null; this.metadataTags = {}; this.audioInfo = null; this.lastLoadedPos = null; this.blockingBit = null; this.readingMutex = new AsyncMutex; this.lastSampleLoaded = false; this.reader = input2._reader; } async computeDuration() { await this.readMetadata(); assert(this.track); return this.track.computeDuration(); } async getMetadataTags() { await this.readMetadata(); return this.metadataTags; } async getTracks() { await this.readMetadata(); assert(this.track); return [this.track]; } async getMimeType() { return "audio/flac"; } async readMetadata() { let currentPos = 4; return this.metadataPromise ??= (async () => { while (this.reader.fileSize === null || currentPos < this.reader.fileSize) { let sizeSlice = this.reader.requestSlice(currentPos, 4); if (sizeSlice instanceof Promise) sizeSlice = await sizeSlice; currentPos += 4; if (sizeSlice === null) { throw new Error(`Metadata block at position ${currentPos} is too small! Corrupted file.`); } assert(sizeSlice); const byte = readU8(sizeSlice); const size4 = readU24Be(sizeSlice); const isLastMetadata = (byte & 128) !== 0; const metaBlockType = byte & 127; switch (metaBlockType) { case FlacBlockType.STREAMINFO: { let streamInfoBlock = this.reader.requestSlice(currentPos, size4); if (streamInfoBlock instanceof Promise) streamInfoBlock = await streamInfoBlock; assert(streamInfoBlock); if (streamInfoBlock === null) { throw new Error(`StreamInfo block at position ${currentPos} is too small! Corrupted file.`); } const streamInfoBytes = readBytes(streamInfoBlock, 34); const bitstream = new Bitstream(streamInfoBytes); const minimumBlockSize = bitstream.readBits(16); const maximumBlockSize = bitstream.readBits(16); const minimumFrameSize = bitstream.readBits(24); const maximumFrameSize = bitstream.readBits(24); const sampleRate = bitstream.readBits(20); const numberOfChannels = bitstream.readBits(3) + 1; bitstream.readBits(5); const totalSamples = bitstream.readBits(36); bitstream.skipBits(16 * 8); const description = new Uint8Array(42); description.set(new Uint8Array([102, 76, 97, 67]), 0); description.set(new Uint8Array([128, 0, 0, 34]), 4); description.set(streamInfoBytes, 8); this.audioInfo = { numberOfChannels, sampleRate, totalSamples, minimumBlockSize, maximumBlockSize, minimumFrameSize, maximumFrameSize, description }; this.track = new InputAudioTrack(this.input, new FlacAudioTrackBacking(this)); break; } case FlacBlockType.VORBIS_COMMENT: { let vorbisCommentBlock = this.reader.requestSlice(currentPos, size4); if (vorbisCommentBlock instanceof Promise) vorbisCommentBlock = await vorbisCommentBlock; assert(vorbisCommentBlock); readVorbisComments(readBytes(vorbisCommentBlock, size4), this.metadataTags); break; } case FlacBlockType.PICTURE: { let pictureBlock = this.reader.requestSlice(currentPos, size4); if (pictureBlock instanceof Promise) pictureBlock = await pictureBlock; assert(pictureBlock); const pictureType = readU32Be(pictureBlock); const mediaTypeLength = readU32Be(pictureBlock); const mediaType = textDecoder.decode(readBytes(pictureBlock, mediaTypeLength)); const descriptionLength = readU32Be(pictureBlock); const description = textDecoder.decode(readBytes(pictureBlock, descriptionLength)); pictureBlock.skip(4 + 4 + 4 + 4); const dataLength = readU32Be(pictureBlock); const data = readBytes(pictureBlock, dataLength); this.metadataTags.images ??= []; this.metadataTags.images.push({ data, mimeType: mediaType, kind: pictureType === 3 ? "coverFront" : pictureType === 4 ? "coverBack" : "unknown", description }); break; } default: break; } currentPos += size4; if (isLastMetadata) { this.lastLoadedPos = currentPos; break; } } })(); } async readNextFlacFrame({ startPos, isFirstPacket }) { assert(this.audioInfo); const minimumHeaderLength = 6; const maximumHeaderSize = 16; const maximumSliceLength = this.audioInfo.maximumFrameSize + maximumHeaderSize; const slice = await this.reader.requestSliceRange(startPos, this.audioInfo.minimumFrameSize, maximumSliceLength); if (!slice) { return null; } const frameHeader = this.readFlacFrameHeader({ slice, isFirstPacket }); if (!frameHeader) { return null; } slice.filePos = startPos + this.audioInfo.minimumFrameSize; while (true) { if (slice.filePos > slice.end - minimumHeaderLength) { return { num: frameHeader.num, blockSize: frameHeader.blockSize, sampleRate: frameHeader.sampleRate, size: slice.end - startPos, isLastFrame: true }; } const nextByte = readU8(slice); if (nextByte === 255) { const positionBeforeReading = slice.filePos; const byteAfterNextByte = readU8(slice); const expected = this.blockingBit === 1 ? 249 : 248; if (byteAfterNextByte !== expected) { slice.filePos = positionBeforeReading; continue; } slice.skip(-2); const lengthIfNextFlacFrameHeaderIsLegit = slice.filePos - startPos; const nextFrameHeader = this.readFlacFrameHeader({ slice, isFirstPacket: false }); if (!nextFrameHeader) { slice.filePos = positionBeforeReading; continue; } if (this.blockingBit === 0) { if (nextFrameHeader.num - frameHeader.num !== 1) { slice.filePos = positionBeforeReading; continue; } } else { if (nextFrameHeader.num - frameHeader.num !== frameHeader.blockSize) { slice.filePos = positionBeforeReading; continue; } } return { num: frameHeader.num, blockSize: frameHeader.blockSize, sampleRate: frameHeader.sampleRate, size: lengthIfNextFlacFrameHeaderIsLegit, isLastFrame: false }; } } } readFlacFrameHeader({ slice, isFirstPacket }) { const startOffset = slice.filePos; const bytes = readBytes(slice, 4); const bitstream = new Bitstream(bytes); const bits = bitstream.readBits(15); if (bits !== 32764) { return null; } if (this.blockingBit === null) { assert(isFirstPacket); const newBlockingBit = bitstream.readBits(1); this.blockingBit = newBlockingBit; } else if (this.blockingBit === 1) { assert(!isFirstPacket); const newBlockingBit = bitstream.readBits(1); if (newBlockingBit !== 1) { return null; } } else if (this.blockingBit === 0) { assert(!isFirstPacket); const newBlockingBit = bitstream.readBits(1); if (newBlockingBit !== 0) { return null; } } else { throw new Error("Invalid blocking bit"); } const blockSizeOrUncommon = getBlockSizeOrUncommon(bitstream.readBits(4)); if (!blockSizeOrUncommon) { return null; } assert(this.audioInfo); const sampleRateOrUncommon = getSampleRateOrUncommon(bitstream.readBits(4), this.audioInfo.sampleRate); if (!sampleRateOrUncommon) { return null; } bitstream.readBits(4); bitstream.readBits(3); const reservedZero = bitstream.readBits(1); if (reservedZero !== 0) { return null; } const num = readCodedNumber(slice); const blockSize = readBlockSize(slice, blockSizeOrUncommon); const sampleRate = readSampleRate(slice, sampleRateOrUncommon); if (sampleRate === null) { return null; } if (sampleRate !== this.audioInfo.sampleRate) { return null; } const size4 = slice.filePos - startOffset; const crc = readU8(slice); slice.skip(-size4); slice.skip(-1); const crcCalculated = calculateCrc8(readBytes(slice, size4)); if (crc !== crcCalculated) { return null; } return { num, blockSize, sampleRate }; } async advanceReader() { await this.readMetadata(); assert(this.lastLoadedPos !== null); assert(this.audioInfo); const startPos = this.lastLoadedPos; const frame2 = await this.readNextFlacFrame({ startPos, isFirstPacket: this.loadedSamples.length === 0 }); if (!frame2) { this.lastSampleLoaded = true; return; } const lastSample = this.loadedSamples[this.loadedSamples.length - 1]; const blockOffset = lastSample ? lastSample.blockOffset + lastSample.blockSize : 0; const sample = { blockOffset, blockSize: frame2.blockSize, byteOffset: startPos, byteSize: frame2.size }; this.lastLoadedPos = this.lastLoadedPos + frame2.size; this.loadedSamples.push(sample); if (frame2.isLastFrame) { this.lastSampleLoaded = true; return; } } } class FlacAudioTrackBacking { constructor(demuxer) { this.demuxer = demuxer; } getId() { return 1; } getCodec() { return "flac"; } getInternalCodecId() { return null; } getNumberOfChannels() { assert(this.demuxer.audioInfo); return this.demuxer.audioInfo.numberOfChannels; } async computeDuration() { const lastPacket = await this.getPacket(Infinity, { metadataOnly: true }); return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0); } getSampleRate() { assert(this.demuxer.audioInfo); return this.demuxer.audioInfo.sampleRate; } getName() { return null; } getLanguageCode() { return UNDETERMINED_LANGUAGE; } getTimeResolution() { assert(this.demuxer.audioInfo); return this.demuxer.audioInfo.sampleRate; } getDisposition() { return { ...DEFAULT_TRACK_DISPOSITION }; } async getFirstTimestamp() { return 0; } async getDecoderConfig() { assert(this.demuxer.audioInfo); return { codec: "flac", numberOfChannels: this.demuxer.audioInfo.numberOfChannels, sampleRate: this.demuxer.audioInfo.sampleRate, description: this.demuxer.audioInfo.description }; } async getPacket(timestamp, options) { assert(this.demuxer.audioInfo); if (timestamp < 0) { throw new Error("Timestamp cannot be negative"); } const release = await this.demuxer.readingMutex.acquire(); try { while (true) { const packetIndex = binarySearchLessOrEqual(this.demuxer.loadedSamples, timestamp, (x) => x.blockOffset / this.demuxer.audioInfo.sampleRate); if (packetIndex === -1) { await this.demuxer.advanceReader(); continue; } const packet = this.demuxer.loadedSamples[packetIndex]; const sampleTimestamp = packet.blockOffset / this.demuxer.audioInfo.sampleRate; const sampleDuration = packet.blockSize / this.demuxer.audioInfo.sampleRate; if (sampleTimestamp + sampleDuration <= timestamp) { if (this.demuxer.lastSampleLoaded) { return this.getPacketAtIndex(this.demuxer.loadedSamples.length - 1, options); } await this.demuxer.advanceReader(); continue; } return this.getPacketAtIndex(packetIndex, options); } } finally { release(); } } async getNextPacket(packet, options) { const release = await this.demuxer.readingMutex.acquire(); try { const nextIndex = packet.sequenceNumber + 1; if (this.demuxer.lastSampleLoaded && nextIndex >= this.demuxer.loadedSamples.length) { return null; } while (nextIndex >= this.demuxer.loadedSamples.length && !this.demuxer.lastSampleLoaded) { await this.demuxer.advanceReader(); } return this.getPacketAtIndex(nextIndex, options); } finally { release(); } } getKeyPacket(timestamp, options) { return this.getPacket(timestamp, options); } getNextKeyPacket(packet, options) { return this.getNextPacket(packet, options); } async getPacketAtIndex(sampleIndex, options) { const rawSample = this.demuxer.loadedSamples[sampleIndex]; if (!rawSample) { return null; } let data; if (options.metadataOnly) { data = PLACEHOLDER_DATA; } else { let slice = this.demuxer.reader.requestSlice(rawSample.byteOffset, rawSample.byteSize); if (slice instanceof Promise) slice = await slice; if (!slice) { return null; } data = readBytes(slice, rawSample.byteSize); } assert(this.demuxer.audioInfo); const timestamp = rawSample.blockOffset / this.demuxer.audioInfo.sampleRate; const duration = rawSample.blockSize / this.demuxer.audioInfo.sampleRate; return new EncodedPacket(data, "key", timestamp, duration, sampleIndex, rawSample.byteSize); } async getFirstPacket(options) { while (this.demuxer.loadedSamples.length === 0 && !this.demuxer.lastSampleLoaded) { await this.demuxer.advanceReader(); } return this.getPacketAtIndex(0, options); } } // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/mpeg-ts/mpeg-ts-misc.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var TIMESCALE = 90000; var TS_PACKET_SIZE = 188; var buildMpegTsMimeType = (codecStrings) => { let string = "video/MP2T"; const uniqueCodecStrings = [...new Set(codecStrings.filter(Boolean))]; if (uniqueCodecStrings.length > 0) { string += `; codecs="${uniqueCodecStrings.join(", ")}"`; } return string; }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/mpeg-ts/mpeg-ts-demuxer.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ var MISSING_PES_PACKET_ERROR = "No PES packet found where one was expected."; class MpegTsDemuxer extends Demuxer { constructor(input2) { super(input2); this.metadataPromise = null; this.elementaryStreams = []; this.tracks = []; this.packetOffset = 0; this.packetStride = -1; this.reader = input2._reader; } async readMetadata() { return this.metadataPromise ??= (async () => { const lengthToCheck = TS_PACKET_SIZE + 16 + 1; let startingSlice = this.reader.requestSlice(0, lengthToCheck); if (startingSlice instanceof Promise) startingSlice = await startingSlice; assert(startingSlice); const startingBytes = readBytes(startingSlice, lengthToCheck); if (startingBytes[0] === 71 && startingBytes[TS_PACKET_SIZE] === 71) { this.packetOffset = 0; this.packetStride = TS_PACKET_SIZE; } else if (startingBytes[0] === 71 && startingBytes[TS_PACKET_SIZE + 16] === 71) { this.packetOffset = 0; this.packetStride = TS_PACKET_SIZE + 16; } else if (startingBytes[4] === 71 && startingBytes[4 + TS_PACKET_SIZE] === 71) { this.packetOffset = 4; this.packetStride = TS_PACKET_SIZE; } else { throw new Error("Unreachable."); } let currentPos = this.packetOffset; let programMapPid = null; let hasProgramAssociationTable = false; let hasProgramMap = false; while (true) { const section = await this.readSection(currentPos, true); if (!section) { break; } const BYTES_BEFORE_SECTION_LENGTH = 3; const BITS_IN_CRC_32 = 32; if (section.pid === 0 && !hasProgramAssociationTable) { const bitstream = new Bitstream(section.payload); const pointerField = bitstream.readAlignedByte(); bitstream.skipBits(8 * pointerField); bitstream.skipBits(14); const sectionLength = bitstream.readBits(10); bitstream.skipBits(40); while (8 * (sectionLength + BYTES_BEFORE_SECTION_LENGTH) - bitstream.pos > BITS_IN_CRC_32) { const programNumber = bitstream.readBits(16); bitstream.skipBits(3); if (programNumber !== 0) { if (programMapPid !== null) { throw new Error("Only files with a single program are supported."); } else { programMapPid = bitstream.readBits(13); } } } if (programMapPid === null) { throw new Error("Program Association Table must link to a Program Map Table."); } hasProgramAssociationTable = true; } else if (section.pid === programMapPid && !hasProgramMap) { const bitstream = new Bitstream(section.payload); const pointerField = bitstream.readAlignedByte(); bitstream.skipBits(8 * pointerField); bitstream.skipBits(12); const sectionLength = bitstream.readBits(12); bitstream.skipBits(43); const pcrPid = bitstream.readBits(13); bitstream.skipBits(6); const programInfoLength = bitstream.readBits(10); bitstream.skipBits(8 * programInfoLength); while (8 * (sectionLength + BYTES_BEFORE_SECTION_LENGTH) - bitstream.pos > BITS_IN_CRC_32) { const streamType = bitstream.readBits(8); bitstream.skipBits(3); const elementaryPid = bitstream.readBits(13); bitstream.skipBits(6); const esInfoLength = bitstream.readBits(10); bitstream.skipBits(8 * esInfoLength); let info = null; switch (streamType) { case 3: case 4: case 15: { const codec = streamType === 15 ? "aac" : "mp3"; info = { type: "audio", codec, aacCodecInfo: null, numberOfChannels: -1, sampleRate: -1 }; } ; break; case 27: case 36: { const codec = streamType === 27 ? "avc" : "hevc"; info = { type: "video", codec, avcCodecInfo: null, hevcCodecInfo: null, colorSpace: { primaries: null, transfer: null, matrix: null, fullRange: null }, width: -1, height: -1, reorderSize: -1 }; } ; break; default: {} } if (info) { this.elementaryStreams.push({ demuxer: this, pid: elementaryPid, streamType, initialized: false, firstSection: null, info }); } } hasProgramMap = true; } else { const elementaryStream = this.elementaryStreams.find((x) => x.pid === section.pid); if (elementaryStream && !elementaryStream.initialized) { const pesPacket = readPesPacket(section); if (!pesPacket) { throw new Error(`Couldn't read first PES packet for Elementary Stream with PID ${elementaryStream.pid}`); } elementaryStream.firstSection = section; if (elementaryStream.info.type === "video") { if (elementaryStream.info.codec === "avc") { elementaryStream.info.avcCodecInfo = extractAvcDecoderConfigurationRecord(pesPacket.data); if (!elementaryStream.info.avcCodecInfo) { throw new Error("Invalid AVC video stream; could not extract AVCDecoderConfigurationRecord" + " from first packet."); } const spsUnit = elementaryStream.info.avcCodecInfo.sequenceParameterSets[0]; assert(spsUnit); const spsInfo = parseAvcSps(spsUnit); elementaryStream.info.width = spsInfo.displayWidth; elementaryStream.info.height = spsInfo.displayHeight; elementaryStream.info.colorSpace = { primaries: COLOR_PRIMARIES_MAP_INVERSE[spsInfo.colourPrimaries], transfer: TRANSFER_CHARACTERISTICS_MAP_INVERSE[spsInfo.transferCharacteristics], matrix: MATRIX_COEFFICIENTS_MAP_INVERSE[spsInfo.matrixCoefficients], fullRange: !!spsInfo.fullRangeFlag }; elementaryStream.info.reorderSize = spsInfo.maxDecFrameBuffering; elementaryStream.initialized = true; } else if (elementaryStream.info.codec === "hevc") { elementaryStream.info.hevcCodecInfo = extractHevcDecoderConfigurationRecord(pesPacket.data); if (!elementaryStream.info.hevcCodecInfo) { throw new Error("Invalid HEVC video stream; could not extract HVCDecoderConfigurationRecord" + " from first packet."); } const spsArray = elementaryStream.info.hevcCodecInfo.arrays.find((a) => a.nalUnitType === HevcNalUnitType.SPS_NUT); const spsUnit = spsArray.nalUnits[0]; assert(spsUnit); const spsInfo = parseHevcSps(spsUnit); elementaryStream.info.width = spsInfo.displayWidth; elementaryStream.info.height = spsInfo.displayHeight; elementaryStream.info.colorSpace = { primaries: COLOR_PRIMARIES_MAP_INVERSE[spsInfo.colourPrimaries], transfer: TRANSFER_CHARACTERISTICS_MAP_INVERSE[spsInfo.transferCharacteristics], matrix: MATRIX_COEFFICIENTS_MAP_INVERSE[spsInfo.matrixCoefficients], fullRange: !!spsInfo.fullRangeFlag }; elementaryStream.info.reorderSize = spsInfo.maxDecFrameBuffering; elementaryStream.initialized = true; } else { throw new Error("Unhandled."); } } else { if (elementaryStream.info.codec === "aac") { const slice = FileSlice.tempFromBytes(pesPacket.data); const header2 = readAdtsFrameHeader(slice); if (!header2) { throw new Error("Invalid AAC audio stream; could not read ADTS frame header from first packet."); } elementaryStream.info.aacCodecInfo = { isMpeg2: false, objectType: header2.objectType }; elementaryStream.info.numberOfChannels = aacChannelMap[header2.channelConfiguration]; elementaryStream.info.sampleRate = aacFrequencyTable[header2.samplingFrequencyIndex]; elementaryStream.initialized = true; } else if (elementaryStream.info.codec === "mp3") { const word = readU32Be(FileSlice.tempFromBytes(pesPacket.data)); const result = readMp3FrameHeader(word, pesPacket.data.byteLength); if (!result.header) { throw new Error("Invalid MP3 audio stream; could not read frame header from first packet."); } elementaryStream.info.numberOfChannels = result.header.channel === 3 ? 1 : 2; elementaryStream.info.sampleRate = result.header.sampleRate; elementaryStream.initialized = true; } else { throw new Error("Unhandled."); } } } } const isDone = hasProgramMap && this.elementaryStreams.every((x) => x.initialized); if (isDone) { break; } assert(section.endPos !== null); currentPos = section.endPos; } for (const stream of this.elementaryStreams) { if (stream.info.type === "video") { this.tracks.push(new InputVideoTrack(this.input, new MpegTsVideoTrackBacking(stream))); } else { this.tracks.push(new InputAudioTrack(this.input, new MpegTsAudioTrackBacking(stream))); } } })(); } async getTracks() { await this.readMetadata(); return this.tracks; } async getMetadataTags() { return {}; } async computeDuration() { const tracks = await this.getTracks(); const trackDurations = await Promise.all(tracks.map((x) => x.computeDuration())); return Math.max(0, ...trackDurations); } async getMimeType() { await this.readMetadata(); const tracks = await this.getTracks(); const codecStrings = await Promise.all(tracks.map((x) => x.getCodecParameterString())); return buildMpegTsMimeType(codecStrings); } async readSection(startPos, full) { let endPos = startPos; let currentPos = startPos; const chunks = []; let chunksByteLength = 0; let firstPacket = null; while (true) { const packet = await this.readPacket(currentPos); currentPos += this.packetStride; if (!packet) { break; } if (!firstPacket) { if (packet.payloadUnitStartIndicator === 0) { break; } firstPacket = packet; } else { if (packet.pid !== firstPacket.pid) { continue; } if (packet.payloadUnitStartIndicator === 1) { break; } } const hasAdaptationField = !!(packet.adaptationFieldControl & 2); const hasPayload = !!(packet.adaptationFieldControl & 1); let adaptationFieldLength = 0; if (hasAdaptationField) { adaptationFieldLength = 1 + packet.body[0]; } if (hasPayload) { if (adaptationFieldLength === 0) { chunks.push(packet.body); chunksByteLength += packet.body.byteLength; } else { chunks.push(packet.body.subarray(adaptationFieldLength)); chunksByteLength += packet.body.byteLength - adaptationFieldLength; } } endPos = currentPos; if (!full && chunksByteLength >= 64) { break; } } if (!firstPacket) { return null; } let merged; if (chunks.length === 1) { merged = chunks[0]; } else { const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0); merged = new Uint8Array(totalLength); let offset = 0; for (const chunk of chunks) { merged.set(chunk, offset); offset += chunk.length; } } return { startPos, endPos: full ? endPos : null, pid: firstPacket.pid, payload: merged }; } async readPacketHeader(pos) { let slice = this.reader.requestSlice(pos, 4); if (slice instanceof Promise) slice = await slice; if (!slice) { return null; } const syncByte = readU8(slice); if (syncByte !== 71) { throw new Error("Invalid TS packet sync byte. Likely an internal bug, please report this file."); } const nextTwoBytes = readU16Be(slice); const transportErrorIndicator = nextTwoBytes >> 15; const payloadUnitStartIndicator = nextTwoBytes >> 14 & 1; const transportPriority = nextTwoBytes >> 13 & 1; const pid = nextTwoBytes & 8191; const nextByte = readU8(slice); const transportScramblingControl = nextByte >> 6; const adaptationFieldControl = nextByte >> 4 & 3; const continuityCounter = nextByte & 15; return { payloadUnitStartIndicator, pid, adaptationFieldControl }; } async readPacket(pos) { let slice = this.reader.requestSlice(pos, TS_PACKET_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) { return null; } const bytes = readBytes(slice, TS_PACKET_SIZE); const syncByte = bytes[0]; if (syncByte !== 71) { throw new Error("Invalid TS packet sync byte. Likely an internal bug, please report this file."); } const nextTwoBytes = (bytes[1] << 8) + bytes[2]; const transportErrorIndicator = nextTwoBytes >> 15; const payloadUnitStartIndicator = nextTwoBytes >> 14 & 1; const transportPriority = nextTwoBytes >> 13 & 1; const pid = nextTwoBytes & 8191; const nextByte = bytes[3]; const transportScramblingControl = nextByte >> 6; const adaptationFieldControl = nextByte >> 4 & 3; const continuityCounter = nextByte & 15; return { payloadUnitStartIndicator, pid, adaptationFieldControl, body: bytes.subarray(4) }; } } var readPesPacketHeader = (section) => { const bitstream = new Bitstream(section.payload); const startCodePrefix = bitstream.readBits(24); if (startCodePrefix !== 1) { return null; } const streamId = bitstream.readBits(8); bitstream.skipBits(16); if (streamId === 188 || streamId === 190 || streamId === 191 || streamId === 240 || streamId === 241 || streamId === 255 || streamId === 242 || streamId === 248) { return null; } bitstream.skipBits(8); const ptsDtsFlags = bitstream.readBits(2); bitstream.skipBits(14); let pts = 0; if (ptsDtsFlags === 2 || ptsDtsFlags === 3) { bitstream.skipBits(4); pts += bitstream.readBits(3) * (1 << 30); bitstream.skipBits(1); pts += bitstream.readBits(15) * (1 << 15); bitstream.skipBits(1); pts += bitstream.readBits(15); } else { throw new Error("PES packets without PTS are not currently supported. If you think this file should be supported," + " please report it."); } return { sectionStartPos: section.startPos, sectionEndPos: section.endPos, pts }; }; var readPesPacket = (section) => { assert(section.endPos !== null); const header2 = readPesPacketHeader(section); if (!header2) { return null; } const bitstream = new Bitstream(section.payload); bitstream.skipBits(32); const pesPacketLength = bitstream.readBits(16); const BYTES_UNTIL_END_OF_PES_PACKET_LENGTH = 6; bitstream.skipBits(16); const pesHeaderDataLength = bitstream.readBits(8); const pesHeaderEndPos = bitstream.pos + 8 * pesHeaderDataLength; bitstream.pos = pesHeaderEndPos; const bytePos = pesHeaderEndPos / 8; assert(Number.isInteger(bytePos)); const data = section.payload.subarray(bytePos, pesPacketLength > 0 ? BYTES_UNTIL_END_OF_PES_PACKET_LENGTH + pesPacketLength : section.payload.byteLength); return { ...header2, data }; }; class MpegTsTrackBacking { constructor(elementaryStream) { this.elementaryStream = elementaryStream; this.referencePesPackets = []; this.endReferencePesPacketAdded = false; this.packetBuffers = new WeakMap; this.packetSectionStarts = new WeakMap; this.mutex = new AsyncMutex; } getId() { return this.elementaryStream.pid; } getCodec() { throw new Error("Not implemented on base class."); } getInternalCodecId() { return this.elementaryStream.streamType; } getName() { return null; } getLanguageCode() { return UNDETERMINED_LANGUAGE; } getDisposition() { return DEFAULT_TRACK_DISPOSITION; } getTimeResolution() { return TIMESCALE; } async computeDuration() { const lastPacket = await this.getPacket(Infinity, { metadataOnly: true }); return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0); } async getFirstTimestamp() { const firstPacket = await this.getFirstPacket({ metadataOnly: true }); return firstPacket?.timestamp ?? 0; } createEncodedPacket(suppliedPacket, duration, options) { return new EncodedPacket(options.metadataOnly ? PLACEHOLDER_DATA : suppliedPacket.data, this.getPacketType(suppliedPacket.data), suppliedPacket.pts / TIMESCALE, Math.max(duration / TIMESCALE, 0), suppliedPacket.sequenceNumber, suppliedPacket.data.byteLength); } maybeInsertReferencePacket(pesPacketHeader, force, dropIfMutexLocked) { if (dropIfMutexLocked && this.mutex.pending > 0) { return; } const index = binarySearchLessOrEqual(this.referencePesPackets, pesPacketHeader.pts, (x) => x.pts); if (index >= 0) { const entry = this.referencePesPackets[index]; if (pesPacketHeader.sectionStartPos <= entry.sectionStartPos) { return false; } if (!force && pesPacketHeader.pts - entry.pts < TIMESCALE / 2) { return false; } if (index < this.referencePesPackets.length - 1) { const nextEntry = this.referencePesPackets[index + 1]; if (nextEntry.sectionStartPos < pesPacketHeader.sectionStartPos) { return false; } if (!force && nextEntry.pts - pesPacketHeader.pts < TIMESCALE / 2) { return false; } } } this.referencePesPackets.splice(index + 1, 0, pesPacketHeader); return true; } async getFirstPacket(options) { const section = this.elementaryStream.firstSection; assert(section); const pesPacket = readPesPacket(section); assert(pesPacket); const context = new PacketReadingContext(this, pesPacket, true); const buffer = new PacketBuffer(this, context); const result = await buffer.readNext(); if (!result) { return null; } const packet = this.createEncodedPacket(result.packet, result.duration, options); this.packetBuffers.set(packet, buffer); this.packetSectionStarts.set(packet, result.packet.sectionStartPos); return packet; } async getNextPacket(packet, options) { let buffer = this.packetBuffers.get(packet); if (buffer) { const result = await buffer.readNext(); if (!result) { return null; } this.packetBuffers.delete(packet); const newPacket = this.createEncodedPacket(result.packet, result.duration, options); this.packetBuffers.set(newPacket, buffer); this.packetSectionStarts.set(newPacket, result.packet.sectionStartPos); return newPacket; } const sectionStartPos = this.packetSectionStarts.get(packet); if (sectionStartPos === undefined) { throw new Error("Packet was not created from this track."); } const demuxer = this.elementaryStream.demuxer; const section = await demuxer.readSection(sectionStartPos, true); assert(section); const pesPacket = readPesPacket(section); assert(pesPacket); const context = new PacketReadingContext(this, pesPacket, true); buffer = new PacketBuffer(this, context); const targetSequenceNumber = packet.sequenceNumber; while (true) { const result = await buffer.readNext(); if (!result) { return null; } if (result.packet.sequenceNumber > targetSequenceNumber) { const newPacket = this.createEncodedPacket(result.packet, result.duration, options); this.packetBuffers.set(newPacket, buffer); this.packetSectionStarts.set(newPacket, result.packet.sectionStartPos); return newPacket; } } } async getNextKeyPacket(packet, options) { let currentPacket = packet; while (true) { currentPacket = await this.getNextPacket(currentPacket, options); if (!currentPacket) { return null; } if (currentPacket.type === "key") { return currentPacket; } } } getPacket(timestamp, options) { return this.doPacketLookup(timestamp, false, options); } getKeyPacket(timestamp, options) { return this.doPacketLookup(timestamp, true, options); } async doPacketLookup(timestamp, keyframesOnly, options) { const searchPts = roundIfAlmostInteger(timestamp * TIMESCALE); const demuxer = this.elementaryStream.demuxer; const reader = demuxer.reader; const release = await this.mutex.acquire(); let currentPesPacketHeader; try { if (this.referencePesPackets.length === 0) { const section2 = this.elementaryStream.firstSection; assert(section2); const pesPacketHeader = readPesPacketHeader(section2); assert(pesPacketHeader); this.maybeInsertReferencePacket(pesPacketHeader, false, false); assert(this.referencePesPackets.length === 1); } let currentIndex = binarySearchLessOrEqual(this.referencePesPackets, searchPts, (x) => x.pts); if (currentIndex === -1) { return null; } const needsToLookForLastPacket = reader.fileSize !== null && currentIndex === this.referencePesPackets.length - 1 && !this.endReferencePesPacketAdded; if (needsToLookForLastPacket) { let currentPos = reader.fileSize - demuxer.packetStride + demuxer.packetOffset; let packetHeader = await demuxer.readPacketHeader(currentPos); if (!packetHeader) { return null; } while (packetHeader.pid !== this.elementaryStream.pid || packetHeader.payloadUnitStartIndicator === 0) { currentPos -= demuxer.packetStride; const previousPacketHeader = await demuxer.readPacketHeader(currentPos); if (!previousPacketHeader) { return null; } packetHeader = previousPacketHeader; } const section2 = await demuxer.readSection(currentPos, false); assert(section2); const pesPacketHeader = readPesPacketHeader(section2); if (!pesPacketHeader) { throw new Error(MISSING_PES_PACKET_ERROR); } this.maybeInsertReferencePacket(pesPacketHeader, true, false); this.endReferencePesPacketAdded = true; } currentIndex = binarySearchLessOrEqual(this.referencePesPackets, searchPts, (x) => x.pts); assert(currentIndex !== -1); while (reader.fileSize !== null) { const currentEntry = this.referencePesPackets[currentIndex]; const nextEntry = this.referencePesPackets[currentIndex + 1]; if (searchPts - currentEntry.pts < TIMESCALE || !nextEntry) { break; } const midpoint = roundToMultiple((currentEntry.sectionStartPos + nextEntry.sectionStartPos) / 2, demuxer.packetStride) + demuxer.packetOffset; let currentPos = midpoint; let packetHeader = await demuxer.readPacketHeader(currentPos); assert(packetHeader); while (currentPos < nextEntry.sectionStartPos && (packetHeader.pid !== this.elementaryStream.pid || packetHeader.payloadUnitStartIndicator === 0)) { currentPos += demuxer.packetStride; const previousPacketHeader = await demuxer.readPacketHeader(currentPos); if (!previousPacketHeader) { return null; } packetHeader = previousPacketHeader; } if (currentPos >= nextEntry.sectionStartPos) { break; } const section2 = await demuxer.readSection(currentPos, false); assert(section2); const pesPacketHeader = readPesPacketHeader(section2); if (!pesPacketHeader) { throw new Error(MISSING_PES_PACKET_ERROR); } const addedPoint = this.maybeInsertReferencePacket(pesPacketHeader, false, false); if (!addedPoint) { break; } if (pesPacketHeader.pts <= searchPts) { currentIndex++; } } currentPesPacketHeader = this.referencePesPackets[currentIndex]; assert(currentPesPacketHeader.pts <= searchPts); } finally { release(); } release(); outer: while (true) { let currentPos = currentPesPacketHeader.sectionStartPos + demuxer.packetStride; while (true) { const packetHeader = await demuxer.readPacketHeader(currentPos); if (!packetHeader) { break outer; } if (packetHeader.pid === this.elementaryStream.pid && packetHeader.payloadUnitStartIndicator === 1) { break; } currentPos += demuxer.packetStride; } const nextSection = await demuxer.readSection(currentPos, false); if (!nextSection) { break; } const nextPesPacketHeader = readPesPacketHeader(nextSection); if (!nextPesPacketHeader) { throw new Error(MISSING_PES_PACKET_ERROR); } if (nextPesPacketHeader.pts > searchPts) { break; } currentPesPacketHeader = nextPesPacketHeader; if (reader.fileSize === null) { this.maybeInsertReferencePacket(nextPesPacketHeader, false, true); } } const reorderSize = this.getReorderSize(); for (let i = 0;i < reorderSize; i++) { let pos = currentPesPacketHeader.sectionStartPos - demuxer.packetStride; while (true) { const packetHeader = await demuxer.readPacketHeader(pos); if (!packetHeader) { break; } if (packetHeader.pid === this.elementaryStream.pid && packetHeader.payloadUnitStartIndicator === 1) { const headerSection = await demuxer.readSection(pos, false); assert(headerSection); const header2 = readPesPacketHeader(headerSection); if (!header2) { throw new Error(MISSING_PES_PACKET_ERROR); } currentPesPacketHeader = header2; break; } pos -= demuxer.packetStride; } } const section = await demuxer.readSection(currentPesPacketHeader.sectionStartPos, true); assert(section); const pesPacket = readPesPacket(section); assert(pesPacket); const context = new PacketReadingContext(this, pesPacket, true); const buffer = new PacketBuffer(this, context); while (true) { const topPts = last(buffer.presentationOrderPackets)?.pts ?? -Infinity; if (topPts >= searchPts) { break; } const didRead = await buffer.readNextDecodeOrderPacket(); if (!didRead) { break; } } const targetIndex = findLastIndex(buffer.presentationOrderPackets, (p) => p.pts <= searchPts && (!keyframesOnly || this.getPacketType(p.data) === "key")); if (targetIndex !== -1) { const targetPacket = buffer.presentationOrderPackets[targetIndex]; const lastDuration = targetIndex === 0 ? 0 : targetPacket.pts - buffer.presentationOrderPackets[targetIndex - 1].pts; while (buffer.decodeOrderPackets[0] !== targetPacket) { buffer.decodeOrderPackets.shift(); } buffer.lastDuration = lastDuration; const result = await buffer.readNext(); assert(result); const packet = this.createEncodedPacket(result.packet, result.duration, options); this.packetBuffers.set(packet, buffer); this.packetSectionStarts.set(packet, result.packet.sectionStartPos); return packet; } if (!keyframesOnly) { return null; } let searchPos = currentPesPacketHeader.sectionStartPos; while (true) { searchPos -= demuxer.packetStride; const packetHeader = await demuxer.readPacketHeader(searchPos); if (!packetHeader) { return null; } if (packetHeader.pid !== this.elementaryStream.pid || packetHeader.payloadUnitStartIndicator !== 1) { continue; } const section2 = await demuxer.readSection(searchPos, true); assert(section2); const pesPacket2 = readPesPacket(section2); if (!pesPacket2) { throw new Error(MISSING_PES_PACKET_ERROR); } const context2 = new PacketReadingContext(this, pesPacket2, false); await this.markNextPacket(context2); if (!context2.suppliedPacket) { continue; } if (this.getPacketType(context2.suppliedPacket.data) !== "key") { continue; } context2.uncapped = true; const buffer2 = new PacketBuffer(this, context2); const result = await buffer2.readNext(); assert(result); const packet = this.createEncodedPacket(result.packet, result.duration, options); this.packetBuffers.set(packet, buffer2); this.packetSectionStarts.set(packet, result.packet.sectionStartPos); return packet; } } } class MpegTsVideoTrackBacking extends MpegTsTrackBacking { constructor(elementaryStream) { super(elementaryStream); this.elementaryStream = elementaryStream; this.decoderConfig = { codec: extractVideoCodecString({ width: this.elementaryStream.info.width, height: this.elementaryStream.info.height, codec: this.elementaryStream.info.codec, codecDescription: null, colorSpace: this.elementaryStream.info.colorSpace, avcType: 1, avcCodecInfo: this.elementaryStream.info.avcCodecInfo, hevcCodecInfo: this.elementaryStream.info.hevcCodecInfo, vp9CodecInfo: null, av1CodecInfo: null }), codedWidth: this.elementaryStream.info.width, codedHeight: this.elementaryStream.info.height, colorSpace: this.elementaryStream.info.colorSpace }; } getCodec() { return this.elementaryStream.info.codec; } getCodedWidth() { return this.elementaryStream.info.width; } getCodedHeight() { return this.elementaryStream.info.height; } getRotation() { return 0; } async getColorSpace() { return this.elementaryStream.info.colorSpace; } async canBeTransparent() { return false; } async getDecoderConfig() { return this.decoderConfig; } getPacketType(packetData) { return determineVideoPacketType(this.elementaryStream.info.codec, this.decoderConfig, packetData) ?? "key"; } getReorderSize() { return this.elementaryStream.info.reorderSize; } async markNextPacket(context) { assert(!context.suppliedPacket); const codec = this.elementaryStream.info.codec; const CHUNK_SIZE = 1024; let packetStartPos = null; while (true) { let remaining = context.ensureBuffered(CHUNK_SIZE); if (remaining instanceof Promise) remaining = await remaining; if (remaining === 0) { break; } const chunkStartPos = context.currentPos; const chunk = context.readBytes(remaining); const length = chunk.byteLength; let i = 0; while (i < length) { const zeroIndex = chunk.indexOf(0, i); if (zeroIndex === -1 || zeroIndex >= length) { break; } i = zeroIndex; const posBeforeZero = chunkStartPos + i; if (i + 4 >= length) { context.seekTo(posBeforeZero); break; } const b1 = chunk[i + 1]; const b2 = chunk[i + 2]; const b3 = chunk[i + 3]; let startCodeLength = 0; let nalUnitTypeByte = null; if (b1 === 0 && b2 === 0 && b3 === 1) { startCodeLength = 4; nalUnitTypeByte = chunk[i + 4]; } else if (b1 === 0 && b2 === 1) { startCodeLength = 3; nalUnitTypeByte = b3; } if (startCodeLength === 0) { i++; continue; } const startCodePos = posBeforeZero; if (packetStartPos === null) { packetStartPos = startCodePos; i += startCodeLength; continue; } if (nalUnitTypeByte !== null) { const nalUnitType = codec === "avc" ? extractNalUnitTypeForAvc(nalUnitTypeByte) : extractNalUnitTypeForHevc(nalUnitTypeByte); const isAud = codec === "avc" ? nalUnitType === AvcNalUnitType.AUD : nalUnitType === HevcNalUnitType.AUD_NUT; if (isAud) { const packetLength = startCodePos - packetStartPos; context.seekTo(packetStartPos); return context.supplyPacket(packetLength, 0); } } i += startCodeLength; } if (remaining < CHUNK_SIZE) { break; } } if (packetStartPos !== null) { const packetLength = context.endPos - packetStartPos; context.seekTo(packetStartPos); return context.supplyPacket(packetLength, 0); } } } class MpegTsAudioTrackBacking extends MpegTsTrackBacking { constructor(elementaryStream) { super(elementaryStream); this.elementaryStream = elementaryStream; } getCodec() { return this.elementaryStream.info.codec; } getNumberOfChannels() { return this.elementaryStream.info.numberOfChannels; } getSampleRate() { return this.elementaryStream.info.sampleRate; } async getDecoderConfig() { return { codec: extractAudioCodecString({ codec: this.elementaryStream.info.codec, codecDescription: null, aacCodecInfo: this.elementaryStream.info.aacCodecInfo }), numberOfChannels: this.elementaryStream.info.numberOfChannels, sampleRate: this.elementaryStream.info.sampleRate }; } getPacketType(packetData) { return "key"; } getReorderSize() { return 1; } async markNextPacket(context) { assert(!context.suppliedPacket); const codec = this.elementaryStream.info.codec; const CHUNK_SIZE = 128; while (true) { let remaining = context.ensureBuffered(CHUNK_SIZE); if (remaining instanceof Promise) remaining = await remaining; const startPos = context.currentPos; while (context.currentPos - startPos < remaining) { const byte = context.readU8(); if (codec === "aac") { if (byte !== 255) { continue; } context.skip(-1); const possibleHeaderStartPos = context.currentPos; let remaining2 = context.ensureBuffered(MAX_ADTS_FRAME_HEADER_SIZE); if (remaining2 instanceof Promise) remaining2 = await remaining2; if (remaining2 < MAX_ADTS_FRAME_HEADER_SIZE) { return; } const headerBytes = context.readBytes(MAX_ADTS_FRAME_HEADER_SIZE); const header2 = readAdtsFrameHeader(FileSlice.tempFromBytes(headerBytes)); if (header2) { context.seekTo(possibleHeaderStartPos); let remaining3 = context.ensureBuffered(header2.frameLength); if (remaining3 instanceof Promise) remaining3 = await remaining3; return context.supplyPacket(remaining3, Math.round(SAMPLES_PER_AAC_FRAME * TIMESCALE / this.elementaryStream.info.sampleRate)); } else { context.seekTo(possibleHeaderStartPos + 1); } } else if (codec === "mp3") { if (byte !== 255) { continue; } context.skip(-1); const possibleHeaderStartPos = context.currentPos; let remaining2 = context.ensureBuffered(FRAME_HEADER_SIZE); if (remaining2 instanceof Promise) remaining2 = await remaining2; if (remaining2 < FRAME_HEADER_SIZE) { return; } const headerBytes = context.readBytes(FRAME_HEADER_SIZE); const word = toDataView(headerBytes).getUint32(0); const result = readMp3FrameHeader(word, null); if (result.header) { context.seekTo(possibleHeaderStartPos); let remaining3 = context.ensureBuffered(result.header.totalSize); if (remaining3 instanceof Promise) remaining3 = await remaining3; const duration = result.header.audioSamplesInFrame * TIMESCALE / this.elementaryStream.info.sampleRate; return context.supplyPacket(remaining3, Math.round(duration)); } else { context.seekTo(possibleHeaderStartPos + 1); } } else { throw new Error("Unreachable"); } } if (remaining < CHUNK_SIZE) { break; } } } } class PacketReadingContext { constructor(backing, startingPesPacket, uncapped) { this.currentPos = 0; this.pesPackets = []; this.currentPesPacketIndex = 0; this.currentPesPacketPos = 0; this.endPos = 0; this.nextPts = 0; this.suppliedPacket = null; this.backing = backing; this.pid = backing.elementaryStream.pid; this.demuxer = backing.elementaryStream.demuxer; this.startingPesPacket = startingPesPacket; this.uncapped = uncapped; } clone() { const clone = new PacketReadingContext(this.backing, this.startingPesPacket, true); clone.currentPos = this.currentPos; clone.pesPackets = [...this.pesPackets]; clone.currentPesPacketIndex = this.currentPesPacketIndex; clone.currentPesPacketPos = this.currentPesPacketPos; clone.endPos = this.endPos; clone.nextPts = this.nextPts; return clone; } ensureBuffered(length) { const remaining = this.endPos - this.currentPos; if (remaining >= length) { return length; } return this.bufferData(length - remaining).then(() => Math.min(this.endPos - this.currentPos, length)); } getCurrentPesPacket() { const packet = this.pesPackets[this.currentPesPacketIndex]; assert(packet); return packet; } async bufferData(length) { const targetEndPos = this.endPos + length; while (this.endPos < targetEndPos) { let pesPacket; if (this.pesPackets.length === 0) { pesPacket = this.startingPesPacket; } else { let currentPos = last(this.pesPackets).sectionEndPos; assert(currentPos !== null); while (true) { const packetHeader = await this.demuxer.readPacketHeader(currentPos); if (!packetHeader) { return; } if (packetHeader.pid === this.pid) { break; } currentPos += this.demuxer.packetStride; } const nextSection = await this.demuxer.readSection(currentPos, true); if (!nextSection) { return; } const nextPesPacket = readPesPacket(nextSection); if (!nextPesPacket) { throw new Error(MISSING_PES_PACKET_ERROR); } pesPacket = nextPesPacket; } this.pesPackets.push(pesPacket); this.endPos += pesPacket.data.byteLength; if (this.pesPackets.length === 1) { this.nextPts = pesPacket.pts; } } } readBytes(length) { const currentPesPacket = this.getCurrentPesPacket(); const relativeStartOffset = this.currentPos - this.currentPesPacketPos; const relativeEndOffset = relativeStartOffset + length; this.currentPos += length; if (relativeEndOffset <= currentPesPacket.data.byteLength) { return currentPesPacket.data.subarray(relativeStartOffset, relativeEndOffset); } const result = new Uint8Array(length); result.set(currentPesPacket.data.subarray(relativeStartOffset)); let offset = currentPesPacket.data.byteLength - relativeStartOffset; while (true) { this.advanceCurrentPacket(); const currentPesPacket2 = this.getCurrentPesPacket(); const relativeEndOffset2 = length - offset; if (relativeEndOffset2 <= currentPesPacket2.data.byteLength) { result.set(currentPesPacket2.data.subarray(0, relativeEndOffset2), offset); break; } result.set(currentPesPacket2.data, offset); offset += currentPesPacket2.data.byteLength; } return result; } readU8() { let currentPesPacket = this.getCurrentPesPacket(); const relativeOffset = this.currentPos - this.currentPesPacketPos; this.currentPos++; if (relativeOffset < currentPesPacket.data.byteLength) { return currentPesPacket.data[relativeOffset]; } this.advanceCurrentPacket(); currentPesPacket = this.getCurrentPesPacket(); return currentPesPacket.data[0]; } seekTo(pos) { if (pos === this.currentPos) { return; } if (pos < this.currentPos) { while (pos < this.currentPesPacketPos) { this.currentPesPacketIndex--; const currentPacket = this.getCurrentPesPacket(); this.currentPesPacketPos -= currentPacket.data.byteLength; this.nextPts = currentPacket.pts; } } else { while (true) { const currentPesPacket = this.getCurrentPesPacket(); const currentEndPos = this.currentPesPacketPos + currentPesPacket.data.byteLength; if (pos < currentEndPos) { break; } this.currentPesPacketPos += currentPesPacket.data.byteLength; this.currentPesPacketIndex++; this.nextPts = this.getCurrentPesPacket().pts; } } this.currentPos = pos; } skip(n) { this.seekTo(this.currentPos + n); } advanceCurrentPacket() { this.currentPesPacketPos += this.getCurrentPesPacket().data.byteLength; this.currentPesPacketIndex++; this.nextPts = this.getCurrentPesPacket().pts; } supplyPacket(packetLength, intrinsicDuration) { const currentPesPacket = this.getCurrentPesPacket(); if (!this.uncapped && currentPesPacket !== this.startingPesPacket) { this.suppliedPacket = null; return; } this.backing.maybeInsertReferencePacket(currentPesPacket, false, true); const pts = this.nextPts; this.nextPts += intrinsicDuration; const sectionStartPos = currentPesPacket.sectionStartPos; const sequenceNumber = sectionStartPos + (this.currentPos - this.currentPesPacketPos); const data = this.readBytes(packetLength); this.suppliedPacket = { pts, data, sequenceNumber, sectionStartPos }; this.pesPackets.splice(0, this.currentPesPacketIndex); this.currentPesPacketIndex = 0; } } class PacketBuffer { constructor(backing, context) { this.decodeOrderPackets = []; this.reorderBuffer = []; this.presentationOrderPackets = []; this.reachedEnd = false; this.lastDuration = 0; this.backing = backing; this.context = context; this.reorderSize = backing.getReorderSize(); assert(this.reorderSize >= 0); } async readNext() { if (this.decodeOrderPackets.length === 0) { const didRead = await this.readNextDecodeOrderPacket(); if (!didRead) { return null; } } await this.ensureCurrentPacketHasNext(); const packet = this.decodeOrderPackets[0]; const presentationIndex = this.presentationOrderPackets.indexOf(packet); assert(presentationIndex !== -1); let duration; if (presentationIndex === this.presentationOrderPackets.length - 1) { duration = this.lastDuration; } else { const nextPacket = this.presentationOrderPackets[presentationIndex + 1]; duration = nextPacket.pts - packet.pts; this.lastDuration = duration; } this.decodeOrderPackets.shift(); while (this.presentationOrderPackets.length > 0) { const first = this.presentationOrderPackets[0]; if (this.decodeOrderPackets.includes(first)) { break; } this.presentationOrderPackets.shift(); } return { packet, duration }; } async readNextDecodeOrderPacket() { if (this.reachedEnd) { return false; } let suppliedPacket; if (this.context.suppliedPacket) { suppliedPacket = this.context.suppliedPacket; } else { await this.backing.markNextPacket(this.context); suppliedPacket = this.context.suppliedPacket; } this.context.suppliedPacket = null; if (!suppliedPacket) { this.reachedEnd = true; this.flushReorderBuffer(); return false; } this.decodeOrderPackets.push(suppliedPacket); this.processPacketThroughReorderBuffer(suppliedPacket); return true; } async ensureCurrentPacketHasNext() { const current = this.decodeOrderPackets[0]; assert(current); while (true) { const presentationIndex = this.presentationOrderPackets.indexOf(current); if (presentationIndex !== -1 && presentationIndex <= this.presentationOrderPackets.length - 2) { break; } const didRead = await this.readNextDecodeOrderPacket(); if (!didRead) { break; } } } processPacketThroughReorderBuffer(packet) { this.reorderBuffer.push(packet); if (this.reorderBuffer.length >= this.reorderSize) { let minIndex = 0; for (let i = 1;i < this.reorderBuffer.length; i++) { if (this.reorderBuffer[i].pts < this.reorderBuffer[minIndex].pts) { minIndex = i; } } const packet2 = this.reorderBuffer.splice(minIndex, 1)[0]; this.presentationOrderPackets.push(packet2); } } flushReorderBuffer() { this.reorderBuffer.sort((a, b) => a.pts - b.pts); this.presentationOrderPackets.push(...this.reorderBuffer); this.reorderBuffer.length = 0; } } // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/input-format.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ class InputFormat { } class IsobmffInputFormat extends InputFormat { async _getMajorBrand(input2) { let slice = input2._reader.requestSlice(0, 12); if (slice instanceof Promise) slice = await slice; if (!slice) return null; slice.skip(4); const fourCc = readAscii(slice, 4); if (fourCc !== "ftyp") { return null; } return readAscii(slice, 4); } _createDemuxer(input2) { return new IsobmffDemuxer(input2); } } class Mp4InputFormat extends IsobmffInputFormat { async _canReadInput(input2) { const majorBrand = await this._getMajorBrand(input2); return !!majorBrand && majorBrand !== "qt "; } get name() { return "MP4"; } get mimeType() { return "video/mp4"; } } class QuickTimeInputFormat extends IsobmffInputFormat { async _canReadInput(input2) { const majorBrand = await this._getMajorBrand(input2); return majorBrand === "qt "; } get name() { return "QuickTime File Format"; } get mimeType() { return "video/quicktime"; } } class MatroskaInputFormat extends InputFormat { async isSupportedEBMLOfDocType(input2, desiredDocType) { let headerSlice = input2._reader.requestSlice(0, MAX_HEADER_SIZE); if (headerSlice instanceof Promise) headerSlice = await headerSlice; if (!headerSlice) return false; const varIntSize = readVarIntSize(headerSlice); if (varIntSize === null) { return false; } if (varIntSize < 1 || varIntSize > 8) { return false; } const id = readUnsignedInt(headerSlice, varIntSize); if (id !== EBMLId.EBML) { return false; } const dataSize = readElementSize(headerSlice); if (typeof dataSize !== "number") { return false; } let dataSlice = input2._reader.requestSlice(headerSlice.filePos, dataSize); if (dataSlice instanceof Promise) dataSlice = await dataSlice; if (!dataSlice) return false; const startPos = headerSlice.filePos; while (dataSlice.filePos <= startPos + dataSize - MIN_HEADER_SIZE) { const header2 = readElementHeader(dataSlice); if (!header2) break; const { id: id2, size: size4 } = header2; const dataStartPos = dataSlice.filePos; if (size4 === undefined) return false; switch (id2) { case EBMLId.EBMLVersion: { const ebmlVersion = readUnsignedInt(dataSlice, size4); if (ebmlVersion !== 1) { return false; } } ; break; case EBMLId.EBMLReadVersion: { const ebmlReadVersion = readUnsignedInt(dataSlice, size4); if (ebmlReadVersion !== 1) { return false; } } ; break; case EBMLId.DocType: { const docType = readAsciiString(dataSlice, size4); if (docType !== desiredDocType) { return false; } } ; break; case EBMLId.DocTypeVersion: { const docTypeVersion = readUnsignedInt(dataSlice, size4); if (docTypeVersion > 4) { return false; } } ; break; } dataSlice.filePos = dataStartPos + size4; } return true; } _canReadInput(input2) { return this.isSupportedEBMLOfDocType(input2, "matroska"); } _createDemuxer(input2) { return new MatroskaDemuxer(input2); } get name() { return "Matroska"; } get mimeType() { return "video/x-matroska"; } } class WebMInputFormat extends MatroskaInputFormat { _canReadInput(input2) { return this.isSupportedEBMLOfDocType(input2, "webm"); } get name() { return "WebM"; } get mimeType() { return "video/webm"; } } class Mp3InputFormat extends InputFormat { async _canReadInput(input2) { let slice = input2._reader.requestSlice(0, 10); if (slice instanceof Promise) slice = await slice; if (!slice) return false; let currentPos = 0; let id3V2HeaderFound = false; while (true) { let slice2 = input2._reader.requestSlice(currentPos, ID3_V2_HEADER_SIZE); if (slice2 instanceof Promise) slice2 = await slice2; if (!slice2) break; const id3V2Header = readId3V2Header(slice2); if (!id3V2Header) { break; } id3V2HeaderFound = true; currentPos = slice2.filePos + id3V2Header.size; } const firstResult = await readNextMp3FrameHeader(input2._reader, currentPos, currentPos + 4096); if (!firstResult) { return false; } if (id3V2HeaderFound) { return true; } currentPos = firstResult.startPos + firstResult.header.totalSize; const secondResult = await readNextMp3FrameHeader(input2._reader, currentPos, currentPos + FRAME_HEADER_SIZE); if (!secondResult) { return false; } const firstHeader = firstResult.header; const secondHeader = secondResult.header; if (firstHeader.channel !== secondHeader.channel || firstHeader.sampleRate !== secondHeader.sampleRate) { return false; } return true; } _createDemuxer(input2) { return new Mp3Demuxer(input2); } get name() { return "MP3"; } get mimeType() { return "audio/mpeg"; } } class WaveInputFormat extends InputFormat { async _canReadInput(input2) { let slice = input2._reader.requestSlice(0, 12); if (slice instanceof Promise) slice = await slice; if (!slice) return false; const riffType = readAscii(slice, 4); if (riffType !== "RIFF" && riffType !== "RIFX" && riffType !== "RF64") { return false; } slice.skip(4); const format = readAscii(slice, 4); return format === "WAVE"; } _createDemuxer(input2) { return new WaveDemuxer(input2); } get name() { return "WAVE"; } get mimeType() { return "audio/wav"; } } class OggInputFormat extends InputFormat { async _canReadInput(input2) { let slice = input2._reader.requestSlice(0, 4); if (slice instanceof Promise) slice = await slice; if (!slice) return false; return readAscii(slice, 4) === "OggS"; } _createDemuxer(input2) { return new OggDemuxer(input2); } get name() { return "Ogg"; } get mimeType() { return "application/ogg"; } } class FlacInputFormat extends InputFormat { async _canReadInput(input2) { let slice = input2._reader.requestSlice(0, 4); if (slice instanceof Promise) slice = await slice; if (!slice) return false; return readAscii(slice, 4) === "fLaC"; } get name() { return "FLAC"; } get mimeType() { return "audio/flac"; } _createDemuxer(input2) { return new FlacDemuxer(input2); } } class AdtsInputFormat extends InputFormat { async _canReadInput(input2) { let slice = input2._reader.requestSliceRange(0, MIN_ADTS_FRAME_HEADER_SIZE, MAX_ADTS_FRAME_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) return false; const firstHeader = readAdtsFrameHeader(slice); if (!firstHeader) { return false; } slice = input2._reader.requestSliceRange(firstHeader.frameLength, MIN_ADTS_FRAME_HEADER_SIZE, MAX_ADTS_FRAME_HEADER_SIZE); if (slice instanceof Promise) slice = await slice; if (!slice) return false; const secondHeader = readAdtsFrameHeader(slice); if (!secondHeader) { return false; } return firstHeader.objectType === secondHeader.objectType && firstHeader.samplingFrequencyIndex === secondHeader.samplingFrequencyIndex && firstHeader.channelConfiguration === secondHeader.channelConfiguration; } _createDemuxer(input2) { return new AdtsDemuxer(input2); } get name() { return "ADTS"; } get mimeType() { return "audio/aac"; } } class MpegTsInputFormat extends InputFormat { async _canReadInput(input2) { const lengthToCheck = TS_PACKET_SIZE + 16 + 1; let slice = input2._reader.requestSlice(0, lengthToCheck); if (slice instanceof Promise) slice = await slice; if (!slice) return false; const bytes = readBytes(slice, lengthToCheck); if (bytes[0] === 71 && bytes[TS_PACKET_SIZE] === 71) { return true; } else if (bytes[0] === 71 && bytes[TS_PACKET_SIZE + 16] === 71) { return true; } else if (bytes[4] === 71 && bytes[4 + TS_PACKET_SIZE] === 71) { return true; } return false; } _createDemuxer(input2) { return new MpegTsDemuxer(input2); } get name() { return "MPEG Transport Stream"; } get mimeType() { return "video/MP2T"; } } var MP4 = /* @__PURE__ */ new Mp4InputFormat; var QTFF = /* @__PURE__ */ new QuickTimeInputFormat; var MATROSKA = /* @__PURE__ */ new MatroskaInputFormat; var WEBM = /* @__PURE__ */ new WebMInputFormat; var MP3 = /* @__PURE__ */ new Mp3InputFormat; var WAVE = /* @__PURE__ */ new WaveInputFormat; var OGG = /* @__PURE__ */ new OggInputFormat; var ADTS = /* @__PURE__ */ new AdtsInputFormat; var FLAC = /* @__PURE__ */ new FlacInputFormat; var MPEG_TS = /* @__PURE__ */ new MpegTsInputFormat; var ALL_FORMATS = [MP4, QTFF, MATROSKA, WEBM, WAVE, OGG, FLAC, MP3, ADTS, MPEG_TS]; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/source.js var nodeAlias = (() => ({})); /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ class Source { constructor() { this._disposed = false; this._sizePromise = null; this.onread = null; } async getSizeOrNull() { if (this._disposed) { throw new InputDisposedError; } return this._sizePromise ??= Promise.resolve(this._retrieveSize()); } async getSize() { if (this._disposed) { throw new InputDisposedError; } const result = await this.getSizeOrNull(); if (result === null) { throw new Error("Cannot determine the size of an unsized source."); } return result; } } var URL_SOURCE_MIN_LOAD_AMOUNT = 0.5 * 2 ** 20; var DEFAULT_RETRY_DELAY = (previousAttempts, error, src) => { const couldBeCorsError = error instanceof Error && (error.message.includes("Failed to fetch") || error.message.includes("Load failed") || error.message.includes("NetworkError when attempting to fetch resource")); if (couldBeCorsError) { let originOfSrc = null; try { if (typeof window !== "undefined" && typeof window.location !== "undefined") { originOfSrc = new URL(src instanceof Request ? src.url : src, window.location.href).origin; } } catch {} const isOnline = typeof navigator !== "undefined" && typeof navigator.onLine === "boolean" ? navigator.onLine : true; if (isOnline && originOfSrc !== null && originOfSrc !== window.location.origin) { console.warn(`Request will not be retried because a CORS error was suspected due to different origins. You can` + ` modify this behavior by providing your own function for the 'getRetryDelay' option.`); return null; } } return Math.min(2 ** (previousAttempts - 2), 16); }; class UrlSource extends Source { constructor(url, options = {}) { if (typeof url !== "string" && !(url instanceof URL) && !(typeof Request !== "undefined" && url instanceof Request)) { throw new TypeError("url must be a string, URL or Request."); } if (!options || typeof options !== "object") { throw new TypeError("options must be an object."); } if (options.requestInit !== undefined && (!options.requestInit || typeof options.requestInit !== "object")) { throw new TypeError("options.requestInit, when provided, must be an object."); } if (options.getRetryDelay !== undefined && typeof options.getRetryDelay !== "function") { throw new TypeError("options.getRetryDelay, when provided, must be a function."); } if (options.maxCacheSize !== undefined && (!isNumber(options.maxCacheSize) || options.maxCacheSize < 0)) { throw new TypeError("options.maxCacheSize, when provided, must be a non-negative number."); } if (options.fetchFn !== undefined && typeof options.fetchFn !== "function") { throw new TypeError("options.fetchFn, when provided, must be a function."); } super(); this._existingResponses = new WeakMap; this._url = url; this._options = options; this._getRetryDelay = options.getRetryDelay ?? DEFAULT_RETRY_DELAY; this._orchestrator = new ReadOrchestrator({ maxCacheSize: options.maxCacheSize ?? 64 * 2 ** 20, maxWorkerCount: 2, runWorker: this._runWorker.bind(this), prefetchProfile: PREFETCH_PROFILES.network }); } async _retrieveSize() { const abortController = new AbortController; const response = await retriedFetch(this._options.fetchFn ?? fetch, this._url, mergeRequestInit(this._options.requestInit ?? {}, { headers: { Range: "bytes=0-" }, signal: abortController.signal }), this._getRetryDelay, () => this._disposed); if (!response.ok) { throw new Error(`Error fetching ${String(this._url)}: ${response.status} ${response.statusText}`); } let worker; let fileSize; if (response.status === 206) { fileSize = this._getTotalLengthFromRangeResponse(response); worker = this._orchestrator.createWorker(0, Math.min(fileSize, URL_SOURCE_MIN_LOAD_AMOUNT)); } else { const contentLength = response.headers.get("Content-Length"); if (contentLength) { fileSize = Number(contentLength); worker = this._orchestrator.createWorker(0, fileSize); this._orchestrator.options.maxCacheSize = Infinity; console.warn("HTTP server did not respond with 206 Partial Content, meaning the entire remote resource now has" + " to be downloaded. For efficient media file streaming across a network, please make sure your" + " server supports range requests."); } else { throw new Error(`HTTP response (status ${response.status}) must surface Content-Length header.`); } } this._orchestrator.fileSize = fileSize; this._existingResponses.set(worker, { response, abortController }); this._orchestrator.runWorker(worker); return fileSize; } _read(start, end) { return this._orchestrator.read(start, end); } async _runWorker(worker) { while (true) { const existing = this._existingResponses.get(worker); this._existingResponses.delete(worker); let abortController = existing?.abortController; let response = existing?.response; if (!abortController) { abortController = new AbortController; response = await retriedFetch(this._options.fetchFn ?? fetch, this._url, mergeRequestInit(this._options.requestInit ?? {}, { headers: { Range: `bytes=${worker.currentPos}-` }, signal: abortController.signal }), this._getRetryDelay, () => this._disposed); } assert(response); if (!response.ok) { throw new Error(`Error fetching ${String(this._url)}: ${response.status} ${response.statusText}`); } if (worker.currentPos > 0 && response.status !== 206) { throw new Error("HTTP server did not respond with 206 Partial Content to a range request. To enable efficient media" + " file streaming across a network, please make sure your server supports range requests."); } if (!response.body) { throw new Error("Missing HTTP response body stream. The used fetch function must provide the response body as a" + " ReadableStream."); } const reader = response.body.getReader(); while (true) { if (worker.currentPos >= worker.targetPos || worker.aborted) { abortController.abort(); worker.running = false; return; } let readResult; try { readResult = await reader.read(); } catch (error) { if (this._disposed) { throw error; } const retryDelayInSeconds = this._getRetryDelay(1, error, this._url); if (retryDelayInSeconds !== null) { console.error("Error while reading response stream. Attempting to resume.", error); await new Promise((resolve) => setTimeout(resolve, 1000 * retryDelayInSeconds)); break; } else { throw error; } } if (worker.aborted) { continue; } const { done, value } = readResult; if (done) { if (worker.currentPos >= worker.targetPos) { this._orchestrator.forgetWorker(worker); worker.running = false; return; } break; } this.onread?.(worker.currentPos, worker.currentPos + value.length); this._orchestrator.supplyWorkerData(worker, value); } } } _getTotalLengthFromRangeResponse(response) { const contentRange = response.headers.get("Content-Range"); if (contentRange) { const match = /\/(\d+)/.exec(contentRange); if (match) { return Number(match[1]); } } const contentLength = response.headers.get("Content-Length"); if (contentLength) { return Number(contentLength); } else { throw new Error("Partial HTTP response (status 206) must surface either Content-Range or" + " Content-Length header."); } } _dispose() { this._orchestrator.dispose(); } } var PREFETCH_PROFILES = { none: (start, end) => ({ start, end }), fileSystem: (start, end) => { const padding3 = 2 ** 16; start = Math.floor((start - padding3) / padding3) * padding3; end = Math.ceil((end + padding3) / padding3) * padding3; return { start, end }; }, network: (start, end, workers) => { const paddingStart = 2 ** 16; start = Math.max(0, Math.floor((start - paddingStart) / paddingStart) * paddingStart); for (const worker of workers) { const maxExtensionAmount = 8 * 2 ** 20; const thresholdPoint = Math.max((worker.startPos + worker.targetPos) / 2, worker.targetPos - maxExtensionAmount); if (closedIntervalsOverlap(start, end, thresholdPoint, worker.targetPos)) { const size4 = worker.targetPos - worker.startPos; const a = Math.ceil((size4 + 1) / maxExtensionAmount) * maxExtensionAmount; const b = 2 ** Math.ceil(Math.log2(size4 + 1)); const extent = Math.min(b, a); end = Math.max(end, worker.startPos + extent); } } end = Math.max(end, start + URL_SOURCE_MIN_LOAD_AMOUNT); return { start, end }; } }; class ReadOrchestrator { constructor(options) { this.options = options; this.fileSize = null; this.nextAge = 0; this.workers = []; this.cache = []; this.currentCacheSize = 0; this.disposed = false; } read(innerStart, innerEnd) { assert(this.fileSize !== null); const prefetchRange = this.options.prefetchProfile(innerStart, innerEnd, this.workers); const outerStart = Math.max(prefetchRange.start, 0); const outerEnd = Math.min(prefetchRange.end, this.fileSize); assert(outerStart <= innerStart && innerEnd <= outerEnd); let result = null; const innerCacheStartIndex = binarySearchLessOrEqual(this.cache, innerStart, (x) => x.start); const innerStartEntry = innerCacheStartIndex !== -1 ? this.cache[innerCacheStartIndex] : null; if (innerStartEntry && innerStartEntry.start <= innerStart && innerEnd <= innerStartEntry.end) { innerStartEntry.age = this.nextAge++; result = { bytes: innerStartEntry.bytes, view: innerStartEntry.view, offset: innerStartEntry.start }; } const outerCacheStartIndex = binarySearchLessOrEqual(this.cache, outerStart, (x) => x.start); const bytes = result ? null : new Uint8Array(innerEnd - innerStart); let contiguousBytesWriteEnd = 0; let lastEnd = outerStart; const outerHoles = []; if (outerCacheStartIndex !== -1) { for (let i = outerCacheStartIndex;i < this.cache.length; i++) { const entry = this.cache[i]; if (entry.start >= outerEnd) { break; } if (entry.end <= outerStart) { continue; } const cappedOuterStart = Math.max(outerStart, entry.start); const cappedOuterEnd = Math.min(outerEnd, entry.end); assert(cappedOuterStart <= cappedOuterEnd); if (lastEnd < cappedOuterStart) { outerHoles.push({ start: lastEnd, end: cappedOuterStart }); } lastEnd = cappedOuterEnd; if (bytes) { const cappedInnerStart = Math.max(innerStart, entry.start); const cappedInnerEnd = Math.min(innerEnd, entry.end); if (cappedInnerStart < cappedInnerEnd) { const relativeOffset = cappedInnerStart - innerStart; bytes.set(entry.bytes.subarray(cappedInnerStart - entry.start, cappedInnerEnd - entry.start), relativeOffset); if (relativeOffset === contiguousBytesWriteEnd) { contiguousBytesWriteEnd = cappedInnerEnd - innerStart; } } } entry.age = this.nextAge++; } if (lastEnd < outerEnd) { outerHoles.push({ start: lastEnd, end: outerEnd }); } } else { outerHoles.push({ start: outerStart, end: outerEnd }); } if (bytes && contiguousBytesWriteEnd >= bytes.length) { result = { bytes, view: toDataView(bytes), offset: innerStart }; } if (outerHoles.length === 0) { assert(result); return result; } const { promise, resolve, reject } = promiseWithResolvers(); const innerHoles = []; for (const outerHole of outerHoles) { const cappedStart = Math.max(innerStart, outerHole.start); const cappedEnd = Math.min(innerEnd, outerHole.end); if (cappedStart === outerHole.start && cappedEnd === outerHole.end) { innerHoles.push(outerHole); } else if (cappedStart < cappedEnd) { innerHoles.push({ start: cappedStart, end: cappedEnd }); } } for (const outerHole of outerHoles) { const pendingSlice = bytes && { start: innerStart, bytes, holes: innerHoles, resolve, reject }; let workerFound = false; for (const worker of this.workers) { const gapTolerance = 2 ** 17; if (closedIntervalsOverlap(outerHole.start - gapTolerance, outerHole.start, worker.currentPos, worker.targetPos)) { worker.targetPos = Math.max(worker.targetPos, outerHole.end); workerFound = true; if (pendingSlice && !worker.pendingSlices.includes(pendingSlice)) { worker.pendingSlices.push(pendingSlice); } if (!worker.running) { this.runWorker(worker); } break; } } if (!workerFound) { const newWorker = this.createWorker(outerHole.start, outerHole.end); if (pendingSlice) { newWorker.pendingSlices = [pendingSlice]; } this.runWorker(newWorker); } } if (!result) { assert(bytes); result = promise.then((bytes2) => ({ bytes: bytes2, view: toDataView(bytes2), offset: innerStart })); } else {} return result; } createWorker(startPos, targetPos) { const worker = { startPos, currentPos: startPos, targetPos, running: false, aborted: this.disposed, pendingSlices: [], age: this.nextAge++ }; this.workers.push(worker); while (this.workers.length > this.options.maxWorkerCount) { let oldestIndex = 0; let oldestWorker = this.workers[0]; for (let i = 1;i < this.workers.length; i++) { const worker2 = this.workers[i]; if (worker2.age < oldestWorker.age) { oldestIndex = i; oldestWorker = worker2; } } if (oldestWorker.running && oldestWorker.pendingSlices.length > 0) { break; } oldestWorker.aborted = true; this.workers.splice(oldestIndex, 1); } return worker; } runWorker(worker) { assert(!worker.running); assert(worker.currentPos < worker.targetPos); worker.running = true; worker.age = this.nextAge++; this.options.runWorker(worker).catch((error) => { worker.running = false; if (worker.pendingSlices.length > 0) { worker.pendingSlices.forEach((x) => x.reject(error)); worker.pendingSlices.length = 0; } else { throw error; } }); } supplyWorkerData(worker, bytes) { assert(!worker.aborted); const start = worker.currentPos; const end = start + bytes.length; this.insertIntoCache({ start, end, bytes, view: toDataView(bytes), age: this.nextAge++ }); worker.currentPos += bytes.length; worker.targetPos = Math.max(worker.targetPos, worker.currentPos); for (let i = 0;i < worker.pendingSlices.length; i++) { const pendingSlice = worker.pendingSlices[i]; const clampedStart = Math.max(start, pendingSlice.start); const clampedEnd = Math.min(end, pendingSlice.start + pendingSlice.bytes.length); if (clampedStart < clampedEnd) { pendingSlice.bytes.set(bytes.subarray(clampedStart - start, clampedEnd - start), clampedStart - pendingSlice.start); } for (let j = 0;j < pendingSlice.holes.length; j++) { const hole = pendingSlice.holes[j]; if (start <= hole.start && end > hole.start) { hole.start = end; } if (hole.end <= hole.start) { pendingSlice.holes.splice(j, 1); j--; } } if (pendingSlice.holes.length === 0) { pendingSlice.resolve(pendingSlice.bytes); worker.pendingSlices.splice(i, 1); i--; } } for (let i = 0;i < this.workers.length; i++) { const otherWorker = this.workers[i]; if (worker === otherWorker || otherWorker.running) { continue; } if (closedIntervalsOverlap(start, end, otherWorker.currentPos, otherWorker.targetPos)) { this.workers.splice(i, 1); i--; } } } forgetWorker(worker) { const index = this.workers.indexOf(worker); assert(index !== -1); this.workers.splice(index, 1); } insertIntoCache(entry) { if (this.options.maxCacheSize === 0) { return; } let insertionIndex = binarySearchLessOrEqual(this.cache, entry.start, (x) => x.start) + 1; if (insertionIndex > 0) { const previous = this.cache[insertionIndex - 1]; if (previous.end >= entry.end) { return; } if (previous.end > entry.start) { const joined = new Uint8Array(entry.end - previous.start); joined.set(previous.bytes, 0); joined.set(entry.bytes, entry.start - previous.start); this.currentCacheSize += entry.end - previous.end; previous.bytes = joined; previous.view = toDataView(joined); previous.end = entry.end; insertionIndex--; entry = previous; } else { this.cache.splice(insertionIndex, 0, entry); this.currentCacheSize += entry.bytes.length; } } else { this.cache.splice(insertionIndex, 0, entry); this.currentCacheSize += entry.bytes.length; } for (let i = insertionIndex + 1;i < this.cache.length; i++) { const next = this.cache[i]; if (entry.end <= next.start) { break; } if (entry.end >= next.end) { this.cache.splice(i, 1); this.currentCacheSize -= next.bytes.length; i--; continue; } const joined = new Uint8Array(next.end - entry.start); joined.set(entry.bytes, 0); joined.set(next.bytes, next.start - entry.start); this.currentCacheSize -= entry.end - next.start; entry.bytes = joined; entry.view = toDataView(joined); entry.end = next.end; this.cache.splice(i, 1); break; } while (this.currentCacheSize > this.options.maxCacheSize) { let oldestIndex = 0; let oldestEntry = this.cache[0]; for (let i = 1;i < this.cache.length; i++) { const entry2 = this.cache[i]; if (entry2.age < oldestEntry.age) { oldestIndex = i; oldestEntry = entry2; } } if (this.currentCacheSize - oldestEntry.bytes.length <= this.options.maxCacheSize) { break; } this.cache.splice(oldestIndex, 1); this.currentCacheSize -= oldestEntry.bytes.length; } } dispose() { for (const worker of this.workers) { worker.aborted = true; } this.workers.length = 0; this.cache.length = 0; this.disposed = true; } } // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/input.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ polyfillSymbolDispose(); class Input { get disposed() { return this._disposed; } constructor(options) { this._demuxerPromise = null; this._format = null; this._disposed = false; if (!options || typeof options !== "object") { throw new TypeError("options must be an object."); } if (!Array.isArray(options.formats) || options.formats.some((x) => !(x instanceof InputFormat))) { throw new TypeError("options.formats must be an array of InputFormat."); } if (!(options.source instanceof Source)) { throw new TypeError("options.source must be a Source."); } if (options.source._disposed) { throw new Error("options.source must not be disposed."); } this._formats = options.formats; this._source = options.source; this._reader = new Reader(options.source); } _getDemuxer() { return this._demuxerPromise ??= (async () => { this._reader.fileSize = await this._source.getSizeOrNull(); for (const format of this._formats) { const canRead = await format._canReadInput(this); if (canRead) { this._format = format; return format._createDemuxer(this); } } throw new Error("Input has an unsupported or unrecognizable format."); })(); } get source() { return this._source; } async getFormat() { await this._getDemuxer(); assert(this._format); return this._format; } async computeDuration() { const demuxer = await this._getDemuxer(); return demuxer.computeDuration(); } async getFirstTimestamp() { const tracks = await this.getTracks(); if (tracks.length === 0) { return 0; } const firstTimestamps = await Promise.all(tracks.map((x) => x.getFirstTimestamp())); return Math.min(...firstTimestamps); } async getTracks() { const demuxer = await this._getDemuxer(); return demuxer.getTracks(); } async getVideoTracks() { const tracks = await this.getTracks(); return tracks.filter((x) => x.isVideoTrack()); } async getAudioTracks() { const tracks = await this.getTracks(); return tracks.filter((x) => x.isAudioTrack()); } async getPrimaryVideoTrack() { const tracks = await this.getTracks(); return tracks.find((x) => x.isVideoTrack()) ?? null; } async getPrimaryAudioTrack() { const tracks = await this.getTracks(); return tracks.find((x) => x.isAudioTrack()) ?? null; } async getMimeType() { const demuxer = await this._getDemuxer(); return demuxer.getMimeType(); } async getMetadataTags() { const demuxer = await this._getDemuxer(); return demuxer.getMetadataTags(); } dispose() { if (this._disposed) { return; } this._disposed = true; this._source._disposed = true; this._source._dispose(); } [Symbol.dispose]() { this.dispose(); } } class InputDisposedError extends Error { constructor(message = "Input has been disposed.") { super(message); this.name = "InputDisposedError"; } } // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/reader.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ class Reader { constructor(source) { this.source = source; } requestSlice(start, length) { if (this.source._disposed) { throw new InputDisposedError; } if (start < 0) { return null; } if (this.fileSize !== null && start + length > this.fileSize) { return null; } const end = start + length; const result = this.source._read(start, end); if (result instanceof Promise) { return result.then((x) => { if (!x) { return null; } return new FileSlice(x.bytes, x.view, x.offset, start, end); }); } else { if (!result) { return null; } return new FileSlice(result.bytes, result.view, result.offset, start, end); } } requestSliceRange(start, minLength, maxLength) { if (this.source._disposed) { throw new InputDisposedError; } if (start < 0) { return null; } if (this.fileSize !== null) { return this.requestSlice(start, clamp(this.fileSize - start, minLength, maxLength)); } else { const promisedAttempt = this.requestSlice(start, maxLength); const handleAttempt = (attempt) => { if (attempt) { return attempt; } const handleFileSize = (fileSize) => { assert(fileSize !== null); return this.requestSlice(start, clamp(fileSize - start, minLength, maxLength)); }; const promisedFileSize = this.source._retrieveSize(); if (promisedFileSize instanceof Promise) { return promisedFileSize.then(handleFileSize); } else { return handleFileSize(promisedFileSize); } }; if (promisedAttempt instanceof Promise) { return promisedAttempt.then(handleAttempt); } else { return handleAttempt(promisedAttempt); } } } } class FileSlice { constructor(bytes, view, offset, start, end) { this.bytes = bytes; this.view = view; this.offset = offset; this.start = start; this.end = end; this.bufferPos = start - offset; } static tempFromBytes(bytes) { return new FileSlice(bytes, toDataView(bytes), 0, 0, bytes.length); } get length() { return this.end - this.start; } get filePos() { return this.offset + this.bufferPos; } set filePos(value) { this.bufferPos = value - this.offset; } get remainingLength() { return Math.max(this.end - this.filePos, 0); } skip(byteCount) { this.bufferPos += byteCount; } slice(filePos, length = this.end - filePos) { if (filePos < this.start || filePos + length > this.end) { throw new RangeError("Slicing outside of original slice."); } return new FileSlice(this.bytes, this.view, this.offset, filePos, filePos + length); } } var checkIsInRange = (slice, bytesToRead) => { if (slice.filePos < slice.start || slice.filePos + bytesToRead > slice.end) { throw new RangeError(`Tried reading [${slice.filePos}, ${slice.filePos + bytesToRead}), but slice is` + ` [${slice.start}, ${slice.end}). This is likely an internal error, please report it alongside the file` + ` that caused it.`); } }; var readBytes = (slice, length) => { checkIsInRange(slice, length); const bytes = slice.bytes.subarray(slice.bufferPos, slice.bufferPos + length); slice.bufferPos += length; return bytes; }; var readU8 = (slice) => { checkIsInRange(slice, 1); return slice.view.getUint8(slice.bufferPos++); }; var readU16 = (slice, littleEndian) => { checkIsInRange(slice, 2); const value = slice.view.getUint16(slice.bufferPos, littleEndian); slice.bufferPos += 2; return value; }; var readU16Be = (slice) => { checkIsInRange(slice, 2); const value = slice.view.getUint16(slice.bufferPos, false); slice.bufferPos += 2; return value; }; var readU24Be = (slice) => { checkIsInRange(slice, 3); const value = getUint24(slice.view, slice.bufferPos, false); slice.bufferPos += 3; return value; }; var readI16Be = (slice) => { checkIsInRange(slice, 2); const value = slice.view.getInt16(slice.bufferPos, false); slice.bufferPos += 2; return value; }; var readU32 = (slice, littleEndian) => { checkIsInRange(slice, 4); const value = slice.view.getUint32(slice.bufferPos, littleEndian); slice.bufferPos += 4; return value; }; var readU32Be = (slice) => { checkIsInRange(slice, 4); const value = slice.view.getUint32(slice.bufferPos, false); slice.bufferPos += 4; return value; }; var readU32Le = (slice) => { checkIsInRange(slice, 4); const value = slice.view.getUint32(slice.bufferPos, true); slice.bufferPos += 4; return value; }; var readI32Be = (slice) => { checkIsInRange(slice, 4); const value = slice.view.getInt32(slice.bufferPos, false); slice.bufferPos += 4; return value; }; var readI32Le = (slice) => { checkIsInRange(slice, 4); const value = slice.view.getInt32(slice.bufferPos, true); slice.bufferPos += 4; return value; }; var readU64 = (slice, littleEndian) => { let low; let high; if (littleEndian) { low = readU32(slice, true); high = readU32(slice, true); } else { high = readU32(slice, false); low = readU32(slice, false); } return high * 4294967296 + low; }; var readU64Be = (slice) => { const high = readU32Be(slice); const low = readU32Be(slice); return high * 4294967296 + low; }; var readI64Be = (slice) => { const high = readI32Be(slice); const low = readU32Be(slice); return high * 4294967296 + low; }; var readI64Le = (slice) => { const low = readU32Le(slice); const high = readI32Le(slice); return high * 4294967296 + low; }; var readF32Be = (slice) => { checkIsInRange(slice, 4); const value = slice.view.getFloat32(slice.bufferPos, false); slice.bufferPos += 4; return value; }; var readF64Be = (slice) => { checkIsInRange(slice, 8); const value = slice.view.getFloat64(slice.bufferPos, false); slice.bufferPos += 8; return value; }; var readAscii = (slice, length) => { checkIsInRange(slice, length); let str = ""; for (let i = 0;i < length; i++) { str += String.fromCharCode(slice.bytes[slice.bufferPos++]); } return str; }; // ../../node_modules/.bun/mediabunny@1.29.0/node_modules/mediabunny/dist/modules/src/index.js /*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ // src/helpers/use-max-media-duration.ts import { useEffect as useEffect68, useState as useState67 } from "react"; var cache = new Map; var getSrc = (s) => { if (s.type === "video") { return s.src; } if (s.type === "audio") { return s.src; } return null; }; var useMaxMediaDuration = (s, fps) => { const src = getSrc(s); const [maxMediaDuration, setMaxMediaDuration] = useState67(src ? cache.get(src) ?? null : Infinity); useEffect68(() => { if (!src) { return; } const input2 = new Input({ formats: ALL_FORMATS, source: new UrlSource(src) }); input2.computeDuration().then((duration) => { cache.set(src, Math.floor(duration * fps)); setMaxMediaDuration(Math.floor(duration * fps)); }).catch((e) => { if (e instanceof InputDisposedError) { return; } return getVideoMetadata2(src).then((metadata) => { const durationOrInfinity = metadata.durationInSeconds ?? Infinity; cache.set(src, Math.floor(durationOrInfinity * fps)); setMaxMediaDuration(Math.floor(durationOrInfinity * fps)); }).catch(() => {}); }); return () => { input2.dispose(); }; }, [src, fps]); if (maxMediaDuration !== null && (s.type === "audio" || s.type === "video")) { return maxMediaDuration / s.playbackRate; } return maxMediaDuration; }; // src/components/AudioWaveform.tsx import { getAudioData, getWaveformPortion } from "@remotion/media-utils"; import { useEffect as useEffect69, useMemo as useMemo106, useRef as useRef39, useState as useState68 } from "react"; import { Internals as Internals51 } from "remotion"; // src/components/AudioWaveformBar.tsx import { useMemo as useMemo105 } from "react"; import { jsx as jsx195 } from "react/jsx-runtime"; var WAVEFORM_BAR_LENGTH = 2; var WAVEFORM_BAR_MARGIN = 1; var container42 = { width: WAVEFORM_BAR_LENGTH, backgroundColor: "rgba(255, 255, 255, 0.6)", marginLeft: WAVEFORM_BAR_MARGIN, borderRadius: 2 }; var AudioWaveformBar = ({ amplitude }) => { const style11 = useMemo105(() => { return { ...container42, height: getTimelineLayerHeight("other") * amplitude * (1 / 0.6366) }; }, [amplitude]); return /* @__PURE__ */ jsx195("div", { style: style11 }); }; // src/components/AudioWaveform.tsx import { jsx as jsx196, jsxs as jsxs95 } from "react/jsx-runtime"; var container43 = { display: "flex", flexDirection: "row", alignItems: "flex-end", position: "absolute", height: getTimelineLayerHeight("other") }; var errorMessage = { fontSize: 13, paddingTop: 6, paddingBottom: 6, paddingLeft: 12, paddingRight: 12, alignSelf: "flex-start", maxWidth: 450, opacity: 0.75 }; var canvasStyle = { position: "absolute" }; var AudioWaveform = ({ src, startFrom, durationInFrames, visualizationWidth, volume, doesVolumeChange, playbackRate }) => { const [metadata, setMetadata] = useState68(null); const [error, setError] = useState68(null); const mountState = useRef39({ isMounted: true }); const vidConf = Internals51.useUnsafeVideoConfig(); if (vidConf === null) { throw new Error("Expected video config"); } const canvas = useRef39(null); useEffect69(() => { const { current } = mountState; current.isMounted = true; return () => { current.isMounted = false; }; }, []); useEffect69(() => { if (!canvas.current) { return; } const context = canvas.current.getContext("2d"); if (!context) { return; } context.clearRect(0, 0, visualizationWidth, getTimelineLayerHeight("other")); if (!doesVolumeChange || typeof volume === "number") { return; } const volumes = volume.split(",").map((v) => Number(v)); context.beginPath(); context.moveTo(0, getTimelineLayerHeight("other")); volumes.forEach((v, index) => { const x = index / (volumes.length - 1) * visualizationWidth; const y = (1 - v) * (getTimelineLayerHeight("other") - TIMELINE_BORDER * 2) + 1; if (index === 0) { context.moveTo(x, y); } else { context.lineTo(x, y); } }); context.strokeStyle = LIGHT_TRANSPARENT; context.stroke(); }, [visualizationWidth, metadata, startFrom, volume, doesVolumeChange]); useEffect69(() => { setError(null); getAudioData(src).then((data) => { if (mountState.current.isMounted) { setMetadata(data); } }).catch((err) => { if (mountState.current.isMounted) { setError(err); } }); }, [src, vidConf.fps]); const normalized = useMemo106(() => { if (!metadata || metadata.numberOfChannels === 0) { return []; } const numberOfSamples = Math.floor(visualizationWidth / (WAVEFORM_BAR_LENGTH + WAVEFORM_BAR_MARGIN)); return getWaveformPortion({ audioData: metadata, startTimeInSeconds: startFrom / vidConf.fps, durationInSeconds: Math.min(durationInFrames / vidConf.fps * playbackRate, metadata.durationInSeconds), numberOfSamples, normalize: false }); }, [ durationInFrames, vidConf.fps, metadata, playbackRate, startFrom, visualizationWidth ]); if (error) { return /* @__PURE__ */ jsx196("div", { style: container43, children: /* @__PURE__ */ jsx196("div", { style: errorMessage, children: "No waveform available. Audio might not support CORS." }) }); } if (!metadata) { return null; } return /* @__PURE__ */ jsxs95("div", { style: container43, children: [ normalized.map((w) => { return /* @__PURE__ */ jsx196(AudioWaveformBar, { amplitude: w.amplitude * (typeof volume === "number" ? volume : 1) }, w.index); }), /* @__PURE__ */ jsx196("canvas", { ref: canvas, style: canvasStyle, width: visualizationWidth, height: getTimelineLayerHeight("other") }) ] }); }; // src/components/Timeline/LoopedTimelineIndicators.tsx import React136 from "react"; import { AbsoluteFill as AbsoluteFill4 } from "remotion"; // src/components/Timeline/LoopedIndicator.tsx import { AbsoluteFill as AbsoluteFill3 } from "remotion"; import { jsx as jsx197, jsxs as jsxs96 } from "react/jsx-runtime"; var width = { width: 0, flexDirection: "row", display: "flex", position: "relative" }; var icon5 = { height: 12 }; var Icon = () => /* @__PURE__ */ jsx197("svg", { viewBox: "0 0 512 512", style: icon5, children: /* @__PURE__ */ jsx197("path", { fill: LIGHT_COLOR, d: "M512 256c0 88.224-71.775 160-160 160H170.067l34.512 32.419c9.875 9.276 10.119 24.883.539 34.464l-10.775 10.775c-9.373 9.372-24.568 9.372-33.941 0l-92.686-92.686c-9.373-9.373-9.373-24.568 0-33.941l92.686-92.686c9.373-9.373 24.568-9.373 33.941 0l10.775 10.775c9.581 9.581 9.337 25.187-.539 34.464L170.067 352H352c52.935 0 96-43.065 96-96 0-13.958-2.996-27.228-8.376-39.204-4.061-9.039-2.284-19.626 4.723-26.633l12.183-12.183c11.499-11.499 30.965-8.526 38.312 5.982C505.814 205.624 512 230.103 512 256zM72.376 295.204C66.996 283.228 64 269.958 64 256c0-52.935 43.065-96 96-96h181.933l-34.512 32.419c-9.875 9.276-10.119 24.883-.539 34.464l10.775 10.775c9.373 9.372 24.568 9.372 33.941 0l92.686-92.686c9.373-9.373 9.373-24.568 0-33.941l-92.686-92.686c-9.373-9.373-24.568-9.373-33.941 0L306.882 29.12c-9.581 9.581-9.337 25.187.539 34.464L341.933 96H160C71.775 96 0 167.776 0 256c0 25.897 6.186 50.376 17.157 72.039 7.347 14.508 26.813 17.481 38.312 5.982l12.183-12.183c7.008-7.008 8.786-17.595 4.724-26.634z" }) }); var topLine = { top: 0, height: 2, width: 1, background: LIGHT_COLOR }; var bottomLine = { top: 0, height: 2, width: 1, background: LIGHT_COLOR }; var topContainer = { justifyContent: "flex-start", alignItems: "center" }; var centerContainer = { justifyContent: "center", alignItems: "center" }; var bottomContainer = { justifyContent: "flex-end", alignItems: "center" }; var LoopedIndicator = () => { return /* @__PURE__ */ jsxs96("div", { style: width, children: [ /* @__PURE__ */ jsx197(AbsoluteFill3, { style: topContainer, children: /* @__PURE__ */ jsx197("div", { style: topLine }) }), /* @__PURE__ */ jsx197(AbsoluteFill3, { style: bottomContainer, children: /* @__PURE__ */ jsx197("div", { style: bottomLine }) }), /* @__PURE__ */ jsx197(AbsoluteFill3, { style: centerContainer, children: /* @__PURE__ */ jsx197(Icon, {}) }) ] }); }; // src/components/Timeline/LoopedTimelineIndicators.tsx import { jsx as jsx198, jsxs as jsxs97 } from "react/jsx-runtime"; var row6 = { flexDirection: "row" }; var LoopedTimelineIndicator = ({ loops }) => { const leftOver = loops % 1; return /* @__PURE__ */ jsxs97(AbsoluteFill4, { style: row6, children: [ new Array(Math.floor(loops)).fill(true).map((_l, i) => { return /* @__PURE__ */ jsxs97(React136.Fragment, { children: [ /* @__PURE__ */ jsx198(Flex, {}), i === loops - 1 ? null : /* @__PURE__ */ jsx198(LoopedIndicator, {}) ] }, i); }), leftOver > 0 ? /* @__PURE__ */ jsx198("div", { style: { flex: leftOver } }) : null ] }); }; // src/components/Timeline/TimelineSequenceFrame.tsx import { jsx as jsx199 } from "react/jsx-runtime"; var relativeFrameStyle = { fontSize: 11, fontFamily: "Arial, Helvetica, sans-serif", color: "white", opacity: 0.5 }; var TimelineSequenceFrame = ({ roundedFrame, premounted, postmounted }) => { return /* @__PURE__ */ jsx199("div", { style: relativeFrameStyle, children: premounted ? "0 (Premounted)" : postmounted !== null ? `${postmounted} (Postmounted)` : roundedFrame }); }; // src/components/Timeline/TimelineVideoInfo.tsx import { useEffect as useEffect70, useRef as useRef40, useState as useState69 } from "react"; import { useVideoConfig as useVideoConfig5 } from "remotion"; // src/helpers/extract-frames.ts async function extractFrames({ src, timestampsInSeconds, onVideoSample, signal }) { const input2 = new Input({ formats: ALL_FORMATS, source: new UrlSource(src) }); const dispose = () => { input2.dispose(); }; if (signal) { signal.addEventListener("abort", dispose, { once: true }); } try { const [durationInSeconds, format, videoTrack] = await Promise.all([ input2.computeDuration(), input2.getFormat(), input2.getPrimaryVideoTrack() ]); if (!videoTrack) { throw new Error("No video track found in the input"); } const timestamps = typeof timestampsInSeconds === "function" ? await timestampsInSeconds({ track: { width: videoTrack.displayWidth, height: videoTrack.displayHeight }, container: format.name, durationInSeconds }) : timestampsInSeconds; if (timestamps.length === 0) { return; } const sink = new VideoSampleSink(videoTrack); for await (const videoSample of sink.samplesAtTimestamps(timestamps)) { if (signal?.aborted) { videoSample?.close(); break; } if (!videoSample) { continue; } onVideoSample(videoSample); } } catch (error) { if (error instanceof InputDisposedError) { return; } throw error; } finally { dispose(); if (signal) { signal.removeEventListener("abort", dispose); } } } // src/helpers/frame-database.ts var KEY_SEPARATOR = "|"; var makeFrameDatabaseKey = (src, timestamp) => `${src}${KEY_SEPARATOR}${timestamp}`; var getFrameDatabaseKeyPrefix = (src) => { return `${src}${KEY_SEPARATOR}`; }; var frameDatabase = new Map; var aspectRatioCache = new Map; var getTimestampFromFrameDatabaseKey = (key4) => { const split = key4.split(KEY_SEPARATOR); return Number(split[split.length - 1]); }; var getAspectRatioFromCache = (src) => { const cached = aspectRatioCache.get(src); if (cached) { return cached; } return null; }; var MAX_FRAMES_IN_CACHE = 12340; var clearOldFrames = () => { if (frameDatabase.size <= MAX_FRAMES_IN_CACHE) { return; } const framesToRemove = Array.from(frameDatabase.entries()).sort((a, b) => a[1].lastUsed - b[1].lastUsed); for (const [key4, frame2] of framesToRemove.slice(0, framesToRemove.length - MAX_FRAMES_IN_CACHE)) { frame2.frame.close(); frameDatabase.delete(key4); } }; var clearFramesForSrc = (src) => { const keysToRemove = []; const prefix = getFrameDatabaseKeyPrefix(src); for (const [key4, frame2] of frameDatabase.entries()) { if (key4.startsWith(prefix)) { frame2.frame.close(); keysToRemove.push(key4); } } for (const key4 of keysToRemove) { frameDatabase.delete(key4); } }; // src/helpers/resize-video-frame.ts var calculateNewDimensionsFromScale = ({ width: width2, height, scale }) => { const scaledWidth = Math.round(width2 * scale); const scaledHeight = Math.round(height * scale); return { width: scaledWidth, height: scaledHeight }; }; var resizeVideoFrame = ({ frame: frame2, scale }) => { if (scale === 1) { return frame2; } const { width: width2, height } = calculateNewDimensionsFromScale({ height: frame2.displayHeight, width: frame2.displayWidth, scale }); const canvas = new OffscreenCanvas(width2, height); const ctx = canvas.getContext("2d"); if (!ctx) { throw new Error("Could not get 2d context"); } canvas.width = width2; canvas.height = height; ctx.scale(scale, scale); ctx.drawImage(frame2, 0, 0); return new VideoFrame(canvas, { displayHeight: height, displayWidth: width2, duration: frame2.duration ?? undefined, timestamp: frame2.timestamp }); }; // src/components/Timeline/TimelineVideoInfo.tsx import { jsx as jsx200 } from "react/jsx-runtime"; var HEIGHT = getTimelineLayerHeight("video") - 2; var containerStyle3 = { height: HEIGHT, width: "100%", backgroundColor: "rgba(0, 0, 0, 0.3)", display: "flex", borderTopLeftRadius: 2, borderBottomLeftRadius: 2, fontSize: 10, fontFamily: "Arial, Helvetica" }; var WEBCODECS_TIMESCALE = 1e6; var MAX_TIME_DEVIATION = WEBCODECS_TIMESCALE * 0.05; var getDurationOfOneFrame = ({ visualizationWidth, aspectRatio, segmentDuration }) => { const framesFitInWidthUnrounded = visualizationWidth / (HEIGHT * aspectRatio); return segmentDuration / framesFitInWidthUnrounded * WEBCODECS_TIMESCALE; }; var fixRounding = (value) => { if (value % 1 >= 0.49999999) { return Math.ceil(value); } return Math.floor(value); }; var calculateTimestampSlots = ({ visualizationWidth, fromSeconds, segmentDuration, aspectRatio }) => { const framesFitInWidthUnrounded = visualizationWidth / (HEIGHT * aspectRatio); const framesFitInWidth = Math.ceil(framesFitInWidthUnrounded); const durationOfOneFrame = getDurationOfOneFrame({ visualizationWidth, aspectRatio, segmentDuration }); const timestampTargets = []; for (let i = 0;i < framesFitInWidth + 1; i++) { const target = fromSeconds * WEBCODECS_TIMESCALE + durationOfOneFrame * (i + 0.5); const snappedToDuration = (Math.round(fixRounding(target / durationOfOneFrame)) - 1) * durationOfOneFrame; timestampTargets.push(snappedToDuration); } return timestampTargets; }; var ensureSlots = ({ filledSlots, naturalWidth, fromSeconds, toSeconds, aspectRatio }) => { const segmentDuration = toSeconds - fromSeconds; const timestampTargets = calculateTimestampSlots({ visualizationWidth: naturalWidth, fromSeconds, segmentDuration, aspectRatio }); for (const timestamp of timestampTargets) { if (!filledSlots.has(timestamp)) { filledSlots.set(timestamp, undefined); } } }; var drawSlot = ({ frame: frame2, ctx, filledSlots, visualizationWidth, timestamp, segmentDuration, fromSeconds }) => { const durationOfOneFrame = getDurationOfOneFrame({ visualizationWidth, aspectRatio: frame2.displayWidth / frame2.displayHeight, segmentDuration }); const relativeTimestamp = timestamp - fromSeconds * WEBCODECS_TIMESCALE; const frameIndex = relativeTimestamp / durationOfOneFrame; const left3 = Math.floor(frameIndex * frame2.displayWidth / window.devicePixelRatio); ctx.drawImage(frame2, left3, 0, frame2.displayWidth / window.devicePixelRatio, frame2.displayHeight / window.devicePixelRatio); filledSlots.set(timestamp, frame2.timestamp); }; var fillWithCachedFrames = ({ ctx, naturalWidth, filledSlots, src, segmentDuration, fromSeconds }) => { const prefix = getFrameDatabaseKeyPrefix(src); const keys = Array.from(frameDatabase.keys()).filter((k) => k.startsWith(prefix)); const targets = Array.from(filledSlots.keys()); for (const timestamp of targets) { let bestKey; let bestDistance = Infinity; for (const key4 of keys) { const distance = Math.abs(getTimestampFromFrameDatabaseKey(key4) - timestamp); if (distance < bestDistance) { bestDistance = distance; bestKey = key4; } } if (!bestKey) { continue; } const frame2 = frameDatabase.get(bestKey); if (!frame2) { continue; } const alreadyFilled = filledSlots.get(timestamp); if (alreadyFilled && Math.abs(alreadyFilled - timestamp) <= Math.abs(frame2.frame.timestamp - timestamp)) { continue; } frame2.lastUsed = Date.now(); drawSlot({ ctx, frame: frame2.frame, filledSlots, visualizationWidth: naturalWidth, timestamp, segmentDuration, fromSeconds }); } }; var fillFrameWhereItFits = ({ frame: frame2, filledSlots, ctx, visualizationWidth, segmentDuration, fromSeconds }) => { const slots = Array.from(filledSlots.keys()); for (let i = 0;i < slots.length; i++) { const slot = slots[i]; if (Math.abs(slot - frame2.timestamp) > MAX_TIME_DEVIATION) { continue; } const filled = filledSlots.get(slot); if (filled && Math.abs(filled - slot) <= Math.abs(filled - frame2.timestamp)) { continue; } drawSlot({ ctx, frame: frame2, filledSlots, visualizationWidth, timestamp: slot, segmentDuration, fromSeconds }); } }; var TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore, durationInFrames, playbackRate }) => { const { fps } = useVideoConfig5(); const ref = useRef40(null); const [error, setError] = useState69(null); const aspectRatio = useRef40(getAspectRatioFromCache(src)); useEffect70(() => { return () => { clearFramesForSrc(src); }; }, [src]); useEffect70(() => { if (error) { return; } const { current } = ref; if (!current) { return; } const controller = new AbortController; const canvas = document.createElement("canvas"); canvas.width = visualizationWidth; canvas.height = HEIGHT; const ctx = canvas.getContext("2d"); if (!ctx) { return; } current.appendChild(canvas); const filledSlots = new Map; const fromSeconds = trimBefore / fps; const toSeconds = fromSeconds + durationInFrames * playbackRate / fps; if (aspectRatio.current !== null) { ensureSlots({ filledSlots, naturalWidth, fromSeconds, toSeconds, aspectRatio: aspectRatio.current }); fillWithCachedFrames({ ctx, naturalWidth, filledSlots, src, segmentDuration: toSeconds - fromSeconds, fromSeconds }); const unfilled = Array.from(filledSlots.keys()).filter((timestamp) => !filledSlots.get(timestamp)); if (unfilled.length === 0) { return () => { current.removeChild(canvas); clearOldFrames(); }; } } clearOldFrames(); extractFrames({ timestampsInSeconds: ({ track }) => { aspectRatio.current = track.width / track.height; aspectRatioCache.set(src, aspectRatio.current); ensureSlots({ filledSlots, fromSeconds, toSeconds, naturalWidth, aspectRatio: aspectRatio.current }); return Array.from(filledSlots.keys()).map((timestamp) => timestamp / WEBCODECS_TIMESCALE); }, src, onVideoSample: (sample) => { const frame2 = sample.toVideoFrame(); const scale = HEIGHT / frame2.displayHeight * window.devicePixelRatio; const transformed = resizeVideoFrame({ frame: frame2, scale }); if (transformed !== frame2) { frame2.close(); } const databaseKey = makeFrameDatabaseKey(src, transformed.timestamp); const existingFrame = frameDatabase.get(databaseKey); if (existingFrame) { existingFrame.frame.close(); } frameDatabase.set(databaseKey, { frame: transformed, lastUsed: Date.now() }); if (aspectRatio.current === null) { throw new Error("Aspect ratio is not set"); } ensureSlots({ filledSlots, fromSeconds, toSeconds, naturalWidth, aspectRatio: aspectRatio.current }); fillFrameWhereItFits({ ctx, filledSlots, visualizationWidth: naturalWidth, frame: transformed, segmentDuration: toSeconds - fromSeconds, fromSeconds }); sample.close(); }, signal: controller.signal }).then(() => { fillWithCachedFrames({ ctx, naturalWidth, filledSlots, src, segmentDuration: toSeconds - fromSeconds, fromSeconds }); }).catch((e) => { setError(e); }).finally(() => { clearOldFrames(); }); return () => { controller.abort(); current.removeChild(canvas); }; }, [ durationInFrames, error, fps, naturalWidth, playbackRate, src, trimBefore, visualizationWidth ]); return /* @__PURE__ */ jsx200("div", { ref, style: containerStyle3 }); }; // src/components/Timeline/TimelineSequence.tsx import { jsx as jsx201, jsxs as jsxs98 } from "react/jsx-runtime"; var AUDIO_GRADIENT = "linear-gradient(rgb(16 171 58), rgb(43 165 63) 60%)"; var VIDEO_GRADIENT = "linear-gradient(to top, #8e44ad, #9b59b6)"; var TimelineSequence = ({ s }) => { const windowWidth = useContext68(TimelineWidthContext); if (windowWidth === null) { return null; } return /* @__PURE__ */ jsx201(Inner4, { windowWidth, s }); }; var Inner4 = ({ s, windowWidth }) => { const video = Internals52.useVideo(); const maxMediaDuration = useMaxMediaDuration(s, video?.fps ?? 30); if (!video) { throw new TypeError("Expected video config"); } const frame2 = useCurrentFrame2(); const relativeFrame = frame2 - s.from; const relativeFrameWithPremount = relativeFrame + (s.premountDisplay ?? 0); const relativeFrameWithPostmount = relativeFrame - s.duration; const roundedFrame = Math.round(relativeFrame * 100) / 100; const isInRange = relativeFrame >= 0 && relativeFrame < s.duration; const isPremounting = relativeFrameWithPremount >= 0 && relativeFrameWithPremount < s.duration && !isInRange; const isPostmounting = relativeFrameWithPostmount >= 0 && relativeFrameWithPostmount < (s.postmountDisplay ?? 0) && !isInRange; const { marginLeft, width: width2, naturalWidth, premountWidth, postmountWidth } = useMemo107(() => { return getTimelineSequenceLayout({ durationInFrames: s.loopDisplay ? s.loopDisplay.durationInFrames * s.loopDisplay.numberOfTimes : s.duration, startFrom: s.loopDisplay ? s.from + s.loopDisplay.startOffset : s.from, startFromMedia: s.type === "sequence" ? 0 : s.startMediaFrom, maxMediaDuration, video, windowWidth, premountDisplay: s.premountDisplay, postmountDisplay: s.postmountDisplay }); }, [maxMediaDuration, s, video, windowWidth]); const style11 = useMemo107(() => { return { background: s.type === "audio" ? AUDIO_GRADIENT : s.type === "video" ? VIDEO_GRADIENT : BLUE, border: SEQUENCE_BORDER_WIDTH + "px solid rgba(255, 255, 255, 0.2)", borderRadius: 2, position: "absolute", height: getTimelineLayerHeight(s.type === "video" ? "video" : "other"), marginLeft, width: width2, color: "white", overflow: "hidden", opacity: isInRange ? 1 : 0.5 }; }, [isInRange, marginLeft, s.type, width2]); if (maxMediaDuration === null) { return null; } return /* @__PURE__ */ jsxs98("div", { style: style11, title: s.displayName, children: [ premountWidth ? /* @__PURE__ */ jsx201("div", { style: { width: premountWidth, height: "100%", background: `repeating-linear-gradient( -45deg, transparent, transparent 2px, rgba(255, 255, 255, ${isPremounting ? 0.5 : 0.2}) 2px, rgba(255, 255, 255, ${isPremounting ? 0.5 : 0.2}) 4px )`, position: "absolute" } }) : null, postmountWidth ? /* @__PURE__ */ jsx201("div", { style: { width: postmountWidth, height: "100%", background: `repeating-linear-gradient( -45deg, transparent, transparent 2px, rgba(255, 255, 255, ${isPostmounting ? 0.5 : 0.2}) 2px, rgba(255, 255, 255, ${isPostmounting ? 0.5 : 0.2}) 4px )`, position: "absolute", right: 0 } }) : null, s.type === "audio" ? /* @__PURE__ */ jsx201(AudioWaveform, { src: s.src, doesVolumeChange: s.doesVolumeChange, visualizationWidth: width2, startFrom: s.startMediaFrom, durationInFrames: s.duration, volume: s.volume, playbackRate: s.playbackRate }) : null, s.type === "video" ? /* @__PURE__ */ jsx201(TimelineVideoInfo, { src: s.src, visualizationWidth: width2, naturalWidth, trimBefore: s.startMediaFrom, durationInFrames: s.duration, playbackRate: s.playbackRate }) : null, s.loopDisplay === undefined ? null : /* @__PURE__ */ jsx201(LoopedTimelineIndicator, { loops: s.loopDisplay.numberOfTimes }), s.type !== "audio" && s.type !== "video" && s.loopDisplay === undefined && (isInRange || isPremounting || isPostmounting) ? /* @__PURE__ */ jsx201("div", { style: { paddingLeft: 5 + (premountWidth ?? 0), height: "100%", display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx201(TimelineSequenceFrame, { premounted: isPremounting, postmounted: isPostmounting ? s.duration - 1 : null, roundedFrame }) }) : null ] }, s.id); }; // src/components/Timeline/is-collapsed.ts var isTrackHidden = (track) => { if (!track.sequence.parent) { return false; } return !track.sequence.showInTimeline; }; // src/components/Timeline/TimelineTracks.tsx import { jsx as jsx202, jsxs as jsxs99 } from "react/jsx-runtime"; var content = { paddingLeft: TIMELINE_PADDING, paddingRight: TIMELINE_PADDING, paddingTop: 1 }; var timelineContent = { minHeight: "100%" }; var TimelineTracks = ({ timeline, hasBeenCut }) => { const timelineStyle = useMemo108(() => { return { ...timelineContent, width: 100 + "%" }; }, []); return /* @__PURE__ */ jsxs99("div", { style: timelineStyle, children: [ /* @__PURE__ */ jsxs99("div", { style: content, children: [ /* @__PURE__ */ jsx202(TimelineTimePadding, {}), timeline.map((track) => { if (isTrackHidden(track)) { return null; } return /* @__PURE__ */ jsx202("div", { style: { height: getTimelineLayerHeight(track.sequence.type === "video" ? "video" : "other"), marginBottom: TIMELINE_ITEM_BORDER_BOTTOM }, children: /* @__PURE__ */ jsx202(TimelineSequence, { s: track.sequence }) }, track.sequence.id); }) ] }), hasBeenCut ? /* @__PURE__ */ jsx202(MaxTimelineTracksReached, {}) : null ] }); }; // src/components/Timeline/Timeline.tsx import { jsx as jsx203, jsxs as jsxs100 } from "react/jsx-runtime"; var container44 = { minHeight: "100%", flex: 1, display: "flex", height: 0, overflowY: "auto", backgroundColor: BACKGROUND }; var noop3 = () => { return; }; var Timeline = () => { const { sequences } = useContext69(Internals53.SequenceManager); const videoConfig = Internals53.useUnsafeVideoConfig(); const timeline = useMemo109(() => { if (!videoConfig) { return []; } return calculateTimeline({ sequences, sequenceDuration: videoConfig.durationInFrames }); }, [sequences, videoConfig]); const durationInFrames = videoConfig?.durationInFrames ?? 0; const filtered = useMemo109(() => { const withoutHidden = timeline.filter((t) => !isTrackHidden(t)); const withoutAfter = withoutHidden.filter((t) => { return t.sequence.from <= durationInFrames && t.sequence.duration > 0; }); return withoutAfter.filter((t) => t.sequence.showInTimeline); }, [durationInFrames, timeline]); const shown = filtered.slice(0, MAX_TIMELINE_TRACKS); const hasBeenCut = filtered.length > shown.length; const inner2 = useMemo109(() => { return { height: shown.reduce((acc, track) => { return acc + getTimelineLayerHeight(track.sequence.type === "video" ? "video" : "other") + Number(TIMELINE_ITEM_BORDER_BOTTOM); }, 0) + TIMELINE_ITEM_BORDER_BOTTOM + (hasBeenCut ? MAX_TIMELINE_TRACKS_NOTICE_HEIGHT : 0) + TIMELINE_TIME_INDICATOR_HEIGHT, display: "flex", flex: 1, minHeight: "100%", overflowX: "hidden" }; }, [hasBeenCut, shown]); return /* @__PURE__ */ jsx203("div", { ref: timelineVerticalScroll, style: container44, className: "css-reset " + VERTICAL_SCROLLBAR_CLASSNAME, children: /* @__PURE__ */ jsx203(TimelineWidthProvider, { children: /* @__PURE__ */ jsx203("div", { style: inner2, children: /* @__PURE__ */ jsxs100(SplitterContainer, { orientation: "vertical", defaultFlex: 0.2, id: "names-to-timeline", maxFlex: 0.5, minFlex: 0.15, children: [ /* @__PURE__ */ jsx203(SplitterElement, { type: "flexer", sticky: /* @__PURE__ */ jsx203(TimelineTimePlaceholders, {}), children: /* @__PURE__ */ jsx203(TimelineList, { timeline: shown }) }), /* @__PURE__ */ jsx203(SplitterHandle, { onCollapse: noop3, allowToCollapse: "none" }), /* @__PURE__ */ jsx203(SplitterElement, { type: "anti-flexer", sticky: null, children: /* @__PURE__ */ jsxs100(TimelineScrollable, { children: [ /* @__PURE__ */ jsx203(TimelineTracks, { timeline: shown, hasBeenCut }), /* @__PURE__ */ jsx203(TimelineInOutPointer, {}), /* @__PURE__ */ jsx203(TimelinePlayCursorSyncer, {}), /* @__PURE__ */ jsx203(TimelineDragHandler, {}), /* @__PURE__ */ jsx203(TimelineTimeIndicators, {}), /* @__PURE__ */ jsx203(TimelineSlider, {}) ] }) }) ] }) }) }) }); }; // src/components/EditorContent.tsx import { jsx as jsx204, jsxs as jsxs101, Fragment as Fragment29 } from "react/jsx-runtime"; var noop4 = () => { return; }; var container45 = { display: "flex", flexDirection: "column", flex: 1, height: 0 }; var EditorContent = ({ readOnlyStudio, children }) => { const isStill = useIsStill(); const { canvasContent } = useContext70(Internals54.CompositionManager); const onlyTopPanel = canvasContent === null || isStill || canvasContent.type !== "composition"; return /* @__PURE__ */ jsxs101("div", { style: container45, children: [ /* @__PURE__ */ jsx204(InitialCompositionLoader, {}), /* @__PURE__ */ jsx204(MenuToolbar, { readOnlyStudio }), /* @__PURE__ */ jsxs101(SplitterContainer, { orientation: "horizontal", id: "top-to-bottom", maxFlex: 0.9, minFlex: 0.2, defaultFlex: 0.75, children: [ /* @__PURE__ */ jsx204(SplitterElement, { sticky: null, type: "flexer", children }), onlyTopPanel ? null : /* @__PURE__ */ jsxs101(Fragment29, { children: [ /* @__PURE__ */ jsx204(SplitterHandle, { allowToCollapse: "none", onCollapse: noop4 }), /* @__PURE__ */ jsx204(SplitterElement, { sticky: null, type: "anti-flexer", children: /* @__PURE__ */ jsx204(Timeline, {}) }) ] }) ] }) ] }); }; // src/components/GlobalKeybindings.tsx import { useContext as useContext71, useEffect as useEffect71 } from "react"; var GlobalKeybindings = () => { const keybindings = useKeybinding(); const { setSelectedModal } = useContext71(ModalsContext); const { setCheckerboard } = useContext71(CheckerboardContext); const { navigateToNextComposition, navigateToPreviousComposition } = useCompositionNavigation(); useEffect71(() => { const nKey = keybindings.registerKeybinding({ event: "keypress", key: "n", callback: () => { showNotification(`To make a new composition, right-click an existing one and select "Duplicate"`, 5000); }, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const cmdKKey = keybindings.registerKeybinding({ event: "keydown", key: "k", callback: () => { setSelectedModal({ type: "quick-switcher", mode: "compositions", invocationTimestamp: Date.now() }); }, triggerIfInputFieldFocused: true, keepRegisteredWhenNotHighestContext: false, commandCtrlKey: true, preventDefault: true }); const cmdIKey = process.env.ASK_AI_ENABLED ? keybindings.registerKeybinding({ event: "keydown", key: "i", callback: () => { askAiModalRef.current?.toggle(); }, triggerIfInputFieldFocused: true, keepRegisteredWhenNotHighestContext: true, commandCtrlKey: true, preventDefault: true }) : null; const cKey = keybindings.registerKeybinding({ event: "keypress", key: "t", callback: () => { setCheckerboard((c) => !c); }, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const questionMark = keybindings.registerKeybinding({ event: "keypress", key: "?", callback: () => { setSelectedModal({ type: "quick-switcher", mode: "docs", invocationTimestamp: Date.now() }); }, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const pageDown = keybindings.registerKeybinding({ event: "keydown", key: "PageDown", callback: navigateToNextComposition, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); const pageUp = keybindings.registerKeybinding({ event: "keydown", key: "PageUp", callback: navigateToPreviousComposition, commandCtrlKey: false, preventDefault: true, triggerIfInputFieldFocused: false, keepRegisteredWhenNotHighestContext: false }); return () => { nKey.unregister(); cKey.unregister(); questionMark.unregister(); cmdKKey.unregister(); cmdIKey?.unregister(); pageDown.unregister(); pageUp.unregister(); }; }, [ keybindings, setCheckerboard, setSelectedModal, navigateToNextComposition, navigateToPreviousComposition ]); return null; }; // src/components/Modals.tsx import { useContext as useContext85 } from "react"; // src/components/InstallPackage.tsx import { apiDocs, descriptions, extraPackages, installableMap } from "@remotion/studio-shared"; import React144, { useCallback as useCallback100, useContext as useContext73, useEffect as useEffect72 } from "react"; import { VERSION as VERSION2 } from "remotion"; // src/api/install-package.ts import { getRemotionEnvironment as getRemotionEnvironment4 } from "remotion"; var installPackages = (packageNames) => { if (!getRemotionEnvironment4().isStudio) { throw new Error("installPackages() is only available in the Studio"); } if (window.remotion_isReadOnlyStudio) { throw new Error("installPackages() is not available in Read-Only Studio"); } return callApi("/api/install-package", { packageNames }); }; // src/components/InstallablePackage.tsx import { jsx as jsx205, jsxs as jsxs102 } from "react/jsx-runtime"; var FONT_SIZE = 13; var InstallablePackageComp = ({ isInstalled, pkg, link, description }) => { return /* @__PURE__ */ jsxs102("div", { style: { fontSize: FONT_SIZE, lineHeight: 1.2, paddingBottom: 4, paddingTop: 4 }, children: [ /* @__PURE__ */ jsx205("a", { href: link, style: { fontSize: FONT_SIZE, color: TEXT_COLOR, textDecoration: "none" }, target: "_blank", children: pkg }), " ", isInstalled ? /* @__PURE__ */ jsx205("span", { style: { opacity: 0.3, fontSize: "inherit" }, children: "(installed)" }) : null, /* @__PURE__ */ jsx205("br", {}), /* @__PURE__ */ jsx205("span", { style: { color: LIGHT_TEXT, fontSize: FONT_SIZE }, children: description }) ] }); }; // src/components/ModalButton.tsx import { useMemo as useMemo110 } from "react"; import { jsx as jsx206 } from "react/jsx-runtime"; var buttonStyle5 = { backgroundColor: BLUE, color: "white" }; var ModalButton = (props) => { const style11 = useMemo110(() => { return { ...buttonStyle5, backgroundColor: props.disabled ? BLUE_DISABLED : BLUE }; }, [props.disabled]); return /* @__PURE__ */ jsx206(Button, { ...props, style: style11 }); }; // src/components/ModalFooter.tsx import { jsx as jsx207 } from "react/jsx-runtime"; var content2 = { padding: 12, paddingRight: 12, flex: 1, fontSize: 13, minWidth: 500 }; var ModalFooterContainer = ({ children }) => { return /* @__PURE__ */ jsx207("div", { style: { ...content2, borderTop: "1px solid black" }, children }); }; // src/components/NewComposition/DismissableModal.tsx import { useCallback as useCallback99, useContext as useContext72 } from "react"; import { jsx as jsx208 } from "react/jsx-runtime"; var DismissableModal = ({ children }) => { const { setSelectedModal } = useContext72(ModalsContext); const onQuit = useCallback99(() => { setSelectedModal(null); }, [setSelectedModal]); return /* @__PURE__ */ jsx208(ModalContainer, { onOutsideClick: onQuit, onEscape: onQuit, children }); }; // src/components/InstallPackage.tsx import { jsx as jsx209, jsxs as jsxs103 } from "react/jsx-runtime"; var container46 = { padding: 20, maxHeight: 400, overflowY: "auto" }; var text2 = { fontSize: 14 }; var InstallPackageModal = ({ packageManager }) => { const [state, setState] = React144.useState({ type: "idle" }); const [map, setMap] = React144.useState({}); const { previewServerState: ctx } = useContext73(StudioServerConnectionCtx); const selectedPackages = Object.keys(map).filter((pkg) => map[pkg]); const onClick = useCallback100(async () => { if (state.type === "done") { setState({ type: "restarting" }); restartStudio(); return; } setState({ type: "installing" }); try { await installPackages(selectedPackages); setState({ type: "done" }); } catch (err) { setState({ type: "error", error: err }); } }, [selectedPackages, state.type]); const canSelectPackages = state.type === "idle" && ctx.type === "connected"; const disabled = !(canSelectPackages || state.type === "done") || selectedPackages.length === 0; const { registerKeybinding } = useKeybinding(); useEffect72(() => { if (disabled) { return; } const enter = registerKeybinding({ callback() { onClick(); }, commandCtrlKey: true, key: "Enter", event: "keydown", preventDefault: true, triggerIfInputFieldFocused: true, keepRegisteredWhenNotHighestContext: true }); return () => { enter.unregister(); }; }, [disabled, onClick, registerKeybinding]); return /* @__PURE__ */ jsxs103(DismissableModal, { children: [ /* @__PURE__ */ jsx209(ModalHeader, { title: "Install packages" }), /* @__PURE__ */ jsx209("div", { style: container46, className: VERTICAL_SCROLLBAR_CLASSNAME, children: state.type === "done" ? /* @__PURE__ */ jsxs103("div", { style: text2, children: [ "Installed package", selectedPackages.length === 1 ? "" : "s", " ", "successfully. Restart the server to complete." ] }) : state.type === "restarting" ? /* @__PURE__ */ jsx209("div", { style: text2, children: "Restarting the Studio server..." }) : state.type === "installing" ? /* @__PURE__ */ jsxs103("div", { style: text2, children: [ "Installing package", selectedPackages.length === 1 ? "" : "s", ". Check your terminal for progress." ] }) : /* @__PURE__ */ jsxs103("div", { style: text2, children: [ Object.entries(installableMap).filter(([, install]) => install).map(([pkgShort]) => { const pkg = pkgShort === "core" ? "remotion" : `@remotion/${pkgShort}`; const isInstalled = window.remotion_installedPackages?.includes(pkg) ?? false; const link = apiDocs[pkgShort]; const description = descriptions[pkgShort]; if (!link) { throw new Error("No link for " + pkg); } if (!description) { throw new Error("No description for " + pkg); } return /* @__PURE__ */ jsxs103(Row, { align: "center", children: [ /* @__PURE__ */ jsx209(Checkbox, { checked: map[pkg], name: pkg, onChange: () => { setMap((prev) => ({ ...prev, [pkg]: !prev[pkg] })); }, disabled: !canSelectPackages || isInstalled }), /* @__PURE__ */ jsx209(Spacing, { x: 1.5 }), /* @__PURE__ */ jsx209(InstallablePackageComp, { description, isInstalled, link, pkg }) ] }, pkg); }), extraPackages.map((extraPkg) => { const isInstalled = window.remotion_installedPackages?.includes(extraPkg.name) ?? false; return /* @__PURE__ */ jsxs103(Row, { align: "center", children: [ /* @__PURE__ */ jsx209(Checkbox, { checked: map[extraPkg.name], name: extraPkg.name, onChange: () => { setMap((prev) => ({ ...prev, [extraPkg.name]: !prev[extraPkg.name] })); }, disabled: !canSelectPackages || isInstalled }), /* @__PURE__ */ jsx209(Spacing, { x: 1.5 }), /* @__PURE__ */ jsx209(InstallablePackageComp, { description: extraPkg.description, isInstalled, link: extraPkg.docsUrl, pkg: `${extraPkg.name}@${extraPkg.version}` }) ] }, extraPkg.name); }) ] }) }), /* @__PURE__ */ jsx209(ModalFooterContainer, { children: /* @__PURE__ */ jsxs103(Row, { align: "center", children: [ state.type === "idle" ? /* @__PURE__ */ jsxs103("span", { style: { color: LIGHT_TEXT, fontSize: 13, lineHeight: 1.2 }, children: [ "This will install ", selectedPackages.length, " package", selectedPackages.length === 1 ? "" : "s", /* @__PURE__ */ jsx209("br", {}), "using ", packageManager, ", Remotion v", VERSION2 ] }) : null, /* @__PURE__ */ jsx209(Flex, {}), /* @__PURE__ */ jsxs103(ModalButton, { onClick, disabled, children: [ state.type === "restarting" ? "Restarting..." : state.type === "installing" ? "Installing..." : state.type === "done" ? "Restart Server" : "Install", disabled ? null : /* @__PURE__ */ jsx209(ShortcutHint, { keyToPress: "↵", cmdOrCtrl: true }) ] }) ] }) }) ] }); }; // src/components/NewComposition/DeleteComposition.tsx import { useCallback as useCallback102, useContext as useContext76, useMemo as useMemo112 } from "react"; // src/components/RenderModal/ResolveCompositionBeforeModal.tsx import React145, { useContext as useContext74, useEffect as useEffect73, useMemo as useMemo111 } from "react"; import { Internals as Internals55 } from "remotion"; import { jsx as jsx210, jsxs as jsxs104 } from "react/jsx-runtime"; var loaderContainer2 = { paddingTop: 40, paddingBottom: 40, paddingLeft: 100, paddingRight: 100, display: "flex", justifyContent: "center", alignItems: "center", flexDirection: "column" }; var loaderLabel2 = { fontSize: 14, color: LIGHT_TEXT, fontFamily: "sans-serif", lineHeight: 1.5 }; var ResolvedCompositionContext = React145.createContext(null); var ResolveCompositionBeforeModal = ({ compositionId, children }) => { const resolved = Internals55.useResolvedVideoConfig(compositionId); const unresolvedContext = useContext74(Internals55.CompositionManager); const unresolved = unresolvedContext.compositions.find((c) => compositionId === c.id); useEffect73(() => { const { current } = Internals55.resolveCompositionsRef; if (!current) { throw new Error("No ref to trigger composition calc"); } current.setCurrentRenderModalComposition(compositionId); return () => { current.setCurrentRenderModalComposition(null); }; }, [compositionId]); if (!unresolved) { throw new Error("Composition not found: " + compositionId); } const value = useMemo111(() => { return { resolved, unresolved }; }, [resolved, unresolved]); if (!resolved || resolved.type === "loading") { return /* @__PURE__ */ jsx210(RunningCalculateMetadata, {}); } if (resolved.type === "error") { return /* @__PURE__ */ jsxs104("div", { style: loaderContainer2, children: [ /* @__PURE__ */ jsx210(Spacing, { y: 2 }), /* @__PURE__ */ jsxs104("div", { style: loaderLabel2, children: [ "Running ", /* @__PURE__ */ jsx210("code", { style: inlineCodeSnippet, children: "calculateMetadata()" }), " ", "yielded an error:" ] }), /* @__PURE__ */ jsx210(Spacing, { y: 1 }), /* @__PURE__ */ jsx210("div", { style: loaderLabel2, children: resolved.error.message || "Unknown error" }) ] }); } return /* @__PURE__ */ jsx210(ResolvedCompositionContext.Provider, { value, children }); }; // src/components/NewComposition/CodemodFooter.tsx import { useCallback as useCallback101, useContext as useContext75, useEffect as useEffect74, useState as useState70 } from "react"; // src/components/NewComposition/DiffPreview.tsx import { jsx as jsx211, jsxs as jsxs105 } from "react/jsx-runtime"; var CodemodDiffPreview = ({ status }) => { if (status.type === "loading") { return null; } if (status.type === "fail") { return /* @__PURE__ */ jsx211("span", { style: { color: FAIL_COLOR, fontSize: 13, lineHeight: 1.2 }, children: status.error }); } return /* @__PURE__ */ jsxs105("div", { style: { lineHeight: 1.2 }, children: [ /* @__PURE__ */ jsx211("span", { style: { color: LIGHT_TEXT, fontSize: 13, lineHeight: 1.2 }, children: "This will edit your Root file." }), /* @__PURE__ */ jsx211("br", {}), /* @__PURE__ */ jsxs105("span", { style: { color: BLUE, fontSize: 13, lineHeight: 1.2 }, children: [ status.diff.additions, " addition", status.diff.additions === 1 ? "" : "s", "," ] }), " ", /* @__PURE__ */ jsxs105("span", { style: { color: SELECTED_GUIDE, fontSize: 13, lineHeight: 1.2 }, children: [ status.diff.deletions, " deletion", status.diff.deletions === 1 ? "" : "s" ] }) ] }); }; // src/components/NewComposition/CodemodFooter.tsx import { jsx as jsx212, jsxs as jsxs106 } from "react/jsx-runtime"; var CodemodFooter = ({ codemod, valid, loadingNotification, successNotification, errorNotification, genericSubmitLabel, submitLabel }) => { const [submitting, setSubmitting] = useState70(false); const { setSelectedModal } = useContext75(ModalsContext); const [codemodStatus, setCanApplyCodemod] = useState70({ type: "loading" }); const [projectInfo, setProjectInfo] = useState70(null); useEffect74(() => { const controller = new AbortController; getProjectInfo(controller.signal).then((info) => { setProjectInfo(info.projectInfo); }).catch((err) => { showNotification(`Could not get project info: ${err.message}. Unable to duplicate composition`, 3000); }); return () => { controller.abort(); }; }, []); const trigger = useCallback101(() => { setSubmitting(true); setSelectedModal(null); const notification2 = showNotification(loadingNotification, null); applyCodemod({ codemod, dryRun: false, signal: new AbortController().signal }).then(() => { notification2.replaceContent(successNotification, 2000); }).catch((err) => { notification2.replaceContent(`${errorNotification}: ${err.message}`, 2000); }); }, [ codemod, errorNotification, loadingNotification, setSelectedModal, successNotification ]); const getCanApplyCodemod = useCallback101(async (signal) => { const res = await applyCodemod({ codemod, dryRun: true, signal }); if (res.success) { setCanApplyCodemod({ type: "success", diff: res.diff }); } else { setCanApplyCodemod({ type: "fail", error: res.reason }); } }, [codemod]); useEffect74(() => { const abortController = new AbortController; let aborted = false; getCanApplyCodemod(abortController.signal).then(() => { return; }).catch((err) => { if (aborted) { return; } showNotification(`Cannot duplicate composition: ${err.message}`, 3000); }); return () => { aborted = true; abortController.abort(); }; }, [codemodStatus, getCanApplyCodemod, setSelectedModal]); const disabled = !valid || submitting || projectInfo === null || codemodStatus.type !== "success"; const { registerKeybinding } = useKeybinding(); useEffect74(() => { if (disabled) { return; } const enter = registerKeybinding({ callback() { trigger(); }, commandCtrlKey: true, key: "Enter", event: "keydown", preventDefault: true, triggerIfInputFieldFocused: true, keepRegisteredWhenNotHighestContext: false }); return () => { enter.unregister(); }; }, [disabled, registerKeybinding, trigger, valid]); return /* @__PURE__ */ jsxs106(Row, { align: "center", children: [ /* @__PURE__ */ jsx212(CodemodDiffPreview, { status: codemodStatus }), /* @__PURE__ */ jsx212(Flex, {}), /* @__PURE__ */ jsx212(Spacing, { block: true, x: 2 }), /* @__PURE__ */ jsxs106(ModalButton, { onClick: trigger, disabled, children: [ projectInfo && projectInfo.relativeRootFile ? submitLabel({ relativeRootPath: projectInfo.relativeRootFile }) : genericSubmitLabel, /* @__PURE__ */ jsx212(ShortcutHint, { keyToPress: "↵", cmdOrCtrl: true }) ] }) ] }); }; // src/components/NewComposition/DeleteComposition.tsx import { jsx as jsx213, jsxs as jsxs107, Fragment as Fragment30 } from "react/jsx-runtime"; var content3 = { padding: 16, fontSize: 14, flex: 1, minWidth: 500 }; var DeleteCompositionLoaded = ({ compositionId }) => { const context = useContext76(ResolvedCompositionContext); if (!context) { throw new Error("Resolved composition context"); } const { unresolved } = context; const codemod = useMemo112(() => { return { type: "delete-composition", idToDelete: compositionId }; }, [compositionId]); const onSubmit = useCallback102((e) => { e.preventDefault(); }, []); return /* @__PURE__ */ jsxs107(Fragment30, { children: [ /* @__PURE__ */ jsx213(ModalHeader, { title: "Delete composition" }), /* @__PURE__ */ jsxs107("form", { onSubmit, children: [ /* @__PURE__ */ jsxs107("div", { style: content3, children: [ "Do you want to delete the", " ", /* @__PURE__ */ jsx213("code", { style: inlineCodeSnippet, children: unresolved.durationInFrames === 1 ? `` : "" }), " ", "with ID ", '"', unresolved.id, '"', "?", /* @__PURE__ */ jsx213("br", {}), "The associated ", /* @__PURE__ */ jsx213("code", { style: inlineCodeSnippet, children: "component" }), " will remain in your code." ] }), /* @__PURE__ */ jsx213(ModalFooterContainer, { children: /* @__PURE__ */ jsx213(CodemodFooter, { errorNotification: `Could not delete composition`, loadingNotification: "Deleting", successNotification: `Deleted ${unresolved.id}`, genericSubmitLabel: `Delete`, submitLabel: ({ relativeRootPath }) => `Delete from ${relativeRootPath}`, codemod, valid: true }) }) ] }) ] }); }; var DeleteComposition = ({ compositionId }) => { return /* @__PURE__ */ jsx213(DismissableModal, { children: /* @__PURE__ */ jsx213(ResolveCompositionBeforeModal, { compositionId, children: /* @__PURE__ */ jsx213(DeleteCompositionLoaded, { compositionId }) }) }); }; // src/components/NewComposition/DuplicateComposition.tsx import { useCallback as useCallback104, useContext as useContext77, useMemo as useMemo113, useState as useState71 } from "react"; import { Internals as Internals57 } from "remotion"; // src/helpers/validate-new-comp-data.ts import { Internals as Internals56 } from "remotion"; var validateCompositionName = (compName, compositions) => { if (!Internals56.isCompositionIdValid(compName)) { return Internals56.invalidCompositionErrorMessage; } if (compositions.find((c) => c.id === compName)) { return `A composition with that name already exists.`; } return null; }; var validateCompositionDimension = (dimension, value) => { if (Number(value) % 2 !== 0) { return `${dimension} should be divisible by 2, since H264 codec doesn't support odd dimensions.`; } if (Number.isNaN(Number(value))) { return "Invalid number."; } if (Number(value) === 0) { return dimension + " cannot be zero."; } return null; }; var validateCompositionDuration = (value) => { if (value % 1 !== 0) { return `Duration must be an integer.`; } if (Number.isNaN(value)) { return "Invalid number."; } if (value === 0) { return "Duration cannot be zero."; } return null; }; // src/components/NewComposition/NewCompDuration.tsx import { useCallback as useCallback103 } from "react"; import { jsx as jsx214, jsxs as jsxs108, Fragment as Fragment31 } from "react/jsx-runtime"; var NewCompDuration = ({ durationInFrames, setDurationInFrames }) => { const onDurationInFramesChanged = useCallback103((newValue) => { setDurationInFrames(Number(newValue)); }, [setDurationInFrames]); const onDurationChangedDirectly = useCallback103((newVal) => { setDurationInFrames(newVal); }, [setDurationInFrames]); const compDurationErrMessage = validateCompositionDuration(durationInFrames); return /* @__PURE__ */ jsxs108("div", { style: optionRow, children: [ /* @__PURE__ */ jsx214("div", { style: label5, children: "Duration in frames" }), /* @__PURE__ */ jsxs108("div", { style: rightRow, children: [ /* @__PURE__ */ jsx214(InputDragger, { type: "number", value: durationInFrames, onTextChange: onDurationInFramesChanged, placeholder: "Duration (frames)", name: "durationInFrames", min: 1, step: 1, required: true, status: "ok", max: 300000, onValueChange: onDurationChangedDirectly, rightAlign: false }), compDurationErrMessage ? /* @__PURE__ */ jsxs108(Fragment31, { children: [ /* @__PURE__ */ jsx214(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx214(ValidationMessage, { align: "flex-start", message: compDurationErrMessage, type: "error" }) ] }) : null ] }) ] }); }; // src/components/NewComposition/DuplicateComposition.tsx import { jsx as jsx215, jsxs as jsxs109, Fragment as Fragment32 } from "react/jsx-runtime"; var content4 = { padding: 12, paddingRight: 12, flex: 1, fontSize: 13, minWidth: 500 }; var comboBoxStyle = { width: 190 }; var DuplicateCompositionLoaded = ({ initialType }) => { const context = useContext77(ResolvedCompositionContext); if (!context) { throw new Error("Resolved composition context"); } const { resolved, unresolved } = context; const [initialCompType] = useState71(initialType); const hadDimensionsDefined = unresolved.width && unresolved.height; const hadFpsDefined = unresolved.fps !== undefined; const hadDurationDefined = unresolved.durationInFrames !== undefined; const [selectedFrameRate, setFrameRate] = useState71(resolved.result.fps); const { compositions } = useContext77(Internals57.CompositionManager); const [type, setType] = useState71(initialCompType); const [newId, setName] = useState71(() => { const numberAtEnd = resolved.result.id.match(/([0-9]+)$/)?.[0]; let prefix = numberAtEnd ? Number(numberAtEnd) : 1; const initialName = resolved.result.id.replace(/([0-9]+)$/, ""); let currentName = initialName; while (true) { currentName = initialName + prefix; const err = validateCompositionName(currentName, compositions); if (!err) { break; } prefix++; } return currentName; }); const [size4, setSize] = useState71(() => ({ width: resolved.result.width, height: resolved.result.height })); const [durationInFrames, setDurationInFrames] = useState71(resolved.result.durationInFrames); const onTypeChanged = useCallback104((newType) => { setType(newType); }, []); const onWidthChanged = useCallback104((newValue) => { setSize((s) => { const { height } = s; const newWidth = Number(newValue); return { height, width: newWidth }; }); }, []); const onWidthDirectlyChanged = useCallback104((newWidth) => { setSize((s) => { const { height } = s; return { height, width: newWidth }; }); }, []); const onHeightDirectlyChanged = useCallback104((newHeight) => { setSize((s) => { const { width: width2 } = s; return { width: width2, height: newHeight }; }); }, []); const onHeightChanged = useCallback104((newValue) => { setSize((s) => { const { width: width2 } = s; const newHeight = Number(newValue); return { width: width2, height: newHeight }; }); }, []); const onNameChange = useCallback104((e) => { setName(e.target.value); }, []); const onTextFpsChange = useCallback104((newFps) => { setFrameRate(Number(newFps)); }, []); const onFpsChange = useCallback104((newFps) => { setFrameRate(newFps); }, []); const compNameErrMessage = validateCompositionName(newId, compositions); const compWidthErrMessage = validateCompositionDimension("Width", size4.width); const compHeightErrMessage = validateCompositionDimension("Height", size4.height); const typeValues = useMemo113(() => { return [ { id: "composition", keyHint: null, label: "", leftItem: null, onClick: () => onTypeChanged("composition"), subMenu: null, type: "item", value: "composition", quickSwitcherLabel: null }, { id: "still", keyHint: null, label: "", leftItem: null, onClick: () => onTypeChanged("still"), subMenu: null, type: "item", value: "still", quickSwitcherLabel: null } ]; }, [onTypeChanged]); const valid = compNameErrMessage === null && compWidthErrMessage === null && compHeightErrMessage === null; const codemod = useMemo113(() => { return { type: "duplicate-composition", idToDuplicate: resolved.result.id, newDurationInFrames: hadDurationDefined ? Number(durationInFrames) : null, newFps: hadFpsDefined ? Number(selectedFrameRate) : null, newHeight: hadDimensionsDefined ? Number(size4.height) : null, newWidth: hadDimensionsDefined ? Number(size4.width) : null, newId, tag: type === "still" ? "Still" : "Composition" }; }, [ durationInFrames, hadDimensionsDefined, hadDurationDefined, hadFpsDefined, newId, resolved.result.id, selectedFrameRate, size4.height, size4.width, type ]); const onSubmit = useCallback104((e) => { e.preventDefault(); }, []); return /* @__PURE__ */ jsxs109(Fragment32, { children: [ /* @__PURE__ */ jsx215(ModalHeader, { title: `Duplicate ${resolved.result.id}` }), /* @__PURE__ */ jsxs109("form", { onSubmit, children: [ /* @__PURE__ */ jsxs109("div", { style: content4, children: [ initialCompType === "composition" ? /* @__PURE__ */ jsxs109("div", { style: optionRow, children: [ /* @__PURE__ */ jsx215("div", { style: label5, children: "Type" }), /* @__PURE__ */ jsx215("div", { style: rightRow, children: /* @__PURE__ */ jsx215(Combobox, { title: "Type of composition", style: comboBoxStyle, values: typeValues, selectedId: type }) }) ] }) : null, /* @__PURE__ */ jsxs109("div", { style: optionRow, children: [ /* @__PURE__ */ jsx215("div", { style: label5, children: "ID" }), /* @__PURE__ */ jsx215("div", { style: rightRow, children: /* @__PURE__ */ jsxs109("div", { children: [ /* @__PURE__ */ jsx215(RemotionInput, { value: newId, onChange: onNameChange, type: "text", autoFocus: true, placeholder: "Composition ID", status: "ok", rightAlign: true }), compNameErrMessage ? /* @__PURE__ */ jsxs109(Fragment32, { children: [ /* @__PURE__ */ jsx215(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx215(ValidationMessage, { align: "flex-start", message: compNameErrMessage, type: "error" }) ] }) : null ] }) }) ] }), hadDimensionsDefined ? /* @__PURE__ */ jsxs109(Fragment32, { children: [ /* @__PURE__ */ jsxs109("div", { style: optionRow, children: [ /* @__PURE__ */ jsx215("div", { style: label5, children: "Width" }), /* @__PURE__ */ jsxs109("div", { style: rightRow, children: [ /* @__PURE__ */ jsx215(InputDragger, { type: "number", value: size4.width, placeholder: "Width", onTextChange: onWidthChanged, name: "width", step: 2, min: 2, required: true, status: "ok", formatter: (w) => `${w}px`, max: 1e8, onValueChange: onWidthDirectlyChanged, rightAlign: false }), compWidthErrMessage ? /* @__PURE__ */ jsxs109(Fragment32, { children: [ /* @__PURE__ */ jsx215(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx215(ValidationMessage, { align: "flex-start", message: compWidthErrMessage, type: "error" }) ] }) : null ] }) ] }), /* @__PURE__ */ jsxs109("div", { style: optionRow, children: [ /* @__PURE__ */ jsx215("div", { style: label5, children: "Height" }), /* @__PURE__ */ jsxs109("div", { style: rightRow, children: [ /* @__PURE__ */ jsx215(InputDragger, { type: "number", value: size4.height, onTextChange: onHeightChanged, placeholder: "Height", name: "height", step: 2, required: true, formatter: (h) => `${h}px`, min: 2, status: "ok", max: 1e8, onValueChange: onHeightDirectlyChanged, rightAlign: false }), compHeightErrMessage ? /* @__PURE__ */ jsxs109(Fragment32, { children: [ /* @__PURE__ */ jsx215(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx215(ValidationMessage, { align: "flex-start", message: compHeightErrMessage, type: "error" }) ] }) : null ] }) ] }) ] }) : null, type === "composition" && hadDurationDefined ? /* @__PURE__ */ jsx215(NewCompDuration, { durationInFrames, setDurationInFrames }) : null, type === "composition" && hadFpsDefined ? /* @__PURE__ */ jsxs109("div", { style: optionRow, children: [ /* @__PURE__ */ jsx215("div", { style: label5, children: "FPS" }), /* @__PURE__ */ jsx215("div", { style: rightRow, children: /* @__PURE__ */ jsx215(InputDragger, { type: "number", value: selectedFrameRate, onTextChange: onTextFpsChange, placeholder: "Frame rate (fps)", name: "fps", min: 1, required: true, status: "ok", max: 240, step: 0.01, onValueChange: onFpsChange, rightAlign: false }) }) ] }) : null ] }), /* @__PURE__ */ jsx215(ModalFooterContainer, { children: /* @__PURE__ */ jsx215(CodemodFooter, { loadingNotification: "Duplicating...", errorNotification: "Could not duplicate composition", successNotification: `Duplicated ${unresolved.id} as ${newId}`, genericSubmitLabel: "Duplicate", submitLabel: ({ relativeRootPath }) => `Add to ${relativeRootPath}`, codemod, valid }) }) ] }) ] }); }; var DuplicateComposition = ({ compositionId, compositionType }) => { return /* @__PURE__ */ jsx215(DismissableModal, { children: /* @__PURE__ */ jsx215(ResolveCompositionBeforeModal, { compositionId, children: /* @__PURE__ */ jsx215(DuplicateCompositionLoaded, { initialType: compositionType }) }) }); }; // src/components/NewComposition/RenameComposition.tsx import { useCallback as useCallback105, useContext as useContext78, useMemo as useMemo114, useState as useState72 } from "react"; import { Internals as Internals58 } from "remotion"; import { jsx as jsx216, jsxs as jsxs110, Fragment as Fragment33 } from "react/jsx-runtime"; var content5 = { padding: 12, paddingRight: 12, flex: 1, fontSize: 13, minWidth: 500 }; var RenameCompositionLoaded = () => { const context = useContext78(ResolvedCompositionContext); if (!context) { throw new Error("Resolved composition context"); } const { resolved } = context; const { compositions } = useContext78(Internals58.CompositionManager); const [newId, setName] = useState72(() => { return resolved.result.id; }); const onNameChange = useCallback105((e) => { setName(e.target.value); }, []); const compNameErrMessage = newId === resolved.result.id ? null : validateCompositionName(newId, compositions); const valid = compNameErrMessage === null && resolved.result.id !== newId; const codemod = useMemo114(() => { return { type: "rename-composition", idToRename: resolved.result.id, newId }; }, [newId, resolved.result.id]); const onSubmit = useCallback105((e) => { e.preventDefault(); }, []); return /* @__PURE__ */ jsxs110(Fragment33, { children: [ /* @__PURE__ */ jsx216(ModalHeader, { title: `Rename ${resolved.result.id}` }), /* @__PURE__ */ jsxs110("form", { onSubmit, children: [ /* @__PURE__ */ jsx216("div", { style: content5, children: /* @__PURE__ */ jsxs110("div", { style: optionRow, children: [ /* @__PURE__ */ jsx216("div", { style: label5, children: "ID" }), /* @__PURE__ */ jsx216("div", { style: rightRow, children: /* @__PURE__ */ jsxs110("div", { children: [ /* @__PURE__ */ jsx216(RemotionInput, { value: newId, onChange: onNameChange, type: "text", autoFocus: true, placeholder: "Composition ID", status: "ok", rightAlign: true }), compNameErrMessage ? /* @__PURE__ */ jsxs110(Fragment33, { children: [ /* @__PURE__ */ jsx216(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx216(ValidationMessage, { align: "flex-start", message: compNameErrMessage, type: "error" }) ] }) : null ] }) }) ] }) }), /* @__PURE__ */ jsx216(ModalFooterContainer, { children: /* @__PURE__ */ jsx216(CodemodFooter, { loadingNotification: "Renaming...", errorNotification: "Could not rename composition", successNotification: `Renamed to ${newId}`, genericSubmitLabel: "Rename", submitLabel: ({ relativeRootPath }) => `Modify ${relativeRootPath}`, codemod, valid }) }) ] }) ] }); }; var RenameComposition = ({ compositionId }) => { return /* @__PURE__ */ jsx216(DismissableModal, { children: /* @__PURE__ */ jsx216(ResolveCompositionBeforeModal, { compositionId, children: /* @__PURE__ */ jsx216(RenameCompositionLoaded, {}) }) }); }; // src/components/OverrideInputProps.tsx import { useCallback as useCallback106, useContext as useContext79, useMemo as useMemo115, useState as useState73 } from "react"; import { Internals as Internals59 } from "remotion"; import { jsx as jsx217, jsxs as jsxs111 } from "react/jsx-runtime"; var style11 = { padding: 12, width: 500 }; var label8 = { color: LIGHT_TEXT, fontSize: 14, marginBottom: 10 }; var textAreaStyle = { fontFamily: "monospace", minHeight: 200 }; var codeSnippet2 = { fontSize: 14, color: BLUE, fontFamily: "monospace" }; var row7 = { display: "flex", flexDirection: "row", alignItems: "center", justifyContent: "space-between" }; var isValidJSON = (value) => { try { JSON.parse(value); return true; } catch { return false; } }; var Inner5 = () => { const [value, setValue] = useState73(() => { const override = Internals59.getInputPropsOverride(); if (override) { return JSON.stringify(override, null, 2); } return null; }); const { setSelectedModal } = useContext79(ModalsContext); const valid = useMemo115(() => { if (!value) return true; return isValidJSON(value); }, [value]); const onChange = useCallback106((newValue) => { if (newValue.trim() === "") { setValue(null); Internals59.setInputPropsOverride(null); return; } setValue(newValue); if (!isValidJSON(newValue)) { return; } Internals59.setInputPropsOverride(JSON.parse(newValue)); }, [setValue]); const onReset = useCallback106(() => { onChange(""); }, [onChange]); const onReloadPage = useCallback106(() => { window.location.reload(); }, []); const onDone = useCallback106(() => { setSelectedModal(null); }, [setSelectedModal]); return /* @__PURE__ */ jsxs111("div", { style: style11, children: [ /* @__PURE__ */ jsxs111("div", { style: label8, children: [ "Enter a valid JSON to override the value that", " ", /* @__PURE__ */ jsx217("code", { style: codeSnippet2, children: "getInputProps()" }), " returns to preview a composition with different props. The Studio must be reloaded to see the changes." ] }), /* @__PURE__ */ jsx217(RemTextarea, { onChange: (e) => onChange(e.target.value), value: value ?? "", status: valid ? "ok" : "error", style: textAreaStyle }), /* @__PURE__ */ jsxs111("div", { style: row7, children: [ /* @__PURE__ */ jsx217(Button, { onClick: onReset, children: "Reset" }), /* @__PURE__ */ jsx217(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx217(Button, { onClick: onReloadPage, children: "Reload page" }), /* @__PURE__ */ jsx217(Flex, {}), /* @__PURE__ */ jsx217(Button, { onClick: onDone, children: "Done" }) ] }) ] }); }; var OverrideInputPropsModal = () => { return /* @__PURE__ */ jsx217(DismissableModal, { children: /* @__PURE__ */ jsx217(Inner5, {}) }); }; // src/components/QuickSwitcher/QuickSwitcherContent.tsx import { useCallback as useCallback107, useContext as useContext80, useEffect as useEffect76, useMemo as useMemo117, useRef as useRef42, useState as useState75 } from "react"; import { Internals as Internals60 } from "remotion"; // src/icons/keys.tsx import { jsx as jsx218 } from "react/jsx-runtime"; var iconStyle5 = { width: 10, display: "inline" }; var ShiftIcon = () => { return /* @__PURE__ */ jsx218("svg", { style: iconStyle5, viewBox: "0 0 448 512", children: /* @__PURE__ */ jsx218("path", { fill: "currentColor", d: "M48.048 304h73.798v128c0 26.51 21.49 48 48 48h108.308c26.51 0 48-21.49 48-48V304h73.789c42.638 0 64.151-51.731 33.941-81.941l-175.943-176c-18.745-18.745-49.137-18.746-67.882 0l-175.952 176C-16.042 252.208 5.325 304 48.048 304zM224 80l176 176H278.154v176H169.846V256H48L224 80z" }) }); }; var ArrowLeft = () => { return /* @__PURE__ */ jsx218("svg", { style: iconStyle5, viewBox: "0 0 448 512", children: /* @__PURE__ */ jsx218("path", { fill: "currentColor", d: "M257.5 445.1l-22.2 22.2c-9.4 9.4-24.6 9.4-33.9 0L7 273c-9.4-9.4-9.4-24.6 0-33.9L201.4 44.7c9.4-9.4 24.6-9.4 33.9 0l22.2 22.2c9.5 9.5 9.3 25-.4 34.3L136.6 216H424c13.3 0 24 10.7 24 24v32c0 13.3-10.7 24-24 24H136.6l120.5 114.8c9.8 9.3 10 24.8.4 34.3z" }) }); }; var ArrowRight = () => { return /* @__PURE__ */ jsx218("svg", { style: iconStyle5, viewBox: "0 0 448 512", children: /* @__PURE__ */ jsx218("path", { fill: "currentColor", d: "M190.5 66.9l22.2-22.2c9.4-9.4 24.6-9.4 33.9 0L441 239c9.4 9.4 9.4 24.6 0 33.9L246.6 467.3c-9.4 9.4-24.6 9.4-33.9 0l-22.2-22.2c-9.5-9.5-9.3-25 .4-34.3L311.4 296H24c-13.3 0-24-10.7-24-24v-32c0-13.3 10.7-24 24-24h287.4L190.9 101.2c-9.8-9.3-10-24.8-.4-34.3z" }) }); }; // src/components/KeyboardShortcutsExplainer.tsx import { jsx as jsx219, jsxs as jsxs112, Fragment as Fragment34 } from "react/jsx-runtime"; var left3 = { width: 85, paddingTop: 8, paddingBottom: 8 }; var key4 = { background: INPUT_BACKGROUND, padding: "3px 6px", color: "white", borderRadius: 3, border: "1px solid " + INPUT_BORDER_COLOR_UNHOVERED, borderBottomWidth: 3, fontSize: 14, fontFamily: "monospace" }; var right2 = { fontSize: 14, color: "#eee" }; var container47 = { paddingLeft: 20, paddingRight: 40, paddingTop: 10, paddingBottom: 10 }; var title4 = { fontWeight: "bold", color: "white", fontSize: 14, marginBottom: 10 }; var keyboardShortcutsDisabled = { padding: 12, width: "100%", fontSize: 14, backgroundColor: "rgba(255, 255, 255, 0.1)" }; var ul = { marginTop: 0, marginBottom: 0 }; var li = { fontSize: 14 }; var KeyboardShortcutsExplainer = () => { return /* @__PURE__ */ jsxs112("div", { children: [ areKeyboardShortcutsDisabled() ? /* @__PURE__ */ jsxs112("div", { style: keyboardShortcutsDisabled, children: [ "Keyboard shortcuts disabled either due to:", /* @__PURE__ */ jsxs112("ul", { style: ul, children: [ /* @__PURE__ */ jsx219("li", { style: li, children: "a) --disable-keyboard-shortcuts being passed" }), /* @__PURE__ */ jsx219("li", { style: li, children: "b) Config.setKeyboardShortcutsEnabled(false) being set or" }), /* @__PURE__ */ jsx219("li", { style: li, children: " c) a Remotion version mismatch." }) ] }) ] }) : null, /* @__PURE__ */ jsxs112(Row, { style: container47, children: [ /* @__PURE__ */ jsxs112(Column, { children: [ /* @__PURE__ */ jsx219("div", { style: title4, children: "Playback" }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsxs112("div", { style: left3, children: [ /* @__PURE__ */ jsx219("kbd", { style: key4, children: /* @__PURE__ */ jsx219(ShiftIcon, {}) }), /* @__PURE__ */ jsx219(Spacing, { x: 0.3 }), /* @__PURE__ */ jsx219("kbd", { style: key4, children: /* @__PURE__ */ jsx219(ArrowLeft, {}) }) ] }), /* @__PURE__ */ jsx219("div", { style: right2, children: "1 second back" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: /* @__PURE__ */ jsx219(ArrowLeft, {}) }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Previous frame" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "Space" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Play / Pause" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: /* @__PURE__ */ jsx219(ArrowRight, {}) }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Next frame" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsxs112("div", { style: left3, children: [ /* @__PURE__ */ jsx219("kbd", { style: key4, children: /* @__PURE__ */ jsx219(ShiftIcon, {}) }), /* @__PURE__ */ jsx219(Spacing, { x: 0.3 }), /* @__PURE__ */ jsx219("kbd", { style: key4, children: /* @__PURE__ */ jsx219(ArrowRight, {}) }) ] }), /* @__PURE__ */ jsx219("div", { style: right2, children: "1 second forward" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "A" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Jump to beginning" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "E" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Jump to end" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "J" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Reverse playback" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "K" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Pause" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "L" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Play / Speed up" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "G" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Go to frame" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "Enter" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Pause & return to playback start" }) ] }), /* @__PURE__ */ jsx219("br", {}), /* @__PURE__ */ jsx219("div", { style: title4, children: "Sidebar" }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsxs112("div", { style: left3, children: [ /* @__PURE__ */ jsx219("kbd", { style: key4, children: cmdOrCtrlCharacter }), /* @__PURE__ */ jsx219(Spacing, { x: 0.3 }), /* @__PURE__ */ jsx219("kbd", { style: key4, children: "B" }) ] }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Toggle left sidebar" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsxs112("div", { style: left3, children: [ /* @__PURE__ */ jsx219("kbd", { style: key4, children: cmdOrCtrlCharacter }), /* @__PURE__ */ jsx219(Spacing, { x: 0.3 }), /* @__PURE__ */ jsx219("kbd", { style: key4, children: "J" }) ] }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Toggle right sidebar" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsxs112("div", { style: left3, children: [ /* @__PURE__ */ jsx219("kbd", { style: key4, children: cmdOrCtrlCharacter }), /* @__PURE__ */ jsx219(Spacing, { x: 0.3 }), /* @__PURE__ */ jsx219("kbd", { style: key4, children: "G" }) ] }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Toggle both sidebars" }) ] }), /* @__PURE__ */ jsx219("br", {}), /* @__PURE__ */ jsx219("div", { style: title4, children: "View" }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "F" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Enter fullscreen" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "Esc" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Exit fullscreen" }) ] }) ] }), /* @__PURE__ */ jsx219(Spacing, { x: 8 }), /* @__PURE__ */ jsxs112(Column, { children: [ /* @__PURE__ */ jsx219("div", { style: title4, children: "Navigation" }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "PageUp" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Previous composition" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "PageDown" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Next composition" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "R" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Render composition" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "T" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Toggle checkerboard background" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "?" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Show keyboard shortcuts" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsxs112("div", { style: left3, children: [ /* @__PURE__ */ jsx219("kbd", { style: key4, children: cmdOrCtrlCharacter }), /* @__PURE__ */ jsx219(Spacing, { x: 0.3 }), /* @__PURE__ */ jsx219("kbd", { style: key4, children: "K" }) ] }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Quick Switcher" }) ] }), /* @__PURE__ */ jsx219("br", {}), /* @__PURE__ */ jsx219("div", { style: title4, children: "Playback range" }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "I" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Set In Point" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "O" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Set Out Point" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "X" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Clear In/Out Points" }) ] }), /* @__PURE__ */ jsx219("br", {}), /* @__PURE__ */ jsx219("div", { style: title4, children: "Zoom" }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "+" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Zoom in" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "-" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Zoom out" }) ] }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsx219("div", { style: left3, children: /* @__PURE__ */ jsx219("kbd", { style: key4, children: "0" }) }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Reset zoom" }) ] }), " ", /* @__PURE__ */ jsx219("br", {}), /* @__PURE__ */ jsx219("div", { style: title4, children: "Props Editor" }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsxs112("div", { style: left3, children: [ /* @__PURE__ */ jsx219("kbd", { style: key4, children: cmdOrCtrlCharacter }), /* @__PURE__ */ jsx219(Spacing, { x: 0.3 }), /* @__PURE__ */ jsx219("kbd", { style: key4, children: "S" }) ] }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Save" }) ] }), process.env.ASK_AI_ENABLED && /* @__PURE__ */ jsxs112(Fragment34, { children: [ /* @__PURE__ */ jsx219("br", {}), /* @__PURE__ */ jsx219("div", { style: title4, children: "AI" }), /* @__PURE__ */ jsxs112(Row, { align: "center", children: [ /* @__PURE__ */ jsxs112("div", { style: left3, children: [ /* @__PURE__ */ jsx219("kbd", { style: key4, children: cmdOrCtrlCharacter }), /* @__PURE__ */ jsx219(Spacing, { x: 0.3 }), /* @__PURE__ */ jsx219("kbd", { style: key4, children: "I" }) ] }), /* @__PURE__ */ jsx219("div", { style: right2, children: "Ask AI" }) ] }) ] }) ] }) ] }) ] }); }; // src/components/QuickSwitcher/AlgoliaCredit.tsx import { jsx as jsx220 } from "react/jsx-runtime"; var link = { display: "inline-flex" }; var AlgoliaCredit = () => { return /* @__PURE__ */ jsx220("a", { style: link, href: "https://www.algolia.com/ref/docsearch/?utm_source=www.remotion.dev&utm_medium=referral&utm_content=powered_by&utm_campaign=docsearch", target: "_blank", rel: "noopener noreferrer", children: /* @__PURE__ */ jsx220("svg", { width: "77", height: "19", "aria-label": "Algolia", role: "img", children: /* @__PURE__ */ jsx220("path", { d: "M2.5067 0h14.0245c1.384.001 2.5058 1.1205 2.5068 2.5017V16.5c-.0014 1.3808-1.1232 2.4995-2.5068 2.5H2.5067C1.1232 18.9995.0014 17.8808 0 16.5V2.4958A2.495 2.495 0 01.735.7294 2.505 2.505 0 012.5068 0zM37.95 15.0695c-3.7068.0168-3.7068-2.986-3.7068-3.4634L34.2372.3576 36.498 0v11.1794c0 .2715 0 1.9889 1.452 1.994v1.8961zm-9.1666-1.8388c.694 0 1.2086-.0397 1.5678-.1088v-2.2934a5.3639 5.3639 0 00-1.3303-.1679 4.8283 4.8283 0 00-.758.0582 2.2845 2.2845 0 00-.688.2024c-.2029.0979-.371.2362-.4919.4142-.1268.1788-.185.2826-.185.5533 0 .5297.185.8359.5205 1.0375.3355.2016.7928.3053 1.365.3053v-.0008zm-.1969-8.1817c.7463 0 1.3768.092 1.8856.2767.5088.1838.9195.4428 1.2204.7717.3068.334.5147.7777.6423 1.251.1327.4723.196.991.196 1.5603v5.798c-.5235.1036-1.05.192-1.5787.2649-.7048.1037-1.4976.156-2.3774.156-.5832 0-1.1215-.0582-1.6016-.167a3.385 3.385 0 01-1.2432-.5364 2.6034 2.6034 0 01-.8037-.9565c-.191-.3922-.29-.9447-.29-1.5208 0-.5533.11-.905.3246-1.2863a2.7351 2.7351 0 01.8849-.9329c.376-.242.8029-.415 1.2948-.5187a7.4517 7.4517 0 011.5381-.156 7.1162 7.1162 0 011.6667.2024V8.886c0-.259-.0296-.5061-.093-.7372a1.5847 1.5847 0 00-.3245-.6158 1.5079 1.5079 0 00-.6119-.4158 2.6788 2.6788 0 00-.966-.173c-.5206 0-.9948.0634-1.4283.1384a6.5481 6.5481 0 00-1.065.259l-.2712-1.849c.2831-.0986.7048-.1964 1.2491-.2943a9.2979 9.2979 0 011.752-.1501v.0008zm44.6597 8.1193c.6947 0 1.2086-.0405 1.567-.1097v-2.2942a5.3743 5.3743 0 00-1.3303-.1679c-.2485 0-.503.0177-.7573.0582a2.2853 2.2853 0 00-.688.2024 1.2333 1.2333 0 00-.4918.4142c-.1268.1788-.1843.2826-.1843.5533 0 .5297.1843.8359.5198 1.0375.3414.2066.7927.3053 1.365.3053v.0009zm-.191-8.1767c.7463 0 1.3768.0912 1.8856.2759.5087.1847.9195.4436 1.2204.7717.3.329.5147.7786.6414 1.251a5.7248 5.7248 0 01.197 1.562v5.7972c-.3466.0742-.874.1602-1.5788.2648-.7049.1038-1.4976.1552-2.3774.1552-.5832 0-1.1215-.0573-1.6016-.167a3.385 3.385 0 01-1.2432-.5356 2.6034 2.6034 0 01-.8038-.9565c-.191-.3922-.2898-.9447-.2898-1.5216 0-.5533.1098-.905.3245-1.2854a2.7373 2.7373 0 01.8849-.9338c.376-.2412.8029-.4141 1.2947-.5178a7.4545 7.4545 0 012.325-.1097c.2781.0287.5672.081.879.156v-.3686a2.7781 2.7781 0 00-.092-.738 1.5788 1.5788 0 00-.3246-.6166 1.5079 1.5079 0 00-.612-.415 2.6797 2.6797 0 00-.966-.1729c-.5205 0-.9947.0633-1.4282.1384a6.5608 6.5608 0 00-1.065.259l-.2712-1.8498c.283-.0979.7048-.1957 1.2491-.2935a9.8597 9.8597 0 011.752-.1494zm-6.79-1.072c-.7576.001-1.373-.6103-1.3759-1.3664 0-.755.6128-1.3664 1.376-1.3664.764 0 1.3775.6115 1.3775 1.3664s-.6195 1.3664-1.3776 1.3664zm1.1393 11.1507h-2.2726V5.3409l2.2734-.3568v10.0845l-.0008.0017zm-3.984 0c-3.707.0168-3.707-2.986-3.707-3.4642L59.7069.3576 61.9685 0v11.1794c0 .2715 0 1.9889 1.452 1.994V15.0703zm-7.3512-4.979c0-.975-.2138-1.7873-.6305-2.3516-.4167-.571-.9998-.852-1.747-.852-.7454 0-1.3302.281-1.7452.852-.4166.5702-.6195 1.3765-.6195 2.3516 0 .9851.208 1.6473.6254 2.2183.4158.576.9998.8587 1.7461.8587.7454 0 1.3303-.2885 1.747-.8595.4158-.5761.6237-1.2315.6237-2.2184v.0009zm2.3132-.006c0 .7609-.1099 1.3361-.3356 1.9654a4.654 4.654 0 01-.9533 1.6076A4.214 4.214 0 0155.613 14.69c-.579.2412-1.4697.3795-1.9143.3795-.4462-.005-1.3303-.1324-1.9033-.3795a4.307 4.307 0 01-1.474-1.0316c-.4115-.4445-.7293-.9801-.9609-1.6076a5.3423 5.3423 0 01-.3465-1.9653c0-.7608.104-1.493.3356-2.1155a4.683 4.683 0 01.9719-1.5958 4.3383 4.3383 0 011.479-1.0257c.5739-.242 1.2043-.3567 1.8864-.3567.6829 0 1.3125.1197 1.8906.3567a4.1245 4.1245 0 011.4816 1.0257 4.7587 4.7587 0 01.9592 1.5958c.2426.6225.3643 1.3547.3643 2.1155zm-17.0198 0c0 .9448.208 1.9932.6238 2.431.4166.4386.955.6579 1.6142.6579.3584 0 .6998-.0523 1.0176-.1502.3186-.0978.5721-.2134.775-.3517V7.0784a8.8706 8.8706 0 00-1.4926-.1906c-.8206-.0236-1.4452.312-1.8847.8468-.4335.5365-.6533 1.476-.6533 2.3516v-.0008zm6.2863 4.4485c0 1.5385-.3938 2.662-1.1866 3.3773-.791.7136-2.0005 1.0712-3.6308 1.0712-.5958 0-1.834-.1156-2.8228-.334l.3643-1.7865c.8282.173 1.9202.2193 2.4932.2193.9077 0 1.555-.1847 1.943-.5533.388-.3686.578-.916.578-1.643v-.3687a6.8289 6.8289 0 01-.8848.3349c-.3634.1096-.786.167-1.261.167-.6246 0-1.1917-.0979-1.7055-.2944a3.5554 3.5554 0 01-1.3244-.8645c-.3642-.3796-.6541-.8579-.8561-1.4289-.2028-.571-.3068-1.59-.3068-2.339 0-.7034.1099-1.5856.3245-2.1735.2198-.5871.5316-1.0949.9542-1.515.4167-.42.9255-.743 1.5213-.98a5.5923 5.5923 0 012.052-.3855c.7353 0 1.4114.092 2.0707.2024.6592.1088 1.2204.2236 1.6776.35v8.945-.0008zM11.5026 4.2418v-.6511c-.0005-.4553-.3704-.8241-.8266-.8241H8.749c-.4561 0-.826.3688-.8265.824v.669c0 .0742.0693.1264.1445.1096a6.0346 6.0346 0 011.6768-.2362 6.125 6.125 0 011.6202.2185.1116.1116 0 00.1386-.1097zm-5.2806.852l-.3296-.3282a.8266.8266 0 00-1.168 0l-.393.3922a.8199.8199 0 000 1.164l.3237.323c.0524.0515.1268.0397.1733-.0117.191-.259.3989-.507.6305-.7372.2374-.2362.48-.4437.7462-.6335.0575-.0354.0634-.1155.017-.1687zm3.5159 2.069v2.818c0 .081.0879.1392.1622.0987l2.5102-1.2964c.0574-.0287.0752-.0987.0464-.1552a3.1237 3.1237 0 00-2.603-1.574c-.0575 0-.115.0456-.115.1097l-.0008-.0009zm.0008 6.789c-2.0933.0005-3.7915-1.6912-3.7947-3.7804C5.9468 8.0821 7.6452 6.39 9.7387 6.391c2.0932-.0005 3.7911 1.6914 3.794 3.7804a3.7783 3.7783 0 01-1.1124 2.675 3.7936 3.7936 0 01-2.6824 1.1054h.0008zM9.738 4.8002c-1.9218 0-3.6975 1.0232-4.6584 2.6841a5.359 5.359 0 000 5.3683c.9609 1.661 2.7366 2.6841 4.6584 2.6841a5.3891 5.3891 0 003.8073-1.5725 5.3675 5.3675 0 001.578-3.7987 5.3574 5.3574 0 00-1.5771-3.797A5.379 5.379 0 009.7387 4.801l-.0008-.0008z", fill: "currentColor", fillRule: "evenodd" }) }) }); }; // src/components/QuickSwitcher/NoResults.tsx import { jsxs as jsxs113 } from "react/jsx-runtime"; var container48 = { padding: 80, color: LIGHT_TEXT, textAlign: "center", fontSize: 14 }; var MODE_TO_STRING = { commands: "commands", compositions: "compositions", docs: "documentation" }; var QuickSwitcherNoResults = ({ query, mode }) => { return /* @__PURE__ */ jsxs113("div", { style: container48, children: [ "No ", MODE_TO_STRING[mode], " matching ", '"', query, '"' ] }); }; // src/components/QuickSwitcher/QuickSwitcherResult.tsx import { useEffect as useEffect75, useMemo as useMemo116, useRef as useRef41, useState as useState74 } from "react"; import { jsx as jsx221, jsxs as jsxs114, Fragment as Fragment35 } from "react/jsx-runtime"; var container49 = { paddingLeft: 16, paddingRight: 16, display: "flex", flexDirection: "row", alignItems: "center", cursor: "pointer" }; var label9 = { whiteSpace: "nowrap", textOverflow: "ellipsis" }; var searchLabel = { ...label9, lineHeight: 1.25 }; var iconStyle6 = { width: 14, height: 14 }; var labelContainer = { overflow: "hidden", flex: 1, paddingTop: 5, paddingBottom: 5 }; var QuickSwitcherResult = ({ result, selected }) => { const [hovered, setIsHovered] = useState74(false); const ref = useRef41(null); const keybindings = useKeybinding(); useEffect75(() => { const { current } = ref; if (!current) { return; } const onMouseEnter = () => setIsHovered(true); const onMouseLeave = () => setIsHovered(false); current.addEventListener("mouseenter", onMouseEnter); current.addEventListener("mouseleave", onMouseLeave); return () => { current.removeEventListener("mouseenter", onMouseEnter); current.removeEventListener("mouseleave", onMouseLeave); }; }, []); useEffect75(() => { if (!selected) { return; } const binding = keybindings.registerKeybinding({ key: "Enter", callback: result.onSelected, commandCtrlKey: false, event: "keydown", preventDefault: true, triggerIfInputFieldFocused: true, keepRegisteredWhenNotHighestContext: false }); return () => { binding.unregister(); }; }, [keybindings, result.onSelected, selected]); useEffect75(() => { if (selected) { ref.current?.scrollIntoView({ block: "nearest", inline: "center" }); } }, [selected]); const style12 = useMemo116(() => { return { ...container49, backgroundColor: getBackgroundFromHoverState({ hovered, selected }) }; }, [hovered, selected]); const labelStyle5 = useMemo116(() => { return { ...result.type === "search-result" ? searchLabel : label9, color: result.type === "search-result" ? LIGHT_TEXT : selected || hovered ? "white" : LIGHT_TEXT, fontSize: 15 }; }, [hovered, result.type, selected]); return /* @__PURE__ */ jsxs114("div", { ref, style: style12, onClick: result.onSelected, children: [ result.type === "composition" ? result.compositionType === "still" ? /* @__PURE__ */ jsx221(StillIcon, { color: selected ? "white" : LIGHT_TEXT, style: iconStyle6 }) : /* @__PURE__ */ jsx221(FilmIcon, { color: selected ? "white" : LIGHT_TEXT, style: iconStyle6 }) : null, /* @__PURE__ */ jsx221(Spacing, { x: 1 }), /* @__PURE__ */ jsx221("div", { style: labelContainer, children: result.type === "search-result" ? /* @__PURE__ */ jsxs114(Fragment35, { children: [ /* @__PURE__ */ jsx221("div", { dangerouslySetInnerHTML: { __html: result.titleLine }, style: labelStyle5 }), /* @__PURE__ */ jsx221("div", { dangerouslySetInnerHTML: { __html: result.subtitleLine }, style: labelStyle5 }) ] }) : /* @__PURE__ */ jsx221("div", { style: labelStyle5, children: result.title }) }) ] }, result.id); }; // src/components/QuickSwitcher/algolia-search.ts import { NoReactInternals as NoReactInternals15 } from "remotion/no-react"; var ALGOLIA_API_KEY = "3e42dbd4f895fe93ff5cf40d860c4a85"; var ALGOLIA_APPLICATION_ID = "PLSDUOL1CA"; var AGOLIA_SEARCH_URL = "https://plsduol1ca-dsn.algolia.net/1/indexes/*/queries"; var algoliaSearch = async (query) => { const url = new URL(AGOLIA_SEARCH_URL); url.searchParams.set("x-algolia-agen", encodeURIComponent("Remotion Studio DocSearch")); url.searchParams.set("x-algolia-api-key", ALGOLIA_API_KEY); url.searchParams.set("x-algolia-application-id", ALGOLIA_APPLICATION_ID); const { results } = await fetch(url.toString(), { headers: { "content-type": "application/x-www-form-urlencoded" }, body: JSON.stringify({ requests: [ { query, indexName: "remotion", params: 'attributesToRetrieve=["hierarchy.lvl0","hierarchy.lvl1","hierarchy.lvl2","hierarchy.lvl3","hierarchy.lvl4","hierarchy.lvl5","hierarchy.lvl6","content","type","url"]&attributesToSnippet=["hierarchy.lvl1:10","hierarchy.lvl2:10","hierarchy.lvl3:10","hierarchy.lvl4:10","hierarchy.lvl5:10","hierarchy.lvl6:10","content:10"]&hitsPerPage=20' } ] }), method: "POST" }).then((res) => res.json()); const { hits } = results[0]; return hits.map((hit) => { const entries = Object.values(hit._highlightResult.hierarchy); const result = entries.find((value) => value.matchLevel === "full") ?? entries.find((value) => value.matchLevel === "partial"); const { subtitle: subtitle3, title: title5 } = splitMatchIntoTitleAndSubtitle(hit); if (!result) { return null; } return { type: "search-result", id: hit.objectID, title: "Should not display", titleLine: title5, subtitleLine: subtitle3, onSelected: () => { window.open(hit.url); } }; }).filter(NoReactInternals15.truthy); }; var splitMatchIntoTitleAndSubtitle = (match) => { const main = match.type === "content" ? match._highlightResult.content : match._highlightResult.hierarchy[match.type]; const title5 = main.value; const subtitle3 = Object.entries(match._highlightResult.hierarchy).filter(([level]) => level !== match.type).map((value) => value[1].value).join(" • "); return { title: title5, subtitle: subtitle3 }; }; // src/components/QuickSwitcher/fuzzy-search.ts function fuzzySearch(query, dataset) { const q = query ? query.trim().toLowerCase() : ""; const matchingIndices = []; if (q.length === 0) { for (let i = 0;i < dataset.length; i++) { matchingIndices.push(i); } return dataset.filter((_, i) => matchingIndices.includes(i)); } dataset.forEach((d, index) => { const s = d.title.trim().toLowerCase(); let i = 0; let n = -1; let l; for (;l = q[i++]; ) if (!~(n = s.indexOf(l, n + 1))) return; matchingIndices.push(index); }); return dataset.filter((_, i) => matchingIndices.includes(i)); } // src/components/QuickSwitcher/QuickSwitcherContent.tsx import { jsx as jsx222, jsxs as jsxs115, Fragment as Fragment36 } from "react/jsx-runtime"; var input2 = { width: "100%" }; var modeSelector = { paddingLeft: 16, paddingRight: 16, display: "flex", flexDirection: "row", paddingTop: 8, paddingBottom: 5 }; var modeItem = { appearance: "none", border: "none", fontFamily: "inherit", padding: 0, fontSize: 13, cursor: "pointer" }; var modeInactive = { ...modeItem, color: LIGHT_TEXT }; var modeActive = { ...modeItem, color: "white", fontWeight: "bold" }; var content6 = { paddingLeft: 16, paddingRight: 16, paddingTop: 4, paddingBottom: 10, display: "flex", flexDirection: "row", alignItems: "center" }; var loopIndex = (index, length) => { if (index < 0) { index += length; } return index % length; }; var stripQuery = (query) => { if (query.startsWith(">") || query.startsWith("?")) { return query.substring(1).trim(); } return query.trim(); }; var mapQueryToMode = (query) => { return query.startsWith(">") ? "commands" : query.startsWith("?") ? "docs" : "compositions"; }; var mapModeToQuery = (mode) => { if (mode === "commands") { return "> "; } if (mode === "compositions") { return ""; } if (mode === "docs") { return "? "; } throw new Error("no mode" + mode); }; var QuickSwitcherContent = ({ initialMode, invocationTimestamp, readOnlyStudio }) => { const { compositions } = useContext80(Internals60.CompositionManager); const [state, setState] = useState75(() => { return { query: mapModeToQuery(initialMode), selectedIndex: 0 }; }); useEffect76(() => { setState({ query: mapModeToQuery(initialMode), selectedIndex: 0 }); }, [initialMode, invocationTimestamp]); const inputRef = useRef42(null); const selectComposition = useSelectComposition(); const closeMenu = useCallback107(() => { return; }, []); const actions = useMenuStructure(closeMenu, readOnlyStudio); const [docResults, setDocResults] = useState75({ type: "initial" }); const { setSelectedModal } = useContext80(ModalsContext); const keybindings = useKeybinding(); const mode = mapQueryToMode(state.query); const actualQuery = useMemo117(() => { return stripQuery(state.query); }, [state.query]); const menuActions = useMemo117(() => { if (mode !== "commands") { return []; } return makeSearchResults(actions, setSelectedModal); }, [actions, mode, setSelectedModal]); const resultsArray = useMemo117(() => { if (mode === "commands") { return fuzzySearch(actualQuery, menuActions); } if (mode === "docs" && docResults.type === "results") { return docResults.results; } return fuzzySearch(actualQuery, compositions.map((c) => { return { id: "composition-" + c.id, title: c.id, type: "composition", onSelected: () => { selectComposition(c, true); setSelectedModal(null); const selector = `.__remotion-composition[data-compname="${c.id}"]`; Internals60.compositionSelectorRef.current?.expandComposition(c.id); waitForElm(selector).then(() => { document.querySelector(selector)?.scrollIntoView({ block: "center" }); }); }, compositionType: isCompositionStill(c) ? "still" : "composition" }; })); }, [ mode, actualQuery, compositions, menuActions, docResults, selectComposition, setSelectedModal ]); const onArrowDown = useCallback107(() => { setState((s) => { return { ...s, selectedIndex: s.selectedIndex + 1 }; }); }, []); const onArrowUp = useCallback107(() => { setState((s) => { return { ...s, selectedIndex: s.selectedIndex - 1 }; }); }, []); useEffect76(() => { const binding = keybindings.registerKeybinding({ key: "ArrowUp", callback: onArrowUp, commandCtrlKey: false, event: "keydown", preventDefault: true, triggerIfInputFieldFocused: true, keepRegisteredWhenNotHighestContext: false }); return () => { binding.unregister(); }; }, [keybindings, onArrowDown, onArrowUp]); useEffect76(() => { if (mode !== "docs") { return; } if (actualQuery.trim() === "") { setDocResults({ type: "initial" }); return; } let cancelled = false; setDocResults({ type: "loading" }); algoliaSearch(actualQuery).then((agoliaResults) => { if (cancelled) { return; } setDocResults({ type: "results", results: agoliaResults }); }).catch((err) => { if (cancelled) { return; } setDocResults({ type: "error", error: err }); }); return () => { cancelled = true; }; }, [actualQuery, mode]); useEffect76(() => { const binding = keybindings.registerKeybinding({ key: "ArrowDown", callback: onArrowDown, commandCtrlKey: false, event: "keydown", preventDefault: true, triggerIfInputFieldFocused: true, keepRegisteredWhenNotHighestContext: false }); return () => { binding.unregister(); }; }, [keybindings, onArrowDown]); const onTextChange = useCallback107((e) => { setState({ query: e.target.value, selectedIndex: 0 }); }, []); const selectedIndexRounded = loopIndex(state.selectedIndex, resultsArray.length); const onActionsSelected = useCallback107(() => { setState((s) => ({ query: `> ${stripQuery(s.query)}`, selectedIndex: 0 })); inputRef.current?.focus(); }, []); const onCompositionsSelected = useCallback107(() => { setState((s) => ({ query: stripQuery(s.query), selectedIndex: 0 })); inputRef.current?.focus(); }, []); const onDocSearchSelected = useCallback107(() => { setState((s) => ({ query: `? ${stripQuery(s.query)}`, selectedIndex: 0 })); setDocResults({ type: "initial" }); inputRef.current?.focus(); }, []); const showKeyboardShortcuts = mode === "docs" && actualQuery.trim() === ""; const showSearchLoadingState = mode === "docs" && docResults.type === "loading"; const container50 = useMemo117(() => { return { width: showKeyboardShortcuts ? 800 : 500 }; }, [showKeyboardShortcuts]); const results = useMemo117(() => { if (showKeyboardShortcuts) { return { maxHeight: 600, overflowY: "auto" }; } return { overflowY: "auto", height: 300 }; }, [showKeyboardShortcuts]); return /* @__PURE__ */ jsxs115("div", { style: container50, children: [ /* @__PURE__ */ jsxs115("div", { style: modeSelector, children: [ /* @__PURE__ */ jsx222("button", { onClick: onCompositionsSelected, style: mode === "compositions" ? modeActive : modeInactive, type: "button", children: "Compositions" }), /* @__PURE__ */ jsx222(Spacing, { x: 1 }), /* @__PURE__ */ jsx222("button", { onClick: onActionsSelected, style: mode === "commands" ? modeActive : modeInactive, type: "button", children: "Actions" }), /* @__PURE__ */ jsx222(Spacing, { x: 1 }), /* @__PURE__ */ jsx222("button", { onClick: onDocSearchSelected, style: mode === "docs" ? modeActive : modeInactive, type: "button", children: "Documentation" }) ] }), /* @__PURE__ */ jsxs115("div", { style: content6, children: [ /* @__PURE__ */ jsx222(RemotionInput, { ref: inputRef, type: "text", style: input2, autoFocus: true, status: "ok", value: state.query, onChange: onTextChange, placeholder: "Search compositions...", rightAlign: false }), showKeyboardShortcuts ? /* @__PURE__ */ jsxs115(Fragment36, { children: [ /* @__PURE__ */ jsx222(Spacing, { x: 2 }), " ", /* @__PURE__ */ jsx222(AlgoliaCredit, {}) ] }) : null ] }), /* @__PURE__ */ jsx222("div", { style: results, className: VERTICAL_SCROLLBAR_CLASSNAME, children: showKeyboardShortcuts ? /* @__PURE__ */ jsx222(KeyboardShortcutsExplainer, {}) : showSearchLoadingState ? null : resultsArray.length === 0 ? /* @__PURE__ */ jsx222(QuickSwitcherNoResults, { mode, query: actualQuery }) : resultsArray.map((result, i) => { return /* @__PURE__ */ jsx222(QuickSwitcherResult, { selected: selectedIndexRounded === i, result }, result.id); }) }) ] }); }; function waitForElm(selector) { return new Promise((resolve) => { if (document.querySelector(selector)) { resolve(document.querySelector(selector)); return; } const observer = new MutationObserver(() => { if (document.querySelector(selector)) { resolve(document.querySelector(selector)); observer.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true }); }); } // src/components/QuickSwitcher/QuickSwitcher.tsx import { jsx as jsx223 } from "react/jsx-runtime"; var QuickSwitcher = ({ initialMode, invocationTimestamp, readOnlyStudio }) => { return /* @__PURE__ */ jsx223(DismissableModal, { children: /* @__PURE__ */ jsx223(QuickSwitcherContent, { readOnlyStudio, invocationTimestamp, initialMode }) }); }; var QuickSwitcher_default = QuickSwitcher; // src/components/RenderModal/RenderStatusModal.tsx import { useCallback as useCallback109, useContext as useContext81 } from "react"; // src/components/RenderModal/ClientRenderProgress.tsx import { formatBytes as formatBytes2 } from "@remotion/studio-shared"; // src/components/RenderQueue/SuccessIcon.tsx import { jsx as jsx224 } from "react/jsx-runtime"; var iconStyle7 = { height: RENDER_STATUS_INDICATOR_SIZE, width: RENDER_STATUS_INDICATOR_SIZE }; var SuccessIcon = () => { return /* @__PURE__ */ jsx224("svg", { style: iconStyle7, viewBox: "0 0 512 512", children: /* @__PURE__ */ jsx224("path", { fill: LIGHT_TEXT, d: "M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM369 209L241 337l-17 17-17-17-64-64-17-17L160 222.1l17 17 47 47L335 175l17-17L385.9 192l-17 17z" }) }); }; // src/components/RenderModal/ClientRenderProgress.tsx import { jsx as jsx225, jsxs as jsxs116 } from "react/jsx-runtime"; var progressItem = { padding: 10, display: "flex", flexDirection: "row", alignItems: "center" }; var label10 = { fontSize: 14, width: 400, color: "white" }; var right3 = { fontSize: 14, color: LIGHT_TEXT, textAlign: "right", flex: 1 }; var RenderingProgress = ({ renderedFrames, totalFrames }) => { const done = renderedFrames === totalFrames; const progress = totalFrames > 0 ? renderedFrames / totalFrames : 0; return /* @__PURE__ */ jsxs116("div", { style: progressItem, children: [ done ? /* @__PURE__ */ jsx225(SuccessIcon, {}) : /* @__PURE__ */ jsx225(CircularProgress, { progress }), /* @__PURE__ */ jsx225(Spacing, { x: 1 }), /* @__PURE__ */ jsx225("div", { style: label10, children: done ? `Rendered ${totalFrames} frames` : `Rendering ${renderedFrames} / ${totalFrames} frames` }) ] }); }; var EncodingProgress = ({ encodedFrames, totalFrames }) => { const done = encodedFrames === totalFrames; const progress = totalFrames > 0 ? encodedFrames / totalFrames : 0; return /* @__PURE__ */ jsxs116("div", { style: progressItem, children: [ done ? /* @__PURE__ */ jsx225(SuccessIcon, {}) : /* @__PURE__ */ jsx225(CircularProgress, { progress }), /* @__PURE__ */ jsx225(Spacing, { x: 1 }), /* @__PURE__ */ jsx225("div", { style: label10, children: done ? `Encoded ${totalFrames} frames` : `Encoding ${encodedFrames} / ${totalFrames} frames` }) ] }); }; var DoneStatus = ({ job }) => { return /* @__PURE__ */ jsxs116("div", { style: progressItem, children: [ /* @__PURE__ */ jsx225(SuccessIcon, {}), /* @__PURE__ */ jsx225(Spacing, { x: 1 }), /* @__PURE__ */ jsx225("div", { style: label10, children: job.outName }), /* @__PURE__ */ jsx225("div", { style: right3, children: formatBytes2(job.metadata.sizeInBytes) }) ] }); }; var ClientRenderProgress = ({ job }) => { if (job.status === "idle" || job.status === "failed" || job.status === "cancelled") { throw new Error("This component should not be rendered when the job is idle, failed, or cancelled"); } if (job.status === "done") { return /* @__PURE__ */ jsxs116("div", { children: [ /* @__PURE__ */ jsx225(Spacing, { y: 0.5 }), /* @__PURE__ */ jsx225(DoneStatus, { job }), /* @__PURE__ */ jsx225(Spacing, { y: 1 }) ] }); } const { renderedFrames, encodedFrames, totalFrames } = job.progress; return /* @__PURE__ */ jsxs116("div", { children: [ /* @__PURE__ */ jsx225(Spacing, { y: 0.5 }), /* @__PURE__ */ jsx225(RenderingProgress, { renderedFrames, totalFrames }), job.type === "client-video" && /* @__PURE__ */ jsx225(EncodingProgress, { encodedFrames, totalFrames }), /* @__PURE__ */ jsx225(Spacing, { y: 1 }) ] }); }; // src/components/RenderModal/GuiRenderStatus.tsx import { useCallback as useCallback108, useMemo as useMemo118 } from "react"; import { jsx as jsx226, jsxs as jsxs117 } from "react/jsx-runtime"; var progressItem2 = { padding: 10, display: "flex", flexDirection: "row", alignItems: "center" }; var label11 = { fontSize: 14, width: 400, color: "white" }; var right4 = { fontSize: 14, color: LIGHT_TEXT, textAlign: "right", flex: 1 }; var BundlingProgress = ({ progress, doneIn }) => { return /* @__PURE__ */ jsxs117("div", { style: progressItem2, children: [ progress === 1 ? /* @__PURE__ */ jsx226(SuccessIcon, {}) : /* @__PURE__ */ jsx226(CircularProgress, { progress }), /* @__PURE__ */ jsx226(Spacing, { x: 1 }), /* @__PURE__ */ jsx226("div", { style: label11, children: progress === 1 ? "Bundled" : `Bundling ${progress * 100}%` }), doneIn ? /* @__PURE__ */ jsxs117("div", { style: right4, children: [ doneIn, "ms" ] }) : null ] }); }; var BrowserSetupProgress = ({ progress, doneIn, startedBundling, alreadyAvailable }) => { return /* @__PURE__ */ jsxs117("div", { style: progressItem2, children: [ progress === 1 || alreadyAvailable ? /* @__PURE__ */ jsx226(SuccessIcon, {}) : /* @__PURE__ */ jsx226(CircularProgress, { progress }), /* @__PURE__ */ jsx226(Spacing, { x: 1 }), /* @__PURE__ */ jsx226("div", { style: label11, children: alreadyAvailable && startedBundling ? "Headless browser already available" : progress === 1 ? "Downloaded Headless Shell" : `Downloading Headless Shell ${Math.round(progress * 100)}%` }), doneIn ? /* @__PURE__ */ jsxs117("div", { style: right4, children: [ doneIn, "ms" ] }) : null ] }); }; var RenderingProgress2 = ({ progress }) => { return /* @__PURE__ */ jsxs117("div", { style: progressItem2, children: [ progress.frames === progress.totalFrames ? /* @__PURE__ */ jsx226(SuccessIcon, {}) : /* @__PURE__ */ jsx226(CircularProgress, { progress: progress.frames / progress.totalFrames }), /* @__PURE__ */ jsx226(Spacing, { x: 1 }), /* @__PURE__ */ jsx226("div", { style: label11, children: progress.doneIn ? `Rendered ${progress.totalFrames} frames` : `Rendering ${progress.frames} / ${progress.totalFrames} frames` }), progress.doneIn ? /* @__PURE__ */ jsxs117("div", { style: right4, children: [ progress.doneIn, "ms" ] }) : null ] }); }; var StitchingProgress = ({ progress }) => { return /* @__PURE__ */ jsxs117("div", { style: progressItem2, children: [ progress.frames === progress.totalFrames ? /* @__PURE__ */ jsx226(SuccessIcon, {}) : /* @__PURE__ */ jsx226(CircularProgress, { progress: progress.frames / progress.totalFrames }), /* @__PURE__ */ jsx226(Spacing, { x: 1 }), /* @__PURE__ */ jsx226("div", { style: label11, children: progress.doneIn ? `Encoded ${progress.totalFrames} frames` : `Encoding ${progress.frames} / ${progress.totalFrames} frames` }), progress.doneIn ? /* @__PURE__ */ jsxs117("div", { style: right4, children: [ progress.doneIn, "ms" ] }) : null ] }); }; var DownloadsProgress = ({ downloads }) => { const allHaveProgress = downloads.every((a) => a.totalBytes); const totalBytes = allHaveProgress ? downloads.reduce((a, b) => a + b.totalBytes, 0) : null; const downloaded = allHaveProgress ? downloads.reduce((a, b) => a + b.downloaded, 0) : null; const progress = allHaveProgress ? downloaded / totalBytes : 0.1; return /* @__PURE__ */ jsxs117("div", { style: progressItem2, children: [ progress === 1 ? /* @__PURE__ */ jsx226(SuccessIcon, {}) : /* @__PURE__ */ jsx226(CircularProgress, { progress }), /* @__PURE__ */ jsx226(Spacing, { x: 1 }), /* @__PURE__ */ jsxs117("div", { style: label11, children: [ "Downloading ", downloads.length, " file", downloads.length === 1 ? "" : "s" ] }) ] }); }; var OpenFile = ({ job }) => { const labelStyle5 = useMemo118(() => { return { ...label11, textAlign: "left", appearance: "none", border: 0, paddingLeft: 0, cursor: job.deletedOutputLocation ? "inherit" : "pointer", textDecoration: job.deletedOutputLocation ? "line-through" : "none" }; }, [job.deletedOutputLocation]); const onClick = useCallback108(() => { openInFileExplorer({ directory: job.outName }); }, [job.outName]); return /* @__PURE__ */ jsxs117("div", { style: progressItem2, children: [ /* @__PURE__ */ jsx226(SuccessIcon, {}), /* @__PURE__ */ jsx226(Spacing, { x: 1 }), /* @__PURE__ */ jsx226("button", { style: labelStyle5, type: "button", onClick, children: job.outName }), /* @__PURE__ */ jsx226("div", { style: right4, children: /* @__PURE__ */ jsx226(RenderQueueOpenInFinderItem, { job }) }) ] }); }; var GuiRenderStatus = ({ job }) => { if (job.status === "idle" || job.status === "failed") { throw new Error("This component should not be rendered when the job is idle"); } return /* @__PURE__ */ jsxs117("div", { children: [ /* @__PURE__ */ jsx226(Spacing, { y: 0.5 }), /* @__PURE__ */ jsx226(BrowserSetupProgress, { ...job.progress.browser, startedBundling: Boolean(job.progress.bundling) }), job.progress.bundling && /* @__PURE__ */ jsx226(BundlingProgress, { progress: job.progress.bundling.progress, doneIn: job.progress.bundling.doneIn }), job.progress.rendering ? /* @__PURE__ */ jsx226(RenderingProgress2, { progress: job.progress.rendering }) : null, job.progress.stitching ? /* @__PURE__ */ jsx226(StitchingProgress, { progress: job.progress.stitching }) : null, job.progress.downloads.length > 0 ? /* @__PURE__ */ jsx226(DownloadsProgress, { downloads: job.progress.downloads }) : null, job.status === "done" ? /* @__PURE__ */ jsx226(OpenFile, { job }) : null, /* @__PURE__ */ jsx226(Spacing, { y: 1 }) ] }); }; // src/components/RenderModal/RenderStatusModal.tsx import { jsx as jsx227, jsxs as jsxs118, Fragment as Fragment37 } from "react/jsx-runtime"; var container50 = { padding: 20, maxWidth: 900, paddingTop: 0 }; var codeBlock = { backgroundColor: "#222", whiteSpace: "pre", padding: 12, borderRadius: 4, fontFamily: "monospace", overflow: "auto", maxHeight: 300 }; var spacer3 = { height: SPACING_UNIT, width: SPACING_UNIT }; var buttonRow = { display: "flex", flexDirection: "row", justifyContent: "flex-end" }; var RenderStatusModal = ({ jobId }) => { const { setSelectedModal } = useContext81(ModalsContext); const { jobs, removeClientJob, cancelClientJob } = useContext81(RenderQueueContext); const job = jobs.find((j) => j.id === jobId); if (!job) { throw new Error("job not found"); } const isClientJob = isClientRenderJob(job); const onQuit = useCallback109(() => { setSelectedModal(null); }, [setSelectedModal]); const onRetry = useCallback109(() => { if (isClientJob) { const retryPayload = makeClientRetryPayload(job); setSelectedModal(retryPayload); } else { const retryPayload = makeRetryPayload(job); setSelectedModal(retryPayload); } }, [job, isClientJob, setSelectedModal]); const onClickOnRemove = useCallback109(() => { setSelectedModal(null); if (isClientJob) { removeClientJob(job.id); showNotification("Removed render", 2000); } else { removeRenderJob(job).catch((err) => { showNotification(`Could not remove job: ${err.message}`, 2000); }); } }, [job, isClientJob, removeClientJob, setSelectedModal]); const onClickOnCancel = useCallback109(() => { if (isClientJob) { cancelClientJob(job.id); } else { cancelRenderJob(job).catch((err) => { showNotification(`Could not cancel job: ${err.message}`, 2000); }); } }, [job, isClientJob, cancelClientJob]); if (job.status === "idle") { throw new Error("should not have rendered this modal"); } return /* @__PURE__ */ jsxs118(ModalContainer, { onOutsideClick: onQuit, onEscape: onQuit, children: [ /* @__PURE__ */ jsx227(ModalHeader, { title: `Render ${job.compositionId}` }), /* @__PURE__ */ jsxs118("div", { style: container50, children: [ job.status === "failed" ? /* @__PURE__ */ jsxs118(Fragment37, { children: [ /* @__PURE__ */ jsx227("p", { children: "The render failed because of the following error:" }), /* @__PURE__ */ jsx227("div", { className: HORIZONTAL_SCROLLBAR_CLASSNAME, style: codeBlock, children: job.error.stack }) ] }) : null, (job.status === "done" || job.status === "running") && (isClientJob ? /* @__PURE__ */ jsx227(ClientRenderProgress, { job }) : /* @__PURE__ */ jsx227(GuiRenderStatus, { job })), /* @__PURE__ */ jsx227("div", { style: spacer3 }), /* @__PURE__ */ jsxs118("div", { style: buttonRow, children: [ job.status === "running" ? /* @__PURE__ */ jsx227(Button, { onClick: onClickOnCancel, children: "Cancel render" }) : /* @__PURE__ */ jsx227(Button, { onClick: onClickOnRemove, children: "Remove render" }), /* @__PURE__ */ jsx227(Flex, {}), job.status === "failed" ? /* @__PURE__ */ jsx227(Button, { onClick: onRetry, children: "Retry" }) : null, /* @__PURE__ */ jsx227("div", { style: spacer3 }), /* @__PURE__ */ jsx227(Button, { onClick: onQuit, children: "Close" }) ] }) ] }) ] }); }; // src/components/RenderModal/ServerRenderModal.tsx import { BrowserSafeApis as BrowserSafeApis10 } from "@remotion/renderer/client"; import { getDefaultOutLocation } from "@remotion/studio-shared"; import { useCallback as useCallback127, useContext as useContext83, useEffect as useEffect79, useMemo as useMemo128, useReducer as useReducer2, useRef as useRef44, useState as useState81 } from "react"; // src/helpers/convert-env-variables.ts var envVariablesObjectToArray = (envVariables) => { return Object.entries(envVariables).map(([key5, one]) => [ key5.trim().toUpperCase(), one.trim() ]); }; var envVariablesArrayToObject = (envVariables) => { return envVariables.map(([key5, val]) => [key5.trim(), val.trim()]).filter(([key5, val]) => key5 && val).reduce((acc, [key5, value]) => { acc[key5.toUpperCase()] = value; return acc; }, {}); }; // src/helpers/render-modal-sections.ts import { useMemo as useMemo119, useState as useState76 } from "react"; var useRenderModalSections = (renderMode, codec) => { const [selectedTab, setTab] = useState76("general"); const shownTabs = useMemo119(() => { if (renderMode === "audio") { return ["general", "data", "audio", "advanced"]; } if (renderMode === "still") { return ["general", "data", "picture", "advanced"]; } if (renderMode === "sequence") { return ["general", "data", "picture", "advanced"]; } if (renderMode === "video") { if (codec === "gif") { return ["general", "data", "picture", "gif", "advanced"]; } return ["general", "data", "picture", "audio", "advanced"]; } throw new TypeError("Unknown render mode"); }, [codec, renderMode]); const tab = useMemo119(() => { if (!shownTabs.includes(selectedTab)) { return shownTabs[0]; } return selectedTab; }, [selectedTab, shownTabs]); return useMemo119(() => { return { tab, setTab, shownTabs }; }, [tab, shownTabs]); }; // src/icons/audio.tsx import { jsx as jsx228 } from "react/jsx-runtime"; var AudioIcon = (props) => /* @__PURE__ */ jsx228("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", ...props, children: /* @__PURE__ */ jsx228("path", { fill: "currentcolor", d: "M243 32.32C105.5 39.15 0 157.8 0 295.5v120.4C0 451.3 28.63 480 64 480h32c17.62 0 32-14.38 32-32V288c0-17.62-14.38-32-32-32H64c-10.79 0-20.8 2.9-29.72 7.7 14.2-106.8 100.5-193.9 210.4-199.4 120.5-5.965 221.7 83.92 234 199.9-9.08-5.1-19.48-8.2-30.68-8.2h-32c-17.62 0-32 14.38-32 32v160c0 17.62 14.38 32 32 32h32c35.38 0 64-28.75 64-64.13V287.9c0-145.4-122-262.88-269-255.58zM64 288h32v160H64c-17.62 0-32-14.5-32-32.13v-95.75C32 302.5 46.38 288 64 288zm416 127.9c0 17.6-14.4 32.1-32 32.1h-32V288h32c17.62 0 32 14.5 32 32.13v95.77z" }) }); // src/icons/data.tsx import { jsx as jsx229 } from "react/jsx-runtime"; var DataIcon = (props) => { return /* @__PURE__ */ jsx229("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 448 512", ...props, children: /* @__PURE__ */ jsx229("path", { fill: "currentcolor", d: "M224 512C100.3 512 0 476.2 0 432V80C0 35.82 100.3 0 224 0C347.7 0 448 35.82 448 80V432C448 476.2 347.7 512 224 512zM416 80.45C415.7 79.69 414.4 77.27 409.8 73.31C402.4 67.11 389.9 60.09 371.6 53.57C335.4 40.62 283.2 32 224 32C164.8 32 112.6 40.62 76.37 53.57C58.1 60.09 45.59 67.11 38.25 73.31C33.55 77.27 32.29 79.69 32 80.45V182.1C46.47 192.7 69.9 202.8 100.9 210.4C135.5 218.9 177.1 224 224 224C270 224 312.5 218.9 347.1 210.4C378.1 202.8 401.5 192.7 416 182.1V80.45zM416 219.5C398.8 228.4 377.9 235.8 354.8 241.5C317.3 250.7 272.2 256 224 256C175.8 256 130.7 250.7 93.22 241.5C70.11 235.8 49.18 228.4 32 219.5V310.1C46.47 320.7 69.9 330.8 100.9 338.4C135.5 346.9 177.1 352 224 352C270 352 312.5 346.9 347.1 338.4C378.1 330.8 401.5 320.7 416 310.1V219.5zM38.25 438.7C45.59 444.9 58.1 451.9 76.37 458.4C112.6 471.4 164.8 480 224 480C283.2 480 335.4 471.4 371.6 458.4C389.9 451.9 402.4 444.9 409.8 438.7C414.4 434.7 415.7 432.3 416 431.6V347.5C398.8 356.4 377.9 363.8 354.8 369.5C317.3 378.7 272.2 384 224 384C175.8 384 130.7 378.7 93.22 369.5C70.11 363.8 49.18 356.4 32 347.5V431.6C32.29 432.3 33.55 434.7 38.25 438.7zM416 431.4C416.1 431.3 416.1 431.3 416.1 431.3L416 431.4zM31.96 431.4C31.94 431.3 31.93 431.3 31.92 431.3L31.96 431.4zM31.96 80.56C31.93 80.65 31.92 80.7 31.92 80.7L31.96 80.56zM416.1 80.7C416.1 80.7 416.1 80.65 416 80.56z" }) }); }; // src/icons/frame.tsx import { jsx as jsx230 } from "react/jsx-runtime"; var PicIcon = (props) => /* @__PURE__ */ jsx230("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", ...props, children: /* @__PURE__ */ jsx230("path", { fill: "currentcolor", d: "M324.9 157.8c-11.38-17.38-39.89-17.31-51.23-.063L200.5 268.5l-16.4-22.6c-11.4-16.8-38.2-16-49.7 0l-64.52 89.16c-6.797 9.406-7.75 21.72-2.547 32C72.53 377.5 83.05 384 94.75 384h322.5c11.41 0 21.8-6.281 27.14-16.38a30.922 30.922 0 0 0-1.516-31.56L324.9 157.8zM95.8 352l62.39-87.38 29.91 41.34c3.1 4.24 8.3 7.24 13.3 6.64 5.25-.125 10.12-2.781 13.02-7.188l83.83-129.9L415 352H95.8zM447.1 32h-384C28.65 32-.01 60.65-.01 96v320c0 35.35 28.65 64 63.1 64h384c35.35 0 64-28.65 64-64V96c.01-35.35-27.79-64-63.99-64zM480 416c0 17.64-14.36 32-32 32H64c-17.64 0-32-14.36-32-32V96c0-17.64 14.36-32 32-32h384c17.64 0 32 14.36 32 32v320zM144 192c26.5 0 48-21.5 48-48s-21.5-48-48-48-48 21.5-48 48 21.5 48 48 48zm0-64c8.822 0 15.1 7.178 15.1 16s-6.3 16-15.1 16-16-7.2-16-16 7.2-16 16-16z" }) }); // src/icons/gear.tsx import { jsx as jsx231 } from "react/jsx-runtime"; var GearIcon = (props) => /* @__PURE__ */ jsx231("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", ...props, children: /* @__PURE__ */ jsx231("path", { fill: "currentcolor", d: "M168 255.1c0-47.7 39.4-88 88-88s88 40.3 88 88c0 49.5-39.4 88.9-88 88.9s-88-39.4-88-88.9zm88-56c-30.9 0-56 26-56 56 0 31.8 25.1 56 56 56s56-24.2 56-56c0-30-25.1-56-56-56zM65.67 230.6l-40.33-36.8c-11.12-10.1-13.68-26.6-6.16-39.6l30.24-52.4c7.52-13.02 23.09-19.05 37.42-14.48l51.96 16.58c13.4-10.34 28.2-18.94 44-25.47l11.7-53.27C197.7 10.47 210.7 0 225.8 0h60.4c15.1 0 28.1 10.47 31.3 25.16l11.7 53.27c14.9 6.53 30.6 15.13 44 25.47l52-16.58c14.3-4.57 29.9 1.46 37.4 14.48l30.2 52.4c7.5 13 5 29.5-6.1 39.6l-40.4 36.8c1.1 8.3 1.7 16.8 1.7 24.5 0 9.5-.6 18-1.7 26.3l40.4 36.8c11.1 10.1 13.6 26.6 6.1 39.6l-30.2 52.4c-7.5 13-23.1 19-37.4 14.5l-52-16.6c-13.4 10.3-29.1 18.9-44 25.5l-11.7 53.2c-3.2 14.7-16.2 25.2-31.3 25.2h-60.4c-15.1 0-28.1-10.5-31.3-25.2l-11.7-53.2c-15.8-6.6-30.6-15.2-44-25.5l-51.96 16.6c-14.33 4.5-29.9-1.5-37.42-14.5l-30.24-52.4c-7.52-13-4.96-29.5 6.16-39.6l40.33-36.8c-1.1-8.3-1.67-16.8-1.67-26.3 0-7.7.57-16.2 1.67-24.5zm92.73-101.4-13.3 10.3-67.97-21.7-30.24 52.4 52.69 48-2.19 16.6c-.92 6.9-1.39 14-1.39 20.3 0 8.1.47 15.2 1.39 22.1l2.19 16.6-52.69 48 30.24 52.4 67.97-21.7 13.3 10.3c11.1 8.6 23.5 15.8 36.6 20.3l15.5 7.3 15.3 69.6h60.4l15.3-69.6 14.6-7.3c14-4.5 26.4-11.7 37.5-20.3l13.3-10.3 68 21.7 30.2-52.4-52.7-48 2.2-16.6c.9-6.9 1.4-14 1.4-21.2 0-7.2-.5-14.3-1.4-21.2l-2.2-16.6 52.7-48-30.2-52.4-68 21.7-13.3-10.3c-11.1-8.6-23.5-15.8-37.5-21.2l-14.6-6.4L286.2 32h-60.4l-15.3 69.6L195 108c-13.1 5.4-25.5 12.6-36.6 21.2z" }) }); // src/icons/gif.tsx import { jsx as jsx232 } from "react/jsx-runtime"; var GifIcon = (props) => /* @__PURE__ */ jsx232("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 576 512", ...props, children: /* @__PURE__ */ jsx232("path", { fill: "currentcolor", d: "M512 32H64C28.65 32 0 60.65 0 96v320c0 35.35 28.65 64 64 64h448c35.35 0 64-28.65 64-64V96c0-35.35-28.7-64-64-64zm32 384c0 17.64-14.36 32-32 32H64c-17.64 0-32-14.36-32-32V96c0-17.64 14.36-32 32-32h448c17.64 0 32 14.36 32 32v320zm-80-256h-96c-8.8 0-16 7.2-16 16v160c0 8.844 7.156 16 16 16s16-7.156 16-16v-64h56c8.844 0 16-7.156 16-16s-7.156-16-16-16h-56v-48h80c8.8 0 16-7.2 16-16s-7.2-16-16-16zm-160 0c-8.8 0-16 7.2-16 16v160c0 8.844 7.156 16 16 16s16-7.156 16-16V176c0-8.8-7.2-16-16-16zm-64 80h-64c-8.8 0-16 7.2-16 16s7.156 16 16 16h48v33.45c-24.83 19.91-69.13 18.16-91.48-4.203-24.95-24.95-24.95-65.55 0-90.5 25.03-25 64.19-25 89.22 0 6.25 6.25 16.38 6.25 22.62 0s6.25-16.38 0-22.62c-37.69-37.72-96.78-37.72-134.5 0-37.42 37.42-37.42 98.33 0 135.8C127.8 341.8 153.5 352 180.6 352c27.06 0 52.84-10.25 70.7-28.12 3-2.98 4.7-7.08 4.7-11.28V256c0-8.8-7.2-16-16-16z" }) }); // src/components/Tabs/vertical.tsx import { useCallback as useCallback110, useMemo as useMemo120, useState as useState77 } from "react"; import { jsx as jsx233 } from "react/jsx-runtime"; var selectorButton2 = { border: "none", flex: 1, padding: 8, paddingLeft: 16, display: "flex", flexDirection: "row", fontSize: 14, color: "inherit", alignItems: "center" }; var VerticalTab = ({ children, onClick, style: style12, selected }) => { const [hovered, setHovered] = useState77(false); const { tabIndex } = useZIndex(); const onPointerEnter = useCallback110(() => { setHovered(true); }, []); const onPointerLeave = useCallback110(() => { setHovered(false); }, []); const definiteStyle = useMemo120(() => { return { ...selectorButton2, backgroundColor: selected ? SELECTED_BACKGROUND : hovered ? CLEAR_HOVER : "transparent", color: selected ? "white" : LIGHT_TEXT, boxShadow: selected ? "none" : undefined, ...style12 }; }, [hovered, selected, style12]); return /* @__PURE__ */ jsx233("button", { style: definiteStyle, type: "button", onClick, tabIndex, onPointerLeave, onPointerEnter, children }); }; // src/components/RenderModal/CrfSetting.tsx import { BrowserSafeApis as BrowserSafeApis2 } from "@remotion/renderer/client"; import { useState as useState79 } from "react"; // src/components/RenderModal/NumberSetting.tsx import { useCallback as useCallback112 } from "react"; // src/components/RenderModal/OptionExplainerBubble.tsx import { BrowserSafeApis } from "@remotion/renderer/client"; // src/components/RenderModal/CliCopyButton.tsx import { useCallback as useCallback111, useEffect as useEffect77, useMemo as useMemo121, useState as useState78 } from "react"; import { jsx as jsx234 } from "react/jsx-runtime"; var svgStyle2 = { width: 16, height: 16, verticalAlign: "sub" }; var copiedStyle = { fontSize: "14px", minHeight: "30px", minWidth: "30px", display: "flex", alignItems: "center", justifyContent: "center" }; var buttonStyle6 = { width: "30px", height: "30px", border: "none", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center" }; var CliCopyButton = ({ valueToCopy }) => { const [copied, setCopied] = useState78(false); const [hovered, setHovered] = useState78(false); const fillColor = useMemo121(() => { return hovered ? "white" : LIGHT_TEXT; }, [hovered]); const clipboardIcon = /* @__PURE__ */ jsx234(ClipboardIcon, { color: fillColor, style: svgStyle2 }); const checkSvg = /* @__PURE__ */ jsx234("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 448 512", style: svgStyle2, children: /* @__PURE__ */ jsx234("path", { fill: fillColor, d: "M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z" }) }); const onPointerEnter = useCallback111(() => { setHovered(true); }, []); const onPointerLeave = useCallback111(() => { setHovered(false); }, []); useEffect77(() => { if (!copied) { return; } const handleClear = () => { setCopied(false); setHovered(false); }; const to = setTimeout(() => handleClear(), 2000); return () => clearTimeout(to); }, [copied]); return copied ? /* @__PURE__ */ jsx234("span", { style: copiedStyle, children: checkSvg }) : /* @__PURE__ */ jsx234("button", { type: "button", onPointerEnter, onPointerLeave, style: buttonStyle6, onClick: () => { navigator.clipboard.writeText(valueToCopy); setCopied(true); }, children: clipboardIcon }); }; // src/components/RenderModal/OptionExplainer.tsx import { jsx as jsx235, jsxs as jsxs119, Fragment as Fragment38 } from "react/jsx-runtime"; var container51 = { fontSize: 14, paddingTop: 10, paddingBottom: 10, backgroundColor: INPUT_BACKGROUND }; var padding3 = { paddingLeft: 20, paddingRight: 20 }; var title5 = { fontSize: 14 }; var description = { fontSize: 14, maxWidth: 400 }; var link2 = { fontSize: 14, maxWidth: 200, color: "#0b84f3", wordWrap: "break-word", textDecoration: "none" }; var infoRow = { ...padding3, fontSize: 14, display: "flex", flexDirection: "row", alignItems: "center" }; var infoRowLabel = { width: 150, fontSize: 14, color: "white" }; var flexSpacer = { display: "flex", flex: 1 }; var copyWrapper = { display: "flex", justifyContent: "flex-end" }; var OptionExplainer = ({ option }) => { return /* @__PURE__ */ jsxs119("div", { style: container51, className: "__remotion-info-button-container", children: [ /* @__PURE__ */ jsxs119("div", { style: padding3, children: [ /* @__PURE__ */ jsxs119("div", { children: [ /* @__PURE__ */ jsx235("strong", { style: title5, children: option.name }), option.docLink ? /* @__PURE__ */ jsxs119(Fragment38, { children: [ /* @__PURE__ */ jsx235(Spacing, { x: 1 }), /* @__PURE__ */ jsx235("a", { style: link2, href: option.docLink, target: "_blank", children: "Docs" }) ] }) : null ] }), /* @__PURE__ */ jsx235("div", { style: description, children: option.description("ssr") }) ] }), /* @__PURE__ */ jsx235(Spacing, { y: 0.5, block: true }), /* @__PURE__ */ jsx235(MenuDivider, {}), /* @__PURE__ */ jsx235(Spacing, { y: 0.5, block: true }), /* @__PURE__ */ jsxs119("div", { children: [ /* @__PURE__ */ jsxs119("div", { style: infoRow, children: [ /* @__PURE__ */ jsx235("div", { style: infoRowLabel, children: "CLI flag" }), /* @__PURE__ */ jsx235("div", { style: flexSpacer }), /* @__PURE__ */ jsxs119("code", { children: [ "--", option.cliFlag ] }), /* @__PURE__ */ jsx235("div", { style: copyWrapper, children: /* @__PURE__ */ jsx235(CliCopyButton, { valueToCopy: option.cliFlag }) }) ] }), option.ssrName ? /* @__PURE__ */ jsxs119("div", { style: infoRow, children: [ /* @__PURE__ */ jsx235("div", { style: infoRowLabel, children: "Node.JS option" }), /* @__PURE__ */ jsx235("div", { style: flexSpacer }), /* @__PURE__ */ jsx235("code", { children: option.ssrName }), /* @__PURE__ */ jsx235(Spacing, { x: 3.75 }) ] }) : null, /* @__PURE__ */ jsx235("div", { style: infoRow }) ] }) ] }); }; // src/components/RenderModal/OptionExplainerBubble.tsx import { jsx as jsx236 } from "react/jsx-runtime"; var OptionExplainerBubble = ({ id }) => { const option = BrowserSafeApis.options[id]; return /* @__PURE__ */ jsx236(InfoBubble, { title: "Learn more about this option", children: /* @__PURE__ */ jsx236(OptionExplainer, { option }) }); }; // src/components/RenderModal/NumberSetting.tsx import { jsx as jsx237, jsxs as jsxs120, Fragment as Fragment39 } from "react/jsx-runtime"; var NumberSetting = ({ name, value, step, hint, onValueChanged, max, min, formatter }) => { const onTextChanged = useCallback112((e) => { onValueChanged((q) => { const newSetting = step < 1 ? parseFloat(e) : parseInt(e, 10); if (Number.isNaN(newSetting)) { return q; } return Math.min(max ?? Infinity, Math.max(newSetting, min)); }); }, [max, min, onValueChanged, step]); const onValueChange = useCallback112((newConcurrency) => { onValueChanged(newConcurrency); }, [onValueChanged]); return /* @__PURE__ */ jsxs120("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs120("div", { style: label5, children: [ name, hint ? /* @__PURE__ */ jsxs120(Fragment39, { children: [ /* @__PURE__ */ jsx237(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx237(OptionExplainerBubble, { id: hint }) ] }) : null ] }), /* @__PURE__ */ jsx237("div", { style: rightRow, children: /* @__PURE__ */ jsx237(RightAlignInput, { children: /* @__PURE__ */ jsx237(InputDragger, { value, name: name.toLowerCase(), onTextChange: onTextChanged, onValueChange, step, placeholder: [min, max].map((f) => f !== null && f !== undefined ? f : "").join("-"), min, max, formatter, status: "ok", rightAlign: true }) }) }) ] }); }; // src/components/RenderModal/CrfSetting.tsx import { jsx as jsx238 } from "react/jsx-runtime"; var getDefaultCrfState = () => { return BrowserSafeApis2.validCodecs.map((c) => { return [c, BrowserSafeApis2.getDefaultCrfForCodec(c)]; }).reduce((acc, [codec, crf]) => { return { ...acc, [codec]: crf }; }, {}); }; var useCrfState = (codec) => { const [state, setState] = useState79(() => getDefaultCrfState()); const range = BrowserSafeApis2.getValidCrfRanges(codec); const setCrf = (updater) => { setState((q) => { const val = q[codec]; if (val === null) { throw new TypeError(`Got unexpected codec "${codec}"`); } return { ...q, [codec]: typeof updater === "number" ? updater : updater(val) }; }); }; return { crf: state[codec], setCrf, minCrf: range[0], maxCrf: range[1] }; }; var CrfSetting = ({ crf, setCrf, min, max, option }) => { return /* @__PURE__ */ jsx238(NumberSetting, { min, max, name: "CRF", onValueChanged: setCrf, value: crf, step: 1, hint: option }); }; // src/components/RenderModal/RenderModalAdvanced.tsx import { BrowserSafeApis as BrowserSafeApis3 } from "@remotion/renderer/client"; import { useCallback as useCallback116, useMemo as useMemo122 } from "react"; // src/helpers/presets-labels.ts var labelx264Preset = (profile) => { if (profile === "ultrafast") { return "ultrafast"; } if (profile === "superfast") { return "superfast"; } if (profile === "veryfast") { return "veryfast"; } if (profile === "faster") { return "faster"; } if (profile === "fast") { return "fast"; } if (profile === "medium") { return "medium"; } if (profile === "slow") { return "slow"; } if (profile === "slower") { return "slower"; } if (profile === "veryslow") { return "veryslow"; } if (profile === "placebo") { return "placebo"; } throw new TypeError(`Unknown x264 preset: ${profile}`); }; // src/components/RenderModal/RenderModalEnvironmentVariables.tsx import { useCallback as useCallback115 } from "react"; // src/components/RenderModal/EnvInput.tsx import React157, { useCallback as useCallback114 } from "react"; // src/components/RenderModal/InlineEyeIcon.tsx import { useCallback as useCallback113 } from "react"; import { jsx as jsx239 } from "react/jsx-runtime"; var clearIcon2 = { height: 14, color: "currentColor" }; var InlineEyeButton = ({ onClick, enabled }) => { const renderAction = useCallback113((color) => { return enabled ? /* @__PURE__ */ jsx239("svg", { style: clearIcon2, viewBox: "0 0 640 512", children: /* @__PURE__ */ jsx239("path", { fill: color, d: "M25.9 3.4C19-2 8.9-.8 3.4 6.1S-.8 23.1 6.1 28.6l608 480c6.9 5.5 17 4.3 22.5-2.6s4.3-17-2.6-22.5L25.9 3.4zM605.5 268.3c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-51.2 0-96 14.8-133.9 36.8l27.3 21.5C244.6 74.2 280.2 64 320 64c70.4 0 127.7 32 170.8 72c43.1 40 71.9 88 85.2 120c-9.2 22.1-25.9 52-49.5 81.5l25.1 19.8c25.6-32 43.7-64.4 53.9-89zM88.4 154.7c-25.6 32-43.7 64.4-53.9 89c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c51.2 0 96-14.8 133.9-36.8l-27.3-21.5C395.4 437.8 359.8 448 320 448c-70.4 0-127.7-32-170.8-72C106.1 336 77.3 288 64 256c9.2-22.1 25.9-52 49.5-81.5L88.4 154.7zM320 384c16.7 0 32.7-3.2 47.4-9.1l-30.9-24.4c-5.4 .9-10.9 1.4-16.5 1.4c-51 0-92.8-39.8-95.8-90.1l-30.9-24.4c-.9 6-1.3 12.2-1.3 18.5c0 70.7 57.3 128 128 128zM448 256c0-70.7-57.3-128-128-128c-16.7 0-32.7 3.2-47.4 9.1l30.9 24.4c5.4-.9 10.9-1.4 16.5-1.4c51 0 92.8 39.8 95.8 90.1l30.9 24.4c.9-6 1.3-12.2 1.3-18.5z" }) }) : /* @__PURE__ */ jsx239("svg", { style: clearIcon2, viewBox: "0 0 576 512", children: /* @__PURE__ */ jsx239("path", { fill: color, d: "M117.2 136C160.3 96 217.6 64 288 64s127.7 32 170.8 72c43.1 40 71.9 88 85.2 120c-13.3 32-42.1 80-85.2 120c-43.1 40-100.4 72-170.8 72s-127.7-32-170.8-72C74.1 336 45.3 288 32 256c13.3-32 42.1-80 85.2-120zM288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM192 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z" }) }); }, [enabled]); return /* @__PURE__ */ jsx239(InlineAction, { renderAction, onClick }); }; // src/components/RenderModal/EnvInput.tsx import { jsx as jsx240, jsxs as jsxs121, Fragment as Fragment40 } from "react/jsx-runtime"; var input3 = { flex: 1, width: "100%" }; var validationStyle = { paddingLeft: optionRow.paddingLeft, paddingRight: optionRow.paddingRight }; var EnvInput = ({ onEnvKeyChange, onEnvValChange, envKey, envVal, onDelete, index, autoFocus, isDuplicate }) => { const [showInPlainText, setShowInPlainText] = React157.useState(false); const [initialWarningKeyMissing, setKeyWarningEligible] = React157.useState(() => { return envKey.trim() === "" && envVal.trim() !== ""; }); const [initialWarningValMissing, setValueWarningEligible] = React157.useState(() => { return envKey.trim() !== "" && envVal.trim() === ""; }); const isKeyMissing = envKey.trim() === "" && initialWarningKeyMissing && envVal.trim() !== ""; const isValMissing = envVal.trim() === "" && initialWarningValMissing && envKey.trim() !== ""; const handleDelete = useCallback114(() => { onDelete(index); }, [index, onDelete]); const togglePlainText = useCallback114(() => { setShowInPlainText((prev) => !prev); }, []); const handleKeyChange = useCallback114((e) => { onEnvKeyChange(index, e.target.value); }, [index, onEnvKeyChange]); const handleValueChange = useCallback114((e) => { onEnvValChange(index, e.target.value); }, [index, onEnvValChange]); const makeKeyWarningEligible = useCallback114(() => { setKeyWarningEligible(true); }, []); const makeValueWarningEligible = useCallback114(() => { setValueWarningEligible(true); }, []); const isNodeEnvKey = envKey.trim() === "NODE_ENV"; return /* @__PURE__ */ jsxs121(Fragment40, { children: [ /* @__PURE__ */ jsxs121(Row, { align: "center", style: optionRow, children: [ /* @__PURE__ */ jsx240(RemotionInput, { status: isNodeEnvKey || isDuplicate || isKeyMissing ? "warning" : "ok", type: "text", placeholder: "Key", style: input3, value: envKey, onBlur: makeKeyWarningEligible, autoFocus, onChange: handleKeyChange, rightAlign: false }), /* @__PURE__ */ jsx240(Spacing, { x: 1 }), /* @__PURE__ */ jsx240(RemotionInput, { status: isValMissing ? "warning" : "ok", placeholder: "Value", type: showInPlainText ? "text" : "password", style: input3, value: envVal, onBlur: makeValueWarningEligible, onChange: handleValueChange, rightAlign: false }), /* @__PURE__ */ jsx240(Spacing, { x: 1.5 }), /* @__PURE__ */ jsx240(InlineEyeButton, { enabled: !showInPlainText, onClick: togglePlainText }), /* @__PURE__ */ jsx240(InlineRemoveButton, { onClick: handleDelete }) ] }), isNodeEnvKey ? /* @__PURE__ */ jsx240("div", { style: validationStyle, children: /* @__PURE__ */ jsx240(ValidationMessage, { align: "flex-start", type: "warning", message: "NODE_ENV will be overwritten by Remotion during the render process." }) }) : null, isDuplicate ? /* @__PURE__ */ jsx240("div", { style: validationStyle, children: /* @__PURE__ */ jsx240(ValidationMessage, { align: "flex-start", type: "warning", message: `${envKey.toUpperCase()} is already defined.` }) }) : null, isKeyMissing ? /* @__PURE__ */ jsx240("div", { style: validationStyle, children: /* @__PURE__ */ jsx240(ValidationMessage, { align: "flex-start", type: "warning", message: "Key is missing." }) }) : null, isValMissing ? /* @__PURE__ */ jsx240("div", { style: validationStyle, children: /* @__PURE__ */ jsx240(ValidationMessage, { align: "flex-start", type: "warning", message: "Value is missing." }) }) : null ] }); }; // src/components/RenderModal/RenderModalEnvironmentVariables.tsx import { jsx as jsx241, jsxs as jsxs122 } from "react/jsx-runtime"; var title6 = { fontSize: 14, fontWeight: "bold", color: LIGHT_TEXT, marginLeft: 16 }; var container52 = { marginTop: 20 }; var button3 = { marginLeft: 16 }; var RenderModalEnvironmentVariables = ({ envVariables, setEnvVariables }) => { const onEnvValChange = useCallback115((index, value) => { setEnvVariables((oldEnv) => { const newEnv = [...oldEnv]; newEnv[index][1] = value; return newEnv; }); }, [setEnvVariables]); const onEnvKeyChange = useCallback115((index, value) => { setEnvVariables((oldEnv) => { const newEnv = [...oldEnv]; newEnv[index][0] = value; return newEnv; }); }, [setEnvVariables]); const onDelete = useCallback115((index) => { setEnvVariables((oldEnv) => oldEnv.filter((_, idx) => idx !== index)); }, [setEnvVariables]); const addField = useCallback115(() => { setEnvVariables((oldEnv) => [...oldEnv, ["", ""]]); }, [setEnvVariables]); const usedKeys = []; return /* @__PURE__ */ jsxs122("div", { style: container52, children: [ /* @__PURE__ */ jsx241("strong", { style: title6, children: "Environment variables" }), envVariables.map((env, i) => { let isDuplicate = false; if (usedKeys.includes(env[0].toUpperCase())) { isDuplicate = true; } usedKeys.push(env[0].toUpperCase()); return /* @__PURE__ */ jsx241(EnvInput, { onEnvKeyChange, onEnvValChange, envKey: env[0], envVal: env[1], onDelete, index: i, isDuplicate, autoFocus: i === envVariables.length - 1 && env[0] === "" }, i); }), /* @__PURE__ */ jsx241(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx241(Button, { style: button3, onClick: addField, children: "+ Add env variable" }), /* @__PURE__ */ jsx241(Spacing, { y: 1, block: true }) ] }); }; // src/components/RenderModal/RenderModalHr.tsx import { jsx as jsx242 } from "react/jsx-runtime"; var hrStyle = { margin: "0 0 0 0", padding: "0 0 0 0", border: "none", borderTop: "1px solid #000", marginRight: 16, marginLeft: 16, marginTop: 8, marginBottom: 8 }; var RenderModalHr = () => { return /* @__PURE__ */ jsx242("div", { style: hrStyle }); }; // src/components/RenderModal/RenderModalAdvanced.tsx import { jsx as jsx243, jsxs as jsxs123 } from "react/jsx-runtime"; var container53 = { flex: 1, overflowY: "auto" }; var RenderModalAdvanced = ({ renderMode, maxConcurrency, minConcurrency, setConcurrency, concurrency, delayRenderTimeout, setDelayRenderTimeout, disallowParallelEncoding, setDisallowParallelEncoding, setDisableWebSecurity, setIgnoreCertificateErrors, setHeadless, headless, ignoreCertificateErrors, disableWebSecurity, openGlOption, setOpenGlOption, setEnvVariables, envVariables, setx264Preset, x264Preset, codec, setMediaCacheSizeInBytes, mediaCacheSizeInBytes, offthreadVideoCacheSizeInBytes, setOffthreadVideoCacheSizeInBytes, offthreadVideoThreads, setOffthreadVideoThreads, enableMultiProcessOnLinux, setChromiumMultiProcessOnLinux, setUserAgent, userAgent, beep, setBeep, repro, setRepro, hardwareAcceleration, chromeModeOption, setChromeModeOption, setHardwareAcceleration, darkMode, setDarkMode }) => { const extendedOpenGlOptions = useMemo122(() => { return [ "angle", "egl", "swangle", "swiftshader", "vulkan", "angle-egl", "default" ]; }, []); const toggleCustomMediaCacheSizeInBytes = useCallback116(() => { setMediaCacheSizeInBytes((previous) => { if (previous === null) { return 1000 * 1000 * 1000; } return null; }); }, [setMediaCacheSizeInBytes]); const toggleCustomOffthreadVideoCacheSizeInBytes = useCallback116(() => { setOffthreadVideoCacheSizeInBytes((previous) => { if (previous === null) { return 512 * 1024 * 1024; } return null; }); }, [setOffthreadVideoCacheSizeInBytes]); const toggleCustomOffthreadVideoThreads = useCallback116(() => { setOffthreadVideoThreads((previous) => { if (previous === null) { return 2; } return null; }); }, [setOffthreadVideoThreads]); const toggleCustomUserAgent = useCallback116(() => { setUserAgent((previous) => { if (previous === null) { return "Mozilla/5.0 (Remotion)"; } return null; }); }, [setUserAgent]); const onDisallowParallelEncodingChanged = useCallback116((e) => { setDisallowParallelEncoding(e.target.checked); }, [setDisallowParallelEncoding]); const onDisableWebSecurityChanged = useCallback116((e) => { setDisableWebSecurity(e.target.checked); }, [setDisableWebSecurity]); const onEnableMultiProcessOnLinux = useCallback116((e) => { setChromiumMultiProcessOnLinux(e.target.checked); }, [setChromiumMultiProcessOnLinux]); const onIgnoreCertificatErrors = useCallback116((e) => { setIgnoreCertificateErrors(e.target.checked); }, [setIgnoreCertificateErrors]); const onHeadless = useCallback116((e) => { setHeadless(e.target.checked); }, [setHeadless]); const onUserAgentChanged = useCallback116((e) => { setUserAgent(e.target.value); }, [setUserAgent]); const onDarkMode = useCallback116((e) => { setDarkMode(e.target.checked); }, [setDarkMode]); const onPlayBeepSound = useCallback116((e) => { setBeep(e.target.checked); }, [setBeep]); const onReproToggle = useCallback116((e) => { setRepro(e.target.checked); }, [setRepro]); const openGlOptions = useMemo122(() => { return extendedOpenGlOptions.map((option) => { return { label: option === "default" ? "Default" : option, onClick: () => setOpenGlOption(option), key: option, leftItem: openGlOption === option ? /* @__PURE__ */ jsx243(Checkmark, {}) : null, id: option, keyHint: null, quickSwitcherLabel: null, subMenu: null, type: "item", value: option }; }); }, [extendedOpenGlOptions, openGlOption, setOpenGlOption]); const chromeModeOptions = useMemo122(() => { return BrowserSafeApis3.validChromeModeOptions.map((option) => { return { label: option, onClick: () => setChromeModeOption(option), key: option, leftItem: chromeModeOption === option ? /* @__PURE__ */ jsx243(Checkmark, {}) : null, id: option, keyHint: null, quickSwitcherLabel: null, subMenu: null, type: "item", value: option }; }); }, [chromeModeOption, setChromeModeOption]); const x264PresetOptions = useMemo122(() => { return BrowserSafeApis3.x264PresetOptions.map((option) => { return { label: labelx264Preset(option), onClick: () => setx264Preset(option), key: option, type: "item", id: option, keyHint: null, leftItem: x264Preset === option ? /* @__PURE__ */ jsx243(Checkmark, {}) : null, quickSwitcherLabel: null, subMenu: null, value: option }; }); }, [setx264Preset, x264Preset]); const hardwareAccelerationValues = useMemo122(() => { return BrowserSafeApis3.hardwareAccelerationOptions.map((option) => { return { label: option, onClick: () => setHardwareAcceleration(option), leftItem: hardwareAcceleration === option ? /* @__PURE__ */ jsx243(Checkmark, {}) : null, subMenu: null, quickSwitcherLabel: null, type: "item", id: option, keyHint: null, value: option }; }); }, [hardwareAcceleration, setHardwareAcceleration]); const changeMediaCacheSizeInBytes = useCallback116((cb) => { setMediaCacheSizeInBytes((prev) => { if (prev === null) { throw new TypeError("Expected previous value"); } if (typeof cb === "function") { return cb(prev); } return cb; }); }, [setMediaCacheSizeInBytes]); const changeOffthreadVideoCacheSizeInBytes = useCallback116((cb) => { setOffthreadVideoCacheSizeInBytes((prev) => { if (prev === null) { throw new TypeError("Expected previous value"); } if (typeof cb === "function") { return cb(prev); } return cb; }); }, [setOffthreadVideoCacheSizeInBytes]); const changeOffthreadVideoThreads = useCallback116((cb) => { setOffthreadVideoThreads((prev) => { if (prev === null) { throw new TypeError("Expected previous value"); } if (typeof cb === "function") { return cb(prev); } return cb; }); }, [setOffthreadVideoThreads]); return /* @__PURE__ */ jsxs123("div", { style: container53, className: VERTICAL_SCROLLBAR_CLASSNAME, children: [ renderMode === "still" ? null : /* @__PURE__ */ jsx243(NumberSetting, { min: minConcurrency, max: maxConcurrency, step: 1, name: "Concurrency", formatter: (w) => `${w}x`, onValueChanged: setConcurrency, value: concurrency }), renderMode === "video" && codec === "h264" ? /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs123("div", { style: label5, children: [ "x264 Preset", /* @__PURE__ */ jsx243(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx243(OptionExplainerBubble, { id: "x264Option" }) ] }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Combobox, { title: x264Preset, selectedId: x264Preset, values: x264PresetOptions }) }) ] }) : null, renderMode === "video" ? /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs123("div", { style: label5, children: [ "Hardware acceleration", /* @__PURE__ */ jsx243(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx243(OptionExplainerBubble, { id: "hardwareAccelerationOption" }) ] }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Combobox, { title: hardwareAcceleration, selectedId: hardwareAcceleration, values: hardwareAccelerationValues }) }) ] }) : null, /* @__PURE__ */ jsx243(NumberSetting, { min: 7000, max: 900000, name: "delayRender() timeout", onValueChanged: setDelayRenderTimeout, formatter: (w) => `${w}ms`, step: 1000, hint: "delayRenderTimeoutInMillisecondsOption", value: delayRenderTimeout }), /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsx243("div", { style: label5, children: "No parallel encoding" }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Checkbox, { checked: disallowParallelEncoding, onChange: onDisallowParallelEncodingChanged, name: "disallow-parallel-encoding" }) }) ] }), renderMode === "audio" ? null : /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs123("div", { style: label5, children: [ "Custom @remotion/media cache size", /* @__PURE__ */ jsx243(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx243(OptionExplainerBubble, { id: "mediaCacheSizeInBytesOption" }) ] }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Checkbox, { checked: mediaCacheSizeInBytes !== null, onChange: toggleCustomMediaCacheSizeInBytes, name: "media-cache-size" }) }) ] }), renderMode === "audio" || mediaCacheSizeInBytes === null ? null : /* @__PURE__ */ jsx243(NumberSetting, { min: 0, max: 2000 * 1024 * 1024, step: 1024, name: "@remotion/media cache size", formatter: (w) => `${w} bytes`, onValueChanged: changeMediaCacheSizeInBytes, value: mediaCacheSizeInBytes }), renderMode === "audio" ? null : /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs123("div", { style: label5, children: [ "Custom OffthreadVideo cache", /* @__PURE__ */ jsx243(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx243(OptionExplainerBubble, { id: "offthreadVideoCacheSizeInBytesOption" }) ] }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Checkbox, { checked: offthreadVideoCacheSizeInBytes !== null, onChange: toggleCustomOffthreadVideoCacheSizeInBytes, name: "custom-audio-bitrate" }) }) ] }), renderMode === "audio" || offthreadVideoCacheSizeInBytes === null ? null : /* @__PURE__ */ jsx243(NumberSetting, { min: 0, max: 2000 * 1024 * 1024, step: 1024, name: "OffthreadVideo cache size", formatter: (w) => `${w} bytes`, onValueChanged: changeOffthreadVideoCacheSizeInBytes, value: offthreadVideoCacheSizeInBytes }), renderMode === "audio" ? null : /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs123("div", { style: label5, children: [ "OffthreadVideo threads ", /* @__PURE__ */ jsx243(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx243(OptionExplainerBubble, { id: "offthreadVideoThreadsOption" }) ] }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Checkbox, { checked: offthreadVideoThreads !== null, onChange: toggleCustomOffthreadVideoThreads, name: "offthread-video-threads" }) }) ] }), renderMode === "audio" || offthreadVideoThreads === null ? null : /* @__PURE__ */ jsx243(NumberSetting, { min: 0, max: 16, step: 1, name: "OffthreadVideo threads", formatter: (w) => `${w}x`, onValueChanged: changeOffthreadVideoThreads, value: offthreadVideoThreads }), /* @__PURE__ */ jsx243(RenderModalHr, {}), /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsx243("div", { style: label5, children: "Disable web security" }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Checkbox, { checked: disableWebSecurity, onChange: onDisableWebSecurityChanged, name: "disable-web-security" }) }) ] }), /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsx243("div", { style: label5, children: "Ignore certificate errors" }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Checkbox, { checked: ignoreCertificateErrors, onChange: onIgnoreCertificatErrors, name: "ignore-certificate-errors" }) }) ] }), /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs123("div", { style: label5, children: [ "Headless mode", /* @__PURE__ */ jsx243(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx243(OptionExplainerBubble, { id: "headlessOption" }) ] }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Checkbox, { checked: headless, onChange: onHeadless, name: "headless" }) }) ] }), /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs123("div", { style: label5, children: [ "Chrome Mode ", /* @__PURE__ */ jsx243(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx243(OptionExplainerBubble, { id: "chromeModeOption" }) ] }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Combobox, { values: chromeModeOptions, selectedId: chromeModeOption, title: "Chrome mode" }) }) ] }), /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs123("div", { style: label5, children: [ "OpenGL render backend ", /* @__PURE__ */ jsx243(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx243(OptionExplainerBubble, { id: "glOption" }) ] }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Combobox, { values: openGlOptions, selectedId: openGlOption, title: "OpenGl option" }) }) ] }), /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs123("div", { style: label5, children: [ "Multi-process Chrome on Linux", /* @__PURE__ */ jsx243(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx243(OptionExplainerBubble, { id: "enableMultiprocessOnLinuxOption" }) ] }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Checkbox, { checked: enableMultiProcessOnLinux, onChange: onEnableMultiProcessOnLinux, name: "enable-multi-process-on-linux" }) }) ] }), /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs123("div", { style: label5, children: [ "Dark Mode", /* @__PURE__ */ jsx243(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx243(OptionExplainerBubble, { id: "darkModeOption" }) ] }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Checkbox, { checked: darkMode, onChange: onDarkMode, name: "dark-mode" }) }) ] }), /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsx243("div", { style: label5, children: "Custom User Agent" }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Checkbox, { checked: userAgent !== null, onChange: toggleCustomUserAgent, name: "custom-user-agent" }) }) ] }), userAgent === null ? null : /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsx243("div", { style: label5, children: "User Agent" }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243("div", { children: /* @__PURE__ */ jsx243(RemotionInput, { style: input, value: userAgent, onChange: onUserAgentChanged, status: "ok", rightAlign: true }) }) }) ] }), /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs123("div", { style: label5, children: [ "Create a reproduction ", /* @__PURE__ */ jsx243(OptionExplainerBubble, { id: "reproOption" }) ] }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Checkbox, { checked: repro, onChange: onReproToggle, name: "repro" }) }) ] }), /* @__PURE__ */ jsxs123("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs123("div", { style: label5, children: [ "Beep when finished ", /* @__PURE__ */ jsx243(OptionExplainerBubble, { id: "beepOnFinishOption" }) ] }), /* @__PURE__ */ jsx243("div", { style: rightRow, children: /* @__PURE__ */ jsx243(Checkbox, { checked: beep, onChange: onPlayBeepSound, name: "beep-when-finished" }) }) ] }), /* @__PURE__ */ jsx243(RenderModalHr, {}), /* @__PURE__ */ jsx243(RenderModalEnvironmentVariables, { envVariables, setEnvVariables }) ] }); }; // src/components/RenderModal/RenderModalAudio.tsx import { BrowserSafeApis as BrowserSafeApis5 } from "@remotion/renderer/client"; import { useCallback as useCallback120 } from "react"; // src/components/RenderModal/EnforceAudioTrackSetting.tsx import { useCallback as useCallback117 } from "react"; import { jsx as jsx244, jsxs as jsxs124 } from "react/jsx-runtime"; var EnforceAudioTrackSetting = ({ enforceAudioTrack, muted, setEnforceAudioTrack }) => { const onEnforceAudioTrackChanged = useCallback117((e) => { setEnforceAudioTrack(e.target.checked); }, [setEnforceAudioTrack]); return /* @__PURE__ */ jsxs124("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs124("div", { style: label5, children: [ "Enforce Audio Track", /* @__PURE__ */ jsx244(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx244(OptionExplainerBubble, { id: "enforceAudioOption" }) ] }), /* @__PURE__ */ jsx244("div", { style: rightRow, children: /* @__PURE__ */ jsx244(Checkbox, { disabled: muted && !enforceAudioTrack, checked: enforceAudioTrack, onChange: onEnforceAudioTrackChanged, name: "enforce-audio-track" }) }) ] }); }; // src/components/RenderModal/MutedSetting.tsx import { useCallback as useCallback118 } from "react"; import { jsx as jsx245, jsxs as jsxs125 } from "react/jsx-runtime"; var MutedSetting = ({ muted, setMuted, enforceAudioTrack }) => { const onMutedChanged = useCallback118((e) => { setMuted(e.target.checked); }, [setMuted]); return /* @__PURE__ */ jsxs125("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs125("div", { style: label5, children: [ "Muted", /* @__PURE__ */ jsx245(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx245(OptionExplainerBubble, { id: "mutedOption" }) ] }), /* @__PURE__ */ jsx245(Spacing, { x: 0.25 }), /* @__PURE__ */ jsx245("div", { style: rightRow, children: /* @__PURE__ */ jsx245(Checkbox, { checked: muted, disabled: enforceAudioTrack && !muted, onChange: onMutedChanged, name: "muted" }) }) ] }); }; // src/components/RenderModal/SeparateAudioOption.tsx import { BrowserSafeApis as BrowserSafeApis4 } from "@remotion/renderer/client"; import { useCallback as useCallback119, useMemo as useMemo123 } from "react"; // src/helpers/use-file-existence.ts import { useContext as useContext82, useEffect as useEffect78, useRef as useRef43, useState as useState80 } from "react"; var useFileExistence = (outName) => { const [exists, setExists] = useState80(false); const { previewServerState: state, subscribeToEvent } = useContext82(StudioServerConnectionCtx); const clientId = state.type === "connected" ? state.clientId : undefined; const currentOutName = useRef43(""); currentOutName.current = outName; useEffect78(() => { if (!clientId) { return; } subscribeToFileExistenceWatcher({ file: outName, clientId }).then((_exists) => { if (currentOutName.current === outName) { setExists(_exists); } }); return () => { unsubscribeFromFileExistenceWatcher({ file: outName, clientId }); }; }, [clientId, outName]); useEffect78(() => { const listener = (event) => { if (event.type !== "watched-file-deleted") { return; } if (event.file !== currentOutName.current) { return; } if (currentOutName.current === outName) { setExists(false); } }; const unsub = subscribeToEvent("watched-file-deleted", listener); return () => { unsub(); }; }, [outName, subscribeToEvent]); useEffect78(() => { const listener = (event) => { if (event.type !== "watched-file-undeleted") { return; } if (event.file !== outName) { return; } if (currentOutName.current === outName) { setExists(true); } }; const unsub = subscribeToEvent("watched-file-undeleted", listener); return () => { unsub(); }; }, [outName, subscribeToEvent]); return exists; }; // src/components/RenderModal/RenderModalOutputName.tsx import { jsx as jsx246, jsxs as jsxs126, Fragment as Fragment41 } from "react/jsx-runtime"; var RenderModalOutputName = ({ existence, inputStyle: inputStyle2, outName, onValueChange, validationMessage, label: labelText }) => { return /* @__PURE__ */ jsxs126("div", { style: optionRow, children: [ /* @__PURE__ */ jsx246(Column, { children: /* @__PURE__ */ jsx246("div", { style: label5, children: labelText }) }), /* @__PURE__ */ jsx246("div", { style: rightRow, children: /* @__PURE__ */ jsxs126("div", { children: [ /* @__PURE__ */ jsx246(RemotionInput, { status: validationMessage ? "error" : existence ? "warning" : "ok", style: inputStyle2, type: "text", value: outName, onChange: onValueChange, rightAlign: true }), validationMessage ? /* @__PURE__ */ jsxs126(Fragment41, { children: [ /* @__PURE__ */ jsx246(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx246(ValidationMessage, { align: "flex-end", message: validationMessage, type: "error" }) ] }) : existence ? /* @__PURE__ */ jsxs126(Fragment41, { children: [ /* @__PURE__ */ jsx246(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx246(ValidationMessage, { align: "flex-end", message: "Will be overwritten", type: "warning" }) ] }) : null ] }) }) ] }); }; // src/components/RenderModal/get-string-before-suffix.ts var getStringBeforeSuffix = (fileName) => { const dotPos = fileName.lastIndexOf("."); if (dotPos === -1) { return fileName; } return fileName.substring(0, dotPos); }; // src/components/RenderModal/SeparateAudioOption.tsx import { jsx as jsx247, jsxs as jsxs127, Fragment as Fragment42 } from "react/jsx-runtime"; var SeparateAudioOptionInput = ({ separateAudioTo, setSeparateAudioTo, audioCodec }) => { const existence = useFileExistence(separateAudioTo); const onValueChange = useCallback119((e) => { setSeparateAudioTo(e.target.value); }, [setSeparateAudioTo]); const validationMessage = useMemo123(() => { const expectedExtension = BrowserSafeApis4.getExtensionFromAudioCodec(audioCodec); const actualExtension = separateAudioTo.split(".").pop(); if (actualExtension !== expectedExtension) { return `Expected extension: .${expectedExtension}`; } return null; }, [audioCodec, separateAudioTo]); return /* @__PURE__ */ jsx247(RenderModalOutputName, { existence, inputStyle: input, onValueChange, outName: separateAudioTo, label: "Separate audio to", validationMessage }); }; var SeparateAudioOption = ({ separateAudioTo, setSeparateAudioTo, audioCodec, outName }) => { const onSeparateAudioChange = useCallback119((e) => { if (e.target.checked) { const extension = BrowserSafeApis4.getExtensionFromAudioCodec(audioCodec); setSeparateAudioTo(`${getStringBeforeSuffix(outName)}.${extension}`); } else { setSeparateAudioTo(null); } }, [audioCodec, outName, setSeparateAudioTo]); return /* @__PURE__ */ jsxs127(Fragment42, { children: [ /* @__PURE__ */ jsxs127("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs127("div", { style: label5, children: [ "Separate audio", /* @__PURE__ */ jsx247(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx247(OptionExplainerBubble, { id: "separateAudioOption" }) ] }), /* @__PURE__ */ jsx247("div", { style: rightRow, children: /* @__PURE__ */ jsx247(Checkbox, { disabled: false, checked: Boolean(separateAudioTo), onChange: onSeparateAudioChange, name: "separate-audio-to" }) }) ] }), separateAudioTo === null ? null : /* @__PURE__ */ jsx247(SeparateAudioOptionInput, { separateAudioTo, setSeparateAudioTo, audioCodec }) ] }); }; // src/components/RenderModal/human-readable-audio-codecs.ts var humanReadableAudioCodec = (audioCodec) => { if (audioCodec === "aac") { return "AAC"; } if (audioCodec === "mp3") { return "MP3"; } if (audioCodec === "pcm-16") { return "Lossless"; } if (audioCodec === "opus") { return "Opus"; } }; // src/components/RenderModal/RenderModalAudio.tsx import { jsx as jsx248, jsxs as jsxs128, Fragment as Fragment43 } from "react/jsx-runtime"; var container54 = { flex: 1, overflowY: "auto" }; var RenderModalAudio = ({ muted, setMuted, renderMode, enforceAudioTrack, setEnforceAudioTrackState, setShouldHaveCustomTargetAudioBitrate, shouldHaveCustomTargetAudioBitrate, setCustomTargetAudioBitrateValue, customTargetAudioBitrate, audioCodec, codec, setAudioCodec, forSeamlessAacConcatenation, setForSeamlessAacConcatenation, separateAudioTo, setSeparateAudioTo, outName }) => { const onShouldHaveTargetAudioBitrateChanged = useCallback120((e) => { setShouldHaveCustomTargetAudioBitrate(e.target.checked); }, [setShouldHaveCustomTargetAudioBitrate]); const onTargetAudioBitrateChanged = useCallback120((e) => { setCustomTargetAudioBitrateValue(e.target.value); }, [setCustomTargetAudioBitrateValue]); const onSeamlessAacConcatenationChanges = useCallback120((e) => { setForSeamlessAacConcatenation(e.target.checked); }, [setForSeamlessAacConcatenation]); const audioCodecOptions = useCallback120((currentCodec) => { return BrowserSafeApis5.supportedAudioCodecs[currentCodec].map((audioCodecOption) => { return { label: humanReadableAudioCodec(audioCodecOption), onClick: () => setAudioCodec(audioCodecOption), key: audioCodecOption, leftItem: codec === audioCodecOption ? /* @__PURE__ */ jsx248(Checkmark, {}) : null, id: audioCodecOption, keyHint: null, quickSwitcherLabel: null, subMenu: null, type: "item", value: audioCodecOption }; }); }, [codec, setAudioCodec]); return /* @__PURE__ */ jsxs128("div", { style: container54, className: VERTICAL_SCROLLBAR_CLASSNAME, children: [ renderMode === "video" ? /* @__PURE__ */ jsxs128(Fragment43, { children: [ /* @__PURE__ */ jsx248(MutedSetting, { enforceAudioTrack, muted, setMuted }), /* @__PURE__ */ jsx248(RenderModalHr, {}) ] }) : null, renderMode === "video" && audioCodecOptions(codec).length >= 2 && !muted ? /* @__PURE__ */ jsxs128("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs128("div", { style: label5, children: [ "Audio Codec ", /* @__PURE__ */ jsx248(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx248(OptionExplainerBubble, { id: "audioCodecOption" }) ] }), /* @__PURE__ */ jsx248("div", { style: rightRow, children: /* @__PURE__ */ jsx248(Combobox, { values: audioCodecOptions(codec), selectedId: audioCodec, title: "AudioCodec" }) }) ] }) : null, (renderMode === "video" || renderMode === "audio") && !muted && /* @__PURE__ */ jsxs128(Fragment43, { children: [ /* @__PURE__ */ jsx248(EnforceAudioTrackSetting, { muted, enforceAudioTrack, setEnforceAudioTrack: setEnforceAudioTrackState }), /* @__PURE__ */ jsx248(RenderModalHr, {}) ] }), renderMode === "video" && !muted ? /* @__PURE__ */ jsx248(SeparateAudioOption, { separateAudioTo, setSeparateAudioTo, audioCodec, outName }) : null, audioCodec === "aac" && !muted ? /* @__PURE__ */ jsxs128("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs128("div", { style: label5, children: [ "For seamless AAC concatenation", /* @__PURE__ */ jsx248(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx248(OptionExplainerBubble, { id: "forSeamlessAacConcatenationOption" }) ] }), /* @__PURE__ */ jsx248("div", { style: rightRow, children: /* @__PURE__ */ jsx248(Checkbox, { disabled: false, checked: forSeamlessAacConcatenation, onChange: onSeamlessAacConcatenationChanges, name: "enforce-audio-track" }) }) ] }) : null, renderMode === "still" || muted ? null : /* @__PURE__ */ jsxs128("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs128("div", { style: label5, children: [ "Custom audio bitrate", " ", /* @__PURE__ */ jsx248(OptionExplainerBubble, { id: "audioBitrateOption" }) ] }), /* @__PURE__ */ jsx248("div", { style: rightRow, children: /* @__PURE__ */ jsx248(Checkbox, { checked: shouldHaveCustomTargetAudioBitrate, onChange: onShouldHaveTargetAudioBitrateChanged, name: "custom-audio-bitrate" }) }) ] }), shouldHaveCustomTargetAudioBitrate && renderMode !== "still" && !muted ? /* @__PURE__ */ jsxs128("div", { style: optionRow, children: [ /* @__PURE__ */ jsx248("div", { style: label5, children: "Target audio bitrate" }), /* @__PURE__ */ jsx248("div", { style: rightRow, children: /* @__PURE__ */ jsx248("div", { children: /* @__PURE__ */ jsx248(RemotionInput, { style: input, value: customTargetAudioBitrate, onChange: onTargetAudioBitrateChanged, status: "ok", rightAlign: true }) }) }) ] }) : null ] }); }; // src/components/RenderModal/RenderModalBasic.tsx import { BrowserSafeApis as BrowserSafeApis6 } from "@remotion/renderer/client"; import { NoReactAPIs } from "@remotion/renderer/pure"; import { useCallback as useCallback123, useMemo as useMemo125 } from "react"; // src/helpers/prores-labels.ts var labelProResProfile = (profile) => { if (profile === "4444") { return "4444"; } if (profile === "4444-xq") { return "4444 XQ (Best)"; } if (profile === "hq") { return "HQ"; } if (profile === "proxy") { return "Proxy (Worst)"; } if (profile === "light") { return "Light"; } if (profile === "standard") { return "Standard"; } throw new TypeError(`Unknown ProRes profile: ${profile}`); }; // src/components/RenderModal/FrameRangeSetting.tsx import { useCallback as useCallback122 } from "react"; // src/components/RenderModal/MultiRangeSlider.tsx import { useCallback as useCallback121, useMemo as useMemo124 } from "react"; import { jsx as jsx249, jsxs as jsxs129 } from "react/jsx-runtime"; var container55 = { borderColor: "black", borderStyle: "solid", borderWidth: "2px", height: 39, width: 220, position: "relative", backgroundColor: INPUT_BACKGROUND, marginLeft: 8, marginRight: 8, borderRadius: 2 }; var sliderRange = { position: "absolute", top: 0, backgroundColor: BLUE, height: 35 }; var MultiRangeSlider = ({ min, max, start, end, step, onLeftThumbDrag, onRightThumbDrag }) => { const getPercent = useCallback121((value) => Math.round((value - min) / (max - min) * 100), [min, max]); const rangeStyle = useMemo124(() => { const minPercent = getPercent(start); const maxPercent = getPercent(end); return { ...sliderRange, left: `${minPercent}%`, width: `${maxPercent - minPercent}%` }; }, [end, getPercent, start]); const onChangeLeft = useCallback121((event) => { const value = Math.min(Number(event.target.value), end - 1); onLeftThumbDrag(value); }, [end, onLeftThumbDrag]); const onChangeRight = useCallback121((event) => { const value = Math.max(Number(event.target.value), start + 1); onRightThumbDrag(value); }, [onRightThumbDrag, start]); return /* @__PURE__ */ jsxs129("div", { style: container55, children: [ /* @__PURE__ */ jsx249("input", { type: "range", min, max, value: start, step, onChange: onChangeLeft, className: "__remotion_thumb" }), /* @__PURE__ */ jsx249("input", { type: "range", min, max, value: end, step, onChange: onChangeRight, className: "__remotion_thumb" }), /* @__PURE__ */ jsx249("div", { style: rangeStyle }) ] }); }; // src/components/RenderModal/FrameRangeSetting.tsx import { jsx as jsx250, jsxs as jsxs130 } from "react/jsx-runtime"; var INPUT_WIDTH = 40; var FrameRangeSetting = ({ startFrame, endFrame, setEndFrame, durationInFrames, setStartFrame }) => { const minStartFrame = 0; const maxEndFrame = durationInFrames - 1; const onStartFrameChangedDirectly = useCallback122((newStartFrame) => { setStartFrame(newStartFrame); }, [setStartFrame]); const onEndFrameChangedDirectly = useCallback122((newEndFrame) => { setEndFrame(newEndFrame); }, [setEndFrame]); const onStartFrameChanged = useCallback122((newVal) => { onStartFrameChangedDirectly(parseInt(newVal, 10)); }, [onStartFrameChangedDirectly]); const onEndFrameChanged = useCallback122((newVal) => { onEndFrameChangedDirectly(parseInt(newVal, 10)); }, [onEndFrameChangedDirectly]); return /* @__PURE__ */ jsxs130("div", { style: optionRow, children: [ /* @__PURE__ */ jsx250("div", { style: label5, children: "Frame range" }), /* @__PURE__ */ jsxs130("div", { style: rightRow, children: [ /* @__PURE__ */ jsx250("div", { style: { width: INPUT_WIDTH }, children: /* @__PURE__ */ jsx250(InputDragger, { min: minStartFrame, max: endFrame - 1, name: "Start frame", value: startFrame, step: 1, onTextChange: onStartFrameChanged, onValueChange: onStartFrameChangedDirectly, status: "ok", rightAlign: true, style: { width: INPUT_WIDTH } }) }), /* @__PURE__ */ jsx250(MultiRangeSlider, { min: minStartFrame, max: maxEndFrame, start: startFrame, end: endFrame, step: 1, onLeftThumbDrag: onStartFrameChangedDirectly, onRightThumbDrag: onEndFrameChangedDirectly }), " ", /* @__PURE__ */ jsx250("div", { style: { width: INPUT_WIDTH }, children: /* @__PURE__ */ jsx250(InputDragger, { min: startFrame + 1, max: maxEndFrame, name: "End frame", value: endFrame, step: 1, onTextChange: onEndFrameChanged, onValueChange: onEndFrameChangedDirectly, status: "ok", rightAlign: true, style: { width: INPUT_WIDTH } }) }) ] }) ] }); }; // src/components/RenderModal/human-readable-codec.ts var humanReadableCodec = (codec) => { if (codec === "aac") { return "AAC"; } if (codec === "mp3") { return "MP3"; } if (codec === "gif") { return "GIF"; } if (codec === "h264") { return "H.264"; } if (codec === "h264-mkv") { return "H.264 (Matroska)"; } if (codec === "h264-ts") { return "H.264 (Transport Stream)"; } if (codec === "h265") { return "H.265"; } if (codec === "prores") { return "ProRes"; } if (codec === "vp8") { return "VP8"; } if (codec === "vp9") { return "VP9"; } if (codec === "wav") { return "Waveform"; } throw new TypeError(`Got unexpected codec "${codec}"`); }; // src/components/RenderModal/human-readable-loglevel.ts var humanReadableLogLevel = (logLevel) => { if (logLevel === "trace") { return "Trace"; } if (logLevel === "verbose") { return "Verbose"; } if (logLevel === "info") { return "Info"; } if (logLevel === "warn") { return "Warn"; } if (logLevel === "error") { return "Error"; } throw new TypeError(`Got unexpected log level "${logLevel}"`); }; // src/components/RenderModal/RenderModalBasic.tsx import { jsx as jsx251, jsxs as jsxs131 } from "react/jsx-runtime"; var container56 = { flex: 1 }; var RenderModalBasic = ({ renderMode, imageFormatOptions, outName, codec, setVideoCodec: setCodec, proResProfile, setProResProfile, frame: frame2, setFrame, resolvedComposition, setOutName, setEndFrame, endFrame, setStartFrame, startFrame, validationMessage, setVerboseLogging, logLevel }) => { const existence = useFileExistence(outName); const videoCodecOptions = useMemo125(() => { return BrowserSafeApis6.validCodecs.filter((c) => { return NoReactAPIs.isAudioCodec(c) === (renderMode === "audio"); }).map((codecOption) => { return { label: humanReadableCodec(codecOption), onClick: () => setCodec(codecOption), key: codecOption, leftItem: codec === codecOption ? /* @__PURE__ */ jsx251(Checkmark, {}) : null, id: codecOption, keyHint: null, quickSwitcherLabel: null, subMenu: null, type: "item", value: codecOption }; }); }, [renderMode, setCodec, codec]); const proResProfileOptions = useMemo125(() => { return BrowserSafeApis6.proResProfileOptions.map((option) => { return { label: labelProResProfile(option), onClick: () => setProResProfile(option), key: option, selected: proResProfile === option, type: "item", id: option, keyHint: null, leftItem: null, quickSwitcherLabel: null, subMenu: null, value: option }; }); }, [proResProfile, setProResProfile]); const onFrameSetDirectly = useCallback123((newFrame) => { setFrame(newFrame); }, [setFrame]); const onFrameChanged = useCallback123((e) => { setFrame((q) => { const newFrame = parseFloat(e); if (Number.isNaN(newFrame)) { return q; } return newFrame; }); }, [setFrame]); const onValueChange = useCallback123((e) => { setOutName(e.target.value); }, [setOutName]); const logLevelOptions = useMemo125(() => { return ["trace", "verbose", "info", "warn", "error"].map((level) => { return { label: humanReadableLogLevel(level), onClick: () => setVerboseLogging(level), leftItem: logLevel === level ? /* @__PURE__ */ jsx251(Checkmark, {}) : null, id: level, keyHint: null, quickSwitcherLabel: null, subMenu: null, type: "item", value: level }; }); }, [logLevel, setVerboseLogging]); return /* @__PURE__ */ jsxs131("div", { style: container56, children: [ renderMode === "still" || renderMode === "sequence" ? /* @__PURE__ */ jsxs131("div", { style: optionRow, children: [ /* @__PURE__ */ jsx251("div", { style: label5, children: "Format" }), /* @__PURE__ */ jsx251("div", { style: rightRow, children: /* @__PURE__ */ jsx251(SegmentedControl, { items: imageFormatOptions, needsWrapping: true }) }) ] }) : /* @__PURE__ */ jsxs131("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs131("div", { style: label5, children: [ "Codec", /* @__PURE__ */ jsx251(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx251(OptionExplainerBubble, { id: "videoCodecOption" }) ] }), /* @__PURE__ */ jsx251("div", { style: rightRow, children: /* @__PURE__ */ jsx251(Combobox, { values: videoCodecOptions, selectedId: codec, title: "Codec" }) }) ] }), renderMode === "still" && resolvedComposition.durationInFrames > 1 ? /* @__PURE__ */ jsxs131("div", { style: optionRow, children: [ /* @__PURE__ */ jsx251("div", { style: label5, children: "Frame" }), /* @__PURE__ */ jsx251("div", { style: rightRow, children: /* @__PURE__ */ jsx251(RightAlignInput, { children: /* @__PURE__ */ jsx251(InputDragger, { value: frame2, onTextChange: onFrameChanged, placeholder: `0-${resolvedComposition.durationInFrames - 1}`, onValueChange: onFrameSetDirectly, name: "frame", step: 1, min: 0, status: "ok", max: resolvedComposition.durationInFrames - 1, rightAlign: true }) }) }) ] }) : null, renderMode === "video" && codec === "prores" ? /* @__PURE__ */ jsxs131("div", { style: optionRow, children: [ /* @__PURE__ */ jsx251("div", { style: label5, children: "ProRes profile" }), /* @__PURE__ */ jsx251("div", { style: rightRow, children: /* @__PURE__ */ jsx251(Combobox, { title: "proResProfile", selectedId: proResProfile, values: proResProfileOptions }) }) ] }) : null, renderMode === "still" ? null : /* @__PURE__ */ jsx251(FrameRangeSetting, { durationInFrames: resolvedComposition.durationInFrames, endFrame, setEndFrame, setStartFrame, startFrame }), /* @__PURE__ */ jsx251(RenderModalOutputName, { existence, inputStyle: input, outName, onValueChange, validationMessage, label: renderMode === "sequence" ? "Folder name" : "Output name" }), /* @__PURE__ */ jsxs131("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs131("div", { style: label5, children: [ "Log Level ", /* @__PURE__ */ jsx251(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx251(OptionExplainerBubble, { id: "logLevelOption" }) ] }), /* @__PURE__ */ jsx251("div", { style: rightRow, children: /* @__PURE__ */ jsx251(Combobox, { values: logLevelOptions, selectedId: logLevel, title: "Log Level" }) }) ] }) ] }); }; // src/components/RenderModal/RenderModalGif.tsx import { useCallback as useCallback125 } from "react"; // src/components/RenderModal/NumberOfLoopsSetting.tsx import { useCallback as useCallback124 } from "react"; import { jsx as jsx252, jsxs as jsxs132 } from "react/jsx-runtime"; var min = 0; var NumberOfLoopsSetting = ({ numberOfGifLoops, setNumberOfGifLoops }) => { const onNumberOfGifLoopsChangedDirectly = useCallback124((newConcurrency) => { setNumberOfGifLoops(newConcurrency); }, [setNumberOfGifLoops]); const onNumberOfGifLoopsChanged = useCallback124((e) => { setNumberOfGifLoops((q) => { const newConcurrency = parseInt(e, 10); if (Number.isNaN(newConcurrency)) { return q; } const newConcurrencyClamped = Math.max(newConcurrency, min); return newConcurrencyClamped; }); }, [setNumberOfGifLoops]); return /* @__PURE__ */ jsxs132("div", { style: optionRow, children: [ /* @__PURE__ */ jsx252("div", { style: label5, children: "Number of loops" }), /* @__PURE__ */ jsx252("div", { style: rightRow, children: /* @__PURE__ */ jsx252(RightAlignInput, { children: /* @__PURE__ */ jsx252(InputDragger, { value: numberOfGifLoops, onTextChange: onNumberOfGifLoopsChanged, placeholder: `${min}-`, onValueChange: onNumberOfGifLoopsChangedDirectly, name: "number-of-gif-loops", step: 1, min, status: "ok", rightAlign: true }) }) }) ] }); }; // src/components/RenderModal/RenderModalGif.tsx import { jsx as jsx253, jsxs as jsxs133 } from "react/jsx-runtime"; var container57 = { flex: 1 }; var RenderModalGif = ({ everyNthFrame, limitNumberOfGifLoops, numberOfGifLoopsSetting, setEveryNthFrameSetting, setLimitNumberOfGifLoops, setNumberOfGifLoopsSetting }) => { const onShouldLimitNumberOfGifLoops = useCallback125((e) => { setLimitNumberOfGifLoops(e.target.checked); }, [setLimitNumberOfGifLoops]); return /* @__PURE__ */ jsxs133("div", { style: container57, children: [ /* @__PURE__ */ jsx253(NumberSetting, { name: "Every nth frame", min: 1, onValueChanged: setEveryNthFrameSetting, value: everyNthFrame, step: 1 }), /* @__PURE__ */ jsxs133("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs133("div", { style: label5, children: [ "Limit GIF loops ", /* @__PURE__ */ jsx253(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx253(OptionExplainerBubble, { id: "numberOfGifLoopsOption" }) ] }), /* @__PURE__ */ jsx253("div", { style: rightRow, children: /* @__PURE__ */ jsx253(Checkbox, { checked: limitNumberOfGifLoops, onChange: onShouldLimitNumberOfGifLoops, name: "limitNumberOfGifLoops" }) }) ] }), limitNumberOfGifLoops ? /* @__PURE__ */ jsx253(NumberOfLoopsSetting, { numberOfGifLoops: numberOfGifLoopsSetting, setNumberOfGifLoops: setNumberOfGifLoopsSetting }) : null ] }); }; // src/components/RenderModal/RenderModalPicture.tsx import { BrowserSafeApis as BrowserSafeApis7 } from "@remotion/renderer/client"; import { useCallback as useCallback126, useMemo as useMemo127 } from "react"; // src/components/RenderModal/JpegQualitySetting.tsx import { jsx as jsx254 } from "react/jsx-runtime"; var MIN_JPEG_QUALITY = 1; var MAX_JPEG_QUALITY = 100; var JpegQualitySetting = ({ jpegQuality, setJpegQuality }) => { return /* @__PURE__ */ jsx254(NumberSetting, { min: MIN_JPEG_QUALITY, max: MAX_JPEG_QUALITY, step: 1, name: "JPEG Quality", onValueChanged: setJpegQuality, value: jpegQuality, hint: "jpegQualityOption" }); }; // src/components/RenderModal/ScaleSetting.tsx import { useMemo as useMemo126 } from "react"; import { jsx as jsx255, jsxs as jsxs134, Fragment as Fragment44 } from "react/jsx-runtime"; var MIN_SCALE = 0.1; var MAX_SCALE = 10; var outputDimensionsStyle = { fontSize: 13, color: LIGHT_TEXT, fontFamily: "sans-serif", paddingRight: 16, textAlign: "right", marginBottom: 14, marginTop: -10 }; var ScaleSetting = ({ scale, setScale, compositionWidth, compositionHeight }) => { const outputDimensions = useMemo126(() => { const outputWidth = Math.round(compositionWidth * scale); const outputHeight = Math.round(compositionHeight * scale); return `${outputWidth}×${outputHeight}`; }, [compositionWidth, compositionHeight, scale]); return /* @__PURE__ */ jsxs134(Fragment44, { children: [ /* @__PURE__ */ jsx255(NumberSetting, { min: MIN_SCALE, max: MAX_SCALE, step: 0.1, name: "Scale", formatter: (w) => { if (typeof w === "number") { return `${w.toFixed(1)}x`; } return `${w}x`; }, onValueChanged: setScale, value: scale, hint: "scaleOption" }), scale !== 1 && /* @__PURE__ */ jsxs134("div", { style: outputDimensionsStyle, children: [ "Output resolution: ", outputDimensions ] }) ] }); }; // src/components/RenderModal/RenderModalPicture.tsx import { jsx as jsx256, jsxs as jsxs135, Fragment as Fragment45 } from "react/jsx-runtime"; var qualityControlModes = ["crf", "bitrate"]; var container58 = { flex: 1, overflowY: "auto" }; var RenderModalPicture = ({ renderMode, scale, setScale, pixelFormat, imageFormatOptions, setQualityControl, qualityControlType, videoImageFormat, setJpegQuality, jpegQuality, maxCrf, minCrf, setCrf, shouldDisplayQualityControlPicker, setCustomTargetVideoBitrateValue, crf, customTargetVideoBitrate, stillImageFormat, colorSpace, setColorSpace, pixelFormatOptions, encodingBufferSize, encodingMaxRate, setEncodingBufferSize, setEncodingMaxRate, compositionWidth, compositionHeight }) => { const colorSpaceOptions = useMemo127(() => { return BrowserSafeApis7.validColorSpaces.map((option) => { return { label: option, onClick: () => setColorSpace(option), key: option, id: option, keyHint: null, leftItem: colorSpace === option ? /* @__PURE__ */ jsx256(Checkmark, {}) : null, quickSwitcherLabel: null, subMenu: null, type: "item", value: option }; }); }, [colorSpace, setColorSpace]); const qualityControlOptions = useMemo127(() => { return qualityControlModes.map((option) => { return { label: option === "crf" ? "CRF" : "Bitrate", onClick: () => setQualityControl(option), key: option, selected: qualityControlType === option }; }); }, [qualityControlType, setQualityControl]); const onTargetVideoBitrateChanged = useCallback126((e) => { setCustomTargetVideoBitrateValue(e.target.value); }, [setCustomTargetVideoBitrateValue]); const onEncodingBufferSizeToggled = useCallback126((e) => { setEncodingBufferSize(e.target.checked ? "10000k" : null); }, [setEncodingBufferSize]); const onEncodingMaxRateToggled = useCallback126((e) => { setEncodingMaxRate(e.target.checked ? "5000k" : null); }, [setEncodingMaxRate]); const onEncodingBufferSizeChanged = useCallback126((e) => { setEncodingBufferSize(e.target.value); }, [setEncodingBufferSize]); const onEncodingMaxRateChanged = useCallback126((e) => { setEncodingMaxRate(e.target.value); }, [setEncodingMaxRate]); return /* @__PURE__ */ jsxs135("div", { style: container58, className: VERTICAL_SCROLLBAR_CLASSNAME, children: [ renderMode === "video" ? /* @__PURE__ */ jsxs135("div", { style: optionRow, children: [ /* @__PURE__ */ jsx256("div", { style: label5, children: "Image Format" }), /* @__PURE__ */ jsx256("div", { style: rightRow, children: /* @__PURE__ */ jsx256(SegmentedControl, { items: imageFormatOptions, needsWrapping: false }) }) ] }) : null, renderMode === "video" && videoImageFormat === "jpeg" && /* @__PURE__ */ jsx256(JpegQualitySetting, { jpegQuality, setJpegQuality }), renderMode === "still" && stillImageFormat === "jpeg" && /* @__PURE__ */ jsx256(JpegQualitySetting, { jpegQuality, setJpegQuality }), renderMode === "video" && qualityControlType !== null ? /* @__PURE__ */ jsx256(RenderModalHr, {}) : null, shouldDisplayQualityControlPicker && renderMode === "video" ? /* @__PURE__ */ jsxs135("div", { style: optionRow, children: [ /* @__PURE__ */ jsx256("div", { style: label5, children: "Quality control" }), /* @__PURE__ */ jsx256("div", { style: rightRow, children: /* @__PURE__ */ jsx256(SegmentedControl, { items: qualityControlOptions, needsWrapping: true }) }) ] }) : null, qualityControlType === "crf" && renderMode !== "still" && renderMode !== "sequence" && crf !== null ? /* @__PURE__ */ jsx256(CrfSetting, { crf, min: minCrf, max: maxCrf, setCrf, option: "crfOption" }) : null, qualityControlType === "bitrate" && renderMode === "video" ? /* @__PURE__ */ jsxs135("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs135("div", { style: label5, children: [ "Target video bitrate", /* @__PURE__ */ jsx256(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx256(OptionExplainerBubble, { id: "videoBitrateOption" }) ] }), /* @__PURE__ */ jsx256("div", { style: rightRow, children: /* @__PURE__ */ jsx256("div", { children: /* @__PURE__ */ jsx256(RemotionInput, { style: input, value: customTargetVideoBitrate, onChange: onTargetVideoBitrateChanged, status: "ok", rightAlign: true }) }) }) ] }) : null, renderMode === "video" ? /* @__PURE__ */ jsxs135(Fragment45, { children: [ /* @__PURE__ */ jsxs135("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs135("div", { style: label5, children: [ "Custom FFmpeg -bufsize", /* @__PURE__ */ jsx256(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx256(OptionExplainerBubble, { id: "encodingBufferSizeOption" }) ] }), /* @__PURE__ */ jsx256("div", { style: rightRow, children: /* @__PURE__ */ jsx256(Checkbox, { checked: encodingBufferSize !== null, onChange: onEncodingBufferSizeToggled, name: "encoding-buffer-size" }) }) ] }), encodingBufferSize === null ? null : /* @__PURE__ */ jsxs135("div", { style: optionRow, children: [ /* @__PURE__ */ jsx256("div", { style: label5, children: "-bufsize value" }), /* @__PURE__ */ jsx256("div", { style: rightRow, children: /* @__PURE__ */ jsx256("div", { children: /* @__PURE__ */ jsx256(RemotionInput, { style: input, value: encodingBufferSize, onChange: onEncodingBufferSizeChanged, status: "ok", rightAlign: true }) }) }) ] }), /* @__PURE__ */ jsxs135("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs135("div", { style: label5, children: [ "Custom FFmpeg -maxrate", /* @__PURE__ */ jsx256(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx256(OptionExplainerBubble, { id: "encodingMaxRateOption" }) ] }), /* @__PURE__ */ jsx256("div", { style: rightRow, children: /* @__PURE__ */ jsx256(Checkbox, { checked: encodingMaxRate !== null, onChange: onEncodingMaxRateToggled, name: "encoding-max-rate" }) }) ] }), encodingMaxRate === null ? null : /* @__PURE__ */ jsxs135("div", { style: optionRow, children: [ /* @__PURE__ */ jsx256("div", { style: label5, children: "-maxrate value" }), /* @__PURE__ */ jsx256("div", { style: rightRow, children: /* @__PURE__ */ jsx256("div", { children: /* @__PURE__ */ jsx256(RemotionInput, { style: input, value: encodingMaxRate, onChange: onEncodingMaxRateChanged, status: "ok", rightAlign: true }) }) }) ] }) ] }) : null, renderMode === "video" ? /* @__PURE__ */ jsx256(RenderModalHr, {}) : null, /* @__PURE__ */ jsx256(ScaleSetting, { scale, setScale, compositionWidth, compositionHeight }), renderMode === "video" ? /* @__PURE__ */ jsx256(RenderModalHr, {}) : null, renderMode === "video" ? /* @__PURE__ */ jsxs135("div", { style: optionRow, children: [ /* @__PURE__ */ jsx256("div", { style: label5, children: "Pixel format" }), /* @__PURE__ */ jsx256("div", { style: rightRow, children: /* @__PURE__ */ jsx256(Combobox, { values: pixelFormatOptions, selectedId: pixelFormat, title: "Pixel Format" }) }) ] }) : null, renderMode === "video" ? /* @__PURE__ */ jsxs135("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs135("div", { style: label5, children: [ "Color space", /* @__PURE__ */ jsx256(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx256(OptionExplainerBubble, { id: "colorSpaceOption" }) ] }), /* @__PURE__ */ jsx256("div", { style: rightRow, children: /* @__PURE__ */ jsx256(Combobox, { values: colorSpaceOptions, selectedId: colorSpace, title: "Color Space" }) }) ] }) : null ] }); }; // src/components/RenderModal/get-default-codecs.ts import { BrowserSafeApis as BrowserSafeApis8 } from "@remotion/renderer/client"; import { NoReactAPIs as NoReactAPIs2 } from "@remotion/renderer/pure"; var getDefaultCodecs = ({ defaultConfigurationVideoCodec, compositionDefaultVideoCodec, renderType, defaultConfigurationAudioCodec }) => { const userPreferredVideoCodec = compositionDefaultVideoCodec ?? defaultConfigurationVideoCodec ?? "h264"; const userPreferredVideoCodecForAudioTab = userPreferredVideoCodec === "aac" ? "aac" : userPreferredVideoCodec === "mp3" ? "mp3" : userPreferredVideoCodec === "wav" ? "wav" : defaultConfigurationAudioCodec === "pcm-16" ? "wav" : defaultConfigurationAudioCodec === "mp3" ? "mp3" : "aac"; const isVideoCodecAnAudioCodec = NoReactAPIs2.isAudioCodec(userPreferredVideoCodec); if (isVideoCodecAnAudioCodec) { return { initialAudioCodec: null, initialRenderType: "audio", initialVideoCodec: userPreferredVideoCodec, initialVideoCodecForAudioTab: userPreferredVideoCodecForAudioTab, initialVideoCodecForVideoTab: NoReactAPIs2.isAudioCodec(defaultConfigurationVideoCodec) ? "h264" : defaultConfigurationVideoCodec }; } const suitableAudioCodecForVideoCodec = BrowserSafeApis8.defaultAudioCodecs[userPreferredVideoCodec].compressed; return { initialAudioCodec: defaultConfigurationAudioCodec ?? suitableAudioCodecForVideoCodec, initialVideoCodec: userPreferredVideoCodec, initialRenderType: renderType, initialVideoCodecForAudioTab: userPreferredVideoCodecForAudioTab, initialVideoCodecForVideoTab: userPreferredVideoCodec }; }; // src/components/RenderModal/out-name-checker.ts import { BrowserSafeApis as BrowserSafeApis9 } from "@remotion/renderer/client"; var invalidCharacters = ["?", "*", "+", ":", "%"]; var isValidStillExtension = (extension, stillImageFormat) => { if (stillImageFormat === "jpeg" && extension === "jpg") { return true; } return extension === stillImageFormat; }; var validateOutnameGui = ({ outName, codec, audioCodec, renderMode, stillImageFormat, separateAudioTo }) => { try { isValidOutName({ audioCodec, codec, outName, renderMode, stillImageFormat, separateAudioTo }); return { valid: true }; } catch (err) { return { valid: false, error: err }; } }; var isValidOutName = ({ outName, codec, audioCodec, renderMode, stillImageFormat, separateAudioTo }) => { const extension = outName.substring(outName.lastIndexOf(".") + 1); const prefix = outName.substring(0, outName.lastIndexOf(".")); const map = BrowserSafeApis9.defaultFileExtensionMap[codec]; if (BrowserSafeApis9.supportedAudioCodecs[codec].length > 0 && !(audioCodec in map.forAudioCodec)) { throw new Error(`Audio codec ${audioCodec} is not supported for codec ${codec}`); } const hasDotAfterSlash = () => { const substrings = prefix.split("/"); for (const str of substrings) { if (str[0] === ".") { return true; } } return false; }; const hasInvalidChar = () => { return prefix.split("").some((char) => invalidCharacters.includes(char)); }; if (renderMode === "video" || renderMode === "audio") { BrowserSafeApis9.validateOutputFilename({ codec, audioCodecSetting: audioCodec ?? null, extension, preferLossless: false, separateAudioTo }); } if (prefix.length < 1 && renderMode !== "sequence") { throw new Error("The prefix must be at least 1 character long"); } if (prefix[0] === "." || hasDotAfterSlash()) { throw new Error("The output name must not start with a dot"); } if (hasInvalidChar()) { throw new Error("Filename can't contain the following characters: ?, *, +, %, :"); } if (renderMode === "still" && stillImageFormat && !isValidStillExtension(extension, stillImageFormat)) { throw new Error(`The extension ${extension} is not supported for still image format ${stillImageFormat}`); } if (renderMode === "sequence") { if (outName.includes(".")) { throw new Error("Folder names must not contain a dot"); } } }; // src/components/RenderModal/render-modals.ts var outerModalStyle = { width: getMaxModalWidth(1000), height: getMaxModalHeight(640), overflow: "hidden", display: "flex", flexDirection: "column" }; var container59 = { display: "flex", flexDirection: "row", alignItems: "center", padding: "12px 16px", borderBottom: "1px solid black" }; var optionsPanel = { display: "flex", width: "100%" }; var horizontalLayout = { display: "flex", flexDirection: "row", overflowY: "auto", flex: 1 }; var leftSidebar = { padding: 12 }; var horizontalTab = { width: 250, display: "flex", flexDirection: "row", alignItems: "center", textAlign: "left", fontSize: 16, fontWeight: "bold", paddingLeft: 15, paddingTop: 12, paddingBottom: 12 }; var iconContainer = { width: 20, height: 20, marginRight: 15, display: "inline-flex", justifyContent: "center", alignItems: "center" }; var icon6 = { color: "currentcolor", height: 20 }; var buttonStyle7 = { backgroundColor: BLUE, color: "white" }; var flexer = { flex: 1 }; // src/components/RenderModal/ServerRenderModal.tsx import { jsx as jsx257, jsxs as jsxs136 } from "react/jsx-runtime"; var initialState2 = { type: "idle" }; var reducer2 = (state, action) => { if (action.type === "start") { return { type: "load" }; } if (action.type === "fail") { return { type: "error" }; } if (action.type === "succeed") { return { type: "success" }; } return state; }; var RenderModal = ({ initialFrame, initialVideoImageFormat, initialStillImageFormat, initialJpegQuality, initialScale, initialLogLevel, initialConcurrency, maxConcurrency, minConcurrency, initialMuted, initialEnforceAudioTrack, initialProResProfile, initialx264Preset, initialPixelFormat, initialVideoBitrate, initialAudioBitrate, initialEveryNthFrame, initialNumberOfGifLoops, initialDelayRenderTimeout, initialOffthreadVideoCacheSizeInBytes, initialEnvVariables, initialDisableWebSecurity, initialGl, initialHeadless, initialIgnoreCertificateErrors, initialEncodingBufferSize, initialEncodingMaxRate, initialOffthreadVideoThreads, initialMediaCacheSizeInBytes, initialDarkMode, initialUserAgent, defaultProps, inFrameMark, outFrameMark, initialColorSpace, initialMultiProcessOnLinux, defaultConfigurationAudioCodec, defaultConfigurationVideoCodec, initialBeep, initialRepro, initialForSeamlessAacConcatenation, renderTypeOfLastRender, initialHardwareAcceleration, defaultMetadata, initialChromeMode, renderDefaults }) => { const { setSelectedModal } = useContext83(ModalsContext); const context = useContext83(ResolvedCompositionContext); if (!context) { throw new Error("Should not be able to render without resolving comp first"); } const { resolved: { result: resolvedComposition }, unresolved: unresolvedComposition } = context; const isMounted = useRef44(true); const [isVideo] = useState81(() => { return typeof resolvedComposition.durationInFrames === "undefined" ? true : resolvedComposition.durationInFrames > 1; }); const [ { initialAudioCodec, initialRenderType, initialVideoCodec, initialVideoCodecForAudioTab, initialVideoCodecForVideoTab } ] = useState81(() => { return getDefaultCodecs({ defaultConfigurationVideoCodec, compositionDefaultVideoCodec: resolvedComposition.defaultCodec, defaultConfigurationAudioCodec, renderType: renderTypeOfLastRender ?? (isVideo ? "video" : "still") }); }); const [state, dispatch] = useReducer2(reducer2, initialState2); const [unclampedFrame, setFrame] = useState81(() => initialFrame); const [saving, setSaving] = useState81(false); const [stillImageFormat, setStillImageFormat] = useState81(() => initialStillImageFormat); const [videoImageFormat, setVideoImageFormat] = useState81(() => initialVideoImageFormat ?? resolvedComposition.defaultVideoImageFormat ?? renderDefaults.videoImageFormat); const [sequenceImageFormat, setSequenceImageFormat] = useState81(() => initialStillImageFormat === "jpeg" ? "jpeg" : "png"); const [concurrency, setConcurrency] = useState81(() => initialConcurrency); const [videoCodecForVideoTab, setVideoCodecForVideoTab] = useState81(() => initialVideoCodecForVideoTab); const [userSelectedAudioCodec, setUserSelectedAudioCodec] = useState81(() => initialAudioCodec); const [separateAudioTo, setSeparateAudioTo] = useState81(null); const [envVariables, setEnvVariables] = useState81(() => envVariablesObjectToArray(initialEnvVariables).filter(([key5]) => key5 !== "NODE_ENV")); const [initialOutName] = useState81(() => { return getDefaultOutLocation({ compositionName: resolvedComposition.id, defaultExtension: initialRenderType === "still" ? initialStillImageFormat : isVideo ? BrowserSafeApis10.getFileExtensionFromCodec(initialVideoCodec, initialAudioCodec) : initialStillImageFormat, type: "asset", compositionDefaultOutName: resolvedComposition.defaultOutName, clientSideRender: false }); }); const [videoCodecForAudioTab, setVideoCodecForAudioTab] = useState81(() => initialVideoCodecForAudioTab); const [mutedState, setMuted] = useState81(() => initialMuted); const [repro, setRepro] = useState81(() => initialRepro); const [enforceAudioTrackState, setEnforceAudioTrackState] = useState81(() => initialEnforceAudioTrack); const [forSeamlessAacConcatenation, setForSeamlessAacConcatenation] = useState81(() => initialForSeamlessAacConcatenation); const [renderMode, setRenderModeState] = useState81(initialRenderType); const [jpegQuality, setJpegQuality] = useState81(() => initialJpegQuality); const [scale, setScale] = useState81(() => initialScale); const [logLevel, setLogLevel] = useState81(() => initialLogLevel); const [disallowParallelEncoding, setDisallowParallelEncoding] = useState81(false); const [disableWebSecurity, setDisableWebSecurity] = useState81(() => initialDisableWebSecurity); const [headless, setHeadless] = useState81(() => initialHeadless); const [beepOnFinish, setBeepOnFinish] = useState81(() => initialBeep); const [ignoreCertificateErrors, setIgnoreCertificateErrors] = useState81(() => initialIgnoreCertificateErrors); const [multiProcessOnLinux, setChromiumMultiProcessOnLinux] = useState81(() => initialMultiProcessOnLinux); const [darkMode, setDarkMode] = useState81(() => initialDarkMode); const [openGlOption, setOpenGlOption] = useState81(() => initialGl ?? "default"); const [colorSpace, setColorSpace] = useState81(() => initialColorSpace); const [userAgent, setUserAgent] = useState81(() => initialUserAgent === null ? null : initialUserAgent.trim() === "" ? null : initialUserAgent); const chromiumOptions = useMemo128(() => { return { headless, disableWebSecurity, ignoreCertificateErrors, gl: openGlOption === "default" ? null : openGlOption, userAgent: userAgent === null ? null : userAgent.trim() === "" ? null : userAgent, enableMultiProcessOnLinux: multiProcessOnLinux, darkMode }; }, [ headless, disableWebSecurity, ignoreCertificateErrors, openGlOption, userAgent, multiProcessOnLinux, darkMode ]); const [outName, setOutName] = useState81(() => initialOutName); const [endFrameOrNull, setEndFrame] = useState81(() => outFrameMark ?? null); const [startFrameOrNull, setStartFrame] = useState81(() => inFrameMark ?? null); const [proResProfileSetting, setProResProfile] = useState81(() => initialProResProfile ?? resolvedComposition.defaultProResProfile ?? "hq"); const [x264PresetSetting, setx264Preset] = useState81(() => initialx264Preset); const [hardwareAcceleration, setHardwareAcceleration] = useState81(() => initialHardwareAcceleration); const [userPreferredPixelFormat, setPixelFormat] = useState81(() => initialPixelFormat ?? resolvedComposition.defaultPixelFormat ?? renderDefaults.pixelFormat); const [preferredQualityControlType, setQualityControl] = useState81(() => initialVideoBitrate === null ? "crf" : "bitrate"); const [ shouldHaveCustomTargetAudioBitrate, setShouldHaveCustomTargetAudioBitrate ] = useState81(() => initialAudioBitrate !== null); const [customTargetAudioBitrate, setCustomTargetAudioBitrateValue] = useState81(() => initialAudioBitrate ?? "320K"); const [customTargetVideoBitrate, setCustomTargetVideoBitrateValue] = useState81(() => initialVideoBitrate ?? "1M"); const [encodingMaxRate, setEncodingMaxRate] = useState81(() => initialEncodingMaxRate ?? null); const [encodingBufferSize, setEncodingBufferSize] = useState81(() => initialEncodingBufferSize ?? null); const [limitNumberOfGifLoops, setLimitNumberOfGifLoops] = useState81(() => initialNumberOfGifLoops !== null); const [numberOfGifLoopsSetting, setNumberOfGifLoopsSetting] = useState81(() => initialNumberOfGifLoops ?? 0); const [delayRenderTimeout, setDelayRenderTimeout] = useState81(() => initialDelayRenderTimeout); const [chromeMode, setChromeMode] = useState81(() => initialChromeMode); const [offthreadVideoCacheSizeInBytes, setOffthreadVideoCacheSizeInBytes] = useState81(initialOffthreadVideoCacheSizeInBytes); const [mediaCacheSizeInBytes, setMediaCacheSizeInBytes] = useState81(initialMediaCacheSizeInBytes); const [offthreadVideoThreads, setOffthreadVideoThreads] = useState81(() => initialOffthreadVideoThreads); const codec = useMemo128(() => { if (renderMode === "audio") { return videoCodecForAudioTab; } return videoCodecForVideoTab; }, [videoCodecForAudioTab, renderMode, videoCodecForVideoTab]); const numberOfGifLoops = useMemo128(() => { if (codec === "gif" && limitNumberOfGifLoops) { return numberOfGifLoopsSetting; } return null; }, [codec, limitNumberOfGifLoops, numberOfGifLoopsSetting]); const audioBitrate = useMemo128(() => { if (shouldHaveCustomTargetAudioBitrate) { return customTargetAudioBitrate; } return null; }, [customTargetAudioBitrate, shouldHaveCustomTargetAudioBitrate]); const supportsCrf = BrowserSafeApis10.codecSupportsCrf(codec); const supportsVideoBitrate = BrowserSafeApis10.codecSupportsVideoBitrate(codec); const supportsBothQualityControls = useMemo128(() => { return supportsCrf && supportsVideoBitrate && hardwareAcceleration !== "if-possible" && hardwareAcceleration !== "required"; }, [hardwareAcceleration, supportsCrf, supportsVideoBitrate]); const qualityControlType = useMemo128(() => { if (hardwareAcceleration === "if-possible" || hardwareAcceleration === "required") { if (supportsVideoBitrate) { return "bitrate"; } return null; } if (supportsBothQualityControls) { return preferredQualityControlType; } if (supportsCrf) { return "crf"; } if (supportsVideoBitrate) { return "bitrate"; } return null; }, [ hardwareAcceleration, preferredQualityControlType, supportsBothQualityControls, supportsCrf, supportsVideoBitrate ]); const videoBitrate = useMemo128(() => { if (qualityControlType === "bitrate") { return customTargetVideoBitrate; } return null; }, [customTargetVideoBitrate, qualityControlType]); const { crf, maxCrf, minCrf, setCrf } = useCrfState(codec); const dispatchIfMounted = useCallback127((payload) => { if (isMounted.current === false) return; dispatch(payload); }, []); const muted = useMemo128(() => { if (renderMode === "video") { return mutedState; } return false; }, [mutedState, renderMode]); const enforceAudioTrack = useMemo128(() => { if (renderMode === "video") { return enforceAudioTrackState; } if (renderMode === "audio") { return enforceAudioTrackState; } return false; }, [enforceAudioTrackState, renderMode]); const proResProfile = useMemo128(() => { if (renderMode === "video" && codec === "prores") { return proResProfileSetting; } return null; }, [codec, proResProfileSetting, renderMode]); const x264Preset = useMemo128(() => { if (renderMode === "video" && codec === "h264") { return x264PresetSetting; } return null; }, [codec, x264PresetSetting, renderMode]); const [inputProps, setInputProps] = useState81(() => defaultProps); const [metadata] = useState81(() => defaultMetadata); const endFrame = useMemo128(() => { if (endFrameOrNull === null) { return resolvedComposition.durationInFrames - 1; } return Math.max(0, Math.min(resolvedComposition.durationInFrames - 1, endFrameOrNull)); }, [resolvedComposition.durationInFrames, endFrameOrNull]); const startFrame = useMemo128(() => { if (startFrameOrNull === null) { return 0; } return Math.max(0, Math.min(endFrame - 1, startFrameOrNull)); }, [endFrame, startFrameOrNull]); const frame2 = useMemo128(() => { const parsed = Math.floor(unclampedFrame); return Math.max(0, Math.min(resolvedComposition.durationInFrames - 1, parsed)); }, [resolvedComposition.durationInFrames, unclampedFrame]); const deriveFinalAudioCodec = useCallback127((passedVideoCodec, passedAudioCodec) => { if (passedAudioCodec !== null && BrowserSafeApis10.supportedAudioCodecs[passedVideoCodec].includes(passedAudioCodec)) { return passedAudioCodec; } return BrowserSafeApis10.defaultAudioCodecs[passedVideoCodec].compressed; }, []); const setDefaultOutName = useCallback127((options) => { if (options.type === "still") { setOutName((prev) => { const newFileName = getStringBeforeSuffix(prev) + "." + options.imageFormat; return newFileName; }); } else if (options.type === "sequence") { setOutName((prev) => { const folderName = getStringBeforeSuffix(prev); return folderName; }); } else { setOutName((prev) => { const codecSuffix = BrowserSafeApis10.getFileExtensionFromCodec(options.codec, deriveFinalAudioCodec(options.codec, options.audioCodec)); const newFileName = getStringBeforeSuffix(prev) + "." + codecSuffix; return newFileName; }); } }, [deriveFinalAudioCodec]); const setAudioCodec = useCallback127((newAudioCodec) => { setUserSelectedAudioCodec(newAudioCodec); setDefaultOutName({ type: "render", codec: videoCodecForVideoTab, audioCodec: newAudioCodec }); setSeparateAudioTo((prev) => { if (prev === null) { return null; } const newExtension = BrowserSafeApis10.getExtensionFromAudioCodec(newAudioCodec); const newFileName = getStringBeforeSuffix(prev) + "." + newExtension; return newFileName; }); }, [setDefaultOutName, videoCodecForVideoTab]); const setCodec = useCallback127((newCodec) => { if (renderMode === "audio") { setVideoCodecForAudioTab(newCodec); } else { setVideoCodecForVideoTab(newCodec); } setDefaultOutName({ type: "render", codec: newCodec, audioCodec: deriveFinalAudioCodec(newCodec, userSelectedAudioCodec) }); }, [ userSelectedAudioCodec, deriveFinalAudioCodec, renderMode, setDefaultOutName ]); const setStillFormat = useCallback127((format) => { setStillImageFormat(format); setDefaultOutName({ type: "still", imageFormat: format }); }, [setDefaultOutName]); const { setSidebarCollapsedState } = useContext83(SidebarContext); const onClickStill = useCallback127(() => { setSidebarCollapsedState({ left: null, right: "expanded" }); persistSelectedOptionsSidebarPanel2("renders"); optionsSidebarTabs.current?.selectRendersPanel(); dispatchIfMounted({ type: "start" }); addStillRenderJob({ compositionId: resolvedComposition.id, outName, imageFormat: stillImageFormat, jpegQuality, frame: frame2, scale, logLevel, chromiumOptions, delayRenderTimeout, envVariables: envVariablesArrayToObject(envVariables), inputProps, offthreadVideoCacheSizeInBytes, multiProcessOnLinux, beepOnFinish, metadata, chromeMode, offthreadVideoThreads, mediaCacheSizeInBytes }).then(() => { dispatchIfMounted({ type: "succeed" }); setSelectedModal(null); }).catch(() => { dispatchIfMounted({ type: "fail" }); }); }, [ setSidebarCollapsedState, dispatchIfMounted, resolvedComposition.id, outName, stillImageFormat, jpegQuality, frame2, scale, logLevel, chromiumOptions, delayRenderTimeout, envVariables, inputProps, offthreadVideoCacheSizeInBytes, multiProcessOnLinux, beepOnFinish, setSelectedModal, metadata, chromeMode, offthreadVideoThreads, mediaCacheSizeInBytes ]); const [everyNthFrameSetting, setEveryNthFrameSetting] = useState81(() => initialEveryNthFrame); const everyNthFrame = useMemo128(() => { if (codec === "gif") { return everyNthFrameSetting; } return 1; }, [codec, everyNthFrameSetting]); const audioCodec = deriveFinalAudioCodec(codec, userSelectedAudioCodec); const availablePixelFormats = useMemo128(() => { return BrowserSafeApis10.validPixelFormatsForCodec(codec); }, [codec]); const pixelFormat = useMemo128(() => { if (availablePixelFormats.includes(userPreferredPixelFormat)) { return userPreferredPixelFormat; } return availablePixelFormats[0]; }, [availablePixelFormats, userPreferredPixelFormat]); const onClickVideo = useCallback127(() => { setSidebarCollapsedState({ left: null, right: "expanded" }); persistSelectedOptionsSidebarPanel2("renders"); optionsSidebarTabs.current?.selectRendersPanel(); dispatchIfMounted({ type: "start" }); addVideoRenderJob({ compositionId: resolvedComposition.id, outName, imageFormat: videoImageFormat, jpegQuality: stillImageFormat === "jpeg" ? jpegQuality : null, scale, logLevel, codec, concurrency, crf: qualityControlType === "crf" && hardwareAcceleration !== "if-possible" && hardwareAcceleration !== "required" ? crf : null, endFrame, startFrame, muted, enforceAudioTrack, proResProfile, x264Preset, pixelFormat, audioBitrate, videoBitrate, everyNthFrame, numberOfGifLoops, delayRenderTimeout, audioCodec, disallowParallelEncoding, chromiumOptions, envVariables: envVariablesArrayToObject(envVariables), inputProps, offthreadVideoCacheSizeInBytes, colorSpace, multiProcessOnLinux, encodingBufferSize, encodingMaxRate, beepOnFinish, repro, forSeamlessAacConcatenation, separateAudioTo, metadata, hardwareAcceleration, chromeMode, offthreadVideoThreads, mediaCacheSizeInBytes }).then(() => { dispatchIfMounted({ type: "succeed" }); setSelectedModal(null); }).catch(() => { dispatchIfMounted({ type: "fail" }); }); }, [ setSidebarCollapsedState, dispatchIfMounted, resolvedComposition.id, outName, videoImageFormat, stillImageFormat, jpegQuality, scale, logLevel, codec, concurrency, qualityControlType, crf, endFrame, startFrame, muted, enforceAudioTrack, proResProfile, x264Preset, pixelFormat, audioBitrate, videoBitrate, everyNthFrame, numberOfGifLoops, delayRenderTimeout, audioCodec, disallowParallelEncoding, chromiumOptions, envVariables, inputProps, offthreadVideoCacheSizeInBytes, colorSpace, multiProcessOnLinux, encodingBufferSize, encodingMaxRate, beepOnFinish, repro, forSeamlessAacConcatenation, separateAudioTo, setSelectedModal, metadata, hardwareAcceleration, chromeMode, offthreadVideoThreads, mediaCacheSizeInBytes ]); const onClickSequence = useCallback127(() => { setSidebarCollapsedState({ left: null, right: "expanded" }); persistSelectedOptionsSidebarPanel2("renders"); optionsSidebarTabs.current?.selectRendersPanel(); dispatchIfMounted({ type: "start" }); addSequenceRenderJob({ compositionId: resolvedComposition.id, outName, imageFormat: sequenceImageFormat, scale, logLevel, concurrency, endFrame, jpegQuality, startFrame, delayRenderTimeout, chromiumOptions, envVariables: envVariablesArrayToObject(envVariables), inputProps, offthreadVideoCacheSizeInBytes, disallowParallelEncoding, multiProcessOnLinux, beepOnFinish, repro, metadata, chromeMode, offthreadVideoThreads, mediaCacheSizeInBytes }).then(() => { dispatchIfMounted({ type: "succeed" }); setSelectedModal(null); }).catch(() => { dispatchIfMounted({ type: "fail" }); }); }, [ setSidebarCollapsedState, dispatchIfMounted, resolvedComposition.id, outName, sequenceImageFormat, scale, logLevel, concurrency, endFrame, jpegQuality, startFrame, delayRenderTimeout, chromiumOptions, envVariables, inputProps, offthreadVideoCacheSizeInBytes, disallowParallelEncoding, multiProcessOnLinux, beepOnFinish, repro, setSelectedModal, metadata, chromeMode, offthreadVideoThreads, mediaCacheSizeInBytes ]); useEffect79(() => { return () => { isMounted.current = false; }; }, []); const imageFormatOptions = useMemo128(() => { if (renderMode === "still") { return [ { label: "PNG", onClick: () => setStillFormat("png"), key: "png", selected: stillImageFormat === "png" }, { label: "JPEG", onClick: () => setStillFormat("jpeg"), key: "jpeg", selected: stillImageFormat === "jpeg" }, { label: "PDF", onClick: () => setStillFormat("pdf"), key: "pdf", selected: stillImageFormat === "pdf" }, { label: "WebP", onClick: () => setStillFormat("webp"), key: "webp", selected: stillImageFormat === "webp" } ]; } if (renderMode === "sequence") { return [ { label: "PNG", onClick: () => setSequenceImageFormat("png"), key: "png", selected: sequenceImageFormat === "png" }, { label: "JPEG", onClick: () => setSequenceImageFormat("jpeg"), key: "jpeg", selected: sequenceImageFormat === "jpeg" } ]; } return [ { label: "PNG", onClick: () => setVideoImageFormat("png"), key: "png", selected: videoImageFormat === "png" }, { label: "JPEG", onClick: () => setVideoImageFormat("jpeg"), key: "jpeg", selected: videoImageFormat === "jpeg" } ]; }, [ renderMode, videoImageFormat, stillImageFormat, setStillFormat, sequenceImageFormat ]); const setRenderMode = useCallback127((newRenderMode) => { setRenderModeState(newRenderMode); if (newRenderMode === "audio") { setDefaultOutName({ type: "render", codec: videoCodecForAudioTab, audioCodec: deriveFinalAudioCodec(videoCodecForAudioTab, userSelectedAudioCodec) }); } if (newRenderMode === "video") { setDefaultOutName({ type: "render", codec: videoCodecForVideoTab, audioCodec: deriveFinalAudioCodec(videoCodecForVideoTab, userSelectedAudioCodec) }); } if (newRenderMode === "still") { setDefaultOutName({ type: "still", imageFormat: stillImageFormat }); } if (newRenderMode === "sequence") { setDefaultOutName({ type: "sequence" }); } }, [ videoCodecForAudioTab, userSelectedAudioCodec, deriveFinalAudioCodec, setDefaultOutName, stillImageFormat, videoCodecForVideoTab ]); const renderTabOptions = useMemo128(() => { if (resolvedComposition?.durationInFrames < 2) { return [ { label: "Still", onClick: () => { setRenderMode("still"); }, key: "still", selected: renderMode === "still" } ]; } return [ { label: "Still", onClick: () => { setRenderMode("still"); }, key: "still", selected: renderMode === "still" }, { label: "Video", onClick: () => { setRenderMode("video"); }, key: "video", selected: renderMode === "video" }, { label: "Audio", onClick: () => { setRenderMode("audio"); }, key: "audio", selected: renderMode === "audio" }, { label: "Image sequence", onClick: () => { setRenderMode("sequence"); }, key: "sequence", selected: renderMode === "sequence" } ]; }, [resolvedComposition?.durationInFrames, renderMode, setRenderMode]); const outnameValidation = validateOutnameGui({ outName, codec, audioCodec, renderMode, stillImageFormat, separateAudioTo }); const { tab, setTab, shownTabs } = useRenderModalSections(renderMode, codec); const { registerKeybinding } = useKeybinding(); const renderDisabled = state.type === "load" || !outnameValidation.valid; const trigger = useCallback127(() => { if (renderMode === "still") { onClickStill(); } else if (renderMode === "sequence") { onClickSequence(); } else { onClickVideo(); } }, [renderMode, onClickStill, onClickSequence, onClickVideo]); useEffect79(() => { if (renderDisabled) { return; } const enter = registerKeybinding({ callback() { trigger(); }, commandCtrlKey: true, key: "Enter", event: "keydown", preventDefault: true, triggerIfInputFieldFocused: true, keepRegisteredWhenNotHighestContext: false }); return () => { enter.unregister(); }; }, [registerKeybinding, renderDisabled, trigger]); const pixelFormatOptions = useMemo128(() => { return availablePixelFormats.map((option) => { return { label: option, onClick: () => setPixelFormat(option), key: option, id: option, keyHint: null, leftItem: pixelFormat === option ? /* @__PURE__ */ jsx257(Checkmark, {}) : null, quickSwitcherLabel: null, subMenu: null, type: "item", value: option }; }); }, [availablePixelFormats, pixelFormat]); return /* @__PURE__ */ jsxs136("div", { style: outerModalStyle, children: [ /* @__PURE__ */ jsx257(ModalHeader, { title: `Render ${resolvedComposition.id}` }), /* @__PURE__ */ jsxs136("div", { style: container59, children: [ /* @__PURE__ */ jsx257(SegmentedControl, { items: renderTabOptions, needsWrapping: false }), /* @__PURE__ */ jsx257("div", { style: flexer }), /* @__PURE__ */ jsxs136(Button, { autoFocus: true, onClick: trigger, disabled: renderDisabled, style: { ...buttonStyle7, backgroundColor: outnameValidation.valid ? BLUE : BLUE_DISABLED }, children: [ state.type === "idle" ? `Render ${renderMode}` : "Rendering...", /* @__PURE__ */ jsx257(ShortcutHint, { keyToPress: "↵", cmdOrCtrl: true }) ] }) ] }), /* @__PURE__ */ jsxs136("div", { style: horizontalLayout, children: [ /* @__PURE__ */ jsxs136("div", { style: leftSidebar, children: [ shownTabs.includes("general") ? /* @__PURE__ */ jsxs136(VerticalTab, { style: horizontalTab, selected: tab === "general", onClick: () => setTab("general"), children: [ /* @__PURE__ */ jsx257("div", { style: iconContainer, children: /* @__PURE__ */ jsx257(FileIcon, { style: icon6 }) }), "General" ] }) : null, shownTabs.includes("data") ? /* @__PURE__ */ jsxs136(VerticalTab, { style: horizontalTab, selected: tab === "data", onClick: () => setTab("data"), children: [ /* @__PURE__ */ jsx257("div", { style: iconContainer, children: /* @__PURE__ */ jsx257(DataIcon, { style: icon6 }) }), "Input Props" ] }) : null, shownTabs.includes("picture") ? /* @__PURE__ */ jsxs136(VerticalTab, { style: horizontalTab, selected: tab === "picture", onClick: () => setTab("picture"), children: [ /* @__PURE__ */ jsx257("div", { style: iconContainer, children: /* @__PURE__ */ jsx257(PicIcon, { style: icon6 }) }), "Picture" ] }) : null, shownTabs.includes("audio") ? /* @__PURE__ */ jsxs136(VerticalTab, { style: horizontalTab, selected: tab === "audio", onClick: () => setTab("audio"), children: [ /* @__PURE__ */ jsx257("div", { style: iconContainer, children: /* @__PURE__ */ jsx257(AudioIcon, { style: icon6 }) }), "Audio" ] }) : null, shownTabs.includes("gif") ? /* @__PURE__ */ jsxs136(VerticalTab, { style: horizontalTab, selected: tab === "gif", onClick: () => setTab("gif"), children: [ /* @__PURE__ */ jsx257("div", { style: iconContainer, children: /* @__PURE__ */ jsx257(GifIcon, { style: icon6 }) }), "GIF" ] }) : null, shownTabs.includes("advanced") ? /* @__PURE__ */ jsxs136(VerticalTab, { style: horizontalTab, selected: tab === "advanced", onClick: () => setTab("advanced"), children: [ /* @__PURE__ */ jsx257("div", { style: iconContainer, children: /* @__PURE__ */ jsx257(GearIcon, { style: icon6 }) }), "Other" ] }) : null ] }), /* @__PURE__ */ jsx257("div", { style: optionsPanel, className: VERTICAL_SCROLLBAR_CLASSNAME, children: tab === "general" ? /* @__PURE__ */ jsx257(RenderModalBasic, { codec, resolvedComposition, frame: frame2, imageFormatOptions, outName, proResProfile, renderMode, setVideoCodec: setCodec, setFrame, setOutName, setProResProfile, endFrame, setEndFrame, setStartFrame, setVerboseLogging: setLogLevel, logLevel, startFrame, validationMessage: outnameValidation.valid ? null : outnameValidation.error.message }) : tab === "picture" ? /* @__PURE__ */ jsx257(RenderModalPicture, { renderMode, scale, setScale, pixelFormat, pixelFormatOptions, imageFormatOptions, crf, setCrf, customTargetVideoBitrate, maxCrf, minCrf, jpegQuality, qualityControlType, setJpegQuality, setColorSpace, colorSpace, setCustomTargetVideoBitrateValue, setQualityControl, videoImageFormat, stillImageFormat, shouldDisplayQualityControlPicker: supportsBothQualityControls, encodingBufferSize, setEncodingBufferSize, encodingMaxRate, setEncodingMaxRate, compositionWidth: resolvedComposition.width, compositionHeight: resolvedComposition.height }) : tab === "audio" ? /* @__PURE__ */ jsx257(RenderModalAudio, { muted, renderMode, setMuted, codec, audioCodec, setAudioCodec, enforceAudioTrack, setEnforceAudioTrackState, customTargetAudioBitrate, setCustomTargetAudioBitrateValue, setShouldHaveCustomTargetAudioBitrate, shouldHaveCustomTargetAudioBitrate, forSeamlessAacConcatenation, setForSeamlessAacConcatenation, separateAudioTo, setSeparateAudioTo, outName }) : tab === "gif" ? /* @__PURE__ */ jsx257(RenderModalGif, { everyNthFrame, limitNumberOfGifLoops, numberOfGifLoopsSetting, setEveryNthFrameSetting, setLimitNumberOfGifLoops, setNumberOfGifLoopsSetting }) : tab === "data" ? /* @__PURE__ */ jsx257(DataEditor, { defaultProps: inputProps, setDefaultProps: setInputProps, unresolvedComposition, mayShowSaveButton: false, propsEditType: "input-props", saving, setSaving, readOnlyStudio: false }) : /* @__PURE__ */ jsx257(RenderModalAdvanced, { x264Preset, setx264Preset, concurrency, maxConcurrency, minConcurrency, renderMode, setConcurrency, delayRenderTimeout, setDelayRenderTimeout, disallowParallelEncoding, setDisallowParallelEncoding, setDisableWebSecurity, setIgnoreCertificateErrors, setHeadless, headless, ignoreCertificateErrors, disableWebSecurity, openGlOption, setOpenGlOption, setEnvVariables, envVariables, offthreadVideoCacheSizeInBytes, setMediaCacheSizeInBytes, mediaCacheSizeInBytes, setOffthreadVideoCacheSizeInBytes, offthreadVideoThreads, setOffthreadVideoThreads, enableMultiProcessOnLinux: multiProcessOnLinux, setChromiumMultiProcessOnLinux, codec, userAgent, setUserAgent, setBeep: setBeepOnFinish, beep: beepOnFinish, repro, setRepro, hardwareAcceleration, setHardwareAcceleration, chromeModeOption: chromeMode, setChromeModeOption: setChromeMode, darkMode, setDarkMode }) }) ] }) ] }); }; var RenderModalWithLoader = (props) => { return /* @__PURE__ */ jsx257(DismissableModal, { children: /* @__PURE__ */ jsx257(ResolveCompositionBeforeModal, { compositionId: props.compositionId, children: /* @__PURE__ */ jsx257(RenderModal, { ...props }) }) }); }; // src/components/RenderModal/WebRenderModal.tsx import { getDefaultOutLocation as getDefaultOutLocation2 } from "@remotion/studio-shared"; import { getDefaultAudioCodecForContainer } from "@remotion/web-renderer"; import { useCallback as useCallback131, useContext as useContext84, useMemo as useMemo133, useState as useState85 } from "react"; // src/icons/certificate.tsx import { jsx as jsx258 } from "react/jsx-runtime"; var CertificateIcon = (props) => /* @__PURE__ */ jsx258("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 576 512", ...props, children: /* @__PURE__ */ jsx258("path", { fill: "currentcolor", d: "M192 32l128 0 0 96c0 35.3 28.7 64 64 64l96 0 0 256c0 17.7-14.3 32-32 32l-192 0 0 32 192 0c35.3 0 64-28.7 64-64l0-261.5c0-17-6.7-33.3-18.7-45.3L370.7 18.7C358.7 6.7 342.5 0 325.5 0L192 0c-35.3 0-64 28.7-64 64l0 80c10.9 0 21.6 1 32 2.9L160 64c0-17.7 14.3-32 32-32zM352 45.3L466.7 160 384 160c-17.7 0-32-14.3-32-32l0-82.7zM32 320a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zM176 438.7l0 66.3-40.1-22.9c-4.9-2.8-11-2.8-15.9 0L80 505 80 438.7c14.8 6 31 9.3 48 9.3s33.2-3.3 48-9.3zm32-18.8c29.3-23.5 48-59.5 48-99.9 0-70.7-57.3-128-128-128S0 249.3 0 320c0 40.4 18.7 76.5 48 99.9l0 101.8c0 12.3 10 22.3 22.3 22.3 3.9 0 7.7-1 11.1-2.9l46.6-26.6 46.6 26.6c3.4 1.9 7.2 2.9 11.1 2.9 12.3 0 22.3-10 22.3-22.3l0-101.8zM128 344a24 24 0 1 1 0-48 24 24 0 1 1 0 48zm0-80a56 56 0 1 0 0 112 56 56 0 1 0 0-112z" }) }); // src/components/RenderModal/use-encodable-audio-codecs.ts import { getEncodableAudioCodecs, getSupportedAudioCodecsForContainer } from "@remotion/web-renderer"; import { useEffect as useEffect80, useRef as useRef45, useState as useState82 } from "react"; var useEncodableAudioCodecs = (container60) => { const cacheRef = useRef45({}); const [codecsByContainer, setCodecsByContainer] = useState82(() => { return { [container60]: getSupportedAudioCodecsForContainer(container60) }; }); useEffect80(() => { const cached = cacheRef.current[container60]; if (cached) { return; } const supported = getSupportedAudioCodecsForContainer(container60); cacheRef.current[container60] = { codecs: supported, status: "fetching" }; getEncodableAudioCodecs(container60).then((encodable) => { cacheRef.current[container60] = { codecs: encodable, status: "done" }; setCodecsByContainer((prev) => ({ ...prev, [container60]: encodable })); }).catch(() => { cacheRef.current[container60] = { codecs: supported, status: "done" }; }); }, [container60]); return codecsByContainer[container60] ?? getSupportedAudioCodecsForContainer(container60); }; // src/components/RenderModal/use-encodable-video-codecs.ts import { getEncodableVideoCodecs, getSupportedVideoCodecsForContainer } from "@remotion/web-renderer"; import { useEffect as useEffect81, useRef as useRef46, useState as useState83 } from "react"; var useEncodableVideoCodecs = (container60) => { const cacheRef = useRef46({}); const [codecsByContainer, setCodecsByContainer] = useState83(() => { return { [container60]: getSupportedVideoCodecsForContainer(container60) }; }); useEffect81(() => { const cached = cacheRef.current[container60]; if (cached) { return; } const supported = getSupportedVideoCodecsForContainer(container60); cacheRef.current[container60] = { codecs: supported, status: "fetching" }; getEncodableVideoCodecs(container60).then((encodable) => { cacheRef.current[container60] = { codecs: encodable, status: "done" }; setCodecsByContainer((prev) => ({ ...prev, [container60]: encodable })); }).catch(() => { cacheRef.current[container60] = { codecs: supported, status: "done" }; }); }, [container60]); return codecsByContainer[container60] ?? getSupportedVideoCodecsForContainer(container60); }; // src/components/RenderModal/WebRendererExperimentalBadge.tsx import { jsx as jsx259, jsxs as jsxs137 } from "react/jsx-runtime"; var row8 = { display: "flex", flexDirection: "row", alignItems: "center", justifyContent: "center" }; var text3 = { fontSize: 14, fontFamily: "sans-serif", color: LIGHT_TEXT }; var icon7 = { width: 14, height: 14, flexShrink: 0, fill: WARNING_COLOR, marginRight: 8 }; var link3 = { color: "inherit", textDecoration: "underline", fontSize: 14 }; var WebRendererExperimentalBadge = () => { return /* @__PURE__ */ jsxs137("div", { style: row8, children: [ /* @__PURE__ */ jsx259(WarningTriangle, { type: "warning", style: icon7 }), /* @__PURE__ */ jsxs137("div", { style: text3, children: [ "The Remotion Web Renderer is experimental.", " ", /* @__PURE__ */ jsx259("a", { href: "https://github.com/remotion-dev/remotion/issues/5913", target: "_blank", rel: "noopener noreferrer", style: link3, children: "Track progress on GitHub" }), " ", "and discuss in the", " ", /* @__PURE__ */ jsx259("a", { href: "https://remotion.dev/discord", target: "_blank", rel: "noopener noreferrer", style: link3, children: "#web-renderer" }), " ", "channel on Discord." ] }) ] }); }; // src/components/RenderModal/WebRenderModalAdvanced.tsx import { useCallback as useCallback128, useMemo as useMemo129 } from "react"; import { jsx as jsx260, jsxs as jsxs138 } from "react/jsx-runtime"; var tabContainer = { flex: 1 }; var WebRenderModalAdvanced = ({ renderMode, delayRenderTimeout, setDelayRenderTimeout, mediaCacheSizeInBytes, setMediaCacheSizeInBytes, hardwareAcceleration, setHardwareAcceleration }) => { const toggleCustomMediaCacheSizeInBytes = useCallback128(() => { setMediaCacheSizeInBytes((previous) => { if (previous === null) { return 1000 * 1000 * 1000; } return null; }); }, [setMediaCacheSizeInBytes]); const changeMediaCacheSizeInBytes = useCallback128((cb) => { setMediaCacheSizeInBytes((prev) => { if (prev === null) { throw new TypeError("Expected previous value"); } if (typeof cb === "function") { return cb(prev); } return cb; }); }, [setMediaCacheSizeInBytes]); const hardwareAccelerationOptions = useMemo129(() => { return [ { label: "No Preference", onClick: () => setHardwareAcceleration("no-preference"), leftItem: hardwareAcceleration === "no-preference" ? /* @__PURE__ */ jsx260(Checkmark, {}) : null, id: "no-preference", keyHint: null, quickSwitcherLabel: null, subMenu: null, type: "item", value: "no-preference" }, { label: "Prefer Hardware", onClick: () => setHardwareAcceleration("prefer-hardware"), leftItem: hardwareAcceleration === "prefer-hardware" ? /* @__PURE__ */ jsx260(Checkmark, {}) : null, id: "prefer-hardware", keyHint: null, quickSwitcherLabel: null, subMenu: null, type: "item", value: "prefer-hardware" }, { label: "Prefer Software", onClick: () => setHardwareAcceleration("prefer-software"), leftItem: hardwareAcceleration === "prefer-software" ? /* @__PURE__ */ jsx260(Checkmark, {}) : null, id: "prefer-software", keyHint: null, quickSwitcherLabel: null, subMenu: null, type: "item", value: "prefer-software" } ]; }, [hardwareAcceleration, setHardwareAcceleration]); return /* @__PURE__ */ jsxs138("div", { style: tabContainer, children: [ /* @__PURE__ */ jsx260(NumberSetting, { name: "Delay Render Timeout", formatter: (v) => `${v}ms`, min: 0, max: 1e9, step: 1000, value: delayRenderTimeout, onValueChanged: setDelayRenderTimeout, hint: "delayRenderTimeoutInMillisecondsOption" }), /* @__PURE__ */ jsxs138("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs138("div", { style: label5, children: [ "Custom @remotion/media cache size ", /* @__PURE__ */ jsx260(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx260(OptionExplainerBubble, { id: "mediaCacheSizeInBytesOption" }) ] }), /* @__PURE__ */ jsx260("div", { style: rightRow, children: /* @__PURE__ */ jsx260(Checkbox, { checked: mediaCacheSizeInBytes !== null, onChange: toggleCustomMediaCacheSizeInBytes, name: "media-cache-size" }) }) ] }), mediaCacheSizeInBytes === null ? null : /* @__PURE__ */ jsx260(NumberSetting, { name: "@remotion/media cache size", formatter: (w) => `${w} bytes`, min: 0, max: 10000000000, step: 10 * 1024 * 1024, value: mediaCacheSizeInBytes, onValueChanged: changeMediaCacheSizeInBytes }), renderMode === "video" ? /* @__PURE__ */ jsxs138("div", { style: optionRow, children: [ /* @__PURE__ */ jsx260("div", { style: label5, children: "Hardware Acceleration" }), /* @__PURE__ */ jsx260("div", { style: rightRow, children: /* @__PURE__ */ jsx260(Combobox, { values: hardwareAccelerationOptions, selectedId: hardwareAcceleration, title: "Hardware Acceleration" }) }) ] }) : null ] }); }; // src/components/RenderModal/WebRenderModalAudio.tsx import { getSupportedAudioCodecsForContainer as getSupportedAudioCodecsForContainer2 } from "@remotion/web-renderer"; import { useMemo as useMemo130 } from "react"; // src/components/RenderModal/quality-options.tsx import { jsx as jsx261 } from "react/jsx-runtime"; var QUALITY_OPTIONS = [ { value: "very-low", label: "Very Low" }, { value: "low", label: "Low" }, { value: "medium", label: "Medium" }, { value: "high", label: "High" }, { value: "very-high", label: "Very High" } ]; var getQualityOptions = (selectedQuality, setQuality) => { return QUALITY_OPTIONS.map(({ value, label: label12 }) => ({ label: label12, onClick: () => setQuality(value), leftItem: selectedQuality === value ? /* @__PURE__ */ jsx261(Checkmark, {}) : null, id: value, keyHint: null, quickSwitcherLabel: null, subMenu: null, type: "item", value })); }; // src/components/RenderModal/WebRenderModalAudio.tsx import { jsx as jsx262, jsxs as jsxs139, Fragment as Fragment46 } from "react/jsx-runtime"; var container60 = { flex: 1, overflowY: "auto" }; var fallbackNoticeStyle = { backgroundColor: "rgba(59, 130, 246, 0.15)", border: "1px solid rgba(59, 130, 246, 0.4)", borderRadius: 4, padding: "8px 12px", marginLeft: 16, marginRight: 16, marginTop: 8, fontSize: 13, lineHeight: 1.4, color: "#60a5fa" }; var humanReadableWebAudioCodec = (audioCodec) => { switch (audioCodec) { case "aac": return "AAC"; case "opus": return "Opus"; default: return audioCodec; } }; var WebRenderModalAudio = ({ muted, setMuted, audioCodec, setAudioCodec, audioBitrate, setAudioBitrate, container: videoContainer, encodableCodecs, effectiveAudioCodec }) => { const containerSupported = useMemo130(() => getSupportedAudioCodecsForContainer2(videoContainer), [videoContainer]); const audioCodecOptions = useMemo130(() => { return containerSupported.map((codec) => { const isEncodable = encodableCodecs.includes(codec); return { label: humanReadableWebAudioCodec(codec), onClick: () => setAudioCodec(codec), leftItem: audioCodec === codec ? /* @__PURE__ */ jsx262(Checkmark, {}) : null, id: codec, keyHint: null, quickSwitcherLabel: null, subMenu: null, type: "item", value: codec, disabled: !isEncodable }; }); }, [containerSupported, encodableCodecs, audioCodec, setAudioCodec]); const audioBitrateOptions = useMemo130(() => getQualityOptions(audioBitrate, setAudioBitrate), [audioBitrate, setAudioBitrate]); return /* @__PURE__ */ jsxs139("div", { style: container60, className: VERTICAL_SCROLLBAR_CLASSNAME, children: [ /* @__PURE__ */ jsx262(MutedSetting, { enforceAudioTrack: false, muted, setMuted }), !muted ? /* @__PURE__ */ jsxs139(Fragment46, { children: [ /* @__PURE__ */ jsx262(RenderModalHr, {}), /* @__PURE__ */ jsxs139("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs139("div", { style: label5, children: [ "Audio Quality", /* @__PURE__ */ jsx262(Spacing, { x: 0.5 }) ] }), /* @__PURE__ */ jsx262("div", { style: rightRow, children: /* @__PURE__ */ jsx262(Combobox, { values: audioBitrateOptions, selectedId: audioBitrate, title: "Audio Quality" }) }) ] }), /* @__PURE__ */ jsxs139("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs139("div", { style: label5, children: [ "Audio Codec", /* @__PURE__ */ jsx262(Spacing, { x: 0.5 }) ] }), /* @__PURE__ */ jsx262("div", { style: rightRow, children: /* @__PURE__ */ jsx262(Combobox, { values: audioCodecOptions, selectedId: audioCodec, title: "Audio Codec" }) }) ] }), effectiveAudioCodec !== audioCodec ? /* @__PURE__ */ jsxs139("div", { style: fallbackNoticeStyle, children: [ humanReadableWebAudioCodec(audioCodec), " is not available in this browser. Using ", humanReadableWebAudioCodec(effectiveAudioCodec), " ", "instead." ] }) : null ] }) : null ] }); }; // src/components/RenderModal/WebRenderModalBasic.tsx import { useMemo as useMemo131 } from "react"; import { jsx as jsx263, jsxs as jsxs140, Fragment as Fragment47 } from "react/jsx-runtime"; var tabContainer2 = { flex: 1 }; var WebRenderModalBasic = ({ renderMode, resolvedComposition, imageFormat, setStillFormat, frame: frame2, onFrameChanged, onFrameSetDirectly, container: container61, setContainerFormat, setCodec, encodableVideoCodecs, effectiveVideoCodec, startFrame, setStartFrame, endFrame, setEndFrame, outName, onOutNameChange, validationMessage, logLevel, setLogLevel }) => { const imageFormatOptions = useMemo131(() => { return [ { label: "PNG", onClick: () => setStillFormat("png"), key: "png", selected: imageFormat === "png" }, { label: "JPEG", onClick: () => setStillFormat("jpeg"), key: "jpeg", selected: imageFormat === "jpeg" }, { label: "WebP", onClick: () => setStillFormat("webp"), key: "webp", selected: imageFormat === "webp" } ]; }, [imageFormat, setStillFormat]); const logLevelOptions = useMemo131(() => { return ["trace", "verbose", "info", "warn", "error"].map((level) => { return { label: humanReadableLogLevel(level), onClick: () => setLogLevel(level), leftItem: logLevel === level ? /* @__PURE__ */ jsx263(Checkmark, {}) : null, id: level, keyHint: null, quickSwitcherLabel: null, subMenu: null, type: "item", value: level }; }); }, [logLevel, setLogLevel]); const containerOptions = useMemo131(() => { return [ { label: "MP4", onClick: () => setContainerFormat("mp4"), leftItem: container61 === "mp4" ? /* @__PURE__ */ jsx263(Checkmark, {}) : null, id: "mp4", keyHint: null, quickSwitcherLabel: null, subMenu: null, type: "item", value: "mp4" }, { label: "WebM", onClick: () => setContainerFormat("webm"), leftItem: container61 === "webm" ? /* @__PURE__ */ jsx263(Checkmark, {}) : null, id: "webm", keyHint: null, quickSwitcherLabel: null, subMenu: null, type: "item", value: "webm" } ]; }, [container61, setContainerFormat]); const codecLabels = useMemo131(() => ({ h264: "H.264", h265: "H.265", vp8: "VP8", vp9: "VP9", av1: "AV1" }), []); const codecOptions = useMemo131(() => { return encodableVideoCodecs.map((c) => ({ label: codecLabels[c], onClick: () => setCodec(c), leftItem: effectiveVideoCodec === c ? /* @__PURE__ */ jsx263(Checkmark, {}) : null, id: c, keyHint: null, quickSwitcherLabel: null, subMenu: null, type: "item", value: c })); }, [encodableVideoCodecs, effectiveVideoCodec, setCodec, codecLabels]); return /* @__PURE__ */ jsxs140("div", { style: tabContainer2, children: [ renderMode === "still" ? /* @__PURE__ */ jsxs140(Fragment47, { children: [ /* @__PURE__ */ jsxs140("div", { style: optionRow, children: [ /* @__PURE__ */ jsx263("div", { style: label5, children: "Format" }), /* @__PURE__ */ jsx263("div", { style: rightRow, children: /* @__PURE__ */ jsx263(SegmentedControl, { items: imageFormatOptions, needsWrapping: true }) }) ] }), resolvedComposition.durationInFrames > 1 ? /* @__PURE__ */ jsxs140("div", { style: optionRow, children: [ /* @__PURE__ */ jsx263("div", { style: label5, children: "Frame" }), /* @__PURE__ */ jsx263("div", { style: rightRow, children: /* @__PURE__ */ jsx263(RightAlignInput, { children: /* @__PURE__ */ jsx263(InputDragger, { value: frame2, onTextChange: onFrameChanged, placeholder: `0-${resolvedComposition.durationInFrames - 1}`, onValueChange: onFrameSetDirectly, name: "frame", step: 1, min: 0, status: "ok", max: resolvedComposition.durationInFrames - 1, rightAlign: true }) }) }) ] }) : null ] }) : /* @__PURE__ */ jsxs140(Fragment47, { children: [ /* @__PURE__ */ jsxs140("div", { style: optionRow, children: [ /* @__PURE__ */ jsx263("div", { style: label5, children: "Container" }), /* @__PURE__ */ jsx263("div", { style: rightRow, children: /* @__PURE__ */ jsx263(Combobox, { values: containerOptions, selectedId: container61, title: "Container" }) }) ] }), /* @__PURE__ */ jsxs140("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs140("div", { style: label5, children: [ "Codec", /* @__PURE__ */ jsx263(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx263(OptionExplainerBubble, { id: "videoCodecOption" }) ] }), /* @__PURE__ */ jsx263("div", { style: rightRow, children: /* @__PURE__ */ jsx263(Combobox, { values: codecOptions, selectedId: effectiveVideoCodec, title: "Codec" }) }) ] }), /* @__PURE__ */ jsx263(FrameRangeSetting, { durationInFrames: resolvedComposition.durationInFrames, startFrame: startFrame ?? 0, endFrame: endFrame ?? resolvedComposition.durationInFrames - 1, setStartFrame, setEndFrame }) ] }), /* @__PURE__ */ jsx263(RenderModalOutputName, { existence: false, inputStyle: input, outName, onValueChange: onOutNameChange, validationMessage, label: "Download name" }), /* @__PURE__ */ jsxs140("div", { style: optionRow, children: [ /* @__PURE__ */ jsxs140("div", { style: label5, children: [ "Log Level ", /* @__PURE__ */ jsx263(Spacing, { x: 0.5 }), /* @__PURE__ */ jsx263(OptionExplainerBubble, { id: "logLevelOption" }) ] }), /* @__PURE__ */ jsx263("div", { style: rightRow, children: /* @__PURE__ */ jsx263(Combobox, { values: logLevelOptions, selectedId: logLevel, title: "Log Level" }) }) ] }) ] }); }; // src/components/RenderModal/WebRenderModalLicense.tsx import { useCallback as useCallback129, useEffect as useEffect82, useState as useState84 } from "react"; // src/icons/check-circle-filled.tsx import { jsx as jsx264 } from "react/jsx-runtime"; var CheckCircleFilled = (props) => /* @__PURE__ */ jsx264("svg", { xmlns: "http://www.w3.org/2000/svg", style: { width: 14, height: 14 }, viewBox: "0 0 512 512", ...props, children: /* @__PURE__ */ jsx264("path", { d: "M256 512a256 256 0 1 1 0-512 256 256 0 1 1 0 512zM374 145.7c-10.7-7.8-25.7-5.4-33.5 5.3L221.1 315.2 169 263.1c-9.4-9.4-24.6-9.4-33.9 0s-9.4 24.6 0 33.9l72 72c5 5 11.8 7.5 18.8 7s13.4-4.1 17.5-9.8L379.3 179.2c7.8-10.7 5.4-25.7-5.3-33.5z" }) }); // src/components/RenderModal/WebRenderModalLicenseKeyDetails.tsx import { jsx as jsx265, jsxs as jsxs141 } from "react/jsx-runtime"; var textStyle2 = { color: LIGHT_TEXT, fontSize: 14, fontFamily: "sans-serif", lineHeight: 1.5, display: "flex", alignItems: "center" }; var linkStyle = { fontSize: 14, fontFamily: "sans-serif", lineHeight: 1.5, cursor: "pointer" }; var bulletStyle = { display: "flex", alignItems: "center", gap: 6 }; var icon8 = { width: 14, height: 14, flexShrink: 0 }; var PRO_HOST = "https://remotion.pro"; var fetchLicenseKeyDetails = async (licenseKey) => { const response = await fetch(`${PRO_HOST}/api/validate-license-key`, { method: "POST", body: JSON.stringify({ licenseKey }), headers: { "Content-Type": "application/json" } }); return response.json(); }; var WebRenderModalLicenseKeyDetails = ({ details }) => { return /* @__PURE__ */ jsxs141("div", { children: [ /* @__PURE__ */ jsxs141("div", { style: bulletStyle, children: [ /* @__PURE__ */ jsx265(CheckCircleFilled, { style: { ...icon8, fill: LIGHT_TEXT } }), /* @__PURE__ */ jsxs141("div", { style: textStyle2, children: [ "Belongs to ", /* @__PURE__ */ jsx265("a", { href: `${PRO_HOST}/projects/${details.projectSlug}`, target: "_blank", style: linkStyle, children: details.projectName }), " - ", /* @__PURE__ */ jsx265("a", { href: `${PRO_HOST}/projects/${details.projectSlug}/usage#client-renders-usage`, target: "_blank", style: linkStyle, children: "View usage" }) ] }) ] }), details.hasActiveSubscription ? /* @__PURE__ */ jsxs141("div", { style: bulletStyle, children: [ /* @__PURE__ */ jsx265(CheckCircleFilled, { style: { ...icon8, fill: LIGHT_TEXT } }), /* @__PURE__ */ jsx265("div", { style: textStyle2, children: "Active Company License" }) ] }) : /* @__PURE__ */ jsxs141("div", { style: bulletStyle, children: [ /* @__PURE__ */ jsx265(WarningTriangle, { type: "warning", style: { ...icon8, fill: WARNING_COLOR } }), /* @__PURE__ */ jsx265("div", { style: textStyle2, children: "No active Company License" }) ] }) ] }); }; // src/components/RenderModal/WebRenderModalLicense.tsx import { jsx as jsx266, jsxs as jsxs142, Fragment as Fragment48 } from "react/jsx-runtime"; var row9 = { display: "flex", flexDirection: "row", paddingLeft: 16, paddingRight: 16 }; var tabContainer3 = { flex: 1 }; var descriptionStyle = { color: LIGHT_TEXT, fontSize: 14, fontFamily: "sans-serif", paddingLeft: 16, paddingRight: 16, paddingTop: 16, paddingBottom: 8, lineHeight: 1.5 }; var paddedDescriptionStyle = { color: LIGHT_TEXT, fontSize: 14, fontFamily: "sans-serif", padding: 9, border: "1px solid " + INPUT_BORDER_COLOR_UNHOVERED, borderRadius: 8, lineHeight: 1.5, marginLeft: 16, marginRight: 16 }; var descriptionLink = { color: "white", fontSize: 14 }; var checkboxLabel = { fontSize: 14, lineHeight: "40px", color: LIGHT_TEXT, flex: 1, fontFamily: "sans-serif", cursor: "pointer", userSelect: "none" }; var inputStyle2 = { minWidth: 250 }; var justifyCenter = { display: "flex", alignItems: "center", gap: 10, flex: 1 }; var codeStyle = { fontSize: 14, fontFamily: "monospace", color: BLUE }; var codeLine = { fontSize: 14, fontFamily: "monospace", color: LIGHT_TEXT, backgroundColor: INPUT_BACKGROUND, padding: 6, borderRadius: 3, marginTop: 6, overflowX: "auto", maxWidth: "100%" }; var codeLineSmall = { ...codeLine, fontSize: 11 }; var LICENSE_KEY_LENGTH = 55; var LICENSE_KEY_PREFIX = "rm_pub_"; var validateLicenseKey = (key5) => { if (key5.length === 0) { return { valid: false, message: null, details: null }; } if (!key5.startsWith(LICENSE_KEY_PREFIX)) { return { valid: false, message: `License key must start with "${LICENSE_KEY_PREFIX}"`, details: null }; } const afterPrefix = key5.slice(LICENSE_KEY_PREFIX.length); if (!/^[a-zA-Z0-9]*$/.test(afterPrefix)) { return { valid: false, message: "License key must contain only alphanumeric characters after the prefix", details: null }; } if (key5.length !== LICENSE_KEY_LENGTH) { return { valid: false, message: `License key must be ${LICENSE_KEY_LENGTH} characters long`, details: null }; } return { valid: true, message: null, details: null }; }; var WebRenderModalLicense = ({ licenseKey, setLicenseKey, initialPublicLicenseKey }) => { const [licenseValidation, setLicenseValidation] = useState84({ valid: true, message: null, details: null }); const [isLoading, setIsLoading] = useState84(false); useEffect82(() => { if (licenseKey === null || licenseKey === "free-license") { return setLicenseValidation({ valid: true, message: null, details: null }); } const validation = validateLicenseKey(licenseKey); if (!validation.valid) { return setLicenseValidation(validation); } setLicenseValidation({ valid: true, message: null, details: null }); setIsLoading(true); fetchLicenseKeyDetails(licenseKey).then((details) => { setIsLoading(false); if (details.isValid) { setLicenseValidation({ valid: true, message: null, details }); } else { setLicenseValidation({ valid: false, message: "License key is invalid or has been reset", details: null }); } }).catch(() => { setIsLoading(false); setLicenseValidation({ valid: false, message: "Failed to fetch license key details", details: null }); }); }, [licenseKey]); const onFreeLicenseChange = useCallback129(() => { setLicenseKey("free-license"); }, [setLicenseKey]); const onCompanyLicenseChange = useCallback129(() => { setLicenseKey(initialPublicLicenseKey ?? ""); }, [initialPublicLicenseKey, setLicenseKey]); const onLicenseKeyChange = useCallback129((e) => { setLicenseKey(e.target.value); }, [setLicenseKey]); return /* @__PURE__ */ jsxs142("div", { style: tabContainer3, children: [ /* @__PURE__ */ jsxs142("div", { style: descriptionStyle, children: [ "Remotion is free if you are an individual or company with a headcount of 3 or less. See", " ", /* @__PURE__ */ jsx266("a", { style: descriptionLink, href: "https://remotion.dev/license", children: "LICENSE.md" }), "." ] }), /* @__PURE__ */ jsx266("div", { style: row9, children: /* @__PURE__ */ jsxs142("div", { style: justifyCenter, children: [ /* @__PURE__ */ jsx266(Checkbox, { checked: licenseKey === "free-license", onChange: onFreeLicenseChange, name: "free-license", rounded: true }), /* @__PURE__ */ jsxs142("div", { style: checkboxLabel, onClick: onFreeLicenseChange, children: [ "I am eligible for the Free License, ", "don't", " print a warning" ] }) ] }) }), licenseKey === "free-license" ? /* @__PURE__ */ jsxs142("div", { style: paddedDescriptionStyle, children: [ "Enjoy Remotion! Add the following to", " ", /* @__PURE__ */ jsx266("code", { style: codeStyle, children: "remotion.config.ts" }), " to persist this setting:", /* @__PURE__ */ jsx266("div", { style: codeLine, children: "Config.setPublicLicenseKey('free-license');" }) ] }) : null, /* @__PURE__ */ jsx266("div", { style: row9, children: /* @__PURE__ */ jsxs142("div", { style: justifyCenter, children: [ /* @__PURE__ */ jsx266(Checkbox, { checked: licenseKey !== "free-license" && licenseKey !== null, onChange: onCompanyLicenseChange, name: "company-license", rounded: true }), /* @__PURE__ */ jsx266("div", { style: checkboxLabel, onClick: onCompanyLicenseChange, children: "I have a Company License" }) ] }) }), licenseKey !== "free-license" && licenseKey !== null ? /* @__PURE__ */ jsxs142("div", { style: paddedDescriptionStyle, children: [ "Add your public license key from", " ", /* @__PURE__ */ jsx266("a", { href: "https://remotion.pro/dashboard", target: "_blank", style: descriptionLink, children: "remotion.pro" }), " ", "below.", /* @__PURE__ */ jsx266(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx266(RemotionInput, { value: licenseKey, onChange: onLicenseKeyChange, placeholder: "remotion.pro public license key (starts with rm_pub_)", status: licenseValidation.valid || licenseKey.length === 0 ? "ok" : "error", rightAlign: false, style: inputStyle2, autoFocus: true }), licenseValidation.message ? /* @__PURE__ */ jsxs142(Fragment48, { children: [ /* @__PURE__ */ jsx266(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx266(ValidationMessage, { message: licenseValidation.message, align: "flex-start", type: "error" }) ] }) : null, licenseValidation.valid && licenseKey.length > 0 ? /* @__PURE__ */ jsxs142(Fragment48, { children: [ /* @__PURE__ */ jsx266(Spacing, { y: 1, block: true }), "Add the following to", " ", /* @__PURE__ */ jsx266("code", { style: codeStyle, children: "remotion.config.ts" }), " to persist this setting:", /* @__PURE__ */ jsx266("div", { style: codeLineSmall, children: "Config.setPublicLicenseKey('" + licenseKey + "');" }) ] }) : null, isLoading && /* @__PURE__ */ jsxs142(Fragment48, { children: [ /* @__PURE__ */ jsx266(Spacing, { y: 1, block: true }), "Loading license key details..." ] }), licenseValidation.details && /* @__PURE__ */ jsxs142(Fragment48, { children: [ /* @__PURE__ */ jsx266(Spacing, { y: 1, block: true }), /* @__PURE__ */ jsx266(WebRenderModalLicenseKeyDetails, { details: licenseValidation.details }) ] }) ] }) : null, licenseKey === null ? /* @__PURE__ */ jsxs142("div", { style: descriptionStyle, children: [ "If you are not eligible for the free license, you need to obtain a", " ", /* @__PURE__ */ jsx266("a", { style: descriptionLink, target: "_blank", href: "https://remotion.pro/license", children: "Company License" }), "." ] }) : null ] }); }; // src/components/RenderModal/WebRenderModalPicture.tsx import { useCallback as useCallback130, useMemo as useMemo132 } from "react"; import { jsx as jsx267, jsxs as jsxs143, Fragment as Fragment49 } from "react/jsx-runtime"; var tabContainer4 = { flex: 1 }; var WebRenderModalPicture = ({ renderMode, videoBitrate, setVideoBitrate, keyframeIntervalInSeconds, setKeyframeIntervalInSeconds, transparent, setTransparent, scale, setScale, compositionWidth, compositionHeight }) => { const qualityOptions = useMemo132(() => getQualityOptions(videoBitrate, setVideoBitrate), [videoBitrate, setVideoBitrate]); const onTransparentChanged = useCallback130((e) => { setTransparent(e.target.checked); }, [setTransparent]); return /* @__PURE__ */ jsxs143("div", { style: tabContainer4, children: [ /* @__PURE__ */ jsx267(ScaleSetting, { scale, setScale, compositionWidth, compositionHeight }), renderMode !== "video" ? null : /* @__PURE__ */ jsxs143(Fragment49, { children: [ /* @__PURE__ */ jsxs143("div", { style: optionRow, children: [ /* @__PURE__ */ jsx267("div", { style: label5, children: "Quality" }), /* @__PURE__ */ jsx267("div", { style: rightRow, children: /* @__PURE__ */ jsx267(Combobox, { values: qualityOptions, selectedId: videoBitrate, title: "Quality" }) }) ] }), /* @__PURE__ */ jsx267(NumberSetting, { name: "Keyframe Interval", formatter: (v) => `${v}s`, min: 1, max: 300, step: 1, value: keyframeIntervalInSeconds, onValueChanged: setKeyframeIntervalInSeconds }), /* @__PURE__ */ jsxs143("div", { style: optionRow, children: [ /* @__PURE__ */ jsx267("div", { style: label5, children: "Transparent" }), /* @__PURE__ */ jsx267("div", { style: rightRow, children: /* @__PURE__ */ jsx267(Checkbox, { checked: transparent, onChange: onTransparentChanged, name: "transparent" }) }) ] }) ] }) ] }); }; // src/components/RenderModal/WebRenderModal.tsx import { jsx as jsx268, jsxs as jsxs144 } from "react/jsx-runtime"; var invalidCharacters2 = ["?", "*", "+", ":", "%"]; var isValidStillExtension2 = (extension, stillImageFormat) => { if (stillImageFormat === "jpeg" && extension === "jpg") { return true; } return extension === stillImageFormat; }; var validateOutnameForStill = ({ outName, stillImageFormat }) => { try { const extension = outName.substring(outName.lastIndexOf(".") + 1); const prefix = outName.substring(0, outName.lastIndexOf(".")); const hasDotAfterSlash = () => { const substrings = prefix.split("/"); for (const str of substrings) { if (str[0] === ".") { return true; } } return false; }; const hasInvalidChar = () => { return prefix.split("").some((char) => invalidCharacters2.includes(char)); }; if (prefix.length < 1) { throw new Error("The prefix must be at least 1 character long"); } if (prefix[0] === "." || hasDotAfterSlash()) { throw new Error("The output name must not start with a dot"); } if (hasInvalidChar()) { throw new Error("Filename can't contain the following characters: ?, *, +, %, :"); } if (!isValidStillExtension2(extension, stillImageFormat)) { throw new Error(`The extension ${extension} is not supported for still image format ${stillImageFormat}`); } return { valid: true }; } catch (err) { return { valid: false, error: err }; } }; var WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark, initialLogLevel, initialLicenseKey, initialStillImageFormat, initialDefaultOutName, initialScale, initialDelayRenderTimeout, initialMediaCacheSizeInBytes, initialContainer, initialVideoCodec, initialAudioCodec, initialAudioBitrate, initialVideoBitrate, initialHardwareAcceleration, initialKeyframeIntervalInSeconds, initialTransparent, initialMuted }) => { const context = useContext84(ResolvedCompositionContext); const { setSelectedModal } = useContext84(ModalsContext); const { setSidebarCollapsedState } = useContext84(SidebarContext); const { addClientStillJob, addClientVideoJob } = useContext84(RenderQueueContext); if (!context) { throw new Error("Should not be able to render without resolving comp first"); } const { resolved: { result: resolvedComposition }, unresolved: unresolvedComposition } = context; const [isVideo] = useState85(() => { return typeof resolvedComposition.durationInFrames === "undefined" ? true : resolvedComposition.durationInFrames > 1; }); const [renderMode, setRenderMode] = useState85(isVideo ? "video" : "still"); const [tab, setTab] = useState85("general"); const [imageFormat, setImageFormat] = useState85(() => initialStillImageFormat ?? "png"); const [frame2, setFrame] = useState85(() => initialFrame); const [logLevel, setLogLevel] = useState85(() => initialLogLevel); const [inputProps, setInputProps] = useState85(() => defaultProps); const [delayRenderTimeout, setDelayRenderTimeout] = useState85(initialDelayRenderTimeout ?? 30000); const [mediaCacheSizeInBytes, setMediaCacheSizeInBytes] = useState85(initialMediaCacheSizeInBytes); const [saving, setSaving] = useState85(false); const [codec, setCodec] = useState85(initialVideoCodec ?? "h264"); const [container61, setContainer] = useState85(initialContainer ?? "mp4"); const [audioCodec, setAudioCodec] = useState85(initialAudioCodec ?? "aac"); const [audioBitrate, setAudioBitrate] = useState85(initialAudioBitrate ?? "medium"); const [videoBitrate, setVideoBitrate] = useState85(initialVideoBitrate ?? "high"); const [hardwareAcceleration, setHardwareAcceleration] = useState85(initialHardwareAcceleration ?? "no-preference"); const [keyframeIntervalInSeconds, setKeyframeIntervalInSeconds] = useState85(initialKeyframeIntervalInSeconds ?? 5); const [startFrame, setStartFrame] = useState85(() => inFrameMark); const [endFrame, setEndFrame] = useState85(() => outFrameMark); const [transparent, setTransparent] = useState85(initialTransparent ?? false); const [muted, setMuted] = useState85(initialMuted ?? false); const [scale, setScale] = useState85(initialScale ?? 1); const [licenseKey, setLicenseKey] = useState85(initialLicenseKey); const encodableAudioCodecs = useEncodableAudioCodecs(container61); const encodableVideoCodecs = useEncodableVideoCodecs(container61); const effectiveAudioCodec = useMemo133(() => { if (encodableAudioCodecs.includes(audioCodec)) { return audioCodec; } return encodableAudioCodecs[0] ?? audioCodec; }, [audioCodec, encodableAudioCodecs]); const effectiveVideoCodec = useMemo133(() => { if (encodableVideoCodecs.includes(codec)) { return codec; } return encodableVideoCodecs[0] ?? codec; }, [codec, encodableVideoCodecs]); const finalEndFrame = useMemo133(() => { if (endFrame === null) { return resolvedComposition.durationInFrames - 1; } return Math.max(0, Math.min(resolvedComposition.durationInFrames - 1, endFrame)); }, [endFrame, resolvedComposition.durationInFrames]); const finalStartFrame = useMemo133(() => { if (startFrame === null) { return 0; } return Math.max(0, Math.min(finalEndFrame, startFrame)); }, [finalEndFrame, startFrame]); const [initialOutNameState] = useState85(() => { if (initialDefaultOutName) { return initialDefaultOutName; } return getDefaultOutLocation2({ compositionName: resolvedComposition.id, defaultExtension: renderMode === "still" ? imageFormat : isVideo ? container61 : imageFormat, type: "asset", compositionDefaultOutName: resolvedComposition.defaultOutName, clientSideRender: true }); }); const [outName, setOutName] = useState85(() => initialOutNameState); const setStillFormat = useCallback131((format) => { setImageFormat(format); setOutName((prev) => { const newFileName = getStringBeforeSuffix(prev) + "." + format; return newFileName; }); }, []); const setContainerFormat = useCallback131((newContainer) => { setContainer(newContainer); setAudioCodec(getDefaultAudioCodecForContainer(newContainer)); setOutName((prev) => { const newFileName = getStringBeforeSuffix(prev) + "." + newContainer; return newFileName; }); }, []); const onRenderModeChange = useCallback131((newMode) => { setRenderMode(newMode); if (newMode === "video") { setOutName((prev) => { const newFileName = getStringBeforeSuffix(prev) + "." + container61; return newFileName; }); } else if (newMode === "still") { setOutName((prev) => { const newFileName = getStringBeforeSuffix(prev) + "." + imageFormat; return newFileName; }); } }, [container61, imageFormat]); const renderTabOptions = useMemo133(() => { const options = [ { label: "Still", onClick: () => { onRenderModeChange("still"); }, key: "still", selected: renderMode === "still" } ]; if (resolvedComposition.durationInFrames > 1) { options.push({ label: "Video", onClick: () => { onRenderModeChange("video"); }, key: "video", selected: renderMode === "video" }); } return options; }, [renderMode, resolvedComposition.durationInFrames, onRenderModeChange]); const onFrameSetDirectly = useCallback131((newFrame) => { setFrame(newFrame); }, [setFrame]); const onFrameChanged = useCallback131((e) => { setFrame((q) => { const newFrame = parseFloat(e); if (Number.isNaN(newFrame)) { return q; } return newFrame; }); }, [setFrame]); const onOutNameChange = useCallback131((e) => { setOutName(e.target.value); }, []); const outnameValidation = useMemo133(() => { if (renderMode === "still") { return validateOutnameForStill({ outName, stillImageFormat: imageFormat }); } try { const extension = outName.substring(outName.lastIndexOf(".") + 1); const prefix = outName.substring(0, outName.lastIndexOf(".")); const hasDotAfterSlash = () => { const substrings = prefix.split("/"); for (const str of substrings) { if (str[0] === ".") { return true; } } return false; }; const hasInvalidChar = () => { return prefix.split("").some((char) => invalidCharacters2.includes(char)); }; if (prefix.length < 1) { throw new Error("The prefix must be at least 1 character long"); } if (prefix[0] === "." || hasDotAfterSlash()) { throw new Error("The output name must not start with a dot"); } if (hasInvalidChar()) { throw new Error("Filename can't contain the following characters: ?, *, +, %, :"); } if (extension !== container61) { throw new Error(`The extension ${extension} is not supported for container format ${container61}`); } return { valid: true }; } catch (err) { return { valid: false, error: err }; } }, [outName, imageFormat, renderMode, container61]); const onAddToQueue = useCallback131(() => { const compositionRef = { component: unresolvedComposition.component, calculateMetadata: unresolvedComposition.calculateMetadata ?? null, width: resolvedComposition.width, height: resolvedComposition.height, fps: resolvedComposition.fps, durationInFrames: resolvedComposition.durationInFrames, defaultProps: resolvedComposition.defaultProps }; if (renderMode === "still") { addClientStillJob({ type: "client-still", compositionId: resolvedComposition.id, outName, imageFormat, frame: frame2, inputProps, delayRenderTimeout, mediaCacheSizeInBytes, logLevel, licenseKey, scale }, compositionRef); } else { addClientVideoJob({ type: "client-video", compositionId: resolvedComposition.id, outName, container: container61, videoCodec: effectiveVideoCodec, audioCodec: effectiveAudioCodec, startFrame: finalStartFrame, endFrame: finalEndFrame, audioBitrate, videoBitrate, hardwareAcceleration, keyframeIntervalInSeconds, transparent, muted, inputProps, delayRenderTimeout, mediaCacheSizeInBytes, logLevel, licenseKey, scale }, compositionRef); } setSidebarCollapsedState({ left: null, right: "expanded" }); persistSelectedOptionsSidebarPanel2("renders"); optionsSidebarTabs.current?.selectRendersPanel(); setSelectedModal(null); }, [ renderMode, unresolvedComposition.component, unresolvedComposition.calculateMetadata, resolvedComposition.width, resolvedComposition.height, resolvedComposition.fps, resolvedComposition.durationInFrames, resolvedComposition.defaultProps, resolvedComposition.id, setSidebarCollapsedState, outName, imageFormat, frame2, inputProps, delayRenderTimeout, mediaCacheSizeInBytes, logLevel, licenseKey, container61, effectiveVideoCodec, effectiveAudioCodec, finalStartFrame, finalEndFrame, audioBitrate, videoBitrate, hardwareAcceleration, keyframeIntervalInSeconds, transparent, muted, setSelectedModal, addClientStillJob, addClientVideoJob, scale ]); return /* @__PURE__ */ jsxs144("div", { style: outerModalStyle, children: [ /* @__PURE__ */ jsx268(ModalHeader, { title: `Render ${resolvedComposition.id}` }), /* @__PURE__ */ jsxs144("div", { style: container59, children: [ /* @__PURE__ */ jsx268(SegmentedControl, { items: renderTabOptions, needsWrapping: false }), /* @__PURE__ */ jsx268("div", { style: flexer }), /* @__PURE__ */ jsxs144(Button, { autoFocus: true, onClick: onAddToQueue, style: buttonStyle7, disabled: !outnameValidation.valid, children: [ "Render ", renderMode, /* @__PURE__ */ jsx268(ShortcutHint, { keyToPress: "↵", cmdOrCtrl: true }) ] }) ] }), /* @__PURE__ */ jsx268("div", { style: container59, children: /* @__PURE__ */ jsx268(WebRendererExperimentalBadge, {}) }), /* @__PURE__ */ jsxs144("div", { style: horizontalLayout, children: [ /* @__PURE__ */ jsxs144("div", { style: leftSidebar, children: [ /* @__PURE__ */ jsxs144(VerticalTab, { style: horizontalTab, selected: tab === "general", onClick: () => setTab("general"), children: [ /* @__PURE__ */ jsx268("div", { style: iconContainer, children: /* @__PURE__ */ jsx268(FileIcon, { style: icon6 }) }), "General" ] }), /* @__PURE__ */ jsxs144(VerticalTab, { style: horizontalTab, selected: tab === "data", onClick: () => setTab("data"), children: [ /* @__PURE__ */ jsx268("div", { style: iconContainer, children: /* @__PURE__ */ jsx268(DataIcon, { style: icon6 }) }), "Input Props" ] }), /* @__PURE__ */ jsxs144(VerticalTab, { style: horizontalTab, selected: tab === "picture", onClick: () => setTab("picture"), children: [ /* @__PURE__ */ jsx268("div", { style: iconContainer, children: /* @__PURE__ */ jsx268(PicIcon, { style: icon6 }) }), "Picture" ] }), renderMode === "video" ? /* @__PURE__ */ jsxs144(VerticalTab, { style: horizontalTab, selected: tab === "audio", onClick: () => setTab("audio"), children: [ /* @__PURE__ */ jsx268("div", { style: iconContainer, children: /* @__PURE__ */ jsx268(AudioIcon, { style: icon6 }) }), "Audio" ] }) : null, /* @__PURE__ */ jsxs144(VerticalTab, { style: horizontalTab, selected: tab === "advanced", onClick: () => setTab("advanced"), children: [ /* @__PURE__ */ jsx268("div", { style: iconContainer, children: /* @__PURE__ */ jsx268(GearIcon, { style: icon6 }) }), "Other" ] }), /* @__PURE__ */ jsxs144(VerticalTab, { style: horizontalTab, selected: tab === "license", onClick: () => setTab("license"), children: [ /* @__PURE__ */ jsx268("div", { style: iconContainer, children: /* @__PURE__ */ jsx268(CertificateIcon, { style: icon6 }) }), "License" ] }) ] }), /* @__PURE__ */ jsx268("div", { style: optionsPanel, className: VERTICAL_SCROLLBAR_CLASSNAME, children: tab === "general" ? /* @__PURE__ */ jsx268(WebRenderModalBasic, { renderMode, resolvedComposition, imageFormat, setStillFormat, frame: frame2, onFrameChanged, onFrameSetDirectly, container: container61, setContainerFormat, setCodec, encodableVideoCodecs, effectiveVideoCodec, startFrame: finalStartFrame, setStartFrame, endFrame: finalEndFrame, setEndFrame, outName, onOutNameChange, validationMessage: outnameValidation.valid ? null : outnameValidation.error.message, logLevel, setLogLevel }) : tab === "data" ? /* @__PURE__ */ jsx268(DataEditor, { defaultProps: inputProps, setDefaultProps: setInputProps, unresolvedComposition, mayShowSaveButton: false, propsEditType: "input-props", saving, setSaving, readOnlyStudio: false }) : tab === "picture" ? /* @__PURE__ */ jsx268(WebRenderModalPicture, { renderMode, videoBitrate, setVideoBitrate, keyframeIntervalInSeconds, setKeyframeIntervalInSeconds, transparent, setTransparent, scale, setScale, compositionWidth: resolvedComposition.width, compositionHeight: resolvedComposition.height }) : tab === "audio" ? /* @__PURE__ */ jsx268(WebRenderModalAudio, { muted, setMuted, audioCodec, setAudioCodec, audioBitrate, setAudioBitrate, container: container61, encodableCodecs: encodableAudioCodecs, effectiveAudioCodec }) : tab === "advanced" ? /* @__PURE__ */ jsx268(WebRenderModalAdvanced, { renderMode, delayRenderTimeout, setDelayRenderTimeout, mediaCacheSizeInBytes, setMediaCacheSizeInBytes, hardwareAcceleration, setHardwareAcceleration }) : /* @__PURE__ */ jsx268(WebRenderModalLicense, { licenseKey, setLicenseKey, initialPublicLicenseKey: initialLicenseKey }) }) ] }) ] }); }; var WebRenderModalWithLoader = (props) => { return /* @__PURE__ */ jsx268(DismissableModal, { children: /* @__PURE__ */ jsx268(ResolveCompositionBeforeModal, { compositionId: props.compositionId, children: /* @__PURE__ */ jsx268(WebRenderModal, { ...props }) }) }); }; // src/components/UpdateModal/UpdateModal.tsx import { useCallback as useCallback134, useMemo as useMemo135 } from "react"; // src/components/CopyButton.tsx import { useCallback as useCallback132, useEffect as useEffect83, useState as useState86 } from "react"; import { jsx as jsx269, jsxs as jsxs145 } from "react/jsx-runtime"; var iconStyle8 = { width: 16, height: 16, color: "white" }; var buttonContainerStyle = { display: "flex", minWidth: "114px" }; var copyIcon = /* @__PURE__ */ jsx269("svg", { "aria-hidden": "true", focusable: "false", "data-prefix": "far", "data-icon": "clipboard", className: "svg-inline--fa fa-clipboard fa-w-12", role: "img", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 384 512", style: iconStyle8, children: /* @__PURE__ */ jsx269("path", { fill: "currentColor", d: "M336 64h-80c0-35.3-28.7-64-64-64s-64 28.7-64 64H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48zM192 40c13.3 0 24 10.7 24 24s-10.7 24-24 24-24-10.7-24-24 10.7-24 24-24zm144 418c0 3.3-2.7 6-6 6H54c-3.3 0-6-2.7-6-6V118c0-3.3 2.7-6 6-6h42v36c0 6.6 5.4 12 12 12h168c6.6 0 12-5.4 12-12v-36h42c3.3 0 6 2.7 6 6z" }) }); var labelStyle5 = { fontSize: 14 }; var CopyButton = ({ textToCopy, label: label12, labelWhenCopied }) => { const [copied, setCopied] = useState86(false); const onClick = useCallback132(() => { copyText(textToCopy).then(() => { setCopied(Date.now()); }).catch((err) => { showNotification(`Could not copy: ${err.message}`, 2000); }); }, [textToCopy]); useEffect83(() => { if (!copied) { return; } const to = setTimeout(() => setCopied(false), 2000); return () => clearTimeout(to); }, [copied]); return /* @__PURE__ */ jsxs145(Button, { onClick, buttonContainerStyle, children: [ copyIcon, /* @__PURE__ */ jsx269(Spacing, { x: 1.5 }), " ", /* @__PURE__ */ jsx269("span", { style: labelStyle5, children: copied ? labelWhenCopied : label12 }) ] }); }; // src/components/UpdateModal/OpenIssueButton.tsx import { useCallback as useCallback133, useMemo as useMemo134, useState as useState87 } from "react"; import { jsx as jsx270 } from "react/jsx-runtime"; var svgStyle3 = { width: "11px", height: "11px" }; var buttonStyle8 = { border: "none", width: "24px", height: "24px", display: "flex", justifyContent: "center", alignItems: "center" }; var OpenIssueButton = ({ link: link4 }) => { const [hovered, setHovered] = useState87(false); const buttonTooltip = `Open GitHub issue in new Tab`; const handleClick = useCallback133(() => { window.open(link4, "_blank"); }, [link4]); const svgFillColor = useMemo134(() => { return hovered ? "white" : LIGHT_TEXT; }, [hovered]); const openInEditorSvg = /* @__PURE__ */ jsx270("svg", { viewBox: "0 0 512 512", style: svgStyle3, children: /* @__PURE__ */ jsx270("path", { fill: svgFillColor, d: "M320 0c-17.7 0-32 14.3-32 32s14.3 32 32 32h82.7L201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L448 109.3V192c0 17.7 14.3 32 32 32s32-14.3 32-32V32c0-17.7-14.3-32-32-32H320zM80 32C35.8 32 0 67.8 0 112V432c0 44.2 35.8 80 80 80H400c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32V432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z" }) }); const onPointerEnter = useCallback133(() => { setHovered(true); }, []); const onPointerLeave = useCallback133(() => { setHovered(false); }, []); return /* @__PURE__ */ jsx270("button", { title: buttonTooltip, type: "button", onPointerEnter, onPointerLeave, style: buttonStyle8, onClick: handleClick, children: openInEditorSvg }); }; // src/components/KnownBugs.tsx import { jsx as jsx271, jsxs as jsxs146 } from "react/jsx-runtime"; var container61 = { display: "flex", flexDirection: "row", alignItems: "center" }; var text4 = { fontSize: 14, flex: 1 }; var KnownBugs = ({ bugs }) => { const bugElements = bugs.map((bug) => { return /* @__PURE__ */ jsxs146("div", { style: container61, children: [ /* @__PURE__ */ jsxs146("div", { style: text4, children: [ "\uD83E\uDEB2 ", bug.title ] }), /* @__PURE__ */ jsx271(OpenIssueButton, { link: bug.link }) ] }, bug.description + bug.link); }); return /* @__PURE__ */ jsx271("div", { children: bugElements }); }; // src/components/UpdateModal/UpdateModal.tsx import { jsx as jsx272, jsxs as jsxs147, Fragment as Fragment50 } from "react/jsx-runtime"; var container62 = { padding: 20, paddingTop: 0 }; var text5 = { fontSize: 14 }; var title7 = { paddingTop: 12, paddingBottom: 8, ...text5 }; var code = { background: SELECTED_BACKGROUND, padding: "12px 10px", fontSize: 14, marginTop: 10, marginBottom: 10 }; var link4 = { fontWeight: "bold", color: BLUE, textDecoration: "none", ...text5 }; var commands = { npm: "npx remotion upgrade", yarn: "yarn remotion upgrade", pnpm: "pnpm exec remotion upgrade", bun: "bun remotion upgrade", unknown: "npx remotion upgrade" }; var UpdateModal = ({ info, knownBugs }) => { const hasKnownBugs = useMemo135(() => { return knownBugs && knownBugs?.length > 0; }, [knownBugs]); const command = commands[info.packageManager]; const onClick = useCallback134(() => { copyText(command).catch((err) => { showNotification(`Could not copy: ${err.message}`, 2000); }); }, [command]); return /* @__PURE__ */ jsxs147(DismissableModal, { children: [ /* @__PURE__ */ jsx272(ModalHeader, { title: "Update available" }), /* @__PURE__ */ jsxs147("div", { style: container62, children: [ hasKnownBugs ? /* @__PURE__ */ jsxs147(Fragment50, { children: [ /* @__PURE__ */ jsxs147("div", { style: title7, children: [ "The currently installed version ", info.currentVersion, " has the following known bugs:" ] }), /* @__PURE__ */ jsx272(KnownBugs, { bugs: knownBugs }), /* @__PURE__ */ jsx272("div", { style: { height: "20px" } }), /* @__PURE__ */ jsx272("div", { style: text5, children: "To upgrade, run the following command:" }) ] }) : /* @__PURE__ */ jsx272("div", { style: title7, children: "A new update for Remotion is available! Run the following command:" }), /* @__PURE__ */ jsxs147(Row, { align: "center", children: [ /* @__PURE__ */ jsx272(Flex, { children: /* @__PURE__ */ jsx272("pre", { onClick, style: code, children: command }) }), /* @__PURE__ */ jsx272(Spacing, { x: 1 }), /* @__PURE__ */ jsx272(CopyButton, { textToCopy: command, label: "Copy", labelWhenCopied: "Copied!" }) ] }), /* @__PURE__ */ jsxs147("div", { style: text5, children: [ "This will upgrade Remotion from ", info.currentVersion, " to", " ", info.latestVersion, "." ] }), /* @__PURE__ */ jsxs147("div", { style: text5, children: [ "Read the", " ", /* @__PURE__ */ jsx272("a", { style: link4, target: "_blank", href: "https://github.com/remotion-dev/remotion/releases", children: "Release notes" }), " ", "to know what", "'s", " new in Remotion." ] }) ] }) ] }); }; // src/components/Modals.tsx import { jsx as jsx273, jsxs as jsxs148, Fragment as Fragment51 } from "react/jsx-runtime"; var Modals = ({ readOnlyStudio }) => { const { selectedModal: modalContextType } = useContext85(ModalsContext); const canRender = useContext85(StudioServerConnectionCtx).previewServerState.type === "connected"; return /* @__PURE__ */ jsxs148(Fragment51, { children: [ modalContextType && modalContextType.type === "duplicate-comp" && /* @__PURE__ */ jsx273(DuplicateComposition, { compositionType: modalContextType.compositionType, compositionId: modalContextType.compositionId }), modalContextType && modalContextType.type === "delete-comp" && /* @__PURE__ */ jsx273(DeleteComposition, { compositionId: modalContextType.compositionId }), modalContextType && modalContextType.type === "rename-comp" && /* @__PURE__ */ jsx273(RenameComposition, { compositionId: modalContextType.compositionId }), modalContextType && modalContextType.type === "input-props-override" && /* @__PURE__ */ jsx273(OverrideInputPropsModal, {}), modalContextType && modalContextType.type === "web-render" && /* @__PURE__ */ jsx273(WebRenderModalWithLoader, { ...modalContextType }), modalContextType && canRender && modalContextType.type === "server-render" && /* @__PURE__ */ jsx273(RenderModalWithLoader, { initialFrame: modalContextType.initialFrame, initialDarkMode: modalContextType.initialDarkMode, compositionId: modalContextType.compositionId, initialVideoImageFormat: modalContextType.initialVideoImageFormat, initialJpegQuality: modalContextType.initialJpegQuality, initialScale: modalContextType.initialScale, initialLogLevel: modalContextType.initialLogLevel, initialOffthreadVideoCacheSizeInBytes: modalContextType.initialOffthreadVideoCacheSizeInBytes, initialOffthreadVideoThreads: modalContextType.initialOffthreadVideoThreads, initialMediaCacheSizeInBytes: modalContextType.initialMediaCacheSizeInBytes, initialConcurrency: modalContextType.initialConcurrency, maxConcurrency: modalContextType.maxConcurrency, minConcurrency: modalContextType.minConcurrency, initialStillImageFormat: modalContextType.initialStillImageFormat, initialMuted: modalContextType.initialMuted, initialEnforceAudioTrack: modalContextType.initialEnforceAudioTrack, initialProResProfile: modalContextType.initialProResProfile, initialx264Preset: modalContextType.initialx264Preset, initialPixelFormat: modalContextType.initialPixelFormat, initialAudioBitrate: modalContextType.initialAudioBitrate, initialVideoBitrate: modalContextType.initialVideoBitrate, initialEveryNthFrame: modalContextType.initialEveryNthFrame, initialNumberOfGifLoops: modalContextType.initialNumberOfGifLoops, initialDelayRenderTimeout: modalContextType.initialDelayRenderTimeout, initialEnvVariables: modalContextType.initialEnvVariables, initialDisableWebSecurity: modalContextType.initialDisableWebSecurity, initialGl: modalContextType.initialOpenGlRenderer, initialHeadless: modalContextType.initialHeadless, initialIgnoreCertificateErrors: modalContextType.initialIgnoreCertificateErrors, initialEncodingBufferSize: modalContextType.initialEncodingBufferSize, initialEncodingMaxRate: modalContextType.initialEncodingMaxRate, initialUserAgent: modalContextType.initialUserAgent, initialColorSpace: modalContextType.initialColorSpace, initialMultiProcessOnLinux: modalContextType.initialMultiProcessOnLinux, initialRepro: modalContextType.initialRepro, initialBeep: modalContextType.initialBeep, initialForSeamlessAacConcatenation: modalContextType.initialForSeamlessAacConcatenation, defaultProps: modalContextType.defaultProps, inFrameMark: modalContextType.inFrameMark, outFrameMark: modalContextType.outFrameMark, defaultConfigurationAudioCodec: modalContextType.defaultConfigurationAudioCodec, defaultConfigurationVideoCodec: modalContextType.defaultConfigurationVideoCodec, renderTypeOfLastRender: modalContextType.renderTypeOfLastRender, defaultMetadata: modalContextType.defaulMetadata, initialHardwareAcceleration: modalContextType.initialHardwareAcceleration, initialChromeMode: modalContextType.initialChromeMode, renderDefaults: modalContextType.renderDefaults }), modalContextType && modalContextType.type === "render-progress" && /* @__PURE__ */ jsx273(RenderStatusModal, { jobId: modalContextType.jobId }), modalContextType && modalContextType.type === "update" && /* @__PURE__ */ jsx273(UpdateModal, { info: modalContextType.info, knownBugs: modalContextType.knownBugs }), modalContextType && modalContextType.type === "install-packages" && /* @__PURE__ */ jsx273(InstallPackageModal, { packageManager: modalContextType.packageManager }), modalContextType && modalContextType.type === "quick-switcher" && /* @__PURE__ */ jsx273(QuickSwitcher_default, { readOnlyStudio, invocationTimestamp: modalContextType.invocationTimestamp, initialMode: modalContextType.mode }), process.env.ASK_AI_ENABLED && /* @__PURE__ */ jsx273(AskAiModal, {}) ] }); }; // src/components/Editor.tsx import { jsx as jsx274, jsxs as jsxs149 } from "react/jsx-runtime"; var background2 = { backgroundColor: BACKGROUND, display: "flex", width: "100%", height: "100%", flexDirection: "column", position: "absolute" }; var DEFAULT_BUFFER_STATE_DELAY_IN_MILLISECONDS = 300; var BUFFER_STATE_DELAY_IN_MILLISECONDS = typeof process.env.BUFFER_STATE_DELAY_IN_MILLISECONDS === "undefined" || process.env.BUFFER_STATE_DELAY_IN_MILLISECONDS === null ? DEFAULT_BUFFER_STATE_DELAY_IN_MILLISECONDS : Number(process.env.BUFFER_STATE_DELAY_IN_MILLISECONDS); var Editor = ({ Root, readOnlyStudio }) => { const size4 = PlayerInternals19.useElementSize(drawRef, { triggerOnWindowResize: false, shouldApplyCssTransforms: true }); useEffect84(() => { if (readOnlyStudio) { return; } const listenToChanges = (e) => { if (window.remotion_unsavedProps) { e.returnValue = "Are you sure you want to leave?"; } }; window.addEventListener("beforeunload", listenToChanges); return () => { window.removeEventListener("beforeunload", listenToChanges); }; }, [readOnlyStudio]); const [canvasMounted, setCanvasMounted] = React177.useState(false); const onMounted = useCallback135(() => { setCanvasMounted(true); }, []); const value = useMemo136(() => { if (!size4) { return null; } return { type: "canvas-size", canvasSize: size4 }; }, [size4]); const MemoRoot = useMemo136(() => { return React177.memo(Root); }, [Root]); return /* @__PURE__ */ jsx274(HigherZIndex, { onEscape: noop, onOutsideClick: noop, children: /* @__PURE__ */ jsxs149(TimelineZoomContext, { children: [ /* @__PURE__ */ jsx274(Internals61.CurrentScaleContext.Provider, { value, children: /* @__PURE__ */ jsxs149("div", { style: background2, children: [ canvasMounted ? /* @__PURE__ */ jsx274(MemoRoot, {}) : null, /* @__PURE__ */ jsxs149(Internals61.CanUseRemotionHooksProvider, { children: [ /* @__PURE__ */ jsx274(EditorContent, { readOnlyStudio, children: /* @__PURE__ */ jsx274(TopPanel, { drawRef, bufferStateDelayInMilliseconds: BUFFER_STATE_DELAY_IN_MILLISECONDS, onMounted, readOnlyStudio }) }), /* @__PURE__ */ jsx274(GlobalKeybindings, {}) ] }) ] }) }), /* @__PURE__ */ jsx274(Modals, { readOnlyStudio }), /* @__PURE__ */ jsx274(NotificationCenter, {}) ] }) }); }; // src/components/EditorContexts.tsx import { PlayerInternals as PlayerInternals20 } from "@remotion/player"; // src/state/preview-size.tsx import { useCallback as useCallback136, useContext as useContext86, useMemo as useMemo137, useState as useState88 } from "react"; import { Internals as Internals62 } from "remotion"; import { jsx as jsx275 } from "react/jsx-runtime"; var key5 = "remotion.previewSize"; var persistPreviewSizeOption = (option) => { localStorage.setItem(key5, JSON.stringify(option)); }; var loadPreviewSizeOption = () => { const item2 = localStorage.getItem(key5); if (item2 === null) { return { size: "auto", translation: { x: 0, y: 0 } }; } return JSON.parse(item2); }; var PreviewSizeProvider = ({ children }) => { const [size4, setSizeState] = useState88(() => loadPreviewSizeOption()); const [translation, setTranslation] = useState88(() => { return { x: 0, y: 0 }; }); const { editorZoomGestures } = useContext86(EditorZoomGesturesContext); const setSize = useCallback136((newValue) => { setSizeState((prevState) => { const newVal = newValue(prevState); persistPreviewSizeOption(newVal); return newVal; }); }, []); const previewSizeCtx = useMemo137(() => { return { size: editorZoomGestures ? size4 : { size: size4.size, translation: { x: 0, y: 0 } }, setSize, translation, setTranslation }; }, [editorZoomGestures, size4, setSize, translation]); return /* @__PURE__ */ jsx275(Internals62.PreviewSizeContext.Provider, { value: previewSizeCtx, children }); }; // src/components/CheckerboardProvider.tsx import { useCallback as useCallback137, useMemo as useMemo138, useState as useState89 } from "react"; import { jsx as jsx276 } from "react/jsx-runtime"; var CheckerboardProvider = ({ children }) => { const [checkerboard, setCheckerboardState] = useState89(() => loadCheckerboardOption()); const setCheckerboard = useCallback137((newValue) => { setCheckerboardState((prevState) => { const newVal = newValue(prevState); persistCheckerboardOption(newVal); return newVal; }); }, []); const checkerboardCtx = useMemo138(() => { return { checkerboard, setCheckerboard }; }, [checkerboard, setCheckerboard]); return /* @__PURE__ */ jsx276(CheckerboardContext.Provider, { value: checkerboardCtx, children }); }; // src/components/MediaVolumeProvider.tsx import { useMemo as useMemo139, useState as useState90 } from "react"; import { Internals as Internals63 } from "remotion"; import { jsx as jsx277 } from "react/jsx-runtime"; var MediaVolumeProvider = ({ children }) => { const [mediaMuted, setMediaMuted] = useState90(() => loadMuteOption()); const [mediaVolume, setMediaVolume] = useState90(1); const mediaVolumeContextValue = useMemo139(() => { return { mediaMuted, mediaVolume }; }, [mediaMuted, mediaVolume]); const setMediaVolumeContextValue = useMemo139(() => { return { setMediaMuted, setMediaVolume }; }, []); return /* @__PURE__ */ jsx277(Internals63.MediaVolumeContext.Provider, { value: mediaVolumeContextValue, children: /* @__PURE__ */ jsx277(Internals63.SetMediaVolumeContext.Provider, { value: setMediaVolumeContextValue, children }) }); }; // src/components/ModalsProvider.tsx import { useMemo as useMemo140, useState as useState91 } from "react"; import { jsx as jsx278 } from "react/jsx-runtime"; var ModalsProvider = ({ children }) => { const [modalContextType, setModalContextType] = useState91(null); const modalsContext = useMemo140(() => { return { selectedModal: modalContextType, setSelectedModal: setModalContextType }; }, [modalContextType]); return /* @__PURE__ */ jsx278(ModalsContext.Provider, { value: modalsContext, children }); }; // src/components/RenderQueue/ClientRenderQueueProcessor.tsx import { renderMediaOnWeb, renderStillOnWeb } from "@remotion/web-renderer"; import { useCallback as useCallback138, useContext as useContext87, useEffect as useEffect85 } from "react"; var downloadBlob = (blob, filename) => { const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; const cleanFilename = filename.includes("/") ? filename.substring(filename.lastIndexOf("/") + 1) : filename; a.download = cleanFilename; a.click(); URL.revokeObjectURL(url); }; var ClientRenderQueueProcessor = () => { const { getAbortController: getAbortController2, getCompositionForJob: getCompositionForJob2, updateClientJobProgress, markClientJobDone, markClientJobFailed, markClientJobCancelled, setProcessJobCallback } = useContext87(RenderQueueContext); const processStillJob = useCallback138(async (job, signal) => { const compositionRef = getCompositionForJob2(job.id); if (!compositionRef) { throw new Error(`Composition not found for job ${job.id}`); } const { blob } = await renderStillOnWeb({ composition: { component: compositionRef.component, width: compositionRef.width, height: compositionRef.height, fps: compositionRef.fps, durationInFrames: compositionRef.durationInFrames, defaultProps: compositionRef.defaultProps, calculateMetadata: compositionRef.calculateMetadata ?? undefined, id: job.compositionId }, frame: job.frame, imageFormat: job.imageFormat, inputProps: job.inputProps, delayRenderTimeoutInMilliseconds: job.delayRenderTimeout, mediaCacheSizeInBytes: job.mediaCacheSizeInBytes, logLevel: job.logLevel, licenseKey: job.licenseKey ?? undefined, scale: job.scale, signal }); return { getBlob: () => Promise.resolve(blob), width: compositionRef.width, height: compositionRef.height }; }, [getCompositionForJob2]); const processVideoJob = useCallback138(async (job, signal, onProgress) => { const compositionRef = getCompositionForJob2(job.id); if (!compositionRef) { throw new Error(`Composition not found for job ${job.id}`); } const totalFrames = job.endFrame - job.startFrame + 1; const { getBlob } = await renderMediaOnWeb({ composition: { component: compositionRef.component, width: compositionRef.width, height: compositionRef.height, fps: compositionRef.fps, durationInFrames: compositionRef.durationInFrames, defaultProps: compositionRef.defaultProps, calculateMetadata: compositionRef.calculateMetadata ?? undefined, id: job.compositionId }, inputProps: job.inputProps, delayRenderTimeoutInMilliseconds: job.delayRenderTimeout, mediaCacheSizeInBytes: job.mediaCacheSizeInBytes, logLevel: job.logLevel, videoCodec: job.videoCodec, audioCodec: job.audioCodec, audioBitrate: job.audioBitrate, container: job.container, videoBitrate: job.videoBitrate, hardwareAcceleration: job.hardwareAcceleration, keyframeIntervalInSeconds: job.keyframeIntervalInSeconds, frameRange: [job.startFrame, job.endFrame], transparent: job.transparent, muted: job.muted, scale: job.scale, signal, onProgress: (progress) => { onProgress(job.id, { renderedFrames: progress.renderedFrames, encodedFrames: progress.encodedFrames, totalFrames }); }, outputTarget: "web-fs", licenseKey: job.licenseKey ?? undefined }); return { getBlob, width: compositionRef.width, height: compositionRef.height }; }, [getCompositionForJob2]); const processJob = useCallback138(async (job) => { const abortController = getAbortController2(job.id); try { let result; if (job.type === "client-still") { result = await processStillJob(job, abortController.signal); } else if (job.type === "client-video") { result = await processVideoJob(job, abortController.signal, updateClientJobProgress); } else { throw new Error(`Unknown job type`); } const blob = await result.getBlob(); downloadBlob(blob, job.outName); const metadata = { width: result.width, height: result.height, sizeInBytes: blob.size }; markClientJobDone(job.id, result.getBlob, metadata); } catch (err) { if (abortController.signal.aborted) { markClientJobCancelled(job.id); } else { markClientJobFailed(job.id, err); } } }, [ getAbortController2, processStillJob, processVideoJob, updateClientJobProgress, markClientJobDone, markClientJobFailed, markClientJobCancelled ]); useEffect85(() => { setProcessJobCallback(processJob); return () => setProcessJobCallback(null); }, [processJob, setProcessJobCallback]); return null; }; // src/components/SetTimelineInOutProvider.tsx import { useEffect as useEffect86, useMemo as useMemo141, useState as useState92 } from "react"; // src/state/marks.ts var localStorageKey5 = () => `remotion.editor.marksv2`; var persistMarks = (marks) => { localStorage.setItem(localStorageKey5(), JSON.stringify(marks)); }; var loadMarks = () => { const item2 = localStorage.getItem(localStorageKey5()); if (item2 === null) { return {}; } return JSON.parse(item2); }; // src/components/SetTimelineInOutProvider.tsx import { jsx as jsx279 } from "react/jsx-runtime"; var SetTimelineInOutProvider = ({ children }) => { const [inAndOutFrames, setInAndOutFrames] = useState92(() => loadMarks()); const setTimelineInOutContextValue = useMemo141(() => { return { setInAndOutFrames }; }, []); useEffect86(() => { persistMarks(inAndOutFrames); }, [inAndOutFrames]); return /* @__PURE__ */ jsx279(TimelineInOutContext.Provider, { value: inAndOutFrames, children: /* @__PURE__ */ jsx279(SetTimelineInOutContext.Provider, { value: setTimelineInOutContextValue, children }) }); }; // src/components/ShowGuidesProvider.tsx import { useCallback as useCallback139, useMemo as useMemo142, useRef as useRef47, useState as useState93 } from "react"; import { jsx as jsx280 } from "react/jsx-runtime"; var ShowGuidesProvider = ({ children }) => { const [guidesList, setGuidesList] = useState93(() => loadGuidesList()); const [selectedGuideId, setSelectedGuideId] = useState93(null); const [hoveredGuideId, setHoveredGuideId] = useState93(null); const [editorShowGuides, setEditorShowGuidesState] = useState93(() => loadEditorShowGuidesOption()); const shouldCreateGuideRef = useRef47(false); const shouldDeleteGuideRef = useRef47(false); const setEditorShowGuides = useCallback139((newValue) => { setEditorShowGuidesState((prevState) => { const newVal = newValue(prevState); persistEditorShowGuidesOption(newVal); return newVal; }); }, []); const editorShowGuidesCtx = useMemo142(() => { return { editorShowGuides, setEditorShowGuides, guidesList, setGuidesList, selectedGuideId, setSelectedGuideId, shouldCreateGuideRef, shouldDeleteGuideRef, hoveredGuideId, setHoveredGuideId }; }, [ editorShowGuides, setEditorShowGuides, guidesList, selectedGuideId, hoveredGuideId ]); return /* @__PURE__ */ jsx280(EditorShowGuidesContext.Provider, { value: editorShowGuidesCtx, children }); }; // src/components/ShowRulersProvider.tsx import { useCallback as useCallback140, useMemo as useMemo143, useState as useState94 } from "react"; import { jsx as jsx281 } from "react/jsx-runtime"; var ShowRulersProvider = ({ children }) => { const [editorShowRulers, setEditorShowRulersState] = useState94(() => loadEditorShowRulersOption()); const setEditorShowRulers = useCallback140((newValue) => { setEditorShowRulersState((prevState) => { const newVal = newValue(prevState); persistEditorShowRulersOption(newVal); return newVal; }); }, []); const editorShowRulersCtx = useMemo143(() => { return { editorShowRulers, setEditorShowRulers }; }, [editorShowRulers, setEditorShowRulers]); return /* @__PURE__ */ jsx281(EditorShowRulersContext.Provider, { value: editorShowRulersCtx, children }); }; // src/components/ZoomGesturesProvider.tsx import { useCallback as useCallback141, useMemo as useMemo144, useState as useState95 } from "react"; import { jsx as jsx282 } from "react/jsx-runtime"; var ZoomGesturesProvider = ({ children }) => { const [editorZoomGestures, setEditorZoomGesturesState] = useState95(() => loadEditorZoomGesturesOption()); const setEditorZoomGestures = useCallback141((newValue) => { setEditorZoomGesturesState((prevState) => { const newVal = newValue(prevState); persistEditorZoomGesturesOption(newVal); return newVal; }); }, []); const editorZoomGesturesCtx = useMemo144(() => { return { editorZoomGestures, setEditorZoomGestures }; }, [editorZoomGestures, setEditorZoomGestures]); return /* @__PURE__ */ jsx282(EditorZoomGesturesContext.Provider, { value: editorZoomGesturesCtx, children }); }; // src/components/EditorContexts.tsx import { jsx as jsx283, jsxs as jsxs150 } from "react/jsx-runtime"; var EditorContexts = ({ children, readOnlyStudio }) => { return /* @__PURE__ */ jsx283(ZodProvider, { children: /* @__PURE__ */ jsx283(VisualControlsProvider, { children: /* @__PURE__ */ jsx283(PreviewServerConnection, { readOnlyStudio, children: /* @__PURE__ */ jsxs150(RenderQueueContextProvider, { children: [ /* @__PURE__ */ jsx283(ClientRenderQueueProcessor, {}), /* @__PURE__ */ jsx283(KeybindingContextProvider, { children: /* @__PURE__ */ jsx283(CheckerboardProvider, { children: /* @__PURE__ */ jsx283(ZoomGesturesProvider, { children: /* @__PURE__ */ jsx283(ShowRulersProvider, { children: /* @__PURE__ */ jsx283(ShowGuidesProvider, { children: /* @__PURE__ */ jsx283(PreviewSizeProvider, { children: /* @__PURE__ */ jsx283(ModalsProvider, { children: /* @__PURE__ */ jsx283(MediaVolumeProvider, { children: /* @__PURE__ */ jsx283(PlayerInternals20.PlayerEmitterProvider, { currentPlaybackRate: null, children: /* @__PURE__ */ jsx283(SidebarContextProvider, { children: /* @__PURE__ */ jsx283(FolderContextProvider, { children: /* @__PURE__ */ jsx283(HighestZIndexProvider, { children: /* @__PURE__ */ jsx283(SetTimelineInOutProvider, { children }) }) }) }) }) }) }) }) }) }) }) }) }) ] }) }) }) }); }; // src/components/Notifications/ServerDisconnected.tsx import { useContext as useContext88 } from "react"; import { jsx as jsx284, jsxs as jsxs151 } from "react/jsx-runtime"; var container63 = { position: "fixed", justifyContent: "flex-end", alignItems: "flex-start", display: "flex", width: "100%", height: "100%", flexDirection: "column", padding: 30, pointerEvents: "none", backgroundColor: "transparent", fontFamily: "SF Pro, Arial, Helvetica, sans-serif" }; var message = { backgroundColor: "#e74c3c", color: "white", paddingLeft: 20, paddingRight: 20, paddingTop: 12, paddingBottom: 12, borderRadius: 4, boxShadow: "0 2px 4px rgba(0, 0, 0, 0.4)", lineHeight: 1.5 }; var inlineCode = { fontSize: 16, fontFamily: "monospace" }; var pageIsGoingToReload = false; window.addEventListener("beforeunload", () => { pageIsGoingToReload = true; }); var ServerDisconnected = () => { const { previewServerState: ctx } = useContext88(StudioServerConnectionCtx); const fav = document.getElementById("__remotion_favicon"); if (ctx.type !== "disconnected") { fav.setAttribute("href", "/favicon.ico"); return null; } if (pageIsGoingToReload) { return null; } const base64Favicon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAARiSURBVHgB7d1NThRBFAfw/2tGgru5gXMD8QZ4AmVjAi6kN0TiAm8gnkBcGARNumcx4E48Ae0JaE9gewLHlQSZelaNgyHGL/RVd1X3+y10RQL58+rVx1QBKKWUUkoppZRSSimllFJKKVUjQs32stEiJcktZiwxzKL9Fvqzb6S0/44JVBKbtwa9aj29U6JjagtkJzsYzBEyBi9d5utsQIULCcxvelgo03R5jBarJZCXw/17E+bt82r4Hy4gtuEQekUbK8h7IHvDV5vMZht+VAlRfmYw3EhXKrSA10Ce2X7RIzpGDVzlgM1wPb2bI2JeA9nN99/b/waoV+XCmTAex1g13gLZyUZrdjjJ0CAbTB5bMN4C2ctHxwxaRABiCsZLIG6KmxC/R2BiCCaBBwn4NgJk10B2GOWj3Wz/IQLlpUL28oOjyy4AG1AZppuhVYt4hWRZ1o8gDGc6rL4YHjxCQMQDOcNCEI38bxnmLTc9d30PARAPZAITZP/4A1ctx3bjcw0NEw8koeQ64tRnu25qeggTb+q2/BmRcyv9K7yw3MTOsmiFuLMOtICblJzS5+Mm+opoIBOgFYHMuL5yVHcoooFQuwJxag9FNJCIG/rv1BqKaCDfzshbqbZQxAJxK3SJI9qATUPJstdef0axQGJbof+jwRc6eQ2PxAIx4DZXx3duSrybv3oCTyR7yACdYR762sIXDKQbFfId4ZGPJi8YCA3QLf05YvF+IhYIEV1Dx9hNu8XdbLQFQV6OcDuFaFNyKiwWiJ19DNBN/VN8XoMQrRABDFqCEJ32CiBisT08rRAZAwjRQAKjgcgQO+rVQARMb3gJ0UAkML+DEA1EwAQkdkNMcnOx1Zcxf8V9ol7y88GSm4tdDKRy1xsgSIes/0BM4ndN5HZ7OzZkMePperqSQ5jkkFWhO6r76WrYJ4b2N+YTumF60QeeSG6/d2DI4rHvW1eCPaT9Q9YZw/sVOMlZVoUWM8zpg/Su97dVJGdZFVrKhbFR05MdYoFcwWmF1rF9kbG8UeP7KaI3qBp628QTHrueUccwdZHw/ZDWNHY7tU1u1B2GIxqIYSO2Dd0Ud79wnq/eaOpBgR4EMVDW/oijINu87d7U6hYaJBrInA0k0iu4bvVtZ1KrBRrm4Vr06GNkF3cO7RCVhvK4pmiFOISkjOStk1lVrBQIiPh5SAyN3fWKWeMuEBjxCmEkh3bVvokAzd5idL2iQqDEA1nASXmK+XFIfeTCo5gFAudllvo8G20TUeNVElMQ58QrxLHD1jbB3GumSnhsz7qHE9BhTEGc87aOm16KJHi7rfqj8yfI53E1j/l9eK8L651stJUQeXx/iuzU1QztZLGIsRp+xvtOh3Qo3/qCedumEC6qZetpJztwz7O6UAa4FNsP7ELTfXbWbskUdjgq9M9VCJoFcwvTlfyPDd9t3XNJjA+2IZcGpmxi+7tpjW3OupurJziZhtKWPzWhlFJKKaWUUkoppZRSSiml/uwrgZ/Bfwo/wccAAAAASUVORK5CYII="; fav.setAttribute("href", base64Favicon); return /* @__PURE__ */ jsx284("div", { style: container63, className: "css-reset", children: /* @__PURE__ */ jsxs151("div", { style: message, children: [ "The studio server has disconnected. ", /* @__PURE__ */ jsx284("br", {}), window.remotion_studioServerCommand ? /* @__PURE__ */ jsxs151("span", { children: [ "Run", " ", /* @__PURE__ */ jsx284("code", { style: inlineCode, children: window.remotion_studioServerCommand }), " ", "to run it again." ] }) : /* @__PURE__ */ jsx284("span", { children: "Fast refresh will not work." }) ] }) }); }; // src/FastRefreshProvider.tsx import { useCallback as useCallback142, useEffect as useEffect87, useMemo as useMemo145, useState as useState96 } from "react"; import { jsx as jsx285 } from "react/jsx-runtime"; var FastRefreshProvider = ({ children }) => { const [fastRefreshes, setFastRefreshes] = useState96(0); const [manualRefreshes, setManualRefreshes] = useState96(0); const increaseManualRefreshes = useCallback142(() => { setManualRefreshes((i) => i + 1); }, []); useEffect87(() => { if (typeof __webpack_module__ !== "undefined") { if (__webpack_module__.hot) { __webpack_module__.hot.addStatusHandler((status) => { if (status === "idle") { setFastRefreshes((i) => i + 1); } }); } } }, []); const value = useMemo145(() => ({ fastRefreshes, manualRefreshes, increaseManualRefreshes }), [fastRefreshes, manualRefreshes, increaseManualRefreshes]); return /* @__PURE__ */ jsx285(FastRefreshContext.Provider, { value, children }); }; // src/helpers/inject-css.ts import { Internals as Internals64 } from "remotion"; var makeDefaultGlobalCSS = () => { const unhoveredDragAreaFactor = 2; const fromMiddle = 50 / unhoveredDragAreaFactor; const hoveredDragAreaFactor = 6; const fromMiddleHovered = 50 / hoveredDragAreaFactor; return ` html { --remotion-cli-internals-blue: #0b84f3; overscroll-behavior-y: none; overscroll-behavior-x: none; } body { overscroll-behavior-y: none; overscroll-behavior-x: none; /* Override Chakra UI position: relative on body */ position: static !important; } .remotion-splitter { user-select: none; } .remotion-splitter-horizontal { transform: scaleY(${unhoveredDragAreaFactor}); background: linear-gradient( to bottom, transparent ${50 - fromMiddle}%, black ${50 - fromMiddle}%, black ${50 + fromMiddle}%, transparent ${50 + fromMiddle}% ); } .remotion-splitter-horizontal.remotion-splitter-active, .remotion-splitter-horizontal.remotion-splitter-hover { background: linear-gradient( to bottom, transparent ${50 - fromMiddleHovered}%, var(--remotion-cli-internals-blue) ${50 - fromMiddleHovered}%, var(--remotion-cli-internals-blue) ${50 + fromMiddleHovered}%, transparent ${50 + fromMiddleHovered}% ); cursor: row-resize; transform: scaleY(${hoveredDragAreaFactor}); z-index: 1000; } .remotion-splitter-vertical { transform: scaleX(${unhoveredDragAreaFactor}); background: linear-gradient( to right, transparent ${50 - fromMiddle}%, black ${50 - fromMiddle}%, black ${50 + fromMiddle}%, transparent ${50 + fromMiddle}% ); } .remotion-splitter-vertical.remotion-splitter-active, .remotion-splitter-vertical.remotion-splitter-hover { background: linear-gradient( to right, transparent ${50 - fromMiddleHovered}%, var(--remotion-cli-internals-blue) ${50 - fromMiddleHovered}%, var(--remotion-cli-internals-blue) ${50 + fromMiddleHovered}%, transparent ${50 + fromMiddleHovered}% ); transform: scaleX(${hoveredDragAreaFactor}); cursor: col-resize; z-index: 1000; } input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } input:focus, textarea:focus, button:focus, a:focus { outline: none; box-shadow: inset 1px 1px #555, inset -1px -1px #555, inset 1px -1px #555, inset -1px 1px #555; } input[type='color'].__remotion_color_picker::-webkit-color-swatch-wrapper { padding: 0; } input[type='color'].__remotion_color_picker::-webkit-color-swatch { border: none; } .__remotion_thumb, .__remotion_thumb::-webkit-slider-thumb { -webkit-appearance: none; -webkit-tap-highlight-color: transparent; } .__remotion_thumb { pointer-events: none; position: absolute; height: 0; outline: none; top: 15.5px; width: 221px; margin-left: -2px; z-index: 2; } /* For Firefox browsers */ .__remotion_thumb::-moz-range-thumb { border: 1px solid black; border-radius: 2px; cursor: pointer; height: 37px; width: 10px; pointer-events: all; border-color: black; background-color: white; position: relative; } /* For Chrome browsers */ .__remotion_thumb::-webkit-slider-thumb { border: 1px solid black; border-radius: 2px; cursor: pointer; height: 39px; width: 10px; pointer-events: all; border-color: black; background-color: white; position: relative; } .${DEFAULT_PROPS_PATH_ACTIVE_CLASSNAME} span { color: var(--remotion-cli-internals-blue) !important; transition: color 0.2s ease-in-out; } `.trim(); }; var injected = false; var injectCSS = () => { if (injected) { return; } Internals64.CSSUtils.injectCSS(makeDefaultGlobalCSS()); injected = true; }; // src/ResolveCompositionConfigInStudio.tsx import { useCallback as useCallback143, useContext as useContext89, useEffect as useEffect88, useImperativeHandle as useImperativeHandle14, useMemo as useMemo146, useState as useState97 } from "react"; import { getInputProps as getInputProps2, Internals as Internals65 } from "remotion"; import { jsx as jsx286 } from "react/jsx-runtime"; var ResolveCompositionConfigInStudio = ({ children }) => { const [currentRenderModalComposition, setCurrentRenderModalComposition] = useState97(null); const { compositions, canvasContent, currentCompositionMetadata } = useContext89(Internals65.CompositionManager); const { fastRefreshes, manualRefreshes } = useContext89(FastRefreshContext); if (manualRefreshes) {} const selectedComposition = useMemo146(() => { return compositions.find((c) => canvasContent && canvasContent.type === "composition" && canvasContent.compositionId === c.id); }, [canvasContent, compositions]); const renderModalComposition = compositions.find((c) => c.id === currentRenderModalComposition); const { props: allEditorProps } = useContext89(Internals65.EditorPropsContext); const env = Internals65.getRemotionEnvironment(); const inputProps = useMemo146(() => { return typeof window === "undefined" || env.isPlayer ? {} : getInputProps2() ?? {}; }, [env.isPlayer]); const [resolvedConfigs, setResolvedConfigs] = useState97({}); const selectedEditorProps = useMemo146(() => { return selectedComposition ? allEditorProps[selectedComposition.id] ?? {} : {}; }, [allEditorProps, selectedComposition]); const renderModalProps = useMemo146(() => { return renderModalComposition ? allEditorProps[renderModalComposition.id] ?? {} : {}; }, [allEditorProps, renderModalComposition]); const hasResolution = Boolean(currentCompositionMetadata); const doResolution = useCallback143(({ calculateMetadata, combinedProps, compositionDurationInFrames, compositionFps, compositionHeight, compositionId, compositionWidth, defaultProps }) => { const controller = new AbortController; if (hasResolution) { return controller; } const { signal } = controller; const result = Internals65.resolveVideoConfigOrCatch({ compositionId, calculateMetadata, inputProps: combinedProps, signal, defaultProps, compositionDurationInFrames, compositionFps, compositionHeight, compositionWidth }); if (result.type === "error") { setResolvedConfigs((r) => ({ ...r, [compositionId]: { type: "error", error: result.error } })); return controller; } const promOrNot = result.result; if (typeof promOrNot === "object" && "then" in promOrNot) { setResolvedConfigs((r) => { const prev = r[compositionId]; if (prev?.type === "success" || prev?.type === "success-and-refreshing") { return { ...r, [compositionId]: { type: "success-and-refreshing", result: prev.result } }; } return { ...r, [compositionId]: { type: "loading" } }; }); promOrNot.then((c) => { if (controller.signal.aborted) { return; } setResolvedConfigs((r) => ({ ...r, [compositionId]: { type: "success", result: c } })); }).catch((err) => { if (controller.signal.aborted) { return; } setResolvedConfigs((r) => ({ ...r, [compositionId]: { type: "error", error: err } })); }); } else { setResolvedConfigs((r) => ({ ...r, [compositionId]: { type: "success", result: promOrNot } })); } return controller; }, [hasResolution]); const currentComposition = canvasContent?.type === "composition" ? canvasContent.compositionId : null; useImperativeHandle14(Internals65.resolveCompositionsRef, () => { return { setCurrentRenderModalComposition: (id) => { setCurrentRenderModalComposition(id); }, reloadCurrentlySelectedComposition: () => { if (!currentComposition) { return; } const composition = compositions.find((c) => c.id === currentComposition); if (!composition) { throw new Error(`Could not find composition with id ${currentComposition}`); } const editorProps = allEditorProps[currentComposition] ?? {}; const defaultProps = { ...composition.defaultProps ?? {}, ...editorProps ?? {} }; const props = { ...defaultProps, ...inputProps ?? {} }; doResolution({ defaultProps, calculateMetadata: composition.calculateMetadata, combinedProps: props, compositionDurationInFrames: composition.durationInFrames ?? null, compositionFps: composition.fps ?? null, compositionHeight: composition.height ?? null, compositionWidth: composition.width ?? null, compositionId: composition.id }); } }; }, [ allEditorProps, compositions, currentComposition, doResolution, inputProps ]); const isTheSame = selectedComposition?.id === renderModalComposition?.id; const currentDefaultProps = useMemo146(() => { return { ...selectedComposition?.defaultProps ?? {}, ...selectedEditorProps ?? {} }; }, [selectedComposition?.defaultProps, selectedEditorProps]); const originalProps = useMemo146(() => { return { ...currentDefaultProps, ...inputProps ?? {} }; }, [currentDefaultProps, inputProps]); const canResolve = selectedComposition && Boolean(selectedComposition.calculateMetadata); const shouldIgnoreUpdate = typeof window !== "undefined" && window.remotion_ignoreFastRefreshUpdate && fastRefreshes <= window.remotion_ignoreFastRefreshUpdate; useEffect88(() => { if (shouldIgnoreUpdate) { return; } if (canResolve) { const controller = doResolution({ calculateMetadata: selectedComposition.calculateMetadata, combinedProps: originalProps, compositionDurationInFrames: selectedComposition.durationInFrames ?? null, compositionFps: selectedComposition.fps ?? null, compositionHeight: selectedComposition.height ?? null, compositionWidth: selectedComposition.width ?? null, defaultProps: currentDefaultProps, compositionId: selectedComposition.id }); return () => { controller.abort(); }; } }, [ canResolve, currentDefaultProps, doResolution, originalProps, selectedComposition?.calculateMetadata, selectedComposition?.durationInFrames, selectedComposition?.fps, selectedComposition?.height, selectedComposition?.id, selectedComposition?.width, shouldIgnoreUpdate ]); useEffect88(() => { if (renderModalComposition && !isTheSame) { const combinedProps = { ...renderModalComposition.defaultProps ?? {}, ...renderModalProps ?? {}, ...inputProps ?? {} }; const controller = doResolution({ calculateMetadata: renderModalComposition.calculateMetadata, compositionDurationInFrames: renderModalComposition.durationInFrames ?? null, compositionFps: renderModalComposition.fps ?? null, compositionHeight: renderModalComposition.height ?? null, compositionId: renderModalComposition.id, compositionWidth: renderModalComposition.width ?? null, defaultProps: currentDefaultProps, combinedProps }); return () => { controller.abort(); }; } }, [ currentDefaultProps, doResolution, inputProps, isTheSame, renderModalComposition, renderModalProps ]); const resolvedConfigsIncludingStaticOnes = useMemo146(() => { const staticComps = compositions.filter((c) => { return c.calculateMetadata === null; }); return { ...resolvedConfigs, ...staticComps.reduce((acc, curr) => { return { ...acc, [curr.id]: { type: "success", result: { ...curr, defaultProps: curr.defaultProps ?? {} } } }; }, {}) }; }, [compositions, resolvedConfigs]); return /* @__PURE__ */ jsx286(Internals65.ResolveCompositionContext.Provider, { value: resolvedConfigsIncludingStaticOnes, children }); }; // src/Studio.tsx import { jsx as jsx287, jsxs as jsxs152 } from "react/jsx-runtime"; var getServerDisconnectedDomElement = () => { return document.getElementById("server-disconnected-overlay"); }; var StudioInner = ({ rootComponent, readOnly }) => { const { fastRefreshes, manualRefreshes } = useContext90(FastRefreshContext); return /* @__PURE__ */ jsx287(Internals66.CompositionManagerProvider, { onlyRenderComposition: null, currentCompositionMetadata: null, initialCompositions: [], initialCanvasContent: null, children: /* @__PURE__ */ jsx287(Internals66.RemotionRootContexts, { frameState: null, audioEnabled: window.remotion_audioEnabled, videoEnabled: window.remotion_videoEnabled, logLevel: window.remotion_logLevel, numberOfAudioTags: window.remotion_numberOfAudioTags, audioLatencyHint: window.remotion_audioLatencyHint ?? "interactive", nonceContextSeed: fastRefreshes + manualRefreshes, children: /* @__PURE__ */ jsx287(ResolveCompositionConfigInStudio, { children: /* @__PURE__ */ jsxs152(EditorContexts, { readOnlyStudio: readOnly, children: [ /* @__PURE__ */ jsx287(Editor, { readOnlyStudio: readOnly, Root: rootComponent }), readOnly ? null : createPortal(/* @__PURE__ */ jsx287(ServerDisconnected, {}), getServerDisconnectedDomElement()) ] }) }) }) }); }; var Studio = ({ rootComponent, readOnly }) => { useLayoutEffect2(() => { injectCSS(); }, []); return /* @__PURE__ */ jsx287(FastRefreshProvider, { children: /* @__PURE__ */ jsx287(StudioInner, { rootComponent, readOnly }) }); }; // src/internals.ts var StudioInternals = { Studio }; export { StudioInternals };