UNPKG

@remotion/studio

Version:

APIs for interacting with the Remotion Studio

424 lines (423 loc) 18.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RenderButton = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const player_1 = require("@remotion/player"); const react_1 = require("react"); const react_dom_1 = __importDefault(require("react-dom")); const remotion_1 = require("remotion"); const client_id_1 = require("../helpers/client-id"); const colors_1 = require("../helpers/colors"); const show_browser_rendering_1 = require("../helpers/show-browser-rendering"); const use_keybinding_1 = require("../helpers/use-keybinding"); const caret_1 = require("../icons/caret"); const render_1 = require("../icons/render"); const in_out_1 = require("../state/in-out"); const modals_1 = require("../state/modals"); const z_index_1 = require("../state/z-index"); const layout_1 = require("./layout"); const is_menu_item_1 = require("./Menu/is-menu-item"); const portals_1 = require("./Menu/portals"); const styles_1 = require("./Menu/styles"); const MenuContent_1 = require("./NewComposition/MenuContent"); const splitButtonContainer = { display: 'inline-flex', flexDirection: 'row', alignItems: 'stretch', borderRadius: 4, border: `1px solid ${colors_1.INPUT_BORDER_COLOR_UNHOVERED}`, backgroundColor: colors_1.INPUT_BACKGROUND, overflow: 'hidden', }; const 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', }; const dividerStyle = { width: 1, backgroundColor: colors_1.INPUT_BORDER_COLOR_UNHOVERED, alignSelf: 'stretch', }; const dropdownTriggerStyle = { paddingLeft: 6, paddingRight: 6, paddingTop: 7, paddingBottom: 7, background: 'transparent', border: 'none', color: 'white', cursor: 'pointer', display: 'flex', alignItems: 'center', }; const mainButtonContent = { paddingLeft: 4, paddingRight: 6, }; const label = { fontSize: 14, }; const RENDER_TYPE_STORAGE_KEY = 'remotion.renderType'; const getInitialRenderType = (readOnlyStudio) => { if (!show_browser_rendering_1.SHOW_BROWSER_RENDERING) { return readOnlyStudio ? 'render-command' : '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 (_a) { // localStorage might not be available } return 'server-render'; }; const RenderButton = ({ readOnlyStudio, }) => { const { inFrame, outFrame } = (0, in_out_1.useTimelineInOutFramePosition)(); const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext); const [preferredRenderType, setPreferredRenderType] = (0, react_1.useState)(() => getInitialRenderType(readOnlyStudio)); const [dropdownOpened, setDropdownOpened] = (0, react_1.useState)(false); const dropdownRef = (0, react_1.useRef)(null); const containerRef = (0, react_1.useRef)(null); const { currentZIndex } = (0, z_index_1.useZIndex)(); const size = player_1.PlayerInternals.useElementSize(dropdownRef, { triggerOnWindowResize: true, shouldApplyCssTransforms: true, }); const refresh = size === null || size === void 0 ? void 0 : size.refresh; const onPointerDown = (0, react_1.useCallback)(() => { setDropdownOpened((o) => { if (!o) { refresh === null || refresh === void 0 ? void 0 : refresh(); } return !o; }); }, [refresh]); const onClickDropdown = (0, react_1.useCallback)((e) => { e.stopPropagation(); const isKeyboardInitiated = e.detail === 0; if (!isKeyboardInitiated) { return; } setDropdownOpened((o) => { if (!o) { refresh === null || refresh === void 0 ? void 0 : refresh(); window.addEventListener('pointerup', (evt) => { if (!(0, is_menu_item_1.isMenuItem)(evt.target)) { setDropdownOpened(false); } }, { once: true, }); } return !o; }); }, [refresh]); const connectionStatus = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx) .previewServerState.type; const canServerRender = connectionStatus === 'connected'; const canRender = canServerRender || show_browser_rendering_1.SHOW_BROWSER_RENDERING || readOnlyStudio; const renderType = (0, react_1.useMemo)(() => { if (readOnlyStudio) { if (!show_browser_rendering_1.SHOW_BROWSER_RENDERING) { return 'render-command'; } return preferredRenderType === 'render-command' ? 'render-command' : 'client-render'; } if (connectionStatus === 'disconnected' && show_browser_rendering_1.SHOW_BROWSER_RENDERING) { return 'client-render'; } if (!show_browser_rendering_1.SHOW_BROWSER_RENDERING) { return 'server-render'; } return preferredRenderType; }, [connectionStatus, preferredRenderType, readOnlyStudio]); const shortcut = (0, use_keybinding_1.areKeyboardShortcutsDisabled)() ? '' : '(R)'; const tooltip = renderType === 'render-command' ? 'Copy a CLI command to render this composition ' + shortcut : canRender ? 'Export the current composition ' + shortcut : 'Connect to the Studio server to render'; const iconStyle = (0, react_1.useMemo)(() => { return { style: { height: 16, color: 'currentColor', }, }; }, []); const video = remotion_1.Internals.useVideo(); const getCurrentFrame = player_1.PlayerInternals.useFrameImperative(); const { props } = (0, react_1.useContext)(remotion_1.Internals.EditorPropsContext); const openServerRenderModal = (0, react_1.useCallback)((copyCommandOnly) => { var _a; var _b, _c; if (!video) { return null; } const defaults = window.remotion_renderDefaults; if (!defaults) { throw new TypeError('Expected defaults'); } setSelectedModal({ type: 'server-render', readOnlyStudio: copyCommandOnly, compositionId: video.id, initialFrame: getCurrentFrame(), initialStillImageFormat: defaults.stillImageFormat, initialVideoImageFormat: null, initialJpegQuality: defaults.jpegQuality, initialScale: (_b = (_a = window.remotion_renderDefaults) === null || _a === void 0 ? void 0 : _a.scale) !== null && _b !== void 0 ? _b : 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: (_c = props[video.id]) !== null && _c !== void 0 ? _c : 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, getCurrentFrame, props, inFrame, outFrame]); const openClientRenderModal = (0, react_1.useCallback)(() => { var _a; if (!video) { return null; } const defaults = window.remotion_renderDefaults; if (!defaults) { throw new TypeError('Expected defaults'); } setSelectedModal({ type: 'web-render', compositionId: video.id, initialFrame: getCurrentFrame(), defaultProps: (_a = props[video.id]) !== null && _a !== void 0 ? _a : 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, getCurrentFrame, props, inFrame, outFrame]); const onClick = (0, react_1.useCallback)(() => { if (renderType === 'render-command') { openServerRenderModal(true); return; } if (!show_browser_rendering_1.SHOW_BROWSER_RENDERING || renderType === 'server-render') { openServerRenderModal(false); } else { openClientRenderModal(); } }, [renderType, openServerRenderModal, openClientRenderModal]); const onHideDropdown = (0, react_1.useCallback)(() => { setDropdownOpened(false); }, []); const handleRenderTypeChange = (0, react_1.useCallback)((newType) => { setPreferredRenderType(newType); try { localStorage.setItem(RENDER_TYPE_STORAGE_KEY, newType); } catch (_a) { // localStorage might not be available } setDropdownOpened(false); if (newType === 'server-render') { openServerRenderModal(false); } else if (newType === 'render-command') { openServerRenderModal(true); } else { openClientRenderModal(); } }, [openClientRenderModal, openServerRenderModal]); const dropdownValues = (0, react_1.useMemo)(() => { if (readOnlyStudio) { return [ { type: 'item', id: 'client-render', label: 'Render on web', value: 'client-render', onClick: () => handleRenderTypeChange('client-render'), keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: null, }, { type: 'item', id: 'render-command', label: 'Render via CLI', value: 'render-command', onClick: () => handleRenderTypeChange('render-command'), keyHint: null, leftItem: null, subMenu: null, quickSwitcherLabel: null, }, ]; } 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, readOnlyStudio]); const spaceToBottom = (0, react_1.useMemo)(() => { const margin = 10; if (size && dropdownOpened) { return size.windowSize.height - (size.top + size.height) - margin; } return 0; }, [dropdownOpened, size]); const spaceToTop = (0, react_1.useMemo)(() => { const margin = 10; if (size && dropdownOpened) { return size.top - margin; } return 0; }, [dropdownOpened, size]); const derivedMaxHeight = (0, react_1.useMemo)(() => { return spaceToTop > spaceToBottom ? spaceToTop : spaceToBottom; }, [spaceToBottom, spaceToTop]); const portalStyle = (0, react_1.useMemo)(() => { if (!dropdownOpened || !size) { return null; } const verticalLayout = spaceToTop > spaceToBottom ? 'bottom' : 'top'; return { ...(verticalLayout === 'top' ? { ...styles_1.menuContainerTowardsBottom, top: size.top + size.height, } : { ...styles_1.menuContainerTowardsTop, bottom: size.windowSize.height - size.top, }), right: size.windowSize.width - size.left - size.width, }; }, [dropdownOpened, size, spaceToBottom, spaceToTop]); const containerStyle = (0, react_1.useMemo)(() => { return { ...splitButtonContainer, borderColor: colors_1.INPUT_BORDER_COLOR_UNHOVERED, opacity: canRender ? 1 : 0.7, cursor: canRender ? 'pointer' : 'inherit', }; }, [canRender]); const renderLabel = renderType === 'server-render' ? 'Render' : renderType === 'render-command' ? 'Render via CLI' : 'Render on web'; const shouldShowDropdown = (0, react_1.useMemo)(() => { if (readOnlyStudio) { return show_browser_rendering_1.SHOW_BROWSER_RENDERING; } if (!show_browser_rendering_1.SHOW_BROWSER_RENDERING) { return false; } return true; }, [readOnlyStudio]); if (!video) { return null; } return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [ jsx_runtime_1.jsx("button", { style: { display: 'none' }, id: "render-modal-button-server", disabled: !canServerRender, onClick: () => openServerRenderModal(false), type: "button" }), ' ', jsx_runtime_1.jsx("button", { style: { display: 'none' }, id: "render-modal-button-client", onClick: openClientRenderModal, type: "button" }), jsx_runtime_1.jsxs("div", { ref: containerRef, style: containerStyle, title: tooltip, children: [ jsx_runtime_1.jsx("button", { type: "button", style: mainButtonStyle, onClick: onClick, id: "render-modal-button", disabled: !canRender, children: jsx_runtime_1.jsxs(layout_1.Row, { align: "center", style: mainButtonContent, children: [ jsx_runtime_1.jsx(render_1.ThinRenderIcon, { fill: "currentcolor", svgProps: iconStyle }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 1 }), jsx_runtime_1.jsx("span", { style: label, children: renderLabel }) ] }) }), shouldShowDropdown ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [ jsx_runtime_1.jsx("div", { style: dividerStyle }), jsx_runtime_1.jsx("button", { ref: dropdownRef, type: "button", style: dropdownTriggerStyle, disabled: !readOnlyStudio && connectionStatus !== 'connected', className: is_menu_item_1.MENU_INITIATOR_CLASSNAME, onPointerDown: onPointerDown, onClick: onClickDropdown, children: jsx_runtime_1.jsx(caret_1.CaretDown, {}) }) ] })) : null] }), portalStyle ? react_dom_1.default.createPortal(jsx_runtime_1.jsx("div", { style: styles_1.fullScreenOverlay, children: jsx_runtime_1.jsx("div", { style: styles_1.outerPortal, className: "css-reset", children: jsx_runtime_1.jsx(z_index_1.HigherZIndex, { onOutsideClick: onHideDropdown, onEscape: onHideDropdown, children: jsx_runtime_1.jsx("div", { style: portalStyle, children: jsx_runtime_1.jsx(MenuContent_1.MenuContent, { onNextMenu: () => { }, onPreviousMenu: () => { }, values: dropdownValues, onHide: onHideDropdown, leaveLeftSpace: false, preselectIndex: dropdownValues.findIndex((v) => v.id === renderType), topItemCanBeUnselected: false, fixedHeight: derivedMaxHeight }) }) }) }) }), (0, portals_1.getPortal)(currentZIndex)) : null] })); }; exports.RenderButton = RenderButton;