@remotion/studio 4.0.433 → 4.0.434

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,7 @@ const react_1 = require("react");
5
5
  const remotion_1 = require("remotion");
6
6
  const client_id_1 = require("../helpers/client-id");
7
7
  const document_title_1 = require("../helpers/document-title");
8
+ const show_browser_rendering_1 = require("../helpers/show-browser-rendering");
8
9
  const use_keybinding_1 = require("../helpers/use-keybinding");
9
10
  const NotificationCenter_1 = require("./Notifications/NotificationCenter");
10
11
  const context_1 = require("./RenderQueue/context");
@@ -45,10 +46,7 @@ const CurrentCompositionKeybindings = ({ readOnlyStudio }) => {
45
46
  if (!video) {
46
47
  return;
47
48
  }
48
- if (readOnlyStudio) {
49
- return (0, NotificationCenter_1.showNotification)('Studio is read-only', 2000);
50
- }
51
- if (type !== 'connected') {
49
+ if (type !== 'connected' && !show_browser_rendering_1.SHOW_BROWSER_RENDERING && !readOnlyStudio) {
52
50
  (0, NotificationCenter_1.showNotification)('Studio server is offline', 2000);
53
51
  return;
54
52
  }
@@ -20,11 +20,12 @@ const ServerRenderModal_1 = require("./RenderModal/ServerRenderModal");
20
20
  const WebRenderModal_1 = require("./RenderModal/WebRenderModal");
21
21
  const UpdateModal_1 = require("./UpdateModal/UpdateModal");
22
22
  const Modals = ({ readOnlyStudio }) => {
23
+ var _a;
23
24
  const { selectedModal: modalContextType } = (0, react_1.useContext)(modals_1.ModalsContext);
24
25
  const canRender = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx).previewServerState.type ===
25
26
  'connected';
26
27
  return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [modalContextType && modalContextType.type === 'duplicate-comp' && (jsx_runtime_1.jsx(DuplicateComposition_1.DuplicateComposition, { compositionType: modalContextType.compositionType, compositionId: modalContextType.compositionId })), modalContextType && modalContextType.type === 'delete-comp' && (jsx_runtime_1.jsx(DeleteComposition_1.DeleteComposition, { compositionId: modalContextType.compositionId })), modalContextType && modalContextType.type === 'rename-comp' && (jsx_runtime_1.jsx(RenameComposition_1.RenameComposition, { compositionId: modalContextType.compositionId })), modalContextType && modalContextType.type === 'input-props-override' && (jsx_runtime_1.jsx(OverrideInputProps_1.OverrideInputPropsModal, {})), modalContextType && modalContextType.type === 'web-render' && (jsx_runtime_1.jsx(WebRenderModal_1.WebRenderModalWithLoader, { ...modalContextType })), modalContextType &&
27
- canRender &&
28
- modalContextType.type === 'server-render' && (jsx_runtime_1.jsx(ServerRenderModal_1.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' && (jsx_runtime_1.jsx(RenderStatusModal_1.RenderStatusModal, { jobId: modalContextType.jobId })), modalContextType && modalContextType.type === 'update' && (jsx_runtime_1.jsx(UpdateModal_1.UpdateModal, { info: modalContextType.info, knownBugs: modalContextType.knownBugs })), modalContextType && modalContextType.type === 'install-packages' && (jsx_runtime_1.jsx(InstallPackage_1.InstallPackageModal, { packageManager: modalContextType.packageManager })), modalContextType && modalContextType.type === 'quick-switcher' && (jsx_runtime_1.jsx(QuickSwitcher_1.default, { readOnlyStudio: readOnlyStudio, invocationTimestamp: modalContextType.invocationTimestamp, initialMode: modalContextType.mode })), process.env.ASK_AI_ENABLED && jsx_runtime_1.jsx(AskAiModal_1.AskAiModal, {})] }));
28
+ modalContextType.type === 'server-render' &&
29
+ (canRender || modalContextType.readOnlyStudio) ? (jsx_runtime_1.jsx(ServerRenderModal_1.RenderModalWithLoader, { readOnlyStudio: (_a = modalContextType.readOnlyStudio) !== null && _a !== void 0 ? _a : false, 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 })) : null, modalContextType && modalContextType.type === 'render-progress' && (jsx_runtime_1.jsx(RenderStatusModal_1.RenderStatusModal, { jobId: modalContextType.jobId })), modalContextType && modalContextType.type === 'update' && (jsx_runtime_1.jsx(UpdateModal_1.UpdateModal, { info: modalContextType.info, knownBugs: modalContextType.knownBugs })), modalContextType && modalContextType.type === 'install-packages' && (jsx_runtime_1.jsx(InstallPackage_1.InstallPackageModal, { packageManager: modalContextType.packageManager })), modalContextType && modalContextType.type === 'quick-switcher' && (jsx_runtime_1.jsx(QuickSwitcher_1.default, { readOnlyStudio: readOnlyStudio, invocationTimestamp: modalContextType.invocationTimestamp, initialMode: modalContextType.mode })), process.env.ASK_AI_ENABLED && jsx_runtime_1.jsx(AskAiModal_1.AskAiModal, {})] }));
29
30
  };
30
31
  exports.Modals = Modals;
