@remotion/cli 3.3.37 → 3.3.38

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 (58) hide show
  1. package/dist/config/log.d.ts +1 -1
  2. package/dist/editor/components/Modals.js +1 -1
  3. package/dist/editor/components/RenderButton.js +8 -29
  4. package/dist/editor/components/RenderModal/CrfSetting.d.ts +2 -2
  5. package/dist/editor/components/RenderModal/CrfSetting.js +24 -11
  6. package/dist/editor/components/RenderModal/RenderModal.d.ts +5 -19
  7. package/dist/editor/components/RenderModal/RenderModal.js +181 -225
  8. package/dist/editor/components/RenderModal/ScaleSetting.js +18 -2
  9. package/dist/editor/components/RenderModal/layout.d.ts +0 -1
  10. package/dist/editor/components/RenderModal/layout.js +2 -8
  11. package/dist/editor/components/RenderQueue/RenderQueueRemoveItem.js +1 -2
  12. package/dist/editor/components/RenderQueue/actions.d.ts +2 -15
  13. package/dist/editor/components/RenderQueue/actions.js +1 -14
  14. package/dist/editor/components/RenderToolbarIcon.js +8 -30
  15. package/dist/editor/components/SegmentedControl.js +1 -1
  16. package/dist/editor/components/SidebarContent.js +1 -2
  17. package/dist/get-cli-options.d.ts +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/preview-server/render-queue/get-default-video-contexts.d.ts +1 -1
  20. package/dist/preview-server/render-queue/job.d.ts +1 -27
  21. package/dist/preview-server/render-queue/make-retry-payload.js +10 -52
  22. package/dist/preview-server/render-queue/process-video.js +9 -16
  23. package/dist/preview-server/routes/add-render.js +0 -13
  24. package/dist/render-flows/render.d.ts +4 -14
  25. package/dist/render-flows/render.js +13 -40
  26. package/package.json +7 -7
  27. package/dist/editor/components/RenderModal/EveryNthFrameSetting.d.ts +0 -5
  28. package/dist/editor/components/RenderModal/EveryNthFrameSetting.js +0 -9
  29. package/dist/editor/components/RenderModal/NumberOfLoopsSetting.d.ts +0 -5
  30. package/dist/editor/components/RenderModal/NumberOfLoopsSetting.js +0 -26
  31. package/dist/editor/components/RenderModal/NumberSetting.d.ts +0 -9
  32. package/dist/editor/components/RenderModal/NumberSetting.js +0 -26
  33. package/dist/editor/components/RenderModal/RenderModalAdvanced.d.ts +0 -11
  34. package/dist/editor/components/RenderModal/RenderModalAdvanced.js +0 -15
  35. package/dist/editor/components/RenderModal/RenderModalAudio.d.ts +0 -13
  36. package/dist/editor/components/RenderModal/RenderModalAudio.js +0 -22
  37. package/dist/editor/components/RenderModal/RenderModalBasic.d.ts +0 -22
  38. package/dist/editor/components/RenderModal/RenderModalBasic.js +0 -70
  39. package/dist/editor/components/RenderModal/RenderModalGeneral.d.ts +0 -0
  40. package/dist/editor/components/RenderModal/RenderModalGeneral.js +0 -1
  41. package/dist/editor/components/RenderModal/RenderModalGif.d.ts +0 -9
  42. package/dist/editor/components/RenderModal/RenderModalGif.js +0 -16
  43. package/dist/editor/components/RenderModal/RenderModalHr.d.ts +0 -2
  44. package/dist/editor/components/RenderModal/RenderModalHr.js +0 -18
  45. package/dist/editor/components/RenderModal/RenderModalPicture.d.ts +0 -28
  46. package/dist/editor/components/RenderModal/RenderModalPicture.js +0 -51
  47. package/dist/editor/components/RenderModal/human-readable-codec.d.ts +0 -2
  48. package/dist/editor/components/RenderModal/human-readable-codec.js +0 -36
  49. package/dist/editor/icons/audio.d.ts +0 -2
  50. package/dist/editor/icons/audio.js +0 -6
  51. package/dist/editor/icons/file.d.ts +0 -2
  52. package/dist/editor/icons/file.js +0 -6
  53. package/dist/editor/icons/frame.d.ts +0 -2
  54. package/dist/editor/icons/frame.js +0 -6
  55. package/dist/editor/icons/gear.d.ts +0 -2
  56. package/dist/editor/icons/gear.js +0 -6
  57. package/dist/editor/icons/gif.d.ts +0 -2
  58. package/dist/editor/icons/gif.js +0 -6
@@ -6,25 +6,19 @@ const client_1 = require("@remotion/renderer/client");
6
6
  const react_1 = require("react");
7
7
  const remotion_1 = require("remotion");
8
8
  const Button_1 = require("../../../preview-server/error-overlay/remotion-overlay/Button");
9
- const audio_1 = require("../../icons/audio");
10
- const file_1 = require("../../icons/file");
11
- const frame_1 = require("../../icons/frame");
12
- const gear_1 = require("../../icons/gear");
13
- const gif_1 = require("../../icons/gif");
9
+ const colors_1 = require("../../helpers/colors");
10
+ const use_file_existence_1 = require("../../helpers/use-file-existence");
14
11
  const modals_1 = require("../../state/modals");
12
+ const CollapsableOptions_1 = require("../CollapsableOptions");
15
13
  const layout_1 = require("../layout");
16
14
  const ModalContainer_1 = require("../ModalContainer");
17
15
  const ModalHeader_1 = require("../ModalHeader");
16
+ const InputDragger_1 = require("../NewComposition/InputDragger");
17
+ const RemInput_1 = require("../NewComposition/RemInput");
18
+ const ValidationMessage_1 = require("../NewComposition/ValidationMessage");
18
19
  const actions_1 = require("../RenderQueue/actions");
