@remotion/studio 4.0.402 → 4.0.404

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.
Files changed (26) hide show
  1. package/dist/components/Menu/MenuSubItem.d.ts +1 -0
  2. package/dist/components/Menu/MenuSubItem.js +13 -5
  3. package/dist/components/NewComposition/ComboBox.d.ts +1 -0
  4. package/dist/components/NewComposition/MenuContent.js +18 -12
  5. package/dist/components/RenderModal/WebRenderModal.js +29 -4
  6. package/dist/components/RenderModal/WebRenderModalAudio.d.ts +9 -1
  7. package/dist/components/RenderModal/WebRenderModalAudio.js +51 -2
  8. package/dist/components/RenderModal/WebRenderModalBasic.d.ts +2 -1
  9. package/dist/components/RenderModal/WebRenderModalBasic.js +21 -60
  10. package/dist/components/RenderModal/WebRenderModalLicense.js +46 -9
  11. package/dist/components/RenderModal/WebRenderModalLicenseKeyDetails.d.ts +13 -0
  12. package/dist/components/RenderModal/WebRenderModalLicenseKeyDetails.js +49 -0
  13. package/dist/components/RenderModal/WebRenderModalPicture.js +2 -60
  14. package/dist/components/RenderModal/quality-options.d.ts +3 -0
  15. package/dist/components/RenderModal/quality-options.js +26 -0
  16. package/dist/components/RenderModal/use-encodable-audio-codecs.d.ts +2 -0
  17. package/dist/components/RenderModal/use-encodable-audio-codecs.js +49 -0
  18. package/dist/components/RenderModal/use-encodable-video-codecs.d.ts +2 -0
  19. package/dist/components/RenderModal/use-encodable-video-codecs.js +49 -0
  20. package/dist/esm/{chunk-71hmw645.js → chunk-5yga8730.js} +784 -444
  21. package/dist/esm/internals.mjs +784 -444
  22. package/dist/esm/previewEntry.mjs +798 -458
  23. package/dist/esm/renderEntry.mjs +1 -1
  24. package/dist/icons/check-circle-filled.d.ts +2 -0
  25. package/dist/icons/check-circle-filled.js +6 -0
  26. package/package.json +9 -9
@@ -16,4 +16,5 @@ export declare const MenuSubItem: React.FC<{
16
16
  readonly onNextMenu: () => void;
17
17
  readonly subMenuActivated: SubMenuActivated;
18
18
  readonly setSubMenuActivated: React.Dispatch<React.SetStateAction<SubMenuActivated>>;
19
+ readonly disabled?: boolean;
19
20
  }>;
@@ -44,7 +44,7 @@ const leftSpace = {
44
44
  justifyContent: 'center',
45
45
  alignItems: 'center',
46
46
  };
47
- const MenuSubItem = ({ label, leaveLeftSpace, leftItem, onActionChosen, id, selected, onItemSelected, keyHint, subMenu, onQuitMenu, subMenuActivated, setSubMenuActivated, }) => {
47
+ const MenuSubItem = ({ label, leaveLeftSpace, leftItem, onActionChosen, id, selected, onItemSelected, keyHint, subMenu, onQuitMenu, subMenuActivated, setSubMenuActivated, disabled, }) => {
48
48
  const [hovered, setHovered] = (0, react_1.useState)(false);
49
49
  const ref = (0, react_1.useRef)(null);
50
50
  const size = player_1.PlayerInternals.useElementSize(ref, {
@@ -56,21 +56,29 @@ const MenuSubItem = ({ label, leaveLeftSpace, leftItem, onActionChosen, id, sele
56
56
  const style = (0, react_1.useMemo)(() => {
57
57
  return {
58
58
  ...container,
59
- backgroundColor: selected ? colors_1.CLEAR_HOVER : 'transparent',
59
+ backgroundColor: selected && !disabled ? colors_1.CLEAR_HOVER : 'transparent',
60
+ opacity: disabled ? 0.5 : 1,
61
+ cursor: disabled ? 'not-allowed' : 'default',
60
62
  };
61
- }, [selected]);
63
+ }, [selected, disabled]);
62
64
  const onPointerUp = (0, react_1.useCallback)((e) => {
65
+ if (disabled) {
66
+ return;
67
+ }
63
68
  if (subMenu) {
64
69
  setSubMenuActivated('with-mouse');
65
70
  setHovered(true);
66
71
  return;
67
72
  }
68
73
  onActionChosen(id, e);
69
- }, [id, onActionChosen, setSubMenuActivated, subMenu]);
74
+ }, [disabled, id, onActionChosen, setSubMenuActivated, subMenu]);
70
75
  const onPointerEnter = (0, react_1.useCallback)(() => {
76
+ if (disabled) {
77
+ return;
78
+ }
71
79
  onItemSelected(id);
72
80
  setHovered(true);
73
- }, [id, onItemSelected]);
81
+ }, [disabled, id, onItemSelected]);
74
82
  const onPointerLeave = (0, react_1.useCallback)(() => {
75
83
  setHovered(false);
76
84
  }, []);
@@ -18,6 +18,7 @@ export type SelectionItem = {
18
18
  leftItem: React.ReactNode;
19
19
  subMenu: SubMenu | null;
20
20
  quickSwitcherLabel: string | null;
21
+ disabled?: boolean;
21
22
  };
22
23
  export type ComboboxValue = DividerItem | SelectionItem;