@@ -8,7 +8,6 @@ const check_fullscreen_support_1 = require("../helpers/check-fullscreen-support"
8
8
  const colors_1 = require("../helpers/colors");
9
9
  const is_current_selected_still_1 = require("../helpers/is-current-selected-still");
10
10
  const mobile_layout_1 = require("../helpers/mobile-layout");
11
- const should_show_render_button_1 = require("../helpers/should-show-render-button");
12
11
  const timeline_layout_1 = require("../helpers/timeline-layout");
13
12
  const loop_1 = require("../state/loop");
14
13
  const CheckboardToggle_1 = require("./CheckboardToggle");
@@ -144,7 +143,7 @@ const PreviewToolbar = ({ readOnlyStudio, bufferStateDelayInMilliseconds }) => {
144
143
  jsx_runtime_1.jsx(layout_1.Spacing, { x: 2 }), jsx_runtime_1.jsx(PlayPause_1.PlayPause, { bufferStateDelayInMilliseconds: bufferStateDelayInMilliseconds, loop: loop, playbackRate: playbackRate }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 2 }), jsx_runtime_1.jsx(LoopToggle_1.LoopToggle, { loop: loop, setLoop: setLoop }), jsx_runtime_1.jsx(MuteToggle_1.MuteToggle, { muted: mediaMuted, setMuted: setMediaMuted }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 2 }), jsx_runtime_1.jsx(TimelineInOutToggle_1.TimelineInOutPointToggle, {}), jsx_runtime_1.jsx(layout_1.Spacing, { x: 2 })
145
144
  ] })) : null, (canvasContent === null || canvasContent === void 0 ? void 0 : canvasContent.type) === 'composition' ? jsx_runtime_1.jsx(CheckboardToggle_1.CheckboardToggle, {}) : null, jsx_runtime_1.jsx(layout_1.Spacing, { x: 1 }), canvasContent && isFullscreenSupported ? jsx_runtime_1.jsx(FullscreenToggle_1.FullScreenToggle, {}) : null, jsx_runtime_1.jsx(layout_1.Flex, {}), isMobileLayout && (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
146
145
  jsx_runtime_1.jsx(layout_1.Flex, {}), jsx_runtime_1.jsx(SizeSelector_1.SizeSelector, {}), isStill || isVideoComposition ? (jsx_runtime_1.jsx(PlaybackRateSelector_1.PlaybackRateSelector, { setPlaybackRate: setPlaybackRate, playbackRate: playbackRate })) : null] })), jsx_runtime_1.jsxs("div", { style: sideContainer, children: [
147
- jsx_runtime_1.jsx(layout_1.Flex, {}), !isMobileLayout && jsx_runtime_1.jsx(FpsCounter_1.FpsCounter, { playbackSpeed: playbackRate }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 2 }), (0, should_show_render_button_1.shouldShowRenderButton)(readOnlyStudio) ? (jsx_runtime_1.jsx(RenderButton_1.RenderButton, { readOnlyStudio: readOnlyStudio })) : null, jsx_runtime_1.jsx(layout_1.Spacing, { x: 1.5 })
146
+ jsx_runtime_1.jsx(layout_1.Flex, {}), !isMobileLayout && jsx_runtime_1.jsx(FpsCounter_1.FpsCounter, { playbackSpeed: playbackRate }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 2 }), jsx_runtime_1.jsx(RenderButton_1.RenderButton, { readOnlyStudio: readOnlyStudio }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 1.5 })
148
147
  ] }), jsx_runtime_1.jsx(PlaybackKeyboardShortcutsManager_1.PlaybackKeyboardShortcutsManager, { setPlaybackRate: setPlaybackRate }), jsx_runtime_1.jsx(PlaybackRatePersistor_1.PlaybackRatePersistor, {}), jsx_runtime_1.jsx("div", { ref: rightScrollIndicatorRef, style: scrollIndicatorRight })
149
148
  ] }));
150
149
  };
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- export type RenderType = 'server-render' | 'client-render';
2
+ export type RenderType = 'server-render' | 'client-render' | 'render-command';
3
3
  export declare const RenderButton: React.FC<{
4
4
  readonly readOnlyStudio: boolean;
5
5
  }>;
@@ -73,7 +73,7 @@ const label = {
73
73
  const RENDER_TYPE_STORAGE_KEY = 'remotion.renderType';
74
74
  const getInitialRenderType = (readOnlyStudio) => {
75
75
  if (!show_browser_rendering_1.SHOW_BROWSER_RENDERING) {
76
- return 'server-render';
76
+ return readOnlyStudio ? 'render-command' : 'server-render';
77
77
  }
78
78
  if (readOnlyStudio) {
79
79
  return 'client-render';
@@ -132,8 +132,17 @@ const RenderButton = ({ readOnlyStudio, }) => {
132
132
  }, [refresh]);
133
133
  const connectionStatus = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx)
134
134
  .previewServerState.type;
135
- const canRender = connectionStatus === 'connected' || show_browser_rendering_1.SHOW_BROWSER_RENDERING;
135
+ const canServerRender = connectionStatus === 'connected';
136
+ const canRender = canServerRender || show_browser_rendering_1.SHOW_BROWSER_RENDERING || readOnlyStudio;
136
137
  const renderType = (0, react_1.useMemo)(() => {
138
+ if (readOnlyStudio) {
139
+ if (!show_browser_rendering_1.SHOW_BROWSER_RENDERING) {
140
+ return 'render-command';
141
+ }
142
+ return preferredRenderType === 'render-command'
143
+ ? 'render-command'
144
+ : 'client-render';
145
+ }
137
146
  if (connectionStatus === 'disconnected' && show_browser_rendering_1.SHOW_BROWSER_RENDERING) {
138
147
  return 'client-render';
139
148
  }
@@ -141,11 +150,13 @@ const RenderButton = ({ readOnlyStudio, }) => {
141
150
  return 'server-render';
142
151
  }
143
152
  return preferredRenderType;
144
- }, [connectionStatus, preferredRenderType]);
153
+ }, [connectionStatus, preferredRenderType, readOnlyStudio]);
145
154
  const shortcut = (0, use_keybinding_1.areKeyboardShortcutsDisabled)() ? '' : '(R)';