19
20
  const SegmentedControl_1 = require("../SegmentedControl");
20
21
  const SidebarContent_1 = require("../SidebarContent");
21
- const Tabs_1 = require("../Tabs");
22
- const CrfSetting_1 = require("./CrfSetting");
23
- const RenderModalAdvanced_1 = require("./RenderModalAdvanced");
24
- const RenderModalAudio_1 = require("./RenderModalAudio");
25
- const RenderModalBasic_1 = require("./RenderModalBasic");
26
- const RenderModalGif_1 = require("./RenderModalGif");
27
- const RenderModalPicture_1 = require("./RenderModalPicture");
28
22
  const initialState = { type: 'idle' };
29
23
  const reducer = (state, action) => {
30
24
  if (action.type === 'start') {
@@ -52,130 +46,69 @@ const container = {
52
46
  width: '100%',
53
47
  borderBottom: '1px solid black',
54
48
  };
55
- const scrollPanel = {
56
- minHeight: '35vh',
57
- maxHeight: '50vh',
58
- overflow: 'auto',
59
- minWidth: 650,
60
- };
61
- const horizontalLayout = {
49
+ const optionRow = {
62
50
  display: 'flex',
63
51
  flexDirection: 'row',
52
+ alignItems: 'flex-start',
53
+ minHeight: 40,
54
+ paddingLeft: 16,
55
+ paddingRight: 16,
64
56
  };
65
- const leftSidebar = {
66
- padding: 12,
57
+ const label = {
58
+ width: 150,
59
+ fontSize: 14,
60
+ lineHeight: '40px',
61
+ color: colors_1.LIGHT_TEXT,
67
62
  };
68
- const horizontalTab = {
69
- width: 250,
63
+ const rightRow = {
70
64
  display: 'flex',
71
65
  flexDirection: 'row',
72
- alignItems: 'center',
73
- textAlign: 'left',
74
- fontSize: 16,
75
- fontWeight: 'bold',
76
- paddingLeft: 15,
77
- paddingTop: 12,
78
- paddingBottom: 12,
79
- };
80
- const iconContainer = {
81
- width: 20,
82
- height: 20,
83
- marginRight: 15,
84
- display: 'inline-flex',
85
- justifyContent: 'center',
86
- alignItems: 'center',
87
- };
88
- const icon = {
89
- color: 'currentcolor',
90
- height: 20,
66
+ justifyContent: 'flex-end',
67
+ alignSelf: 'center',
68
+ flex: 1,
91
69
  };
92
- const buttonStyle = {
93
- backgroundColor: 'var(--blue)',
94
- color: 'white',
70
+ const buttonRow = {
71
+ display: 'flex',
72
+ flexDirection: 'row',
73
+ justifyContent: 'flex-end',
74
+ borderTop: '1px solid black',
75
+ paddingTop: 8,
76
+ paddingBottom: 8,
77
+ paddingLeft: 16,
78
+ paddingRight: 16,
95
79
  };
96
- const flexer = {
97
- flex: 1,
80
+ const input = {
81
+ minWidth: 250,
82
+ textAlign: 'right',
98
83
  };
99
- const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, initialStillImageFormat, initialQuality, initialScale, initialVerbose, initialOutName, initialRenderType, initialAudioCodec, initialVideoCodec, initialConcurrency, maxConcurrency, minConcurrency, initialMuted, initialEnforceAudioTrack, initialProResProfile, initialPixelFormat, initialVideoBitrate, initialAudioBitrate, initialEveryNthFrame, initialNumberOfGifLoops, initialDelayRenderTimeout, }) => {
84
+ const MIN_QUALITY = 1;
85
+ const MAX_QUALITY = 100;
86
+ const MIN_SCALE = 0.1;
87
+ const MAX_SCALE = 10;
88
+ const RenderModal = ({ compositionId, initialFrame, initialImageFormat, initialQuality, initialScale, initialVerbose, initialOutName, initialRenderType, initialCodec, }) => {
100
89
  const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
101
90
  const onQuit = (0, react_1.useCallback)(() => {
102
91
  setSelectedModal(null);
103
92
  }, [setSelectedModal]);
104
93
  const isMounted = (0, react_1.useRef)(true);
105
94
  const [state, dispatch] = (0, react_1.useReducer)(reducer, initialState);
106
- const [unclampedFrame, setFrame] = (0, react_1.useState)(() => initialFrame);
107
- const [stillImageFormat, setStillImageFormat] = (0, react_1.useState)(() => initialStillImageFormat);
108
- const [videoImageFormat, setVideoImageFormat] = (0, react_1.useState)(() => initialVideoImageFormat);
109
- const [concurrency, setConcurrency] = (0, react_1.useState)(() => initialConcurrency);
110
- const [videoCodec, setVideoSpecificalCodec] = (0, react_1.useState)(() => initialVideoCodec);
111
- const [audioCodec, setAudioSpecificalCodec] = (0, react_1.useState)(() => initialAudioCodec);
112
- const [mutedState, setMuted] = (0, react_1.useState)(() => initialMuted);
113
- const [enforceAudioTrackState, setEnforceAudioTrackState] = (0, react_1.useState)(() => initialEnforceAudioTrack);
114
- const [renderMode, setRenderModeState] = (0, react_1.useState)(initialRenderType);
115
- const [quality, setQuality] = (0, react_1.useState)(() => initialQuality);
95
+ const [frame, setFrame] = (0, react_1.useState)(() => initialFrame);
96
+ const [imageFormat, setImageFormat] = (0, react_1.useState)(() => initialImageFormat);
97
+ const [videoCodec, setVideoCodec] = (0, react_1.useState)(initialCodec);
98
+ const [videoImageFormat, setVideoImageFormat] = (0, react_1.useState)('jpeg');
99
+ const [renderMode, setRenderMode] = (0, react_1.useState)(initialRenderType);
100
+ const [quality, setQuality] = (0, react_1.useState)(() => initialQuality !== null && initialQuality !== void 0 ? initialQuality : 80);
116
101
  const [scale, setScale] = (0, react_1.useState)(() => initialScale);
117
102
  const [verbose, setVerboseLogging] = (0, react_1.useState)(() => initialVerbose);
118
103
  const [outName, setOutName] = (0, react_1.useState)(() => initialOutName);
119
- const [endFrameOrNull, setEndFrame] = (0, react_1.useState)(() => null);
120
- const [startFrameOrNull, setStartFrame] = (0, react_1.useState)(() => null);
121
- const [proResProfileSetting, setProResProfile] = (0, react_1.useState)(() => initialProResProfile);
122
- const [pixelFormat, setPixelFormat] = (0, react_1.useState)(() => initialPixelFormat);
123
- const [qualityControlType, setQualityControl] = (0, react_1.useState)(() => initialVideoBitrate === null ? 'crf' : 'bitrate');
124
- const [shouldHaveCustomTargetAudioBitrate, setShouldHaveCustomTargetAudioBitrate,] = (0, react_1.useState)(() => initialAudioBitrate !== null);
125
- const [customTargetAudioBitrate, setCustomTargetAudioBitrateValue] = (0, react_1.useState)(() => initialAudioBitrate !== null && initialAudioBitrate !== void 0 ? initialAudioBitrate : '256K');
126
- const [customTargetVideoBitrate, setCustomTargetVideoBitrateValue] = (0, react_1.useState)(() => initialVideoBitrate !== null && initialVideoBitrate !== void 0 ? initialVideoBitrate : '1M');
127
- const [limitNumberOfGifLoops, setLimitNumberOfGifLoops] = (0, react_1.useState)(() => initialNumberOfGifLoops !== null);
128
- const [numberOfGifLoopsSetting, setNumberOfGifLoopsSetting] = (0, react_1.useState)(() => initialNumberOfGifLoops !== null && initialNumberOfGifLoops !== void 0 ? initialNumberOfGifLoops : 1);
129
- // TODO: Allow to modify
130
- const [delayRenderTimeout] = (0, react_1.useState)(() => initialDelayRenderTimeout);
131
- const codec = (0, react_1.useMemo)(() => {
132
- if (renderMode === 'audio') {
133
- return audioCodec;
134
- }
135
- return videoCodec;
136
- }, [audioCodec, renderMode, videoCodec]);
137
- const numberOfGifLoops = (0, react_1.useMemo)(() => {
138
- if (codec === 'gif' && limitNumberOfGifLoops) {
139
- return numberOfGifLoopsSetting;
140
- }
141
- return null;
142
- }, [codec, limitNumberOfGifLoops, numberOfGifLoopsSetting]);
143
- const audioBitrate = (0, react_1.useMemo)(() => {
144
- if (shouldHaveCustomTargetAudioBitrate) {
145
- return customTargetAudioBitrate;
146
- }
147
- return null;
148
- }, [customTargetAudioBitrate, shouldHaveCustomTargetAudioBitrate]);
149
- const videoBitrate = (0, react_1.useMemo)(() => {
150
- if (qualityControlType === 'bitrate') {
151
- return customTargetVideoBitrate;
152
- }
153
- return null;
154
- }, [customTargetVideoBitrate, qualityControlType]);
155
- const { crf, maxCrf, minCrf, setCrf, shouldDisplayOption: shouldDisplayCrfOption, } = (0, CrfSetting_1.useCrfState)(codec);
156
104
  const dispatchIfMounted = (0, react_1.useCallback)((payload) => {
157
105
  if (isMounted.current === false)
158
106
  return;
159
107
  dispatch(payload);
160
108
  }, []);
161
- const muted = (0, react_1.useMemo)(() => {
162
- if (renderMode === 'video') {
163
- return mutedState;
164
- }
165
- return false;
166
- }, [mutedState, renderMode]);
167
- const enforceAudioTrack = (0, react_1.useMemo)(() => {
168
- if (renderMode === 'video') {
169
- return enforceAudioTrackState;
170
- }
171
- return false;
172
- }, [enforceAudioTrackState, renderMode]);
173
- const proResProfile = (0, react_1.useMemo)(() => {
174
- if (renderMode === 'video' && codec === 'prores') {
175
- return proResProfileSetting;
176
- }
177
- return null;
178
- }, [codec, proResProfileSetting, renderMode]);
109
+ const onValueChange = (0, react_1.useCallback)((e) => {
110
+ setOutName(e.target.value);
111
+ }, []);
179
112
  const { compositions } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
180
113
  const currentComposition = (0, react_1.useMemo)(() => {
181
114
  for (const composition of compositions) {
@@ -188,55 +121,27 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
188
121
  if (currentComposition === null) {
189
122
  throw new Error('This composition does not exist');
190
123
  }
191
- const endFrame = (0, react_1.useMemo)(() => {
192
- if (endFrameOrNull === null) {
193
- return currentComposition.durationInFrames - 1;
194
- }
195
- return Math.max(0, Math.min(currentComposition.durationInFrames - 1, endFrameOrNull));
196
- }, [currentComposition.durationInFrames, endFrameOrNull]);
197
- const startFrame = (0, react_1.useMemo)(() => {
198
- if (startFrameOrNull === null) {
199
- return 0;
200
- }
201
- return Math.max(0, Math.min(endFrame - 1, startFrameOrNull));
202
- }, [endFrame, startFrameOrNull]);
203
- const frame = (0, react_1.useMemo)(() => {
204
- const parsed = Math.floor(unclampedFrame);
205
- return Math.max(0, Math.min(currentComposition.durationInFrames - 1, parsed));
206
- }, [currentComposition.durationInFrames, unclampedFrame]);
207
124
  const getStringBeforeSuffix = (0, react_1.useCallback)((fileName) => {
208
125
  const dotPos = fileName.lastIndexOf('.');
209
126
  const bitBeforeDot = fileName.substring(0, dotPos);
210
127
  return bitBeforeDot;
211
128
  }, []);
212
- const setDefaultOutName = (0, react_1.useCallback)((options) => {
213
- if (options.type === 'still') {
214
- setOutName((prev) => {
215
- const newFileName = getStringBeforeSuffix(prev) + '.' + options.imageFormat;
216
- return newFileName;
217
- });
218
- }
219
- else {
220
- setOutName((prev) => {
221
- const codecSuffix = client_1.BrowserSafeApis.getFileExtensionFromCodec(options.codec);
222
- const newFileName = getStringBeforeSuffix(prev) + '.' + codecSuffix;
223
- return newFileName;
224
- });
225
- }
129
+ const setCodec = (0, react_1.useCallback)((codec) => {
130
+ setVideoCodec(codec);
131
+ setOutName((prev) => {
132
+ // TODO: Check file extension for h264-mkv (render throws error atm)
133
+ const codecSuffix = client_1.BrowserSafeApis.getFileExtensionFromCodec(codec, 'final');
134
+ const newFileName = getStringBeforeSuffix(prev) + '.' + codecSuffix;
135
+ return newFileName;
136
+ });
226
137
  }, [getStringBeforeSuffix]);
227
- const setCodec = (0, react_1.useCallback)((newCodec) => {
228
- if (renderMode === 'audio') {
229
- setAudioSpecificalCodec(newCodec);
230
- }
231
- else {
232
- setVideoSpecificalCodec(newCodec);
233
- }
234
- setDefaultOutName({ type: 'render', codec: newCodec });
235
- }, [renderMode, setDefaultOutName]);
236
138
  const setStillFormat = (0, react_1.useCallback)((format) => {
237
- setStillImageFormat(format);
238
- setDefaultOutName({ type: 'still', imageFormat: format });
239
- }, [setDefaultOutName]);
139
+ setImageFormat(format);
140
+ setOutName((prev) => {
141
+ const newFileName = getStringBeforeSuffix(prev) + '.' + format;
142
+ return newFileName;
143
+ });
144
+ }, [getStringBeforeSuffix]);
240
145
  const onClickStill = (0, react_1.useCallback)(() => {
241
146
  var _a;
242
147
  (_a = SidebarContent_1.leftSidebarTabs.current) === null || _a === void 0 ? void 0 : _a.selectRendersPanel();
@@ -244,8 +149,8 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
244
149
  (0, actions_1.addStillRenderJob)({
245
150
  compositionId,
246
151
  outName,
247
- imageFormat: stillImageFormat,
248
- quality: stillImageFormat === 'jpeg' ? quality : null,
152
+ imageFormat,
153
+ quality: imageFormat === 'jpeg' ? quality : null,
249
154
  frame,
250
155
  scale,
251
156
  verbose,
@@ -261,20 +166,13 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
261
166
  compositionId,
262
167
  dispatchIfMounted,
263
168
  frame,
264
- stillImageFormat,
169
+ imageFormat,
265
170
  outName,
266
171
  quality,
267
172
  scale,
268
173
  setSelectedModal,
269
174
  verbose,
270
175
  ]);
271
- const [everyNthFrameSetting, setEveryNthFrameSetting] = (0, react_1.useState)(() => initialEveryNthFrame);
272
- const everyNthFrame = (0, react_1.useMemo)(() => {
273
- if (codec === 'gif') {
274
- return everyNthFrameSetting;
275
- }
276
- return 1;
277
- }, [codec, everyNthFrameSetting]);
278
176
  const onClickVideo = (0, react_1.useCallback)(() => {
279
177
  var _a;
280
178
  (_a = SidebarContent_1.leftSidebarTabs.current) === null || _a === void 0 ? void 0 : _a.selectRendersPanel();
@@ -283,23 +181,11 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
283
181
  compositionId,
284
182
  outName,
285
183
  imageFormat: videoImageFormat,
286
- quality: stillImageFormat === 'jpeg' ? quality : null,
184
+ quality: imageFormat === 'jpeg' ? quality : null,
287
185
  scale,
288
186
  verbose,
289
- codec,
290
- concurrency,
291
- crf: qualityControlType === 'crf' ? crf : null,
292
- endFrame,
293
- startFrame,
294
- muted,
295
- enforceAudioTrack,
296
- proResProfile,
297
- pixelFormat,
298
- audioBitrate,
299
- videoBitrate,
300
- everyNthFrame,
301
- numberOfGifLoops,
302
- delayRenderTimeout,
187
+ // TODO: Make this configurable
188
+ codec: videoCodec,
303
189
  })
304
190
  .then(() => {
305
191
  dispatchIfMounted({ type: 'succeed' });
@@ -309,36 +195,70 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
309
195
  dispatchIfMounted({ type: 'fail' });
310
196
  });
311
197
  }, [
312
- dispatchIfMounted,
313
198
  compositionId,
199
+ dispatchIfMounted,
200
+ imageFormat,
314
201
  outName,
315
- videoImageFormat,
316
- stillImageFormat,
317
202
  quality,
318
203
  scale,
319
- verbose,
320
- codec,
321
- concurrency,
322
- qualityControlType,
323
- crf,
324
- endFrame,
325
- startFrame,
326
- muted,
327
- enforceAudioTrack,
328
- proResProfile,
329
- pixelFormat,
330
- audioBitrate,
331
- videoBitrate,
332
- everyNthFrame,
333
- numberOfGifLoops,
334
- delayRenderTimeout,
335
204
  setSelectedModal,
205
+ verbose,
206
+ videoCodec,
207
+ videoImageFormat,
336
208
  ]);
209
+ const onQualityChangedDirectly = (0, react_1.useCallback)((newQuality) => {
210
+ setQuality(newQuality);
211
+ }, []);
212
+ const onQualityChanged = (0, react_1.useCallback)((e) => {
213
+ setQuality((q) => {
214
+ const newQuality = parseInt(e, 10);
215
+ if (Number.isNaN(newQuality)) {
216
+ return q;
217
+ }
218
+ const newQualityClamped = Math.min(MAX_QUALITY, Math.max(newQuality, MIN_QUALITY));
219
+ return newQualityClamped;
220
+ });
221
+ }, []);
222
+ const onScaleSetDirectly = (0, react_1.useCallback)((newScale) => {
223
+ setScale(newScale);
224
+ }, []);
225
+ const onScaleChanged = (0, react_1.useCallback)((e) => {
226
+ setScale((q) => {
227
+ const newScale = parseFloat(e);
228
+ if (Number.isNaN(newScale)) {
229
+ return q;
230
+ }
231
+ const newScaleClamped = Math.min(MAX_SCALE, Math.max(newScale, MIN_SCALE));
232
+ return newScaleClamped;
233
+ });
234
+ }, []);
235
+ const onFrameSetDirectly = (0, react_1.useCallback)((newFrame) => {
236
+ console.log(newFrame);
237
+ if (newFrame > currentComposition.durationInFrames - 1) {
238
+ setFrame(currentComposition.durationInFrames - 1);
239
+ }
240
+ else {
241
+ setFrame(newFrame);
242
+ }
243
+ }, [currentComposition.durationInFrames, setFrame]);
244
+ const onFrameChanged = (0, react_1.useCallback)((e) => {
245
+ setFrame((q) => {
246
+ const newFrame = parseFloat(e);
247
+ if (Number.isNaN(newFrame)) {
248
+ return q;
249
+ }
250
+ // TODO: User could change frame inbetween 😈
251
+ return newFrame > currentComposition.durationInFrames - 1
252
+ ? currentComposition.durationInFrames - 1
253
+ : newFrame;
254
+ });
255
+ }, []);
337
256
  (0, react_1.useEffect)(() => {
338
257
  return () => {
339
258
  isMounted.current = false;
340
259
  };
341
260
  }, []);
261
+ const existence = (0, use_file_existence_1.useFileExistence)(outName);
342
262
  const imageFormatOptions = (0, react_1.useMemo)(() => {
343
263
  return [
344
264
  {
@@ -348,7 +268,7 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
348
268
  : () => setVideoImageFormat('png'),
349
269
  key: 'png',
350
270
  selected: renderMode === 'still'
351
- ? stillImageFormat === 'png'
271
+ ? imageFormat === 'png'
352
272
  : videoImageFormat === 'png',
353
273
  },
354
274
  {
@@ -358,23 +278,32 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
358
278
  : () => setVideoImageFormat('jpeg'),
359
279
  key: 'jpeg',
360
280
  selected: renderMode === 'still'
361
- ? stillImageFormat === 'jpeg'
281
+ ? imageFormat === 'jpeg'
362
282
  : videoImageFormat === 'jpeg',
363
283
  },
364
284
  ];
365
- }, [stillImageFormat, renderMode, setStillFormat, videoImageFormat]);
366
- const setRenderMode = (0, react_1.useCallback)((newRenderMode) => {
367
- setRenderModeState(newRenderMode);
368
- if (newRenderMode === 'audio') {
369
- setDefaultOutName({ type: 'render', codec: audioCodec });
370
- }
371
- if (newRenderMode === 'video') {
372
- setDefaultOutName({ type: 'render', codec: videoCodec });
373
- }
374
- if (newRenderMode === 'still') {
375
- setDefaultOutName({ type: 'still', imageFormat: stillImageFormat });
376
- }
377
- }, [audioCodec, setDefaultOutName, stillImageFormat, videoCodec]);
285
+ }, [imageFormat, renderMode, setStillFormat, videoImageFormat]);
286
+ const videoCodecOptions = (0, react_1.useMemo)(() => {
287
+ const codecs = [
288
+ 'h264',
289
+ 'h265',
290
+ 'vp8',
291
+ 'vp9',
292
+ 'mp3',
293
+ 'aac',
294
+ 'wav',
295
+ 'prores',
296
+ 'gif',
297
+ ];
298
+ return codecs.map((codec) => {
299
+ return {
300
+ label: codec,
301
+ onClick: () => setCodec(codec),
302
+ key: codec,
303
+ selected: videoCodec === codec,
304
+ };
305
+ });
306
+ }, [setCodec, videoCodec]);
378
307
  const renderTabOptions = (0, react_1.useMemo)(() => {
379
308
  if ((currentComposition === null || currentComposition === void 0 ? void 0 : currentComposition.durationInFrames) < 2) {
380
309
  return [
@@ -382,6 +311,7 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
382
311
  label: 'Still',
383
312
  onClick: () => {
384
313
  setRenderMode('still');
314
+ setStillFormat(imageFormat);
385
315
  },
386
316
  key: 'still',
387
317
  selected: renderMode === 'still',
@@ -393,6 +323,7 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
393
323
  label: 'Still',
394
324
  onClick: () => {
395
325
  setRenderMode('still');
326
+ setStillFormat(imageFormat);
396
327
  },
397
328
  key: 'still',
398
329
  selected: renderMode === 'still',
@@ -401,21 +332,46 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
401
332
  label: 'Video',
402
333
  onClick: () => {
403
334
  setRenderMode('video');
335
+ setCodec(videoCodec);
404
336
  },
405
337
  key: 'video',
406
338
  selected: renderMode === 'video',
407
339
  },
408
- {
409
- label: 'Audio',
410
- onClick: () => {
411
- setRenderMode('audio');
412
- },
413
- key: 'audio',
414
- selected: renderMode === 'audio',
415
- },
416
340
  ];
417
- }, [currentComposition === null || currentComposition === void 0 ? void 0 : currentComposition.durationInFrames, renderMode, setRenderMode]);
418
- const [tab, setTab] = (0, react_1.useState)('general');
419
- return ((0, jsx_runtime_1.jsxs)(ModalContainer_1.ModalContainer, { onOutsideClick: onQuit, onEscape: onQuit, children: [(0, jsx_runtime_1.jsx)(ModalHeader_1.NewCompHeader, { title: `Render ${compositionId}` }), (0, jsx_runtime_1.jsxs)("div", { style: container, children: [(0, jsx_runtime_1.jsx)(SegmentedControl_1.SegmentedControl, { items: renderTabOptions, needsWrapping: false }), (0, jsx_runtime_1.jsx)("div", { style: flexer }), (0, jsx_runtime_1.jsx)(Button_1.Button, { autoFocus: true, onClick: renderMode === 'still' ? onClickStill : onClickVideo, disabled: state.type === 'load', style: buttonStyle, children: state.type === 'idle' ? `Render ${renderMode}` : 'Rendering...' })] }), (0, jsx_runtime_1.jsxs)("div", { style: horizontalLayout, children: [(0, jsx_runtime_1.jsxs)("div", { style: leftSidebar, children: [(0, jsx_runtime_1.jsxs)(Tabs_1.Tab, { style: horizontalTab, selected: tab === 'general', onClick: () => setTab('general'), children: [(0, jsx_runtime_1.jsx)("div", { style: iconContainer, children: (0, jsx_runtime_1.jsx)(file_1.FileIcon, { style: icon }) }), "General"] }), (0, jsx_runtime_1.jsxs)(Tabs_1.Tab, { style: horizontalTab, selected: tab === 'picture', onClick: () => setTab('picture'), children: [(0, jsx_runtime_1.jsx)("div", { style: iconContainer, children: (0, jsx_runtime_1.jsx)(frame_1.PicIcon, { style: icon }) }), "Picture"] }), renderMode === 'still' ? null : ((0, jsx_runtime_1.jsxs)(Tabs_1.Tab, { style: horizontalTab, selected: tab === 'audio', onClick: () => setTab('audio'), children: [(0, jsx_runtime_1.jsx)("div", { style: iconContainer, children: (0, jsx_runtime_1.jsx)(audio_1.AudioIcon, { style: icon }) }), "Audio"] })), codec === 'gif' ? ((0, jsx_runtime_1.jsxs)(Tabs_1.Tab, { style: horizontalTab, selected: tab === 'gif', onClick: () => setTab('gif'), children: [(0, jsx_runtime_1.jsx)("div", { style: iconContainer, children: (0, jsx_runtime_1.jsx)(gif_1.GifIcon, { style: icon }) }), "GIF"] })) : null, (0, jsx_runtime_1.jsxs)(Tabs_1.Tab, { style: horizontalTab, selected: tab === 'advanced', onClick: () => setTab('advanced'), children: [(0, jsx_runtime_1.jsx)("div", { style: iconContainer, children: (0, jsx_runtime_1.jsx)(gear_1.GearIcon, { style: icon }) }), "Other"] })] }), (0, jsx_runtime_1.jsxs)("div", { style: scrollPanel, children: [(0, jsx_runtime_1.jsx)(layout_1.Spacing, { block: true, y: 0.5 }), tab === 'general' ? ((0, jsx_runtime_1.jsx)(RenderModalBasic_1.RenderModalBasic, { codec: codec, currentComposition: currentComposition, frame: frame, imageFormatOptions: imageFormatOptions, outName: outName, proResProfile: proResProfile, renderMode: renderMode, setCodec: setCodec, setFrame: setFrame, setOutName: setOutName, setProResProfile: setProResProfile, endFrame: endFrame, setEndFrame: setEndFrame, setStartFrame: setStartFrame, startFrame: startFrame })) : tab === 'picture' ? ((0, jsx_runtime_1.jsx)(RenderModalPicture_1.RenderModalPicture, { renderMode: renderMode, scale: scale, setScale: setScale, pixelFormat: pixelFormat, setPixelFormat: setPixelFormat, imageFormatOptions: imageFormatOptions, crf: crf, setCrf: setCrf, customTargetVideoBitrate: customTargetVideoBitrate, maxCrf: maxCrf, minCrf: minCrf, quality: quality, qualityControlType: qualityControlType, setQuality: setQuality, setCustomTargetVideoBitrateValue: setCustomTargetVideoBitrateValue, setQualityControl: setQualityControl, shouldDisplayCrfOption: shouldDisplayCrfOption, videoImageFormat: videoImageFormat, stillImageFormat: stillImageFormat })) : tab === 'audio' ? ((0, jsx_runtime_1.jsx)(RenderModalAudio_1.RenderModalAudio, { muted: muted, renderMode: renderMode, setMuted: setMuted, enforceAudioTrack: enforceAudioTrack, setEnforceAudioTrackState: setEnforceAudioTrackState, customTargetAudioBitrate: customTargetAudioBitrate, setCustomTargetAudioBitrateValue: setCustomTargetAudioBitrateValue, setShouldHaveCustomTargetAudioBitrate: setShouldHaveCustomTargetAudioBitrate, shouldHaveCustomTargetAudioBitrate: shouldHaveCustomTargetAudioBitrate })) : tab === 'gif' ? ((0, jsx_runtime_1.jsx)(RenderModalGif_1.RenderModalGif, { everyNthFrame: everyNthFrame, limitNumberOfGifLoops: limitNumberOfGifLoops, numberOfGifLoopsSetting: numberOfGifLoopsSetting, setEveryNthFrameSetting: setEveryNthFrameSetting, setLimitNumberOfGifLoops: setLimitNumberOfGifLoops, setNumberOfGifLoopsSetting: setNumberOfGifLoopsSetting })) : ((0, jsx_runtime_1.jsx)(RenderModalAdvanced_1.RenderModalAdvanced, { concurrency: concurrency, maxConcurrency: maxConcurrency, minConcurrency: minConcurrency, renderMode: renderMode, setConcurrency: setConcurrency, setVerboseLogging: setVerboseLogging, verbose: verbose })), (0, jsx_runtime_1.jsx)(layout_1.Spacing, { block: true, y: 0.5 })] })] })] }));
341
+ }, [
342
+ currentComposition === null || currentComposition === void 0 ? void 0 : currentComposition.durationInFrames,
343
+ imageFormat,
344
+ renderMode,
345
+ setCodec,
346
+ setStillFormat,
347
+ videoCodec,
348
+ ]);
349
+ const onVerboseLoggingChanged = (0, react_1.useCallback)((e) => {
350
+ setVerboseLogging(e.target.checked);
351
+ }, []);
352
+ if (renderMode === 'still') {
353
+ return ((0, jsx_runtime_1.jsxs)(ModalContainer_1.ModalContainer, { onOutsideClick: onQuit, onEscape: onQuit, children: [(0, jsx_runtime_1.jsx)(ModalHeader_1.NewCompHeader, { title: `Render ${compositionId}` }), (0, jsx_runtime_1.jsx)("div", { style: container, children: (0, jsx_runtime_1.jsx)(SegmentedControl_1.SegmentedControl, { items: renderTabOptions, needsWrapping: false }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(layout_1.Spacing, { block: true, y: 0.5 }), (0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Format" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)(SegmentedControl_1.SegmentedControl, { items: imageFormatOptions, needsWrapping: true }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Output name" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(RemInput_1.RemotionInput
354
+ // TODO: Validate and reject folders or weird file names
355
+ , {
356
+ // TODO: Validate and reject folders or weird file names
357
+ warning: existence, style: input, type: "text", value: outName, onChange: onValueChange }), existence ? ((0, jsx_runtime_1.jsx)(ValidationMessage_1.ValidationMessage, { align: "flex-end", message: "Will be overwritten" })) : null] }) })] }), currentComposition.durationInFrames > 1 ? ((0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Frame" }), (0, jsx_runtime_1.jsxs)("div", { style: rightRow, children: [(0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { value: frame, onTextChange: onFrameChanged,
358
+ // TODO: Actual frame
359
+ placeholder: "0-100",
360
+ // TODO: Debug the number input field
361
+ onValueChange: onFrameSetDirectly, name: "frame", step: 1, min: 0,
362
+ // TODO: Add actual frame
363
+ max: currentComposition.durationInFrames - 1 }), ' '] })] })) : null, (0, jsx_runtime_1.jsxs)(CollapsableOptions_1.CollapsableOptions, { showLabel: "Show advanced settings", hideLabel: "Hide advanced settings", children: [(0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Scale" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { value: scale, onTextChange: onScaleChanged, placeholder: "0.1-10",
364
+ // TODO: Does not allow non-integer steps
365
+ // TODO: Cannot click and type in 0.2
366
+ onValueChange: onScaleSetDirectly, name: "scale", step: 0.1, min: MIN_SCALE, max: MAX_SCALE }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Verbose logging" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)("input", { type: 'checkbox', checked: verbose, onChange: onVerboseLoggingChanged }) })] }), imageFormat === 'jpeg' && ((0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "JPEG Quality" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { value: quality, onTextChange: onQualityChanged, placeholder: "0-100", onValueChange: onQualityChangedDirectly, name: "quality", step: 1, min: MIN_QUALITY, max: MAX_QUALITY }) })] }))] }), (0, jsx_runtime_1.jsx)(layout_1.Spacing, { block: true, y: 0.5 }), (0, jsx_runtime_1.jsx)("div", { style: buttonRow, children: (0, jsx_runtime_1.jsx)(Button_1.Button, { autoFocus: true, onClick: onClickStill, disabled: state.type === 'load', children: state.type === 'idle' ? 'Render still' : 'Rendering...' }) })] })] }));
367
+ }
368
+ return ((0, jsx_runtime_1.jsxs)(ModalContainer_1.ModalContainer, { onOutsideClick: onQuit, onEscape: onQuit, children: [(0, jsx_runtime_1.jsx)(ModalHeader_1.NewCompHeader, { title: `Render ${compositionId}` }), (0, jsx_runtime_1.jsx)("div", { style: container, children: (0, jsx_runtime_1.jsx)(SegmentedControl_1.SegmentedControl, { items: renderTabOptions, needsWrapping: false }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(layout_1.Spacing, { block: true, y: 0.5 }), (0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Codec" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)(SegmentedControl_1.SegmentedControl, { items: videoCodecOptions, needsWrapping: true }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Output name" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(RemInput_1.RemotionInput
369
+ // TODO: Validate and reject folders or weird file names
370
+ , {
371
+ // TODO: Validate and reject folders or weird file names
372
+ warning: existence, style: input, type: "text", value: outName, onChange: onValueChange }), existence ? ((0, jsx_runtime_1.jsx)(ValidationMessage_1.ValidationMessage, { align: "flex-end", message: "Will be overwritten" })) : null] }) })] }), (0, jsx_runtime_1.jsxs)(CollapsableOptions_1.CollapsableOptions, { showLabel: "Show advanced settings", hideLabel: "Hide advanced settings", children: [(0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Scale" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { value: scale, onTextChange: onScaleChanged, placeholder: "0.1-10",
373
+ // TODO: Direct input does not allow non-integer steps
374
+ // TODO: Cannot click and type in 0.2
375
+ onValueChange: onScaleSetDirectly, name: "scale", step: 0.1, min: MIN_SCALE, max: MAX_SCALE }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Verbose logging" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)("input", { type: 'checkbox', checked: verbose, onChange: onVerboseLoggingChanged }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Image Format" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)(SegmentedControl_1.SegmentedControl, { items: imageFormatOptions, needsWrapping: false }) })] }), videoImageFormat === 'jpeg' && ((0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "JPEG Quality" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { value: quality, onTextChange: onQualityChanged, placeholder: "0-100", onValueChange: onQualityChangedDirectly, name: "quality", step: 1, min: MIN_QUALITY, max: MAX_QUALITY }) })] }))] }), (0, jsx_runtime_1.jsx)(layout_1.Spacing, { block: true, y: 0.5 }), (0, jsx_runtime_1.jsx)("div", { style: buttonRow, children: (0, jsx_runtime_1.jsx)(Button_1.Button, { autoFocus: true, onClick: onClickVideo, disabled: state.type === 'load', children: state.type === 'idle' ? 'Render video' : 'Rendering...' }) })] })] }));
420
376
  };