23
24
  export declare const Combobox: React.FC<{
@@ -38,6 +38,9 @@ const MenuContent = ({ onHide, values, preselectIndex, onNextMenu, onPreviousMen
38
38
  const onItemSelected = (0, react_1.useCallback)((id) => {
39
39
  setSelectedItem(id);
40
40
  }, []);
41
+ const isItemSelectable = (0, react_1.useCallback)((v) => {
42
+ return v.type !== 'divider' && !v.disabled;
43
+ }, []);
41
44
  const onArrowUp = (0, react_1.useCallback)(() => {
42
45
  setSelectedItem((prevItem) => {
43
46
  if (prevItem === null) {
@@ -47,34 +50,34 @@ const MenuContent = ({ onHide, values, preselectIndex, onNextMenu, onPreviousMen
47
50
  if ((topItemCanBeUnselected && index === 0) || prevItem === null) {
48
51
  return null;
49
52
  }
50
- const previousItems = values.filter((v, i) => i < index && v.type !== 'divider');
53
+ const previousItems = values.filter((v, i) => i < index && isItemSelectable(v));
51
54
  if (previousItems.length > 0) {
52
55
  return previousItems[previousItems.length - 1].id;
53
56
  }
54
- const firstNonDivider = values.find((v) => v.type !== 'divider');
55
- if (firstNonDivider) {
56
- return firstNonDivider.id;
57
+ const firstSelectable = values.find((v) => isItemSelectable(v));
58
+ if (firstSelectable) {
59
+ return firstSelectable.id;
57
60
  }
58
61
  throw new Error('could not find previous item');
59
62
  });
60
- }, [topItemCanBeUnselected, values]);
63
+ }, [topItemCanBeUnselected, values, isItemSelectable]);
61
64
  const onArrowDown = (0, react_1.useCallback)(() => {
62
65
  setSelectedItem((prevItem) => {
63
66
  const index = values.findIndex((val) => val.id === prevItem);
64
- const nextItem = values.find((v, i) => i > index && v.type !== 'divider');
67
+ const nextItem = values.find((v, i) => i > index && isItemSelectable(v));
65
68
  if (nextItem) {
66
69
  return nextItem.id;
67
70
  }
68
- const lastNonDivider = values
71
+ const lastSelectable = values
69
72
  .slice()
70
73
  .reverse()
71
- .find((v) => v.type !== 'divider');
72
- if (lastNonDivider) {
73
- return lastNonDivider.id;
74
+ .find((v) => isItemSelectable(v));
75
+ if (lastSelectable) {
76
+ return lastSelectable.id;
74
77
  }
75
78
  throw new Error('could not find next item');
76
79
  });
77
- }, [values]);
80
+ }, [values, isItemSelectable]);
78
81
  const onEnter = (0, react_1.useCallback)(() => {
79
82
  if (selectedItem === null) {
80
83
  return onHide();
@@ -86,6 +89,9 @@ const MenuContent = ({ onHide, values, preselectIndex, onNextMenu, onPreviousMen
86
89
  if (item.type === 'divider') {
87
90
  throw new Error('cannot find divider');
88
91
  }
92
+ if (item.disabled) {
93
+ return;
94
+ }
89
95
  if (item.subMenu) {
90
96
  return setSubMenuActivated('without-mouse');
91
97
  }
@@ -249,7 +255,7 @@ const MenuContent = ({ onHide, values, preselectIndex, onNextMenu, onPreviousMen
249
255
  }
250
256
  onHide();
251
257
  };
252
- return ((0, jsx_runtime_1.jsx)(MenuSubItem_1.MenuSubItem, { selected: item.id === selectedItem, onActionChosen: onClick, onItemSelected: onItemSelected, label: item.label, id: item.id, keyHint: item.keyHint, leaveLeftSpace: leaveLeftSpace, leftItem: item.leftItem, subMenu: item.subMenu, onQuitMenu: onHide, onNextMenu: onNextMenu, subMenuActivated: subMenuActivated, setSubMenuActivated: setSubMenuActivated }, item.id));
258
+ return ((0, jsx_runtime_1.jsx)(MenuSubItem_1.MenuSubItem, { selected: item.id === selectedItem, onActionChosen: onClick, onItemSelected: onItemSelected, label: item.label, id: item.id, keyHint: item.keyHint, leaveLeftSpace: leaveLeftSpace, leftItem: item.leftItem, subMenu: item.subMenu, onQuitMenu: onHide, onNextMenu: onNextMenu, subMenuActivated: subMenuActivated, setSubMenuActivated: setSubMenuActivated, disabled: item.disabled }, item.id));
253
259
  }) }));
254
260
  };
255
261
  exports.MenuContent = MenuContent;
@@ -22,6 +22,8 @@ const DataEditor_1 = require("./DataEditor");
22
22
  const get_string_before_suffix_1 = require("./get-string-before-suffix");
23
23
  const render_modals_1 = require("./render-modals");
24
24
  const ResolveCompositionBeforeModal_1 = require("./ResolveCompositionBeforeModal");
25
+ const use_encodable_audio_codecs_1 = require("./use-encodable-audio-codecs");
26
+ const use_encodable_video_codecs_1 = require("./use-encodable-video-codecs");
25
27
  const WebRendererExperimentalBadge_1 = require("./WebRendererExperimentalBadge");
26
28
  const WebRenderModalAdvanced_1 = require("./WebRenderModalAdvanced");
27
29
  const WebRenderModalAudio_1 = require("./WebRenderModalAudio");
@@ -99,6 +101,8 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
99
101
  // Video-specific state
100
102
  const [codec, setCodec] = (0, react_1.useState)('h264');
101
103
  const [container, setContainer] = (0, react_1.useState)('mp4');
104
+ const [audioCodec, setAudioCodec] = (0, react_1.useState)('aac');
105
+ const [audioBitrate, setAudioBitrate] = (0, react_1.useState)('medium');
102
106
  const [videoBitrate, setVideoBitrate] = (0, react_1.useState)('high');
103
107
  const [hardwareAcceleration, setHardwareAcceleration] = (0, react_1.useState)('no-preference');
104
108
  const [keyframeIntervalInSeconds, setKeyframeIntervalInSeconds] = (0, react_1.useState)(5);
@@ -108,6 +112,22 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
108
112
  const [transparent, setTransparent] = (0, react_1.useState)(false);
109
113
  const [muted, setMuted] = (0, react_1.useState)(false);
110
114
  const [licenseKey, setLicenseKey] = (0, react_1.useState)(initialLicenseKey);
115
+ const encodableAudioCodecs = (0, use_encodable_audio_codecs_1.useEncodableAudioCodecs)(container);
116
+ const encodableVideoCodecs = (0, use_encodable_video_codecs_1.useEncodableVideoCodecs)(container);
117
+ const effectiveAudioCodec = (0, react_1.useMemo)(() => {
118
+ var _a;
119
+ if (encodableAudioCodecs.includes(audioCodec)) {
120
+ return audioCodec;
121
+ }
122
+ return (_a = encodableAudioCodecs[0]) !== null && _a !== void 0 ? _a : audioCodec;
123
+ }, [audioCodec, encodableAudioCodecs]);
124
+ const effectiveVideoCodec = (0, react_1.useMemo)(() => {
125
+ var _a;
126
+ if (encodableVideoCodecs.includes(codec)) {
127
+ return codec;
128
+ }
129
+ return (_a = encodableVideoCodecs[0]) !== null && _a !== void 0 ? _a : codec;
130
+ }, [codec, encodableVideoCodecs]);
111
131
  const finalEndFrame = (0, react_1.useMemo)(() => {
112
132
  if (endFrame === null) {
113
133
  return resolvedComposition.durationInFrames - 1;
@@ -149,6 +169,7 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
149
169
  }, []);
150
170
  const setContainerFormat = (0, react_1.useCallback)((newContainer) => {
151
171
  setContainer(newContainer);
172
+ setAudioCodec((0, web_renderer_1.getDefaultAudioCodecForContainer)(newContainer));
152
173
  setOutName((prev) => {
153
174
  const newFileName = (0, get_string_before_suffix_1.getStringBeforeSuffix)(prev) + '.' + newContainer;
154
175
  return newFileName;
@@ -162,7 +183,7 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
162
183
  return newFileName;
163
184
  });
164
185
  }
165
- else {
186
+ else if (newMode === 'still') {
166
187
  setOutName((prev) => {
167
188
  const newFileName = (0, get_string_before_suffix_1.getStringBeforeSuffix)(prev) + '.' + imageFormat;
168
189
  return newFileName;
@@ -316,7 +337,9 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
316
337
  delayRenderTimeoutInMilliseconds: delayRenderTimeout,
317
338
  mediaCacheSizeInBytes,
318
339
  logLevel,
319
- videoCodec: codec,
340
+ videoCodec: effectiveVideoCodec,
341
+ audioCodec: effectiveAudioCodec,
342
+ audioBitrate,
320
343
  container,
321
344
  videoBitrate,
322
345
  hardwareAcceleration,
@@ -348,7 +371,9 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
348
371
  delayRenderTimeout,
349
372
  mediaCacheSizeInBytes,
350
373
  logLevel,
351
- codec,
374
+ effectiveVideoCodec,
375
+ effectiveAudioCodec,
376
+ audioBitrate,
352
377
  container,
353
378
  videoBitrate,
354
379
  hardwareAcceleration,
@@ -376,7 +401,7 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
376
401
  }, [renderMode, onRenderStill, onRenderVideo]);
377
402
  return ((0, jsx_runtime_1.jsxs)("div", { style: render_modals_1.outerModalStyle, children: [(0, jsx_runtime_1.jsx)(ModalHeader_1.ModalHeader, { title: `Render ${resolvedComposition.id}` }), (0, jsx_runtime_1.jsxs)("div", { style: render_modals_1.container, children: [(0, jsx_runtime_1.jsx)(SegmentedControl_1.SegmentedControl, { items: renderTabOptions, needsWrapping: false }), (0, jsx_runtime_1.jsx)("div", { style: render_modals_1.flexer }), (0, jsx_runtime_1.jsxs)(Button_1.Button, { autoFocus: true, onClick: onRender, style: render_modals_1.buttonStyle, disabled: !outnameValidation.valid, children: [renderProgress
378
403
  ? `Rendering... ${renderProgress.renderedFrames}/${finalEndFrame}`
379
- : `Render ${renderMode}`, (0, jsx_runtime_1.jsx)(ShortcutHint_1.ShortcutHint, { keyToPress: "\u21B5", cmdOrCtrl: true })] })] }), (0, jsx_runtime_1.jsx)("div", { style: render_modals_1.container, children: (0, jsx_runtime_1.jsx)(WebRendererExperimentalBadge_1.WebRendererExperimentalBadge, {}) }), (0, jsx_runtime_1.jsxs)("div", { style: render_modals_1.horizontalLayout, children: [(0, jsx_runtime_1.jsxs)("div", { style: render_modals_1.leftSidebar, children: [(0, jsx_runtime_1.jsxs)(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'general', onClick: () => setTab('general'), children: [(0, jsx_runtime_1.jsx)("div", { style: render_modals_1.iconContainer, children: (0, jsx_runtime_1.jsx)(file_1.FileIcon, { style: render_modals_1.icon }) }), "General"] }), (0, jsx_runtime_1.jsxs)(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'data', onClick: () => setTab('data'), children: [(0, jsx_runtime_1.jsx)("div", { style: render_modals_1.iconContainer, children: (0, jsx_runtime_1.jsx)(data_1.DataIcon, { style: render_modals_1.icon }) }), "Input Props"] }), renderMode === 'video' ? ((0, jsx_runtime_1.jsxs)(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'picture', onClick: () => setTab('picture'), children: [(0, jsx_runtime_1.jsx)("div", { style: render_modals_1.iconContainer, children: (0, jsx_runtime_1.jsx)(frame_1.PicIcon, { style: render_modals_1.icon }) }), "Picture"] })) : null, renderMode === 'video' ? ((0, jsx_runtime_1.jsxs)(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'audio', onClick: () => setTab('audio'), children: [(0, jsx_runtime_1.jsx)("div", { style: render_modals_1.iconContainer, children: (0, jsx_runtime_1.jsx)(audio_1.AudioIcon, { style: render_modals_1.icon }) }), "Audio"] })) : null, (0, jsx_runtime_1.jsxs)(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'advanced', onClick: () => setTab('advanced'), children: [(0, jsx_runtime_1.jsx)("div", { style: render_modals_1.iconContainer, children: (0, jsx_runtime_1.jsx)(gear_1.GearIcon, { style: render_modals_1.icon }) }), "Other"] }), (0, jsx_runtime_1.jsxs)(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'license', onClick: () => setTab('license'), children: [(0, jsx_runtime_1.jsx)("div", { style: render_modals_1.iconContainer, children: (0, jsx_runtime_1.jsx)(certificate_1.CertificateIcon, { style: render_modals_1.icon }) }), "License"] })] }), (0, jsx_runtime_1.jsx)("div", { style: render_modals_1.optionsPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: tab === 'general' ? ((0, jsx_runtime_1.jsx)(WebRenderModalBasic_1.WebRenderModalBasic, { renderMode: renderMode, resolvedComposition: resolvedComposition, imageFormat: imageFormat, setStillFormat: setStillFormat, frame: frame, onFrameChanged: onFrameChanged, onFrameSetDirectly: onFrameSetDirectly, container: container, setContainerFormat: setContainerFormat, codec: codec, setCodec: setCodec, startFrame: finalStartFrame, setStartFrame: setStartFrame, endFrame: finalEndFrame, setEndFrame: setEndFrame, outName: outName, onOutNameChange: onOutNameChange, validationMessage: outnameValidation.valid ? null : outnameValidation.error.message, logLevel: logLevel, setLogLevel: setLogLevel })) : tab === 'data' ? ((0, jsx_runtime_1.jsx)(DataEditor_1.DataEditor, { defaultProps: inputProps, setDefaultProps: setInputProps, unresolvedComposition: unresolvedComposition, mayShowSaveButton: false, propsEditType: "input-props", saving: saving, setSaving: setSaving, readOnlyStudio: false })) : tab === 'picture' ? ((0, jsx_runtime_1.jsx)(WebRenderModalPicture_1.WebRenderModalPicture, { renderMode: renderMode, videoBitrate: videoBitrate, setVideoBitrate: setVideoBitrate, keyframeIntervalInSeconds: keyframeIntervalInSeconds, setKeyframeIntervalInSeconds: setKeyframeIntervalInSeconds, transparent: transparent, setTransparent: setTransparent })) : tab === 'audio' ? ((0, jsx_runtime_1.jsx)(WebRenderModalAudio_1.WebRenderModalAudio, { muted: muted, setMuted: setMuted })) : tab === 'advanced' ? ((0, jsx_runtime_1.jsx)(WebRenderModalAdvanced_1.WebRenderModalAdvanced, { renderMode: renderMode, delayRenderTimeout: delayRenderTimeout, setDelayRenderTimeout: setDelayRenderTimeout, mediaCacheSizeInBytes: mediaCacheSizeInBytes, setMediaCacheSizeInBytes: setMediaCacheSizeInBytes, hardwareAcceleration: hardwareAcceleration, setHardwareAcceleration: setHardwareAcceleration })) : ((0, jsx_runtime_1.jsx)(WebRenderModalLicense_1.WebRenderModalLicense, { licenseKey: licenseKey, setLicenseKey: setLicenseKey, initialPublicLicenseKey: initialLicenseKey })) })] })] }));
404
+ : `Render ${renderMode}`, (0, jsx_runtime_1.jsx)(ShortcutHint_1.ShortcutHint, { keyToPress: "\u21B5", cmdOrCtrl: true })] })] }), (0, jsx_runtime_1.jsx)("div", { style: render_modals_1.container, children: (0, jsx_runtime_1.jsx)(WebRendererExperimentalBadge_1.WebRendererExperimentalBadge, {}) }), (0, jsx_runtime_1.jsxs)("div", { style: render_modals_1.horizontalLayout, children: [(0, jsx_runtime_1.jsxs)("div", { style: render_modals_1.leftSidebar, children: [(0, jsx_runtime_1.jsxs)(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'general', onClick: () => setTab('general'), children: [(0, jsx_runtime_1.jsx)("div", { style: render_modals_1.iconContainer, children: (0, jsx_runtime_1.jsx)(file_1.FileIcon, { style: render_modals_1.icon }) }), "General"] }), (0, jsx_runtime_1.jsxs)(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'data', onClick: () => setTab('data'), children: [(0, jsx_runtime_1.jsx)("div", { style: render_modals_1.iconContainer, children: (0, jsx_runtime_1.jsx)(data_1.DataIcon, { style: render_modals_1.icon }) }), "Input Props"] }), renderMode === 'video' ? ((0, jsx_runtime_1.jsxs)(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'picture', onClick: () => setTab('picture'), children: [(0, jsx_runtime_1.jsx)("div", { style: render_modals_1.iconContainer, children: (0, jsx_runtime_1.jsx)(frame_1.PicIcon, { style: render_modals_1.icon }) }), "Picture"] })) : null, renderMode === 'video' ? ((0, jsx_runtime_1.jsxs)(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'audio', onClick: () => setTab('audio'), children: [(0, jsx_runtime_1.jsx)("div", { style: render_modals_1.iconContainer, children: (0, jsx_runtime_1.jsx)(audio_1.AudioIcon, { style: render_modals_1.icon }) }), "Audio"] })) : null, (0, jsx_runtime_1.jsxs)(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'advanced', onClick: () => setTab('advanced'), children: [(0, jsx_runtime_1.jsx)("div", { style: render_modals_1.iconContainer, children: (0, jsx_runtime_1.jsx)(gear_1.GearIcon, { style: render_modals_1.icon }) }), "Other"] }), (0, jsx_runtime_1.jsxs)(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'license', onClick: () => setTab('license'), children: [(0, jsx_runtime_1.jsx)("div", { style: render_modals_1.iconContainer, children: (0, jsx_runtime_1.jsx)(certificate_1.CertificateIcon, { style: render_modals_1.icon }) }), "License"] })] }), (0, jsx_runtime_1.jsx)("div", { style: render_modals_1.optionsPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: tab === 'general' ? ((0, jsx_runtime_1.jsx)(WebRenderModalBasic_1.WebRenderModalBasic, { renderMode: renderMode, resolvedComposition: resolvedComposition, imageFormat: imageFormat, setStillFormat: setStillFormat, frame: frame, onFrameChanged: onFrameChanged, onFrameSetDirectly: onFrameSetDirectly, container: container, setContainerFormat: setContainerFormat, setCodec: setCodec, encodableVideoCodecs: encodableVideoCodecs, effectiveVideoCodec: effectiveVideoCodec, startFrame: finalStartFrame, setStartFrame: setStartFrame, endFrame: finalEndFrame, setEndFrame: setEndFrame, outName: outName, onOutNameChange: onOutNameChange, validationMessage: outnameValidation.valid ? null : outnameValidation.error.message, logLevel: logLevel, setLogLevel: setLogLevel })) : tab === 'data' ? ((0, jsx_runtime_1.jsx)(DataEditor_1.DataEditor, { defaultProps: inputProps, setDefaultProps: setInputProps, unresolvedComposition: unresolvedComposition, mayShowSaveButton: false, propsEditType: "input-props", saving: saving, setSaving: setSaving, readOnlyStudio: false })) : tab === 'picture' ? ((0, jsx_runtime_1.jsx)(WebRenderModalPicture_1.WebRenderModalPicture, { renderMode: renderMode, videoBitrate: videoBitrate, setVideoBitrate: setVideoBitrate, keyframeIntervalInSeconds: keyframeIntervalInSeconds, setKeyframeIntervalInSeconds: setKeyframeIntervalInSeconds, transparent: transparent, setTransparent: setTransparent })) : tab === 'audio' ? ((0, jsx_runtime_1.jsx)(WebRenderModalAudio_1.WebRenderModalAudio, { muted: muted, setMuted: setMuted, audioCodec: audioCodec, setAudioCodec: setAudioCodec, audioBitrate: audioBitrate, setAudioBitrate: setAudioBitrate, container: container, encodableCodecs: encodableAudioCodecs, effectiveAudioCodec: effectiveAudioCodec })) : tab === 'advanced' ? ((0, jsx_runtime_1.jsx)(WebRenderModalAdvanced_1.WebRenderModalAdvanced, { renderMode: renderMode, delayRenderTimeout: delayRenderTimeout, setDelayRenderTimeout: setDelayRenderTimeout, mediaCacheSizeInBytes: mediaCacheSizeInBytes, setMediaCacheSizeInBytes: setMediaCacheSizeInBytes, hardwareAcceleration: hardwareAcceleration, setHardwareAcceleration: setHardwareAcceleration })) : ((0, jsx_runtime_1.jsx)(WebRenderModalLicense_1.WebRenderModalLicense, { licenseKey: licenseKey, setLicenseKey: setLicenseKey, initialPublicLicenseKey: initialLicenseKey })) })] })] }));
380
405
  };