146
- const tooltip = canRender
147
- ? 'Export the current composition ' + shortcut
148
- : 'Connect to the Studio server to render';
155
+ const tooltip = renderType === 'render-command'
156
+ ? 'Copy a CLI command to render this composition ' + shortcut
157
+ : canRender
158
+ ? 'Export the current composition ' + shortcut
159
+ : 'Connect to the Studio server to render';
149
160
  const iconStyle = (0, react_1.useMemo)(() => {
150
161
  return {
151
162
  style: {
@@ -157,7 +168,7 @@ const RenderButton = ({ readOnlyStudio, }) => {
157
168
  const video = remotion_1.Internals.useVideo();
158
169
  const getCurrentFrame = player_1.PlayerInternals.useFrameImperative();
159
170
  const { props } = (0, react_1.useContext)(remotion_1.Internals.EditorPropsContext);
160
- const openServerRenderModal = (0, react_1.useCallback)(() => {
171
+ const openServerRenderModal = (0, react_1.useCallback)((copyCommandOnly) => {
161
172
  var _a;
162
173
  var _b, _c;
163
174
  if (!video) {
@@ -169,6 +180,7 @@ const RenderButton = ({ readOnlyStudio, }) => {
169
180
  }
170
181
  setSelectedModal({
171
182
  type: 'server-render',
183
+ readOnlyStudio: copyCommandOnly,
172
184
  compositionId: video.id,
173
185
  initialFrame: getCurrentFrame(),
174
186
  initialStillImageFormat: defaults.stillImageFormat,
@@ -253,8 +265,12 @@ const RenderButton = ({ readOnlyStudio, }) => {
253
265
  });
254
266
  }, [video, setSelectedModal, getCurrentFrame, props, inFrame, outFrame]);
255
267
  const onClick = (0, react_1.useCallback)(() => {
268
+ if (renderType === 'render-command') {
269
+ openServerRenderModal(true);
270
+ return;
271
+ }
256
272
  if (!show_browser_rendering_1.SHOW_BROWSER_RENDERING || renderType === 'server-render') {
257
- openServerRenderModal();
273
+ openServerRenderModal(false);
258
274
  }
259
275
  else {
260
276
  openClientRenderModal();
@@ -273,13 +289,42 @@ const RenderButton = ({ readOnlyStudio, }) => {
273
289
  }
274
290
  setDropdownOpened(false);
275
291
  if (newType === 'server-render') {
276
- openServerRenderModal();
292
+ openServerRenderModal(false);
293
+ }
294
+ else if (newType === 'render-command') {
295
+ openServerRenderModal(true);
277
296
  }
278
297
  else {
279
298
  openClientRenderModal();
280
299
  }
281
- }, [openServerRenderModal, openClientRenderModal]);
300
+ }, [openClientRenderModal, openServerRenderModal]);
282
301
  const dropdownValues = (0, react_1.useMemo)(() => {
302
+ if (readOnlyStudio) {
303
+ return [
304
+ {
305
+ type: 'item',
306
+ id: 'client-render',
307
+ label: 'Render on web',
308
+ value: 'client-render',
309
+ onClick: () => handleRenderTypeChange('client-render'),
310
+ keyHint: null,
311
+ leftItem: null,
312
+ subMenu: null,
313
+ quickSwitcherLabel: null,
314
+ },
315
+ {
316
+ type: 'item',
317
+ id: 'render-command',
318
+ label: 'Render via CLI',
319
+ value: 'render-command',
320
+ onClick: () => handleRenderTypeChange('render-command'),
321
+ keyHint: null,
322
+ leftItem: null,
323
+ subMenu: null,
324
+ quickSwitcherLabel: null,
325
+ },
326
+ ];
327
+ }
283
328
  return [
284
329
  {
285
330
  type: 'item',
@@ -304,7 +349,7 @@ const RenderButton = ({ readOnlyStudio, }) => {
304
349
  quickSwitcherLabel: null,
305
350
  },
306
351
  ];
307
- }, [handleRenderTypeChange]);
352
+ }, [handleRenderTypeChange, readOnlyStudio]);
308
353
  const spaceToBottom = (0, react_1.useMemo)(() => {
309
354
  const margin = 10;
310
355
  if (size && dropdownOpened) {
@@ -348,13 +393,15 @@ const RenderButton = ({ readOnlyStudio, }) => {
348
393
  cursor: canRender ? 'pointer' : 'inherit',
349
394
  };
350
395
  }, [canRender]);
351
- const renderLabel = renderType === 'server-render' ? 'Render' : 'Render on web';
396
+ const renderLabel = renderType === 'server-render'
397
+ ? 'Render'
398
+ : renderType === 'render-command'
399
+ ? 'Render via CLI'
400
+ : 'Render on web';
352
401
  const shouldShowDropdown = (0, react_1.useMemo)(() => {
353
- // Server render is not available
354
402
  if (readOnlyStudio) {
355
- return false;
403
+ return show_browser_rendering_1.SHOW_BROWSER_RENDERING;
356
404
  }
357
- // client render is not available
358
405
  if (!show_browser_rendering_1.SHOW_BROWSER_RENDERING) {
359
406
  return false;
360
407
  }
@@ -364,11 +411,11 @@ const RenderButton = ({ readOnlyStudio, }) => {
364
411
  return null;
365
412
  }
366
413
  return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
367
- jsx_runtime_1.jsx("button", { style: { display: 'none' }, id: "render-modal-button-server", disabled: !canRender, onClick: openServerRenderModal, 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: [
414
+ 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: [
368
415
  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: [
369
416
  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 })
370
417
  ] }) }), shouldShowDropdown ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
371
- jsx_runtime_1.jsx("div", { style: dividerStyle }), jsx_runtime_1.jsx("button", { ref: dropdownRef, type: "button", style: dropdownTriggerStyle, disabled: connectionStatus !== 'connected', className: is_menu_item_1.MENU_INITIATOR_CLASSNAME, onPointerDown: onPointerDown, onClick: onClickDropdown, children: jsx_runtime_1.jsx(caret_1.CaretDown, {}) })
418
+ 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, {}) })
372
419
  ] })) : null] }), portalStyle
373
420
  ? 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))
374
421
  : null] }));