421
377
  exports.RenderModal = RenderModal;
@@ -2,10 +2,26 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ScaleSetting = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
- const NumberSetting_1 = require("./NumberSetting");
5
+ const react_1 = require("react");
6
+ const InputDragger_1 = require("../NewComposition/InputDragger");
7
+ const RemInput_1 = require("../NewComposition/RemInput");
8
+ const layout_1 = require("./layout");
6
9
  const MIN_SCALE = 0.1;
7
10
  const MAX_SCALE = 10;
8
11
  const ScaleSetting = ({ scale, setScale }) => {
9
- return ((0, jsx_runtime_1.jsx)(NumberSetting_1.NumberSetting, { min: MIN_SCALE, max: MAX_SCALE, step: 0.1, name: "Scale", onValueChanged: setScale, value: scale }));
12
+ const onScaleSetDirectly = (0, react_1.useCallback)((newScale) => {
13
+ setScale(newScale);
14
+ }, [setScale]);
15
+ const onScaleChanged = (0, react_1.useCallback)((e) => {
16
+ setScale((q) => {
17
+ const newScale = parseFloat(e);
18
+ if (Number.isNaN(newScale)) {
19
+ return q;
20
+ }
21
+ const newScaleClamped = Math.min(MAX_SCALE, Math.max(newScale, MIN_SCALE));
22
+ return newScaleClamped;
23
+ });
24
+ }, [setScale]);
25
+ return ((0, jsx_runtime_1.jsxs)("div", { style: layout_1.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_1.label, children: "Scale" }), (0, jsx_runtime_1.jsx)("div", { style: layout_1.rightRow, children: (0, jsx_runtime_1.jsx)(RemInput_1.RightAlignInput, { children: (0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { value: scale, onTextChange: onScaleChanged, placeholder: `${MIN_SCALE}-${MAX_SCALE}`, onValueChange: onScaleSetDirectly, name: "scale", step: 0.1, min: MIN_SCALE, max: MAX_SCALE }) }) })] }));
10
26
  };