381
406
  const WebRenderModalWithLoader = (props) => {
382
407
  return ((0, jsx_runtime_1.jsx)(DismissableModal_1.DismissableModal, { children: (0, jsx_runtime_1.jsx)(ResolveCompositionBeforeModal_1.ResolveCompositionBeforeModal, { compositionId: props.compositionId, children: (0, jsx_runtime_1.jsx)(WebRenderModal, { ...props }) }) }));
@@ -1,5 +1,13 @@
1
- import type React from 'react';
1
+ import type { WebRendererAudioCodec, WebRendererContainer, WebRendererQuality } from '@remotion/web-renderer';
2
+ import React from 'react';
2
3
  export declare const WebRenderModalAudio: React.FC<{
3
4
  readonly muted: boolean;
4
5
  readonly setMuted: React.Dispatch<React.SetStateAction<boolean>>;
6
+ readonly audioCodec: WebRendererAudioCodec;
7
+ readonly setAudioCodec: React.Dispatch<React.SetStateAction<WebRendererAudioCodec>>;
8
+ readonly audioBitrate: WebRendererQuality;
9
+ readonly setAudioBitrate: React.Dispatch<React.SetStateAction<WebRendererQuality>>;
10
+ readonly container: WebRendererContainer;
11
+ readonly encodableCodecs: WebRendererAudioCodec[];
12
+ readonly effectiveAudioCodec: WebRendererAudioCodec;
5
13
  }>;
@@ -2,13 +2,62 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WebRenderModalAudio = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const web_renderer_1 = require("@remotion/web-renderer");
6
+ const react_1 = require("react");
7
+ const Checkmark_1 = require("../../icons/Checkmark");
5
8
  const is_menu_item_1 = require("../Menu/is-menu-item");
9
+ const ComboBox_1 = require("../NewComposition/ComboBox");
10
+ const layout_1 = require("../layout");
6
11
  const MutedSetting_1 = require("./MutedSetting");
12
+ const RenderModalHr_1 = require("./RenderModalHr");
13
+ const layout_2 = require("./layout");
14
+ const quality_options_1 = require("./quality-options");
7
15
  const container = {
8
16
  flex: 1,
9
17
  overflowY: 'auto',
10
18
  };
11
- const WebRenderModalAudio = ({ muted, setMuted }) => {
12
- return ((0, jsx_runtime_1.jsx)("div", { style: container, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: (0, jsx_runtime_1.jsx)(MutedSetting_1.MutedSetting, { enforceAudioTrack: false, muted: muted, setMuted: setMuted }) }));
19
+ const fallbackNoticeStyle = {
20
+ backgroundColor: 'rgba(59, 130, 246, 0.15)',
21
+ border: '1px solid rgba(59, 130, 246, 0.4)',
22
+ borderRadius: 4,
23
+ padding: '8px 12px',
24
+ marginLeft: 16,
25
+ marginRight: 16,
26
+ marginTop: 8,
27
+ fontSize: 13,
28
+ lineHeight: 1.4,
29
+ color: '#60a5fa',
30
+ };
31
+ const humanReadableWebAudioCodec = (audioCodec) => {
32
+ switch (audioCodec) {
33
+ case 'aac':
34
+ return 'AAC';
35
+ case 'opus':
36
+ return 'Opus';
37
+ default:
38
+ return audioCodec;
39
+ }
40
+ };
41
+ const WebRenderModalAudio = ({ muted, setMuted, audioCodec, setAudioCodec, audioBitrate, setAudioBitrate, container: videoContainer, encodableCodecs, effectiveAudioCodec, }) => {
42
+ const containerSupported = (0, react_1.useMemo)(() => (0, web_renderer_1.getSupportedAudioCodecsForContainer)(videoContainer), [videoContainer]);
43
+ const audioCodecOptions = (0, react_1.useMemo)(() => {
44
+ return containerSupported.map((codec) => {
45
+ const isEncodable = encodableCodecs.includes(codec);
46
+ return {
47
+ label: humanReadableWebAudioCodec(codec),
48
+ onClick: () => setAudioCodec(codec),
49
+ leftItem: audioCodec === codec ? (0, jsx_runtime_1.jsx)(Checkmark_1.Checkmark, {}) : null,
50
+ id: codec,
51
+ keyHint: null,
52
+ quickSwitcherLabel: null,
53
+ subMenu: null,
54
+ type: 'item',
55
+ value: codec,
56
+ disabled: !isEncodable,
57
+ };
58
+ });
59
+ }, [containerSupported, encodableCodecs, audioCodec, setAudioCodec]);
60
+ const audioBitrateOptions = (0, react_1.useMemo)(() => (0, quality_options_1.getQualityOptions)(audioBitrate, setAudioBitrate), [audioBitrate, setAudioBitrate]);
61
+ return ((0, jsx_runtime_1.jsxs)("div", { style: container, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: [(0, jsx_runtime_1.jsx)(MutedSetting_1.MutedSetting, { enforceAudioTrack: false, muted: muted, setMuted: setMuted }), !muted ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(RenderModalHr_1.RenderModalHr, {}), (0, jsx_runtime_1.jsxs)("div", { style: layout_2.optionRow, children: [(0, jsx_runtime_1.jsxs)("div", { style: layout_2.label, children: ["Audio Quality", (0, jsx_runtime_1.jsx)(layout_1.Spacing, { x: 0.5 })] }), (0, jsx_runtime_1.jsx)("div", { style: layout_2.rightRow, children: (0, jsx_runtime_1.jsx)(ComboBox_1.Combobox, { values: audioBitrateOptions, selectedId: audioBitrate, title: "Audio Quality" }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: layout_2.optionRow, children: [(0, jsx_runtime_1.jsxs)("div", { style: layout_2.label, children: ["Audio Codec", (0, jsx_runtime_1.jsx)(layout_1.Spacing, { x: 0.5 })] }), (0, jsx_runtime_1.jsx)("div", { style: layout_2.rightRow, children: (0, jsx_runtime_1.jsx)(ComboBox_1.Combobox, { values: audioCodecOptions, selectedId: audioCodec, title: "Audio Codec" }) })] }), effectiveAudioCodec !== audioCodec ? ((0, jsx_runtime_1.jsxs)("div", { style: fallbackNoticeStyle, children: [humanReadableWebAudioCodec(audioCodec), " is not available in this browser. Using ", humanReadableWebAudioCodec(effectiveAudioCodec), ' ', "instead."] })) : null] })) : null] }));
13
62
  };
14
63
  exports.WebRenderModalAudio = WebRenderModalAudio;
@@ -13,8 +13,9 @@ type WebRenderModalBasicProps = {
13
13
  readonly onFrameSetDirectly: (newFrame: number) => void;
14
14
  readonly container: WebRendererContainer;
15
15
  readonly setContainerFormat: (container: WebRendererContainer) => void;
16
- readonly codec: WebRendererVideoCodec;
17
16
  readonly setCodec: (codec: WebRendererVideoCodec) => void;
17
+ readonly encodableVideoCodecs: WebRendererVideoCodec[];
18
+ readonly effectiveVideoCodec: WebRendererVideoCodec;
18
19
  readonly startFrame: number | null;
19
20
  readonly setStartFrame: React.Dispatch<React.SetStateAction<number | null>>;
20
21
  readonly endFrame: number | null;
@@ -17,7 +17,7 @@ const RenderModalOutputName_1 = require("./RenderModalOutputName");
17
17
  const tabContainer = {
18
18
  flex: 1,
19
19
  };
20
- const WebRenderModalBasic = ({ renderMode, resolvedComposition, imageFormat, setStillFormat, frame, onFrameChanged, onFrameSetDirectly, container, setContainerFormat, codec, setCodec, startFrame, setStartFrame, endFrame, setEndFrame, outName, onOutNameChange, validationMessage, logLevel, setLogLevel, }) => {
20
+ const WebRenderModalBasic = ({ renderMode, resolvedComposition, imageFormat, setStillFormat, frame, onFrameChanged, onFrameSetDirectly, container, setContainerFormat, setCodec, encodableVideoCodecs, effectiveVideoCodec, startFrame, setStartFrame, endFrame, setEndFrame, outName, onOutNameChange, validationMessage, logLevel, setLogLevel, }) => {
21
21
  const imageFormatOptions = (0, react_1.useMemo)(() => {
22
22
  return [
23
23
  {
@@ -81,65 +81,26 @@ const WebRenderModalBasic = ({ renderMode, resolvedComposition, imageFormat, set
81
81
  },
82
82
  ];
83
83
  }, [container, setContainerFormat]);
84
+ const codecLabels = (0, react_1.useMemo)(() => ({
85
+ h264: 'H.264',
86
+ h265: 'H.265',
87
+ vp8: 'VP8',
88
+ vp9: 'VP9',
89
+ av1: 'AV1',
90
+ }), []);
84
91
  const codecOptions = (0, react_1.useMemo)(() => {
85
- return [
86
- {
87
- label: 'H.264',
88
- onClick: () => setCodec('h264'),
89
- leftItem: codec === 'h264' ? (0, jsx_runtime_1.jsx)(Checkmark_1.Checkmark, {}) : null,
90
- id: 'h264',
91
- keyHint: null,
92
- quickSwitcherLabel: null,
93
- subMenu: null,
94
- type: 'item',
95
- value: 'h264',
96
- },
97
- {
98
- label: 'H.265',
99
- onClick: () => setCodec('h265'),
100
- leftItem: codec === 'h265' ? (0, jsx_runtime_1.jsx)(Checkmark_1.Checkmark, {}) : null,
101
- id: 'h265',
102
- keyHint: null,
103
- quickSwitcherLabel: null,
104
- subMenu: null,
105
- type: 'item',
106
- value: 'h265',
107
- },
108
- {
109
- label: 'VP8',
110
- onClick: () => setCodec('vp8'),
111
- leftItem: codec === 'vp8' ? (0, jsx_runtime_1.jsx)(Checkmark_1.Checkmark, {}) : null,
112
- id: 'vp8',
113
- keyHint: null,
114
- quickSwitcherLabel: null,
115
- subMenu: null,
116
- type: 'item',
117
- value: 'vp8',
118
- },
119
- {
120
- label: 'VP9',
121
- onClick: () => setCodec('vp9'),
122
- leftItem: codec === 'vp9' ? (0, jsx_runtime_1.jsx)(Checkmark_1.Checkmark, {}) : null,
123
- id: 'vp9',
124
- keyHint: null,
125
- quickSwitcherLabel: null,
126
- subMenu: null,
127
- type: 'item',
128
- value: 'vp9',
129
- },
130
- {
131
- label: 'AV1',
132
- onClick: () => setCodec('av1'),
133
- leftItem: codec === 'av1' ? (0, jsx_runtime_1.jsx)(Checkmark_1.Checkmark, {}) : null,
134
- id: 'av1',
135
- keyHint: null,
136
- quickSwitcherLabel: null,
137
- subMenu: null,
138
- type: 'item',
139
- value: 'av1',
140
- },
141
- ];
142
- }, [codec, setCodec]);
143
- return ((0, jsx_runtime_1.jsxs)("div", { style: tabContainer, children: [renderMode === 'still' ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { style: layout_2.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_2.label, children: "Format" }), (0, jsx_runtime_1.jsx)("div", { style: layout_2.rightRow, children: (0, jsx_runtime_1.jsx)(SegmentedControl_1.SegmentedControl, { items: imageFormatOptions, needsWrapping: true }) })] }), resolvedComposition.durationInFrames > 1 ? ((0, jsx_runtime_1.jsxs)("div", { style: layout_2.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_2.label, children: "Frame" }), (0, jsx_runtime_1.jsx)("div", { style: layout_2.rightRow, children: (0, jsx_runtime_1.jsx)(RemInput_1.RightAlignInput, { children: (0, 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 }) }) })] })) : null] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { style: layout_2.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_2.label, children: "Container" }), (0, jsx_runtime_1.jsx)("div", { style: layout_2.rightRow, children: (0, jsx_runtime_1.jsx)(ComboBox_1.Combobox, { values: containerOptions, selectedId: container, title: "Container" }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: layout_2.optionRow, children: [(0, jsx_runtime_1.jsxs)("div", { style: layout_2.label, children: ["Codec", (0, jsx_runtime_1.jsx)(layout_1.Spacing, { x: 0.5 }), (0, jsx_runtime_1.jsx)(OptionExplainerBubble_1.OptionExplainerBubble, { id: "videoCodecOption" })] }), (0, jsx_runtime_1.jsx)("div", { style: layout_2.rightRow, children: (0, jsx_runtime_1.jsx)(ComboBox_1.Combobox, { values: codecOptions, selectedId: codec, title: "Codec" }) })] }), (0, jsx_runtime_1.jsx)(FrameRangeSetting_1.FrameRangeSetting, { durationInFrames: resolvedComposition.durationInFrames, startFrame: startFrame !== null && startFrame !== void 0 ? startFrame : 0, endFrame: endFrame !== null && endFrame !== void 0 ? endFrame : resolvedComposition.durationInFrames - 1, setStartFrame: setStartFrame, setEndFrame: setEndFrame })] })), (0, jsx_runtime_1.jsx)(RenderModalOutputName_1.RenderModalOutputName, { existence: false, inputStyle: layout_2.input, outName: outName, onValueChange: onOutNameChange, validationMessage: validationMessage, label: "Download name" }), (0, jsx_runtime_1.jsxs)("div", { style: layout_2.optionRow, children: [(0, jsx_runtime_1.jsxs)("div", { style: layout_2.label, children: ["Log Level ", (0, jsx_runtime_1.jsx)(layout_1.Spacing, { x: 0.5 }), (0, jsx_runtime_1.jsx)(OptionExplainerBubble_1.OptionExplainerBubble, { id: "logLevelOption" })] }), (0, jsx_runtime_1.jsx)("div", { style: layout_2.rightRow, children: (0, jsx_runtime_1.jsx)(ComboBox_1.Combobox, { values: logLevelOptions, selectedId: logLevel, title: "Log Level" }) })] })] }));
92
+ return encodableVideoCodecs.map((c) => ({
93
+ label: codecLabels[c],
94
+ onClick: () => setCodec(c),
95
+ leftItem: effectiveVideoCodec === c ? (0, jsx_runtime_1.jsx)(Checkmark_1.Checkmark, {}) : null,
96
+ id: c,
97
+ keyHint: null,
98
+ quickSwitcherLabel: null,
99
+ subMenu: null,
100
+ type: 'item',
101
+ value: c,
102
+ }));
103
+ }, [encodableVideoCodecs, effectiveVideoCodec, setCodec, codecLabels]);
104
+ return ((0, jsx_runtime_1.jsxs)("div", { style: tabContainer, children: [renderMode === 'still' ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { style: layout_2.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_2.label, children: "Format" }), (0, jsx_runtime_1.jsx)("div", { style: layout_2.rightRow, children: (0, jsx_runtime_1.jsx)(SegmentedControl_1.SegmentedControl, { items: imageFormatOptions, needsWrapping: true }) })] }), resolvedComposition.durationInFrames > 1 ? ((0, jsx_runtime_1.jsxs)("div", { style: layout_2.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_2.label, children: "Frame" }), (0, jsx_runtime_1.jsx)("div", { style: layout_2.rightRow, children: (0, jsx_runtime_1.jsx)(RemInput_1.RightAlignInput, { children: (0, 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 }) }) })] })) : null] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { style: layout_2.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_2.label, children: "Container" }), (0, jsx_runtime_1.jsx)("div", { style: layout_2.rightRow, children: (0, jsx_runtime_1.jsx)(ComboBox_1.Combobox, { values: containerOptions, selectedId: container, title: "Container" }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: layout_2.optionRow, children: [(0, jsx_runtime_1.jsxs)("div", { style: layout_2.label, children: ["Codec", (0, jsx_runtime_1.jsx)(layout_1.Spacing, { x: 0.5 }), (0, jsx_runtime_1.jsx)(OptionExplainerBubble_1.OptionExplainerBubble, { id: "videoCodecOption" })] }), (0, jsx_runtime_1.jsx)("div", { style: layout_2.rightRow, children: (0, jsx_runtime_1.jsx)(ComboBox_1.Combobox, { values: codecOptions, selectedId: effectiveVideoCodec, title: "Codec" }) })] }), (0, jsx_runtime_1.jsx)(FrameRangeSetting_1.FrameRangeSetting, { durationInFrames: resolvedComposition.durationInFrames, startFrame: startFrame !== null && startFrame !== void 0 ? startFrame : 0, endFrame: endFrame !== null && endFrame !== void 0 ? endFrame : resolvedComposition.durationInFrames - 1, setStartFrame: setStartFrame, setEndFrame: setEndFrame })] })), (0, jsx_runtime_1.jsx)(RenderModalOutputName_1.RenderModalOutputName, { existence: false, inputStyle: layout_2.input, outName: outName, onValueChange: onOutNameChange, validationMessage: validationMessage, label: "Download name" }), (0, jsx_runtime_1.jsxs)("div", { style: layout_2.optionRow, children: [(0, jsx_runtime_1.jsxs)("div", { style: layout_2.label, children: ["Log Level ", (0, jsx_runtime_1.jsx)(layout_1.Spacing, { x: 0.5 }), (0, jsx_runtime_1.jsx)(OptionExplainerBubble_1.OptionExplainerBubble, { id: "logLevelOption" })] }), (0, jsx_runtime_1.jsx)("div", { style: layout_2.rightRow, children: (0, jsx_runtime_1.jsx)(ComboBox_1.Combobox, { values: logLevelOptions, selectedId: logLevel, title: "Log Level" }) })] })] }));
144
105
  };