@@ -22,4 +22,5 @@ export declare const RenderModalBasic: React.FC<{
22
22
  readonly validationMessage: string | null;
23
23
  readonly setVerboseLogging: React.Dispatch<React.SetStateAction<LogLevel>>;
24
24
  readonly logLevel: LogLevel;
25
+ readonly showOutputName: boolean;
25
26
  }>;
@@ -22,7 +22,7 @@ const RenderModalOutputName_1 = require("./RenderModalOutputName");
22
22
  const container = {
23
23
  flex: 1,
24
24
  };
25
- const RenderModalBasic = ({ renderMode, imageFormatOptions, outName, codec, setVideoCodec: setCodec, proResProfile, setProResProfile, frame, setFrame, resolvedComposition, setOutName, setEndFrame, endFrame, setStartFrame, startFrame, validationMessage, setVerboseLogging, logLevel, }) => {
25
+ const RenderModalBasic = ({ renderMode, imageFormatOptions, outName, codec, setVideoCodec: setCodec, proResProfile, setProResProfile, frame, setFrame, resolvedComposition, setOutName, setEndFrame, endFrame, setStartFrame, startFrame, validationMessage, setVerboseLogging, logLevel, showOutputName, }) => {
26
26
  const existence = (0, use_file_existence_1.useFileExistence)(outName);
27
27
  const videoCodecOptions = (0, react_1.useMemo)(() => {
28
28
  return client_1.BrowserSafeApis.validCodecs
@@ -101,7 +101,7 @@ const RenderModalBasic = ({ renderMode, imageFormatOptions, outName, codec, setV
101
101
  jsx_runtime_1.jsx("div", { style: layout_2.label, children: "Frame" }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(RemInput_1.RightAlignInput, { children: jsx_runtime_1.jsx(InputDragger_1.InputDragger, { value: frame, onTextChange: onFrameChanged, placeholder: `0-${resolvedComposition.durationInFrames - 1}`, onValueChange: onFrameSetDirectly, name: "frame", step: 1, min: 0, status: "ok", max: resolvedComposition.durationInFrames - 1, rightAlign: true }) }) })
102
102
  ] })) : null, renderMode === 'video' && codec === 'prores' ? (jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
103
103
  jsx_runtime_1.jsx("div", { style: layout_2.label, children: "ProRes profile" }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(ComboBox_1.Combobox, { title: 'proResProfile', selectedId: proResProfile, values: proResProfileOptions }) })
104
- ] })) : null, renderMode === 'still' ? null : (jsx_runtime_1.jsx(FrameRangeSetting_1.FrameRangeSetting, { durationInFrames: resolvedComposition.durationInFrames, endFrame: endFrame, setEndFrame: setEndFrame, setStartFrame: setStartFrame, startFrame: startFrame })), jsx_runtime_1.jsx(RenderModalOutputName_1.RenderModalOutputName, { existence: existence, inputStyle: layout_2.input, outName: outName, onValueChange: onValueChange, validationMessage: validationMessage, label: renderMode === 'sequence' ? 'Folder name' : 'Output name' }), jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
104
+ ] })) : null, renderMode === 'still' ? null : (jsx_runtime_1.jsx(FrameRangeSetting_1.FrameRangeSetting, { durationInFrames: resolvedComposition.durationInFrames, endFrame: endFrame, setEndFrame: setEndFrame, setStartFrame: setStartFrame, startFrame: startFrame })), showOutputName ? (jsx_runtime_1.jsx(RenderModalOutputName_1.RenderModalOutputName, { existence: existence, inputStyle: layout_2.input, outName: outName, onValueChange: onValueChange, validationMessage: validationMessage, label: renderMode === 'sequence' ? 'Folder name' : 'Output name' })) : null, jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
105
105
  jsx_runtime_1.jsxs("div", { style: layout_2.label, children: ["Log Level ",
106
106
  jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(OptionExplainerBubble_1.OptionExplainerBubble, { id: "logLevelOption" })
107
107
  ] }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(ComboBox_1.Combobox, { values: logLevelOptions, selectedId: logLevel, title: "Log Level" }) })
@@ -5,6 +5,7 @@ import React from 'react';
5
5
  import type { _InternalTypes } from 'remotion';
6
6
  import type { RenderType } from './RenderModalAdvanced';
7
7
  type RenderModalProps = {
8
+ readonly readOnlyStudio: boolean;
8
9
  readonly compositionId: string;
9
10
  readonly initialFrame: number;
10
11
  readonly initialVideoImageFormat: VideoImageFormat | null;
@@ -8,6 +8,8 @@ const react_1 = require("react");
8
8
  const ShortcutHint_1 = require("../../error-overlay/remotion-overlay/ShortcutHint");
9
9
  const colors_1 = require("../../helpers/colors");
10
10
  const convert_env_variables_1 = require("../../helpers/convert-env-variables");
11
+ const copy_text_1 = require("../../helpers/copy-text");
12
+ const make_render_command_1 = require("../../helpers/make-render-command");
11
13
  const render_modal_sections_1 = require("../../helpers/render-modal-sections");
12
14
  const use_keybinding_1 = require("../../helpers/use-keybinding");
13
15
  const audio_1 = require("../../icons/audio");
@@ -23,6 +25,7 @@ const Button_1 = require("../Button");
23
25
  const is_menu_item_1 = require("../Menu/is-menu-item");
24
26
  const ModalHeader_1 = require("../ModalHeader");
25
27
  const DismissableModal_1 = require("../NewComposition/DismissableModal");
28
+ const NotificationCenter_1 = require("../Notifications/NotificationCenter");
26
29
  const OptionsPanel_1 = require("../OptionsPanel");
27
30
  const actions_1 = require("../RenderQueue/actions");
28
31
  const SegmentedControl_1 = require("../SegmentedControl");
@@ -58,7 +61,7 @@ const reducer = (state, action) => {
58
61
  }
59
62
  return state;
60
63
  };