11
27
  exports.ScaleSetting = ScaleSetting;
@@ -2,4 +2,3 @@
2
2
  export declare const optionRow: React.CSSProperties;
3
3
  export declare const label: React.CSSProperties;
4
4
  export declare const rightRow: React.CSSProperties;
5
- export declare const input: React.CSSProperties;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.input = exports.rightRow = exports.label = exports.optionRow = void 0;
3
+ exports.rightRow = exports.label = exports.optionRow = void 0;
4
4
  const colors_1 = require("../../helpers/colors");
5
5
  exports.optionRow = {
6
6
  display: 'flex',
@@ -9,12 +9,10 @@ exports.optionRow = {
9
9
  minHeight: 40,
10
10
  paddingLeft: 16,
11
11
  paddingRight: 16,
12
- paddingTop: 8,
13
- paddingBottom: 8,
14
12
  };
15
13
  exports.label = {
16
14
  width: 150,
17
- fontSize: 15,
15
+ fontSize: 14,
18
16
  lineHeight: '40px',
19
17
  color: colors_1.LIGHT_TEXT,
20
18
  };
@@ -25,7 +23,3 @@ exports.rightRow = {
25
23
  alignSelf: 'center',
26
24
  flex: 1,
27
25
  };
28
- exports.input = {
29
- minWidth: 250,
30
- textAlign: 'right',
31
- };
@@ -4,12 +4,11 @@ exports.RenderQueueRemoveItem = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const InlineAction_1 = require("../InlineAction");
7
- const NotificationCenter_1 = require("../Notifications/NotificationCenter");
8
7
  const actions_1 = require("./actions");
9
8
  const RenderQueueRemoveItem = ({ job }) => {
10
9
  const onClick = (0, react_1.useCallback)(() => {
11
10
  (0, actions_1.removeRenderJob)(job).catch((err) => {
12
- (0, NotificationCenter_1.sendErrorNotification)(`Could not remove item: ${err.message}`);
11
+ // TODO: Handle error
13
12
  console.log(err);
14
13
  });
15
14
  }, [job]);