145
106
  exports.WebRenderModalBasic = WebRenderModalBasic;
@@ -8,6 +8,7 @@ const Checkbox_1 = require("../Checkbox");
8
8
  const RemInput_1 = require("../NewComposition/RemInput");
9
9
  const ValidationMessage_1 = require("../NewComposition/ValidationMessage");
10
10
  const layout_1 = require("../layout");
11
+ const WebRenderModalLicenseKeyDetails_1 = require("./WebRenderModalLicenseKeyDetails");
11
12
  exports.row = {
12
13
  display: 'flex',
13
14
  flexDirection: 'row',
@@ -84,12 +85,13 @@ const LICENSE_KEY_LENGTH = 55;
84
85
  const LICENSE_KEY_PREFIX = 'rm_pub_';
85
86
  const validateLicenseKey = (key) => {
86
87
  if (key.length === 0) {
87
- return { valid: false, message: null };
88
+ return { valid: false, message: null, details: null };
88
89
  }
89
90
  if (!key.startsWith(LICENSE_KEY_PREFIX)) {
90
91
  return {
91
92
  valid: false,
92
93
  message: `License key must start with "${LICENSE_KEY_PREFIX}"`,
94
+ details: null,
93
95
  };
94
96
  }
95
97
  const afterPrefix = key.slice(LICENSE_KEY_PREFIX.length);
@@ -97,17 +99,58 @@ const validateLicenseKey = (key) => {
97
99
  return {
98
100
  valid: false,
99
101
  message: 'License key must contain only alphanumeric characters after the prefix',
102
+ details: null,
100
103
  };
101
104
  }
102
105
  if (key.length !== LICENSE_KEY_LENGTH) {
103
106
  return {
104
107
  valid: false,
105
108
  message: `License key must be ${LICENSE_KEY_LENGTH} characters long`,
109
+ details: null,
106
110
  };
107
111
  }
108
- return { valid: true, message: null };
112
+ return { valid: true, message: null, details: null };
109
113
  };
110
114
  const WebRenderModalLicense = ({ licenseKey, setLicenseKey, initialPublicLicenseKey, }) => {
115
+ const [licenseValidation, setLicenseValidation] = (0, react_1.useState)({ valid: true, message: null, details: null });
116
+ const [isLoading, setIsLoading] = (0, react_1.useState)(false);
117
+ (0, react_1.useEffect)(() => {
118
+ if (licenseKey === null || licenseKey === 'free-license') {
119
+ return setLicenseValidation({
120
+ valid: true,
121
+ message: null,
122
+ details: null,
123
+ });
124
+ }
125
+ const validation = validateLicenseKey(licenseKey);
126
+ if (!validation.valid) {
127
+ return setLicenseValidation(validation);
128
+ }
129
+ setLicenseValidation({ valid: true, message: null, details: null });
130
+ setIsLoading(true);
131
+ (0, WebRenderModalLicenseKeyDetails_1.fetchLicenseKeyDetails)(licenseKey)
132
+ .then((details) => {
133
+ setIsLoading(false);
134
+ if (details.isValid) {
135
+ setLicenseValidation({ valid: true, message: null, details });
136
+ }
137
+ else {
138
+ setLicenseValidation({
139
+ valid: false,
140
+ message: 'License key is invalid or has been reset',
141
+ details: null,
142
+ });
143
+ }
144
+ })
145
+ .catch(() => {
146
+ setIsLoading(false);
147
+ setLicenseValidation({
148
+ valid: false,
149
+ message: 'Failed to fetch license key details',
150
+ details: null,
151
+ });
152
+ });
153
+ }, [licenseKey]);
111
154
  const onFreeLicenseChange = (0, react_1.useCallback)(() => {
112
155
  setLicenseKey('free-license');
113
156
  }, [setLicenseKey]);
@@ -117,14 +160,8 @@ const WebRenderModalLicense = ({ licenseKey, setLicenseKey, initialPublicLicense
117
160
  const onLicenseKeyChange = (0, react_1.useCallback)((e) => {
118
161
  setLicenseKey(e.target.value);
119
162
  }, [setLicenseKey]);
120
- const licenseValidation = (0, react_1.useMemo)(() => {
121
- if (licenseKey === null || licenseKey === 'free-license') {
122
- return { valid: true, message: null };
123
- }
124
- return validateLicenseKey(licenseKey);
125
- }, [licenseKey]);
126
163
  return ((0, jsx_runtime_1.jsxs)("div", { style: tabContainer, children: [(0, jsx_runtime_1.jsxs)("div", { style: descriptionStyle, children: ["Remotion is free if you are an individual or company with a headcount of 3 or less. See", ' ', (0, jsx_runtime_1.jsx)("a", { style: descriptionLink, href: "https://remotion.dev/license", children: "LICENSE.md" }), "."] }), (0, jsx_runtime_1.jsx)("div", { style: exports.row, children: (0, jsx_runtime_1.jsxs)("div", { style: justifyCenter, children: [(0, jsx_runtime_1.jsx)(Checkbox_1.Checkbox, { checked: licenseKey === 'free-license', onChange: onFreeLicenseChange, name: "free-license", rounded: true }), (0, jsx_runtime_1.jsxs)("div", { style: checkboxLabel, onClick: onFreeLicenseChange, children: ["I am eligible for the Free License, ", "don't", " print a warning"] })] }) }), licenseKey === 'free-license' ? ((0, jsx_runtime_1.jsxs)("div", { style: paddedDescriptionStyle, children: ["Enjoy Remotion! Add the following to", ' ', (0, jsx_runtime_1.jsx)("code", { style: codeStyle, children: "remotion.config.ts" }), " to persist this setting:", (0, jsx_runtime_1.jsx)("div", { style: codeLine, children: "Config.setPublicLicenseKey('free-license');" })] })) : null, (0, jsx_runtime_1.jsx)("div", { style: exports.row, children: (0, jsx_runtime_1.jsxs)("div", { style: justifyCenter, children: [(0, jsx_runtime_1.jsx)(Checkbox_1.Checkbox, { checked: licenseKey !== 'free-license' && licenseKey !== null, onChange: onCompanyLicenseChange, name: "company-license", rounded: true }), (0, jsx_runtime_1.jsx)("div", { style: checkboxLabel, onClick: onCompanyLicenseChange, children: "I have a Company License" })] }) }), licenseKey !== 'free-license' && licenseKey !== null ? ((0, jsx_runtime_1.jsxs)("div", { style: paddedDescriptionStyle, children: ["Add your public license from", ' ', (0, jsx_runtime_1.jsx)("a", { href: "https://remotion.pro/dashboard", target: "_blank", style: descriptionLink, children: "remotion.pro" }), ' ', "key below.", (0, jsx_runtime_1.jsx)(layout_1.Spacing, { y: 1, block: true }), (0, jsx_runtime_1.jsx)(RemInput_1.RemotionInput, { value: licenseKey, onChange: onLicenseKeyChange, placeholder: "remotion.pro public license key (starts with rm_pub_)", status: licenseValidation.valid || licenseKey.length === 0
127
164
  ? 'ok'
128
- : 'error', rightAlign: false, style: inputStyle, autoFocus: true }), licenseValidation.message ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(layout_1.Spacing, { y: 1, block: true }), (0, jsx_runtime_1.jsx)(ValidationMessage_1.ValidationMessage, { message: licenseValidation.message, align: "flex-start", type: "error" })] })) : null, licenseValidation.valid && licenseKey.length > 0 ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(layout_1.Spacing, { y: 1, block: true }), "Add the following to", ' ', (0, jsx_runtime_1.jsx)("code", { style: codeStyle, children: "remotion.config.ts" }), " to persist this setting:", (0, jsx_runtime_1.jsx)("div", { style: codeLineSmall, children: "Config.setPublicLicenseKey('" + licenseKey + "');" })] })) : null] })) : null, licenseKey === null ? ((0, jsx_runtime_1.jsxs)("div", { style: descriptionStyle, children: ["If you are not eligible for the free license, you need to obtain a", ' ', (0, jsx_runtime_1.jsx)("a", { style: descriptionLink, target: "_blank", href: "https://remotion.pro/license", children: "Company License" }), "."] })) : null] }));
165
+ : 'error', rightAlign: false, style: inputStyle, autoFocus: true }), licenseValidation.message ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(layout_1.Spacing, { y: 1, block: true }), (0, jsx_runtime_1.jsx)(ValidationMessage_1.ValidationMessage, { message: licenseValidation.message, align: "flex-start", type: "error" })] })) : null, licenseValidation.valid && licenseKey.length > 0 ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(layout_1.Spacing, { y: 1, block: true }), "Add the following to", ' ', (0, jsx_runtime_1.jsx)("code", { style: codeStyle, children: "remotion.config.ts" }), " to persist this setting:", (0, jsx_runtime_1.jsx)("div", { style: codeLineSmall, children: "Config.setPublicLicenseKey('" + licenseKey + "');" })] })) : null, isLoading && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(layout_1.Spacing, { y: 1, block: true }), "Loading license key details..."] })), licenseValidation.details && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(layout_1.Spacing, { y: 1, block: true }), (0, jsx_runtime_1.jsx)(WebRenderModalLicenseKeyDetails_1.WebRenderModalLicenseKeyDetails, { details: licenseValidation.details })] }))] })) : null, licenseKey === null ? ((0, jsx_runtime_1.jsxs)("div", { style: descriptionStyle, children: ["If you are not eligible for the free license, you need to obtain a", ' ', (0, jsx_runtime_1.jsx)("a", { style: descriptionLink, target: "_blank", href: "https://remotion.pro/license", children: "Company License" }), "."] })) : null] }));
129
166
  };