61
- const 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, }) => {
64
+ const RenderModal = ({ readOnlyStudio, 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, }) => {
62
65
  const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
63
66
  const context = (0, react_1.useContext)(ResolveCompositionBeforeModal_1.ResolvedCompositionContext);
64
67
  if (!context) {
@@ -753,8 +756,148 @@ const RenderModal = ({ initialFrame, initialVideoImageFormat, initialStillImageF
753
756
  });
754
757
  const { tab, setTab, shownTabs } = (0, render_modal_sections_1.useRenderModalSections)(renderMode, codec);
755
758
  const { registerKeybinding } = (0, use_keybinding_1.useKeybinding)();
756
- const renderDisabled = state.type === 'load' || !outnameValidation.valid;
759
+ const readOnlyRenderCommand = (0, react_1.useMemo)(() => {
760
+ if (!readOnlyStudio) {
761
+ return null;
762
+ }
763
+ return (0, make_render_command_1.makeReadOnlyStudioRenderCommand)({
764
+ remotionVersion: window.remotion_version,
765
+ locationHref: window.location.href,
766
+ compositionId: resolvedComposition.id,
767
+ outName,
768
+ renderMode,
769
+ renderDefaults,
770
+ durationInFrames: resolvedComposition.durationInFrames,
771
+ concurrency,
772
+ frame,
773
+ startFrame,
774
+ endFrame,
775
+ stillImageFormat,
776
+ sequenceImageFormat,
777
+ videoImageFormat,
778
+ jpegQuality: renderMode === 'video'
779
+ ? stillImageFormat === 'jpeg'
780
+ ? jpegQuality
781
+ : null
782
+ : renderMode === 'audio'
783
+ ? null
784
+ : jpegQuality,
785
+ codec,
786
+ muted,
787
+ enforceAudioTrack,
788
+ proResProfile,
789
+ x264Preset,
790
+ pixelFormat,
791
+ crf: qualityControlType === 'crf' &&
792
+ hardwareAcceleration !== 'if-possible' &&
793
+ hardwareAcceleration !== 'required'
794
+ ? crf
795
+ : null,
796
+ videoBitrate,
797
+ audioBitrate,
798
+ audioCodec,
799
+ everyNthFrame,
800
+ numberOfGifLoops,
801
+ disallowParallelEncoding,
802
+ encodingBufferSize,
803
+ encodingMaxRate,
804
+ forSeamlessAacConcatenation,
805
+ separateAudioTo,
806
+ colorSpace,
807
+ scale,
808
+ logLevel,
809
+ delayRenderTimeout,
810
+ hardwareAcceleration,
811
+ chromeMode,
812
+ headless,
813
+ disableWebSecurity,
814
+ ignoreCertificateErrors,
815
+ gl: openGlOption === 'default' ? null : openGlOption,
816
+ userAgent,
817
+ multiProcessOnLinux,
818
+ darkMode,
819
+ offthreadVideoCacheSizeInBytes,
820
+ offthreadVideoThreads,
821
+ mediaCacheSizeInBytes,
822
+ beepOnFinish,
823
+ repro,
824
+ metadata,
825
+ envVariables: (0, convert_env_variables_1.envVariablesArrayToObject)(envVariables),
826
+ inputProps,
827
+ });
828
+ }, [
829
+ audioBitrate,
830
+ audioCodec,
831
+ beepOnFinish,
832
+ chromeMode,
833
+ codec,
834
+ colorSpace,
835
+ concurrency,
836
+ crf,
837
+ darkMode,
838
+ delayRenderTimeout,
839
+ disableWebSecurity,
840
+ disallowParallelEncoding,
841
+ endFrame,
842
+ encodingBufferSize,
843
+ encodingMaxRate,
844
+ enforceAudioTrack,
845
+ envVariables,
846
+ everyNthFrame,
847
+ frame,
848
+ forSeamlessAacConcatenation,
849
+ hardwareAcceleration,
850
+ headless,
851
+ ignoreCertificateErrors,
852
+ inputProps,
853
+ jpegQuality,
854
+ logLevel,
855
+ mediaCacheSizeInBytes,
856
+ metadata,
857
+ multiProcessOnLinux,
858
+ muted,
859
+ numberOfGifLoops,
860
+ offthreadVideoCacheSizeInBytes,
861
+ offthreadVideoThreads,
862
+ openGlOption,
863
+ outName,
864
+ pixelFormat,
865
+ proResProfile,
866
+ qualityControlType,
867
+ readOnlyStudio,
868
+ renderDefaults,
869
+ renderMode,
870
+ repro,
871
+ resolvedComposition.durationInFrames,
872
+ resolvedComposition.id,
873
+ scale,
874
+ separateAudioTo,
875
+ sequenceImageFormat,
876
+ startFrame,
877
+ stillImageFormat,
878
+ userAgent,
879
+ videoImageFormat,
880
+ videoBitrate,
881
+ x264Preset,
882
+ ]);
883
+ const [commandCopiedAt, setCommandCopiedAt] = (0, react_1.useState)(null);
884
+ const renderDisabled = readOnlyStudio
885
+ ? false
886
+ : !outnameValidation.valid || state.type === 'load';
757
887
  const trigger = (0, react_1.useCallback)(() => {
888
+ if (readOnlyStudio) {
889
+ if (!readOnlyRenderCommand) {
890
+ return;
891
+ }
892
+ (0, copy_text_1.copyText)(readOnlyRenderCommand)
893
+ .then(() => {
894
+ setCommandCopiedAt(Date.now());
895
+ })
896
+ .catch((err) => {
897
+ (0, NotificationCenter_1.showNotification)(`Could not copy: ${err.message}`, 2000);
898
+ });
899
+ return;
900
+ }
758
901
  if (renderMode === 'still') {
759
902
  onClickStill();
760
903
  }
@@ -764,7 +907,23 @@ const RenderModal = ({ initialFrame, initialVideoImageFormat, initialStillImageF
764
907
  else {
765
908
  onClickVideo();
766
909
  }
767
- }, [renderMode, onClickStill, onClickSequence, onClickVideo]);
910
+ }, [
911
+ onClickSequence,
912
+ onClickStill,
913
+ onClickVideo,
914
+ readOnlyRenderCommand,
915
+ readOnlyStudio,
916
+ renderMode,
917
+ ]);
918
+ (0, react_1.useEffect)(() => {
919
+ if (commandCopiedAt === null) {
920
+ return;
921
+ }
922
+ const timeout = setTimeout(() => {
923
+ setCommandCopiedAt(null);
924
+ }, 2000);
925
+ return () => clearTimeout(timeout);
926
+ }, [commandCopiedAt]);
768
927
  (0, react_1.useEffect)(() => {
769
928
  if (renderDisabled) {
770
929
  return;
@@ -805,7 +964,13 @@ const RenderModal = ({ initialFrame, initialVideoImageFormat, initialStillImageF
805
964
  jsx_runtime_1.jsx(SegmentedControl_1.SegmentedControl, { items: renderTabOptions, needsWrapping: false }), jsx_runtime_1.jsx("div", { style: render_modals_1.flexer }), jsx_runtime_1.jsxs(Button_1.Button, { autoFocus: true, onClick: trigger, disabled: renderDisabled, style: {
806
965
  ...render_modals_1.buttonStyle,
807
966
  backgroundColor: outnameValidation.valid ? colors_1.BLUE : colors_1.BLUE_DISABLED,
808
- }, children: [state.type === 'idle' ? `Render ${renderMode}` : 'Rendering...', jsx_runtime_1.jsx(ShortcutHint_1.ShortcutHint, { keyToPress: "\u21B5", cmdOrCtrl: true })
967
+ }, children: [readOnlyStudio
968
+ ? commandCopiedAt
969
+ ? 'Copied command!'
970
+ : 'Copy command'
971
+ : state.type === 'idle'
972
+ ? `Render ${renderMode}`
973
+ : 'Rendering...', jsx_runtime_1.jsx(ShortcutHint_1.ShortcutHint, { keyToPress: "\u21B5", cmdOrCtrl: true })
809
974
  ] })
810
975
  ] }), jsx_runtime_1.jsxs("div", { style: render_modals_1.horizontalLayout, children: [
811
976
  jsx_runtime_1.jsxs("div", { style: render_modals_1.leftSidebar, children: [shownTabs.includes('general') ? (jsx_runtime_1.jsxs(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'general', onClick: () => setTab('general'), children: [
@@ -820,7 +985,7 @@ const RenderModal = ({ initialFrame, initialVideoImageFormat, initialStillImageF
820
985
  jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(gif_1.GifIcon, { style: render_modals_1.icon }) }),
821
986
  "GIF"] })) : null, shownTabs.includes('advanced') ? (jsx_runtime_1.jsxs(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'advanced', onClick: () => setTab('advanced'), children: [
822
987
  jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(gear_1.GearIcon, { style: render_modals_1.icon }) }),
823
- "Other"] })) : null] }), jsx_runtime_1.jsx("div", { style: render_modals_1.optionsPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: tab === 'general' ? (jsx_runtime_1.jsx(RenderModalBasic_1.RenderModalBasic, { codec: codec, resolvedComposition: resolvedComposition, frame: frame, imageFormatOptions: imageFormatOptions, outName: outName, proResProfile: proResProfile, renderMode: renderMode, setVideoCodec: setCodec, setFrame: setFrame, setOutName: setOutName, setProResProfile: setProResProfile, endFrame: endFrame, setEndFrame: setEndFrame, setStartFrame: setStartFrame, setVerboseLogging: setLogLevel, logLevel: logLevel, startFrame: startFrame, validationMessage: outnameValidation.valid ? null : outnameValidation.error.message })) : tab === 'picture' ? (jsx_runtime_1.jsx(RenderModalPicture_1.RenderModalPicture, { renderMode: renderMode, scale: scale, setScale: setScale, pixelFormat: pixelFormat, pixelFormatOptions: pixelFormatOptions, imageFormatOptions: imageFormatOptions, crf: crf, setCrf: setCrf, customTargetVideoBitrate: customTargetVideoBitrate, maxCrf: maxCrf, minCrf: minCrf, jpegQuality: jpegQuality, qualityControlType: qualityControlType, setJpegQuality: setJpegQuality, setColorSpace: setColorSpace, colorSpace: colorSpace, setCustomTargetVideoBitrateValue: setCustomTargetVideoBitrateValue, setQualityControl: setQualityControl, videoImageFormat: videoImageFormat, stillImageFormat: stillImageFormat, shouldDisplayQualityControlPicker: supportsBothQualityControls, encodingBufferSize: encodingBufferSize, setEncodingBufferSize: setEncodingBufferSize, encodingMaxRate: encodingMaxRate, setEncodingMaxRate: setEncodingMaxRate, compositionWidth: resolvedComposition.width, compositionHeight: resolvedComposition.height })) : tab === 'audio' ? (jsx_runtime_1.jsx(RenderModalAudio_1.RenderModalAudio, { muted: muted, renderMode: renderMode, setMuted: setMuted, codec: codec, audioCodec: audioCodec, setAudioCodec: setAudioCodec, enforceAudioTrack: enforceAudioTrack, setEnforceAudioTrackState: setEnforceAudioTrackState, customTargetAudioBitrate: customTargetAudioBitrate, setCustomTargetAudioBitrateValue: setCustomTargetAudioBitrateValue, setShouldHaveCustomTargetAudioBitrate: setShouldHaveCustomTargetAudioBitrate, shouldHaveCustomTargetAudioBitrate: shouldHaveCustomTargetAudioBitrate, forSeamlessAacConcatenation: forSeamlessAacConcatenation, setForSeamlessAacConcatenation: setForSeamlessAacConcatenation, separateAudioTo: separateAudioTo, setSeparateAudioTo: setSeparateAudioTo, outName: outName })) : tab === 'gif' ? (jsx_runtime_1.jsx(RenderModalGif_1.RenderModalGif, { everyNthFrame: everyNthFrame, limitNumberOfGifLoops: limitNumberOfGifLoops, numberOfGifLoopsSetting: numberOfGifLoopsSetting, setEveryNthFrameSetting: setEveryNthFrameSetting, setLimitNumberOfGifLoops: setLimitNumberOfGifLoops, setNumberOfGifLoopsSetting: setNumberOfGifLoopsSetting })) : tab === 'data' ? (jsx_runtime_1.jsx(DataEditor_1.DataEditor, { defaultProps: inputProps, setDefaultProps: setInputProps, unresolvedComposition: unresolvedComposition, mayShowSaveButton: false, propsEditType: "input-props", saving: saving, setSaving: setSaving, readOnlyStudio: false })) : (jsx_runtime_1.jsx(RenderModalAdvanced_1.RenderModalAdvanced, { x264Preset: x264Preset, setx264Preset: setx264Preset, concurrency: concurrency, maxConcurrency: maxConcurrency, minConcurrency: minConcurrency, renderMode: renderMode, setConcurrency: setConcurrency, delayRenderTimeout: delayRenderTimeout, setDelayRenderTimeout: setDelayRenderTimeout, disallowParallelEncoding: disallowParallelEncoding, setDisallowParallelEncoding: setDisallowParallelEncoding, setDisableWebSecurity: setDisableWebSecurity, setIgnoreCertificateErrors: setIgnoreCertificateErrors, setHeadless: setHeadless, headless: headless, ignoreCertificateErrors: ignoreCertificateErrors, disableWebSecurity: disableWebSecurity, openGlOption: openGlOption, setOpenGlOption: setOpenGlOption, setEnvVariables: setEnvVariables, envVariables: envVariables, offthreadVideoCacheSizeInBytes: offthreadVideoCacheSizeInBytes, setMediaCacheSizeInBytes: setMediaCacheSizeInBytes, mediaCacheSizeInBytes: mediaCacheSizeInBytes, setOffthreadVideoCacheSizeInBytes: setOffthreadVideoCacheSizeInBytes, offthreadVideoThreads: offthreadVideoThreads, setOffthreadVideoThreads: setOffthreadVideoThreads, enableMultiProcessOnLinux: multiProcessOnLinux, setChromiumMultiProcessOnLinux: setChromiumMultiProcessOnLinux, codec: codec, userAgent: userAgent, setUserAgent: setUserAgent, setBeep: setBeepOnFinish, beep: beepOnFinish, repro: repro, setRepro: setRepro, hardwareAcceleration: hardwareAcceleration, setHardwareAcceleration: setHardwareAcceleration, chromeModeOption: chromeMode, setChromeModeOption: setChromeMode, darkMode: darkMode, setDarkMode: setDarkMode })) })
988
+ "Other"] })) : null] }), jsx_runtime_1.jsx("div", { style: render_modals_1.optionsPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: tab === 'general' ? (jsx_runtime_1.jsx(RenderModalBasic_1.RenderModalBasic, { codec: codec, resolvedComposition: resolvedComposition, frame: frame, imageFormatOptions: imageFormatOptions, outName: outName, proResProfile: proResProfile, renderMode: renderMode, setVideoCodec: setCodec, setFrame: setFrame, setOutName: setOutName, setProResProfile: setProResProfile, endFrame: endFrame, setEndFrame: setEndFrame, setStartFrame: setStartFrame, setVerboseLogging: setLogLevel, logLevel: logLevel, showOutputName: !readOnlyStudio, startFrame: startFrame, validationMessage: outnameValidation.valid ? null : outnameValidation.error.message })) : tab === 'picture' ? (jsx_runtime_1.jsx(RenderModalPicture_1.RenderModalPicture, { renderMode: renderMode, scale: scale, setScale: setScale, pixelFormat: pixelFormat, pixelFormatOptions: pixelFormatOptions, imageFormatOptions: imageFormatOptions, crf: crf, setCrf: setCrf, customTargetVideoBitrate: customTargetVideoBitrate, maxCrf: maxCrf, minCrf: minCrf, jpegQuality: jpegQuality, qualityControlType: qualityControlType, setJpegQuality: setJpegQuality, setColorSpace: setColorSpace, colorSpace: colorSpace, setCustomTargetVideoBitrateValue: setCustomTargetVideoBitrateValue, setQualityControl: setQualityControl, videoImageFormat: videoImageFormat, stillImageFormat: stillImageFormat, shouldDisplayQualityControlPicker: supportsBothQualityControls, encodingBufferSize: encodingBufferSize, setEncodingBufferSize: setEncodingBufferSize, encodingMaxRate: encodingMaxRate, setEncodingMaxRate: setEncodingMaxRate, compositionWidth: resolvedComposition.width, compositionHeight: resolvedComposition.height })) : tab === 'audio' ? (jsx_runtime_1.jsx(RenderModalAudio_1.RenderModalAudio, { muted: muted, renderMode: renderMode, setMuted: setMuted, codec: codec, audioCodec: audioCodec, setAudioCodec: setAudioCodec, enforceAudioTrack: enforceAudioTrack, setEnforceAudioTrackState: setEnforceAudioTrackState, customTargetAudioBitrate: customTargetAudioBitrate, setCustomTargetAudioBitrateValue: setCustomTargetAudioBitrateValue, setShouldHaveCustomTargetAudioBitrate: setShouldHaveCustomTargetAudioBitrate, shouldHaveCustomTargetAudioBitrate: shouldHaveCustomTargetAudioBitrate, forSeamlessAacConcatenation: forSeamlessAacConcatenation, setForSeamlessAacConcatenation: setForSeamlessAacConcatenation, separateAudioTo: separateAudioTo, setSeparateAudioTo: setSeparateAudioTo, outName: outName })) : tab === 'gif' ? (jsx_runtime_1.jsx(RenderModalGif_1.RenderModalGif, { everyNthFrame: everyNthFrame, limitNumberOfGifLoops: limitNumberOfGifLoops, numberOfGifLoopsSetting: numberOfGifLoopsSetting, setEveryNthFrameSetting: setEveryNthFrameSetting, setLimitNumberOfGifLoops: setLimitNumberOfGifLoops, setNumberOfGifLoopsSetting: setNumberOfGifLoopsSetting })) : tab === 'data' ? (jsx_runtime_1.jsx(DataEditor_1.DataEditor, { defaultProps: inputProps, setDefaultProps: setInputProps, unresolvedComposition: unresolvedComposition, mayShowSaveButton: false, propsEditType: "input-props", saving: saving, setSaving: setSaving, readOnlyStudio: readOnlyStudio })) : (jsx_runtime_1.jsx(RenderModalAdvanced_1.RenderModalAdvanced, { x264Preset: x264Preset, setx264Preset: setx264Preset, concurrency: concurrency, maxConcurrency: maxConcurrency, minConcurrency: minConcurrency, renderMode: renderMode, setConcurrency: setConcurrency, delayRenderTimeout: delayRenderTimeout, setDelayRenderTimeout: setDelayRenderTimeout, disallowParallelEncoding: disallowParallelEncoding, setDisallowParallelEncoding: setDisallowParallelEncoding, setDisableWebSecurity: setDisableWebSecurity, setIgnoreCertificateErrors: setIgnoreCertificateErrors, setHeadless: setHeadless, headless: headless, ignoreCertificateErrors: ignoreCertificateErrors, disableWebSecurity: disableWebSecurity, openGlOption: openGlOption, setOpenGlOption: setOpenGlOption, setEnvVariables: setEnvVariables, envVariables: envVariables, offthreadVideoCacheSizeInBytes: offthreadVideoCacheSizeInBytes, setMediaCacheSizeInBytes: setMediaCacheSizeInBytes, mediaCacheSizeInBytes: mediaCacheSizeInBytes, setOffthreadVideoCacheSizeInBytes: setOffthreadVideoCacheSizeInBytes, offthreadVideoThreads: offthreadVideoThreads, setOffthreadVideoThreads: setOffthreadVideoThreads, enableMultiProcessOnLinux: multiProcessOnLinux, setChromiumMultiProcessOnLinux: setChromiumMultiProcessOnLinux, codec: codec, userAgent: userAgent, setUserAgent: setUserAgent, setBeep: setBeepOnFinish, beep: beepOnFinish, repro: repro, setRepro: setRepro, hardwareAcceleration: hardwareAcceleration, setHardwareAcceleration: setHardwareAcceleration, chromeModeOption: chromeMode, setChromeModeOption: setChromeMode, darkMode: darkMode, setDarkMode: setDarkMode })) })
824
989
  ] })