130
167
  exports.WebRenderModalLicense = WebRenderModalLicense;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ export type LicenseKeyDetails = {
3
+ readonly isValid: boolean;
4
+ readonly hasActiveSubscription: boolean;
5
+ readonly projectName: string;
6
+ readonly projectSlug: string;
7
+ };
8
+ type WebRenderModalLicenseKeyDetailsProps = {
9
+ readonly details: LicenseKeyDetails;
10
+ };
11
+ export declare const fetchLicenseKeyDetails: (licenseKey: string) => Promise<LicenseKeyDetails>;
12
+ export declare const WebRenderModalLicenseKeyDetails: React.FC<WebRenderModalLicenseKeyDetailsProps>;
13
+ export {};
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebRenderModalLicenseKeyDetails = exports.fetchLicenseKeyDetails = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const colors_1 = require("../../helpers/colors");
6
+ const check_circle_filled_1 = require("../../icons/check-circle-filled");
7
+ const ValidationMessage_1 = require("../NewComposition/ValidationMessage");
8
+ const textStyle = {
9
+ color: colors_1.LIGHT_TEXT,
10
+ fontSize: 14,
11
+ fontFamily: 'sans-serif',
12
+ lineHeight: 1.5,
13
+ display: 'flex',
14
+ alignItems: 'center',
15
+ };
16
+ const linkStyle = {
17
+ fontSize: 14,
18
+ fontFamily: 'sans-serif',
19
+ lineHeight: 1.5,
20
+ cursor: 'pointer',
21
+ };
22
+ const bulletStyle = {
23
+ display: 'flex',
24
+ alignItems: 'center',
25
+ gap: 6,
26
+ };
27
+ const icon = {
28
+ width: 14,
29
+ height: 14,
30
+ flexShrink: 0,
31
+ };
32
+ const PRO_HOST = 'https://remotion.pro';
33
+ const fetchLicenseKeyDetails = async (licenseKey) => {
34
+ const response = await fetch(`${PRO_HOST}/api/validate-license-key`, {
35
+ method: 'POST',
36
+ body: JSON.stringify({
37
+ licenseKey,
38
+ }),
39
+ headers: {
40
+ 'Content-Type': 'application/json',
41
+ },
42
+ });
43
+ return response.json();
44
+ };
45
+ exports.fetchLicenseKeyDetails = fetchLicenseKeyDetails;
46
+ const WebRenderModalLicenseKeyDetails = ({ details }) => {
47
+ return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { style: bulletStyle, children: [(0, jsx_runtime_1.jsx)(check_circle_filled_1.CheckCircleFilled, { style: { ...icon, fill: colors_1.LIGHT_TEXT } }), (0, jsx_runtime_1.jsxs)("div", { style: textStyle, children: ["Belongs to\u00A0", (0, jsx_runtime_1.jsx)("a", { href: `${PRO_HOST}/projects/${details.projectSlug}`, target: "_blank", style: linkStyle, children: details.projectName }), "\u00A0-\u00A0", (0, jsx_runtime_1.jsx)("a", { href: `${PRO_HOST}/projects/${details.projectSlug}/usage#client-renders-usage`, target: "_blank", style: linkStyle, children: "View usage" })] })] }), details.hasActiveSubscription ? ((0, jsx_runtime_1.jsxs)("div", { style: bulletStyle, children: [(0, jsx_runtime_1.jsx)(check_circle_filled_1.CheckCircleFilled, { style: { ...icon, fill: colors_1.LIGHT_TEXT } }), (0, jsx_runtime_1.jsx)("div", { style: textStyle, children: "Active Company License" })] })) : ((0, jsx_runtime_1.jsxs)("div", { style: bulletStyle, children: [(0, jsx_runtime_1.jsx)(ValidationMessage_1.WarningTriangle, { type: "warning", style: { ...icon, fill: colors_1.WARNING_COLOR } }), (0, jsx_runtime_1.jsx)("div", { style: textStyle, children: "No active Company License" })] }))] }));
48
+ };
49
+ exports.WebRenderModalLicenseKeyDetails = WebRenderModalLicenseKeyDetails;