825
990
  ] }));
826
991
  };
@@ -68,14 +68,15 @@ const WebRenderModalAudio = ({ renderMode, muted, setMuted, audioCodec, setAudio
68
68
  const audioBitrateOptions = (0, react_1.useMemo)(() => (0, quality_options_1.getQualityOptions)(audioBitrate, setAudioBitrate), [audioBitrate, setAudioBitrate]);
69
69
  const isAudioOnly = renderMode === 'audio';
70
70
  const showAudioSettings = isAudioOnly || !muted;
71
+ const showAudioCodecSetting = !isAudioOnly || containerSupported.length > 1;
71
72
  return (jsx_runtime_1.jsxs("div", { style: container, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: [isAudioOnly ? null : (jsx_runtime_1.jsx(MutedSetting_1.MutedSetting, { enforceAudioTrack: false, muted: muted, setMuted: setMuted })), showAudioSettings ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [isAudioOnly ? null : jsx_runtime_1.jsx(RenderModalHr_1.RenderModalHr, {}), jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
72
73
  jsx_runtime_1.jsxs("div", { style: layout_2.label, children: ["Audio Quality",
73
74
  jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 })
74
75
  ] }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(ComboBox_1.Combobox, { values: audioBitrateOptions, selectedId: audioBitrate, title: "Audio Quality" }) })
75
- ] }), jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
76
+ ] }), showAudioCodecSetting ? (jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
76
77
  jsx_runtime_1.jsxs("div", { style: layout_2.label, children: ["Audio Codec",
77
78
  jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 })
78
79
  ] }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(ComboBox_1.Combobox, { values: audioCodecOptions, selectedId: audioCodec, title: "Audio Codec" }) })
79
- ] }), effectiveAudioCodec !== audioCodec ? (jsx_runtime_1.jsxs("div", { style: fallbackNoticeStyle, children: [humanReadableWebAudioCodec(audioCodec), " is not available in this browser. Using ", humanReadableWebAudioCodec(effectiveAudioCodec), ' ', "instead."] })) : null] })) : null] }));
80
+ ] })) : null, showAudioCodecSetting && effectiveAudioCodec !== audioCodec ? (jsx_runtime_1.jsxs("div", { style: fallbackNoticeStyle, children: [humanReadableWebAudioCodec(audioCodec), " is not available in this browser. Using ", humanReadableWebAudioCodec(effectiveAudioCodec), ' ', "instead."] })) : null] })) : null] }));
80
81
  };
81
82
  exports.WebRenderModalAudio = WebRenderModalAudio;
@@ -80,6 +80,7 @@ const SidebarRenderButton = ({ composition, visible }) => {
80
80
  initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes,
81
81
  renderDefaults: defaults,
82
82
  initialDarkMode: defaults.darkMode,
83
+ readOnlyStudio: false,
83
84
  });
84
85
  if (isMobileLayout) {
85
86
  setSidebarCollapsedState({ left: 'collapsed', right: 'collapsed' });