@remotion/studio 4.0.468 → 4.0.470

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 (134) hide show
  1. package/dist/Studio.js +1 -1
  2. package/dist/api/save-render-output.js +3 -12
  3. package/dist/components/AudioWaveform.d.ts +1 -0
  4. package/dist/components/AudioWaveform.js +41 -23
  5. package/dist/components/CompositionSelectorItem.js +1 -1
  6. package/dist/components/ContextMenu.d.ts +1 -0
  7. package/dist/components/ContextMenu.js +8 -3
  8. package/dist/components/EditorContent.js +5 -4
  9. package/dist/components/EditorGuides/Guide.js +1 -1
  10. package/dist/components/ExpandedTracksProvider.js +3 -8
  11. package/dist/components/Menu/MenuItem.d.ts +1 -1
  12. package/dist/components/Menu/MenuItem.js +2 -2
  13. package/dist/components/Menu/MenuSubItem.js +5 -5
  14. package/dist/components/MenuBuildIndicator.js +0 -1
  15. package/dist/components/NewComposition/InputDragger.js +1 -0
  16. package/dist/components/Preview.js +4 -1
  17. package/dist/components/PreviewToolbar.js +2 -4
  18. package/dist/components/SelectedOutlineOverlay.d.ts +18 -0
  19. package/dist/components/SelectedOutlineOverlay.js +645 -0
  20. package/dist/components/Timeline/Timeline.js +18 -3
  21. package/dist/components/Timeline/TimelineDeleteKeybindings.d.ts +2 -0
  22. package/dist/components/Timeline/TimelineDeleteKeybindings.js +86 -0
  23. package/dist/components/Timeline/TimelineDragHandler.js +19 -244
  24. package/dist/components/Timeline/{TimelineEffectGroupRow.d.ts → TimelineEffectItem.d.ts} +1 -1
  25. package/dist/components/Timeline/{TimelineEffectGroupRow.js → TimelineEffectItem.js} +61 -35
  26. package/dist/components/Timeline/{TimelineEffectFieldRow.d.ts → TimelineEffectPropItem.d.ts} +4 -1
  27. package/dist/components/Timeline/{TimelineEffectFieldRow.js → TimelineEffectPropItem.js} +100 -16
  28. package/dist/components/Timeline/TimelineExpandArrowButton.js +5 -1
  29. package/dist/components/Timeline/TimelineExpandedKeyframeRow.d.ts +11 -0
  30. package/dist/components/Timeline/TimelineExpandedKeyframeRow.js +55 -0
  31. package/dist/components/Timeline/TimelineExpandedRow.d.ts +1 -0
  32. package/dist/components/Timeline/TimelineExpandedRow.js +29 -11
  33. package/dist/components/Timeline/TimelineExpandedSection.d.ts +1 -0
  34. package/dist/components/Timeline/TimelineExpandedSection.js +4 -4
  35. package/dist/components/Timeline/TimelineExpandedTrackKeyframes.d.ts +1 -1
  36. package/dist/components/Timeline/TimelineExpandedTrackKeyframes.js +9 -120
  37. package/dist/components/Timeline/TimelineFieldLabel.d.ts +6 -0
  38. package/dist/components/Timeline/TimelineFieldLabel.js +30 -0
  39. package/dist/components/Timeline/TimelineInOutDragHandler.d.ts +2 -0
  40. package/dist/components/Timeline/TimelineInOutDragHandler.js +324 -0
  41. package/dist/components/Timeline/TimelineInOutPointer.js +3 -1
  42. package/dist/components/Timeline/TimelineInOutPointerHandle.d.ts +1 -0
  43. package/dist/components/Timeline/TimelineInOutPointerHandle.js +5 -4
  44. package/dist/components/Timeline/TimelineItemStack.d.ts +5 -0
  45. package/dist/components/Timeline/TimelineItemStack.js +82 -0
  46. package/dist/components/Timeline/TimelineKeyframeControls.d.ts +17 -0
  47. package/dist/components/Timeline/TimelineKeyframeControls.js +217 -0
  48. package/dist/components/Timeline/TimelineKeyframeDiamond.d.ts +7 -0
  49. package/dist/components/Timeline/TimelineKeyframeDiamond.js +88 -0
  50. package/dist/components/Timeline/TimelineKeyframedValue.d.ts +8 -0
  51. package/dist/components/Timeline/TimelineKeyframedValue.js +36 -0
  52. package/dist/components/Timeline/TimelineLayerEye.js +1 -0
  53. package/dist/components/Timeline/TimelineList.js +3 -11
  54. package/dist/components/Timeline/TimelineMediaInfo.d.ts +13 -1
  55. package/dist/components/Timeline/TimelineMediaInfo.js +33 -80
  56. package/dist/components/Timeline/TimelineRowChrome.d.ts +10 -1
  57. package/dist/components/Timeline/TimelineRowChrome.js +64 -6
  58. package/dist/components/Timeline/TimelineSchemaField.js +7 -2
  59. package/dist/components/Timeline/TimelineSelection.d.ts +85 -0
  60. package/dist/components/Timeline/TimelineSelection.js +417 -0
  61. package/dist/components/Timeline/TimelineSequence.d.ts +2 -0
  62. package/dist/components/Timeline/TimelineSequence.js +18 -6
  63. package/dist/components/Timeline/TimelineSequenceFrame.js +1 -0
  64. package/dist/components/Timeline/{TimelineListItem.d.ts → TimelineSequenceItem.d.ts} +2 -2
  65. package/dist/components/Timeline/{TimelineListItem.js → TimelineSequenceItem.js} +148 -63
  66. package/dist/components/Timeline/TimelineSequenceName.d.ts +7 -0
  67. package/dist/components/Timeline/TimelineSequenceName.js +50 -0
  68. package/dist/components/Timeline/{TimelineFieldRow.d.ts → TimelineSequencePropItem.d.ts} +5 -3
  69. package/dist/components/Timeline/TimelineSequencePropItem.js +183 -0
  70. package/dist/components/Timeline/TimelineTimeIndicators.js +0 -1
  71. package/dist/components/Timeline/TimelineTrack.d.ts +5 -0
  72. package/dist/components/Timeline/TimelineTrack.js +66 -0
  73. package/dist/components/Timeline/TimelineTracks.js +2 -16
  74. package/dist/components/Timeline/TimelineTranslateField.js +14 -22
  75. package/dist/components/Timeline/TimelineUvCoordinateField.d.ts +11 -0
  76. package/dist/components/Timeline/TimelineUvCoordinateField.js +126 -0
  77. package/dist/components/Timeline/TimelineVideoInfo.js +11 -12
  78. package/dist/components/Timeline/call-add-keyframe.d.ts +23 -0
  79. package/dist/components/Timeline/call-add-keyframe.js +54 -0
  80. package/dist/components/Timeline/call-delete-keyframe.d.ts +21 -0
  81. package/dist/components/Timeline/call-delete-keyframe.js +50 -0
  82. package/dist/components/Timeline/delete-selected-keyframe.d.ts +11 -0
  83. package/dist/components/Timeline/delete-selected-keyframe.js +51 -0
  84. package/dist/components/Timeline/delete-selected-timeline-item.d.ts +17 -0
  85. package/dist/components/Timeline/delete-selected-timeline-item.js +183 -0
  86. package/dist/components/Timeline/duplicate-selected-timeline-item.d.ts +13 -0
  87. package/dist/components/Timeline/duplicate-selected-timeline-item.js +66 -0
  88. package/dist/components/Timeline/find-track-for-node-path-info.d.ts +7 -0
  89. package/dist/components/Timeline/find-track-for-node-path-info.js +13 -0
  90. package/dist/components/Timeline/get-keyframe-navigation.d.ts +9 -0
  91. package/dist/components/Timeline/get-keyframe-navigation.js +26 -0
  92. package/dist/components/Timeline/get-node-keyframes.d.ts +11 -0
  93. package/dist/components/Timeline/get-node-keyframes.js +23 -0
  94. package/dist/components/Timeline/get-timeline-keyframes.js +6 -8
  95. package/dist/components/Timeline/parse-keyframe-field-from-node-path.d.ts +8 -0
  96. package/dist/components/Timeline/parse-keyframe-field-from-node-path.js +26 -0
  97. package/dist/components/Timeline/save-sequence-prop.d.ts +14 -2
  98. package/dist/components/Timeline/save-sequence-prop.js +42 -7
  99. package/dist/components/Timeline/timeline-field-row-layout.d.ts +1 -0
  100. package/dist/components/Timeline/timeline-field-row-layout.js +11 -1
  101. package/dist/components/Timeline/timeline-row-layout.d.ts +1 -0
  102. package/dist/components/Timeline/timeline-row-layout.js +2 -1
  103. package/dist/components/Timeline/timeline-translate-utils.d.ts +2 -0
  104. package/dist/components/Timeline/timeline-translate-utils.js +20 -0
  105. package/dist/components/Timeline/use-expanded-track-keyframe-rows.d.ts +17 -0
  106. package/dist/components/Timeline/use-expanded-track-keyframe-rows.js +52 -0
  107. package/dist/components/Timeline/use-open-sequence-in-editor.d.ts +6 -0
  108. package/dist/components/Timeline/use-open-sequence-in-editor.js +31 -0
  109. package/dist/components/composition-menu-items.js +32 -1
  110. package/dist/error-overlay/remotion-overlay/OpenInEditor.js +0 -1
  111. package/dist/esm/{chunk-8q828zk7.js → chunk-dny42qnq.js} +10566 -6221
  112. package/dist/esm/internals.mjs +10566 -6221
  113. package/dist/esm/previewEntry.mjs +10548 -6203
  114. package/dist/esm/renderEntry.mjs +3 -1
  115. package/dist/helpers/colors.d.ts +1 -1
  116. package/dist/helpers/colors.js +1 -1
  117. package/dist/helpers/format-file-location.d.ts +9 -0
  118. package/dist/helpers/format-file-location.js +27 -0
  119. package/dist/helpers/get-box-quads-polyfill-internals.d.ts +82 -0
  120. package/dist/helpers/get-box-quads-polyfill-internals.js +2395 -0
  121. package/dist/helpers/get-box-quads-ponyfill.d.ts +10 -0
  122. package/dist/helpers/get-box-quads-ponyfill.js +23 -0
  123. package/dist/helpers/open-in-editor.d.ts +1 -1
  124. package/dist/helpers/open-in-editor.js +11 -26
  125. package/dist/helpers/timeline-layout.d.ts +8 -7
  126. package/dist/helpers/timeline-layout.js +9 -8
  127. package/dist/helpers/timeline-node-path-key.d.ts +2 -0
  128. package/dist/helpers/timeline-node-path-key.js +10 -0
  129. package/dist/helpers/use-menu-structure.js +8 -16
  130. package/dist/renderEntry.js +2 -2
  131. package/package.json +10 -10
  132. package/dist/components/Timeline/TimelineFieldRow.js +0 -113
  133. package/dist/components/Timeline/TimelineStack/index.d.ts +0 -8
  134. package/dist/components/Timeline/TimelineStack/index.js +0 -119
package/dist/Studio.js CHANGED
@@ -17,7 +17,7 @@ const getServerDisconnectedDomElement = () => {
17
17
  };
18
18
  const StudioInner = ({ rootComponent, readOnly }) => {
19
19
  var _a, _b;
20
- return (jsx_runtime_1.jsx(remotion_1.Internals.CompositionManagerProvider, { onlyRenderComposition: null, currentCompositionMetadata: null, initialCompositions: [], initialCanvasContent: null, children: jsx_runtime_1.jsx(remotion_1.Internals.RemotionRootContexts, { frameState: null, audioEnabled: window.remotion_audioEnabled, videoEnabled: window.remotion_videoEnabled, logLevel: (_a = window.remotion_logLevel) !== null && _a !== void 0 ? _a : 'info', numberOfAudioTags: window.remotion_numberOfAudioTags, audioLatencyHint: (_b = window.remotion_audioLatencyHint) !== null && _b !== void 0 ? _b : 'playback', children: jsx_runtime_1.jsx(use_static_files_1.StaticFilesProvider, { children: jsx_runtime_1.jsx(ResolveCompositionConfigInStudio_1.ResolveCompositionConfigInStudio, { children: jsx_runtime_1.jsxs(EditorContexts_1.EditorContexts, { readOnlyStudio: readOnly, children: [
20
+ return (jsx_runtime_1.jsx(remotion_1.Internals.CompositionManagerProvider, { onlyRenderComposition: null, currentCompositionMetadata: null, initialCompositions: [], initialCanvasContent: null, children: jsx_runtime_1.jsx(remotion_1.Internals.RemotionRootContexts, { frameState: null, audioEnabled: window.remotion_audioEnabled, videoEnabled: window.remotion_videoEnabled, logLevel: (_a = window.remotion_logLevel) !== null && _a !== void 0 ? _a : 'info', numberOfAudioTags: window.remotion_numberOfAudioTags, audioLatencyHint: (_b = window.remotion_audioLatencyHint) !== null && _b !== void 0 ? _b : 'playback', previewSampleRate: window.remotion_previewSampleRate, children: jsx_runtime_1.jsx(use_static_files_1.StaticFilesProvider, { children: jsx_runtime_1.jsx(ResolveCompositionConfigInStudio_1.ResolveCompositionConfigInStudio, { children: jsx_runtime_1.jsxs(EditorContexts_1.EditorContexts, { readOnlyStudio: readOnly, children: [
21
21
  jsx_runtime_1.jsx(Editor_1.Editor, { readOnlyStudio: readOnly, Root: rootComponent }), readOnly
22
22
  ? null
23
23
  : (0, react_dom_1.createPortal)(jsx_runtime_1.jsx(ServerDisconnected_1.ServerDisconnected, {}), getServerDisconnectedDomElement())] }) }) }) }) }));
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.unregisterClientRender = exports.registerClientRender = exports.saveOutputFile = void 0;
4
+ const call_api_1 = require("../components/call-api");
4
5
  const throwIfNotOk = async (response) => {
5
6
  if (!response.ok) {
6
7
  try {
@@ -26,20 +27,10 @@ const saveOutputFile = async ({ blob, filePath, }) => {
26
27
  };
27
28
  exports.saveOutputFile = saveOutputFile;
28
29
  const registerClientRender = async (render) => {
29
- const response = await fetch('/api/register-client-render', {
30
- method: 'POST',
31
- headers: { 'Content-Type': 'application/json' },
32
- body: JSON.stringify(render),
33
- });
34
- await throwIfNotOk(response);
30
+ await (0, call_api_1.callApi)('/api/register-client-render', render);
35
31
  };
36
32
  exports.registerClientRender = registerClientRender;
37
33
  const unregisterClientRender = async (id) => {
38
- const response = await fetch('/api/unregister-client-render', {
39
- method: 'POST',
40
- headers: { 'Content-Type': 'application/json' },
41
- body: JSON.stringify({ id }),
42
- });
43
- await throwIfNotOk(response);
34
+ await (0, call_api_1.callApi)('/api/unregister-client-render', { id });
44
35
  };
45
36
  exports.unregisterClientRender = unregisterClientRender;
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import type { LoopDisplay } from 'remotion';
3
3
  export declare const AudioWaveform: React.FC<{
4
4
  readonly src: string;
5
+ readonly height: number;
5
6
  readonly visualizationWidth: number;
6
7
  readonly startFrom: number;
7
8
  readonly durationInFrames: number;
@@ -21,12 +21,15 @@ const canUseAudioWaveformWorker = () => {
21
21
  }
22
22
  return 'transferControlToOffscreen' in HTMLCanvasElement.prototype;
23
23
  };
24
- const container = {
25
- display: 'flex',
26
- flexDirection: 'row',
27
- alignItems: 'center',
28
- position: 'absolute',
29
- inset: 0,
24
+ const getContainerStyle = (height) => {
25
+ return {
26
+ display: 'flex',
27
+ flexDirection: 'row',
28
+ alignItems: 'center',
29
+ position: 'relative',
30
+ width: '100%',
31
+ height,
32
+ };
30
33
  };
31
34
  const errorMessage = {
32
35
  fontSize: 13,
@@ -38,6 +41,9 @@ const errorMessage = {
38
41
  maxWidth: 450,
39
42
  opacity: 0.75,
40
43
  };
44
+ const getWaveformErrorMessage = () => {
45
+ return new Error('No waveform available. The audio could not be decoded or may not support CORS.');
46
+ };
41
47
  const waveformCanvasStyle = {
42
48
  pointerEvents: 'none',
43
49
  width: '100%',
@@ -74,7 +80,7 @@ const drawLoopedWaveform = ({ canvas, peaks, volume, visualizationWidth, loopWid
74
80
  ctx.fillStyle = pattern;
75
81
  ctx.fillRect(0, 0, w, h);
76
82
  };
77
- const AudioWaveform = ({ src, startFrom, durationInFrames, visualizationWidth, volume, doesVolumeChange, playbackRate, loopDisplay, }) => {
83
+ const AudioWaveform = ({ src, height, startFrom, durationInFrames, visualizationWidth, volume, doesVolumeChange, playbackRate, loopDisplay, }) => {
78
84
  const [peaks, setPeaks] = (0, react_1.useState)(null);
79
85
  const [error, setError] = (0, react_1.useState)(null);
80
86
  const [waveformCanvasKey, setWaveformCanvasKey] = (0, react_1.useState)(0);
@@ -83,12 +89,12 @@ const AudioWaveform = ({ src, startFrom, durationInFrames, visualizationWidth, v
83
89
  if (vidConf === null) {
84
90
  throw new Error('Expected video config');
85
91
  }
86
- const containerRef = (0, react_1.useRef)(null);
87
92
  const waveformCanvas = (0, react_1.useRef)(null);
88
93
  const volumeCanvas = (0, react_1.useRef)(null);
89
94
  const waveformWorker = (0, react_1.useRef)(null);
90
95
  const hasTransferredCanvas = (0, react_1.useRef)(false);
91
96
  const latestRequestId = (0, react_1.useRef)(0);
97
+ const shouldRenderVolumeOverlay = doesVolumeChange && typeof volume === 'string';
92
98
  (0, react_1.useEffect)(() => {
93
99
  if (canUseWorkerPath) {
94
100
  return;
@@ -118,6 +124,7 @@ const AudioWaveform = ({ src, startFrom, durationInFrames, visualizationWidth, v
118
124
  return;
119
125
  }
120
126
  const worker = (0, timeline_utils_1.makeAudioWaveformWorker)();
127
+ let workerFailed = false;
121
128
  waveformWorker.current = worker;
122
129
  worker.addEventListener('message', (event) => {
123
130
  if (event.data.type === 'error') {
@@ -127,6 +134,17 @@ const AudioWaveform = ({ src, startFrom, durationInFrames, visualizationWidth, v
127
134
  setError(new Error(event.data.message));
128
135
  }
129
136
  });
137
+ worker.addEventListener('error', (event) => {
138
+ event.preventDefault();
139
+ workerFailed = true;
140
+ if (worker !== waveformWorker.current) {
141
+ return;
142
+ }
143
+ worker.terminate();
144
+ waveformWorker.current = null;
145
+ hasTransferredCanvas.current = false;
146
+ setError(getWaveformErrorMessage());
147
+ });
130
148
  let offscreen;
131
149
  try {
132
150
  offscreen = canvasElement.transferControlToOffscreen();
@@ -143,7 +161,9 @@ const AudioWaveform = ({ src, startFrom, durationInFrames, visualizationWidth, v
143
161
  hasTransferredCanvas.current = true;
144
162
  worker.postMessage({ type: 'init', canvas: offscreen }, [offscreen]);
145
163
  return () => {
146
- worker.postMessage({ type: 'dispose' });
164
+ if (!workerFailed) {
165
+ worker.postMessage({ type: 'dispose' });
166
+ }
147
167
  worker.terminate();
148
168
  waveformWorker.current = null;
149
169
  hasTransferredCanvas.current = false;
@@ -173,11 +193,10 @@ const AudioWaveform = ({ src, startFrom, durationInFrames, visualizationWidth, v
173
193
  ]);
174
194
  (0, react_1.useEffect)(() => {
175
195
  const { current: canvasElement } = waveformCanvas;
176
- const { current: containerElement } = containerRef;
177
- if (!canvasElement || !containerElement) {
196
+ if (!canvasElement) {
178
197
  return;
179
198
  }
180
- const h = containerElement.clientHeight;
199
+ const h = height;
181
200
  const w = Math.ceil(visualizationWidth);
182
201
  const vol = typeof volume === 'number' ? volume : 1;
183
202
  if (canUseWorkerPath) {
@@ -229,6 +248,7 @@ const AudioWaveform = ({ src, startFrom, durationInFrames, visualizationWidth, v
229
248
  }, [
230
249
  canUseWorkerPath,
231
250
  durationInFrames,
251
+ height,
232
252
  loopDisplay,
233
253
  playbackRate,
234
254
  portionPeaks,
@@ -240,12 +260,14 @@ const AudioWaveform = ({ src, startFrom, durationInFrames, visualizationWidth, v
240
260
  waveformCanvasKey,
241
261
  ]);
242
262
  (0, react_1.useEffect)(() => {
263
+ if (!shouldRenderVolumeOverlay) {
264
+ return;
265
+ }
243
266
  const { current: volumeCanvasElement } = volumeCanvas;
244
- const { current: containerElement } = containerRef;
245
- if (!volumeCanvasElement || !containerElement) {
267
+ if (!volumeCanvasElement) {
246
268
  return;
247
269
  }
248
- const h = containerElement.clientHeight;
270
+ const h = height;
249
271
  const context = volumeCanvasElement.getContext('2d');
250
272
  if (!context) {
251
273
  return;
@@ -253,9 +275,6 @@ const AudioWaveform = ({ src, startFrom, durationInFrames, visualizationWidth, v
253
275
  volumeCanvasElement.width = Math.ceil(visualizationWidth);
254
276
  volumeCanvasElement.height = h;
255
277
  context.clearRect(0, 0, visualizationWidth, h);
256
- if (!doesVolumeChange || typeof volume === 'number') {
257
- return;
258
- }
259
278
  const volumes = volume.split(',').map((v) => Number(v));
260
279
  context.beginPath();
261
280
  context.moveTo(0, h);
@@ -271,17 +290,16 @@ const AudioWaveform = ({ src, startFrom, durationInFrames, visualizationWidth, v
271
290
  });
272
291
  context.strokeStyle = colors_1.LIGHT_TRANSPARENT;
273
292
  context.stroke();
274
- }, [visualizationWidth, volume, doesVolumeChange]);
293
+ }, [height, shouldRenderVolumeOverlay, visualizationWidth, volume]);
275
294
  if (error) {
276
295
  // eslint-disable-next-line no-console
277
296
  console.error(error);
278
- return (jsx_runtime_1.jsx("div", { style: container, children: jsx_runtime_1.jsx("div", { style: errorMessage, children: "No waveform available. Audio might not support CORS." }) }));
297
+ return (jsx_runtime_1.jsx("div", { style: getContainerStyle(height), children: jsx_runtime_1.jsx("div", { style: errorMessage, children: "No waveform available. The audio could not be decoded or may not support CORS." }) }));
279
298
  }
280
299
  if (!canUseWorkerPath && !peaks) {
281
300
  return null;
282
301
  }
283
- return (jsx_runtime_1.jsxs("div", { ref: containerRef, style: container, children: [
284
- jsx_runtime_1.jsx("canvas", { ref: waveformCanvas, style: waveformCanvasStyle }, waveformCanvasKey), jsx_runtime_1.jsx("canvas", { ref: volumeCanvas, style: volumeCanvasStyle })
285
- ] }));
302
+ return (jsx_runtime_1.jsxs("div", { style: getContainerStyle(height), children: [
303
+ jsx_runtime_1.jsx("canvas", { ref: waveformCanvas, style: waveformCanvasStyle }, waveformCanvasKey), shouldRenderVolumeOverlay ? (jsx_runtime_1.jsx("canvas", { ref: volumeCanvas, style: volumeCanvasStyle })) : null] }));
286
304
  };
287
305
  exports.AudioWaveform = AudioWaveform;
@@ -131,7 +131,7 @@ const CompositionSelectorItem = ({ item, level, currentComposition, tabIndex, se
131
131
  })
132
132
  : null] }));
133
133
  }
134
- return (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { values: contextMenu, children: jsx_runtime_1.jsx(layout_1.Row, { align: "center", children: jsx_runtime_1.jsxs("a", { ref: compositionRowRef, style: style, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, tabIndex: tabIndex, onClick: onClick, onKeyPress: onKeyPress, type: "button", title: item.composition.id, className: "__remotion-composition", "data-compname": item.composition.id, children: [(0, is_composition_still_1.isCompositionStill)(item.composition) ? (jsx_runtime_1.jsx(still_1.StillIcon, { color: hovered || selected ? 'white' : colors_1.LIGHT_TEXT, style: iconStyle })) : (jsx_runtime_1.jsx(video_1.FilmIcon, { color: hovered || selected ? 'white' : colors_1.LIGHT_TEXT, style: iconStyle })), jsx_runtime_1.jsx(layout_1.Spacing, { x: 1 }), jsx_runtime_1.jsx("div", { style: label, children: item.composition.id }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(CompositionContextButton_1.CompositionContextButton, { values: contextMenu, visible: hovered }), jsx_runtime_1.jsx(SidebarRenderButton_1.SidebarRenderButton, { visible: hovered, composition: item.composition })
134
+ return (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { values: contextMenu, onOpen: null, children: jsx_runtime_1.jsx(layout_1.Row, { align: "center", children: jsx_runtime_1.jsxs("a", { ref: compositionRowRef, style: style, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, tabIndex: tabIndex, onClick: onClick, onKeyPress: onKeyPress, type: "button", title: item.composition.id, className: "__remotion-composition", "data-compname": item.composition.id, children: [(0, is_composition_still_1.isCompositionStill)(item.composition) ? (jsx_runtime_1.jsx(still_1.StillIcon, { color: hovered || selected ? 'white' : colors_1.LIGHT_TEXT, style: iconStyle })) : (jsx_runtime_1.jsx(video_1.FilmIcon, { color: hovered || selected ? 'white' : colors_1.LIGHT_TEXT, style: iconStyle })), jsx_runtime_1.jsx(layout_1.Spacing, { x: 1 }), jsx_runtime_1.jsx("div", { style: label, children: item.composition.id }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(CompositionContextButton_1.CompositionContextButton, { values: contextMenu, visible: hovered }), jsx_runtime_1.jsx(SidebarRenderButton_1.SidebarRenderButton, { visible: hovered, composition: item.composition })
135
135
  ] }) }) }));
136
136
  };
137
137
  exports.CompositionSelectorItem = CompositionSelectorItem;
@@ -3,4 +3,5 @@ import type { ComboboxValue } from './NewComposition/ComboBox';
3
3
  export declare const ContextMenu: React.FC<{
4
4
  readonly children: React.ReactNode;
5
5
  readonly values: ComboboxValue[];
6
+ readonly onOpen: (() => void) | null;
6
7
  }>;
@@ -14,7 +14,7 @@ const z_index_1 = require("../state/z-index");
14
14
  const portals_1 = require("./Menu/portals");
15
15
  const styles_1 = require("./Menu/styles");
16
16
  const MenuContent_1 = require("./NewComposition/MenuContent");
17
- const ContextMenu = ({ children, values }) => {
17
+ const ContextMenu = ({ children, values, onOpen }) => {
18
18
  const ref = (0, react_1.useRef)(null);
19
19
  const [opened, setOpened] = (0, react_1.useState)({ type: 'not-open' });
20
20
  const { currentZIndex } = (0, z_index_1.useZIndex)();
@@ -34,6 +34,7 @@ const ContextMenu = ({ children, values }) => {
34
34
  const onClick = (e) => {
35
35
  e.preventDefault();
36
36
  e.stopPropagation();
37
+ onOpen === null || onOpen === void 0 ? void 0 : onOpen();
37
38
  setOpened({ type: 'open', left: e.clientX, top: e.clientY });
38
39
  return false;
39
40
  };
@@ -41,7 +42,7 @@ const ContextMenu = ({ children, values }) => {
41
42
  return () => {
42
43
  current.removeEventListener('contextmenu', onClick);
43
44
  };
44
- }, [size]);
45
+ }, [onOpen, size]);
45
46
  const spaceToBottom = (0, react_1.useMemo)(() => {
46
47
  if (size && opened.type === 'open') {
47
48
  return size.windowSize.height - opened.top;
@@ -91,9 +92,13 @@ const ContextMenu = ({ children, values }) => {
91
92
  const onHide = (0, react_1.useCallback)(() => {
92
93
  setOpened({ type: 'not-open' });
93
94
  }, []);
95
+ // Prevent deselection of a selected item
96
+ const onPointerDown = (0, react_1.useCallback)((e) => {
97
+ e.stopPropagation();
98
+ }, []);
94
99
  return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
95
100
  jsx_runtime_1.jsx("div", { ref: ref, onContextMenu: () => false, style: style, children: children }), portalStyle
96
- ? react_dom_1.default.createPortal(jsx_runtime_1.jsx("div", { style: styles_1.fullScreenOverlay, children: jsx_runtime_1.jsx("div", { style: styles_1.outerPortal, className: "css-reset", children: jsx_runtime_1.jsx(z_index_1.HigherZIndex, { onOutsideClick: onHide, onEscape: onHide, children: jsx_runtime_1.jsx("div", { style: portalStyle, children: jsx_runtime_1.jsx(MenuContent_1.MenuContent, { onNextMenu: noop_1.noop, onPreviousMenu: noop_1.noop, values: values, onHide: onHide, leaveLeftSpace: true, preselectIndex: false, topItemCanBeUnselected: false, fixedHeight: null }) }) }) }) }), (0, portals_1.getPortal)(currentZIndex))
101
+ ? react_dom_1.default.createPortal(jsx_runtime_1.jsx("div", { style: styles_1.fullScreenOverlay, children: jsx_runtime_1.jsx("div", { style: styles_1.outerPortal, className: "css-reset", children: jsx_runtime_1.jsx(z_index_1.HigherZIndex, { onOutsideClick: onHide, onEscape: onHide, children: jsx_runtime_1.jsx("div", { style: portalStyle, onPointerDown: onPointerDown, children: jsx_runtime_1.jsx(MenuContent_1.MenuContent, { onNextMenu: noop_1.noop, onPreviousMenu: noop_1.noop, values: values, onHide: onHide, leaveLeftSpace: true, preselectIndex: false, topItemCanBeUnselected: false, fixedHeight: null }) }) }) }) }), (0, portals_1.getPortal)(currentZIndex))
97
102
  : null] }));
98
103
  };
99
104
  exports.ContextMenu = ContextMenu;
@@ -11,6 +11,7 @@ const SplitterElement_1 = require("./Splitter/SplitterElement");
11
11
  const SplitterHandle_1 = require("./Splitter/SplitterHandle");
12
12
  const Timeline_1 = require("./Timeline/Timeline");
13
13
  const TimelineEmptyState_1 = require("./Timeline/TimelineEmptyState");
14
+ const TimelineSelection_1 = require("./Timeline/TimelineSelection");
14
15
  const noop = () => undefined;
15
16
  const container = {
16
17
  display: 'flex',
@@ -21,10 +22,10 @@ const container = {
21
22
  const EditorContent = ({ readOnlyStudio, children }) => {
22
23
  const { canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
23
24
  const showTimeline = canvasContent !== null && canvasContent.type === 'composition';
24
- return (jsx_runtime_1.jsxs("div", { style: container, children: [
25
- jsx_runtime_1.jsx(InitialCompositionLoader_1.InitialCompositionLoader, {}), jsx_runtime_1.jsx(MenuToolbar_1.MenuToolbar, { readOnlyStudio: readOnlyStudio }), jsx_runtime_1.jsxs(SplitterContainer_1.SplitterContainer, { orientation: "horizontal", id: "top-to-bottom", maxFlex: 0.9, minFlex: 0.2, defaultFlex: 0.75, children: [
26
- jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { sticky: null, type: "flexer", children: children }), jsx_runtime_1.jsx(SplitterHandle_1.SplitterHandle, { allowToCollapse: "none", onCollapse: noop }), jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { sticky: null, type: "anti-flexer", children: showTimeline ? jsx_runtime_1.jsx(Timeline_1.Timeline, {}) : jsx_runtime_1.jsx(TimelineEmptyState_1.TimelineEmptyState, {}) })
27
- ] })
25
+ const content = (jsx_runtime_1.jsxs(SplitterContainer_1.SplitterContainer, { orientation: "horizontal", id: "top-to-bottom", maxFlex: 0.9, minFlex: 0.2, defaultFlex: 0.75, children: [
26
+ jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { sticky: null, type: "flexer", children: children }), jsx_runtime_1.jsx(SplitterHandle_1.SplitterHandle, { allowToCollapse: "none", onCollapse: noop }), jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { sticky: null, type: "anti-flexer", children: showTimeline ? jsx_runtime_1.jsx(Timeline_1.Timeline, {}) : jsx_runtime_1.jsx(TimelineEmptyState_1.TimelineEmptyState, {}) })
28
27
  ] }));
28
+ return (jsx_runtime_1.jsxs("div", { style: container, children: [
29
+ jsx_runtime_1.jsx(InitialCompositionLoader_1.InitialCompositionLoader, {}), jsx_runtime_1.jsx(MenuToolbar_1.MenuToolbar, { readOnlyStudio: readOnlyStudio }), showTimeline ? (jsx_runtime_1.jsx(TimelineSelection_1.TimelineSelectionProvider, { children: content })) : (content)] }));
29
30
  };
30
31
  exports.EditorContent = EditorContent;
@@ -80,7 +80,7 @@ const GuideComp = ({ guide, canvasDimensions, scale }) => {
80
80
  },
81
81
  ];
82
82
  }, [guide.id, setGuidesList]);
83
- return (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { values: values, children: jsx_runtime_1.jsx("div", { style: guideStyle, onMouseDown: onMouseDown, className: "__remotion_editor_guide", onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, children: jsx_runtime_1.jsx("div", { style: guideContentStyle, className: [
83
+ return (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { values: values, onOpen: null, children: jsx_runtime_1.jsx("div", { style: guideStyle, onMouseDown: onMouseDown, className: "__remotion_editor_guide", onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, children: jsx_runtime_1.jsx("div", { style: guideContentStyle, className: [
84
84
  '__remotion_editor_guide_content',
85
85
  selectedGuideId === guide.id || hoveredGuideId === guide.id
86
86
  ? '__remotion_editor_guide_selected'
@@ -2,15 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ExpandedTracksProvider = exports.ExpandedTracksSetterContext = exports.ExpandedTracksGetterContext = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
- const studio_shared_1 = require("@remotion/studio-shared");
6
5
  const react_1 = require("react");
7
6
  const migrate_expanded_tracks_for_subscription_key_1 = require("../helpers/migrate-expanded-tracks-for-subscription-key");
8
7
  const persist_boolean_map_1 = require("../helpers/persist-boolean-map");
9
- const nodePathInfoToExpandedKey = (info) => [
10
- (0, studio_shared_1.stringifySequenceExpandedRowKey)(info.sequenceSubscriptionKey),
11
- info.auxiliaryKeys.join('.'),
12
- info.index,
13
- ].join('.');
8
+ const timeline_node_path_key_1 = require("../helpers/timeline-node-path-key");
14
9
  const SESSION_STORAGE_KEY = 'remotion.editor.expandedTracks';
15
10
  const loadExpandedTracks = () => {
16
11
  return (0, persist_boolean_map_1.loadPersistedBooleanMap)(SESSION_STORAGE_KEY);
@@ -32,7 +27,7 @@ const ExpandedTracksProvider = ({ children }) => {
32
27
  const [expandedTracks, setExpandedTracks] = (0, react_1.useState)(loadExpandedTracks);
33
28
  const toggleTrack = (0, react_1.useCallback)((nodePathInfo) => {
34
29
  setExpandedTracks((prev) => {
35
- const key = nodePathInfoToExpandedKey(nodePathInfo);
30
+ const key = (0, timeline_node_path_key_1.timelineNodePathInfoToKey)(nodePathInfo);
36
31
  const next = (0, persist_boolean_map_1.toggleBooleanMapKey)(prev, key);
37
32
  (0, persist_boolean_map_1.persistBooleanMap)(SESSION_STORAGE_KEY, next);
38
33
  return next;
@@ -51,7 +46,7 @@ const ExpandedTracksProvider = ({ children }) => {
51
46
  const getterValue = (0, react_1.useMemo)(() => ({
52
47
  getIsExpanded: (nodePathInfo) => {
53
48
  var _a;
54
- return (_a = expandedTracks[nodePathInfoToExpandedKey(nodePathInfo)]) !== null && _a !== void 0 ? _a : false;
49
+ return (_a = expandedTracks[(0, timeline_node_path_key_1.timelineNodePathInfoToKey)(nodePathInfo)]) !== null && _a !== void 0 ? _a : false;
55
50
  },
56
51
  }), [expandedTracks]);
57
52
  const setterValue = (0, react_1.useMemo)(() => ({
@@ -1,7 +1,7 @@
1
1
  import type { SetStateAction } from 'react';
2
2
  import React from 'react';
3
3
  import type { ComboboxValue } from '../NewComposition/ComboBox';
4
- export type MenuId = 'remotion' | 'file' | 'view' | 'composition' | 'install' | 'tools' | 'help';
4
+ export type MenuId = 'remotion' | 'file' | 'view' | 'composition' | 'tools' | 'help';
5
5
  export type Menu = {
6
6
  id: MenuId;
7
7
  label: React.ReactNode;
@@ -20,8 +20,8 @@ const container = {
20
20
  paddingLeft: 10,
21
21
  paddingRight: 10,
22
22
  cursor: 'default',
23
- paddingTop: 8,
24
- paddingBottom: 8,
23
+ paddingTop: 6,
24
+ paddingBottom: 6,
25
25
  userSelect: 'none',
26
26
  WebkitUserSelect: 'none',
27
27
  border: 'none',
@@ -19,9 +19,9 @@ const portals_1 = require("./portals");
19
19
  const styles_1 = require("./styles");
20
20
  const SubMenu_1 = require("./SubMenu");
21
21
  const container = {
22
- paddingTop: 8,
23
- paddingBottom: 8,
24
- paddingLeft: 12,
22
+ paddingTop: 4,
23
+ paddingBottom: 4,
24
+ paddingLeft: 8,
25
25
  paddingRight: 8,
26
26
  cursor: 'default',
27
27
  };
@@ -38,8 +38,8 @@ const keyHintCss = {
38
38
  fontSize: 13,
39
39
  };
40
40
  const leftSpace = {
41
- width: 24,
42
- marginLeft: -6,
41
+ width: 20,
42
+ marginLeft: -2,
43
43
  display: 'inline-flex',
44
44
  justifyContent: 'center',
45
45
  alignItems: 'center',
@@ -56,7 +56,6 @@ const MenuBuildIndicator = () => {
56
56
  originalFunctionName: null,
57
57
  originalScriptCode: null,
58
58
  })
59
- .then((res) => res.json())
60
59
  .then(({ success }) => {
61
60
  if (!success) {
62
61
  (0, NotificationCenter_1.showNotification)(`Could not open ${window.remotion_editorName}`, 2000);
@@ -120,6 +120,7 @@ const InputDraggerForwardRefFn = ({ onValueChange, onValueChangeEnd, min: _min,
120
120
  return Math.ceil(val * factor) / factor;
121
121
  };
122
122
  const onPointerDown = (0, react_1.useCallback)((e) => {
123
+ e.stopPropagation();
123
124
  pointerDownRef.current = true;
124
125
  const target = e.currentTarget;
125
126
  const { pageX, pageY, button } = e;
@@ -11,6 +11,7 @@ const colors_1 = require("../helpers/colors");
11
11
  const checkerboard_1 = require("../state/checkerboard");
12
12
  const is_menu_item_1 = require("./Menu/is-menu-item");
13
13
  const RenderPreview_1 = require("./RenderPreview");
14
+ const SelectedOutlineOverlay_1 = require("./SelectedOutlineOverlay");
14
15
  const Spinner_1 = require("./Spinner");
15
16
  const StaticFilePreview_1 = require("./StaticFilePreview");
16
17
  const centeredContainer = {
@@ -146,7 +147,9 @@ const CompWhenItHasDimensions = ({ contentDimensions, canvasSize, canvasContent,
146
147
  if (canvasContent.type === 'output-blob') {
147
148
  return (jsx_runtime_1.jsx("div", { style: outer, children: jsx_runtime_1.jsx(RenderPreview_1.RenderPreview, { path: canvasContent.displayName, assetMetadata: assetMetadata, getBlob: canvasContent.getBlob }) }));
148
149
  }
149
- return (jsx_runtime_1.jsx("div", { style: outer, children: jsx_runtime_1.jsx(PortalContainer, { contentDimensions: contentDimensions, scale: scale, xCorrection: xCorrection, yCorrection: yCorrection }) }));
150
+ return (jsx_runtime_1.jsxs("div", { style: outer, children: [
151
+ jsx_runtime_1.jsx(PortalContainer, { contentDimensions: contentDimensions, scale: scale, xCorrection: xCorrection, yCorrection: yCorrection }), jsx_runtime_1.jsx(SelectedOutlineOverlay_1.SelectedOutlineOverlay, { scale: scale })
152
+ ] }));
150
153
  };
151
154
  const PortalContainer = ({ scale, xCorrection, yCorrection, contentDimensions }) => {
152
155
  const { checkerboard } = (0, react_1.useContext)(checkerboard_1.CheckerboardContext);
@@ -24,13 +24,11 @@ const RenderButton_1 = require("./RenderButton");
24
24
  const SizeSelector_1 = require("./SizeSelector");
25
25
  const TimelineZoomControls_1 = require("./Timeline/TimelineZoomControls");
26
26
  const TimelineInOutToggle_1 = require("./TimelineInOutToggle");
27
- const TOOLBAR_HEIGHT = 56;
27
+ const TOOLBAR_HEIGHT = 50;
28
28
  const container = {
29
29
  display: 'flex',
30
30
  justifyContent: 'center',
31
31
  borderTop: '1px solid rgba(0, 0, 0, 0.5)',
32
- paddingTop: 2,
33
- paddingBottom: 2,
34
32
  alignItems: 'center',
35
33
  flexDirection: 'row',
36
34
  background: colors_1.BACKGROUND,
@@ -64,7 +62,7 @@ const scrollIndicatorRight = {
64
62
  };
65
63
  const sideContainer = {
66
64
  width: 300,
67
- height: 38,
65
+ height: 36,
68
66
  display: 'flex',
69
67
  flexDirection: 'row',
70
68
  alignItems: 'center',
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import { type TimelineSelection } from './Timeline/TimelineSelection';
3
+ type OutlinePoint = {
4
+ readonly x: number;
5
+ readonly y: number;
6
+ };
7
+ type UvCoordinate = readonly [number, number];
8
+ export declare const getUvHandlePosition: (points: readonly [OutlinePoint, OutlinePoint, OutlinePoint, OutlinePoint], uv: UvCoordinate) => OutlinePoint;
9
+ export declare const getUvCoordinateForPoint: (points: readonly [OutlinePoint, OutlinePoint, OutlinePoint, OutlinePoint], point: OutlinePoint) => UvCoordinate;
10
+ type SelectedEffectFields = {
11
+ allFields: boolean;
12
+ fieldKeys: Set<string>;
13
+ };
14
+ export declare const getSelectedEffectFieldsBySequenceKey: (selectedItems: readonly TimelineSelection[]) => Map<string, Map<number, SelectedEffectFields>>;
15
+ export declare const SelectedOutlineOverlay: React.FC<{
16
+ readonly scale: number;
17
+ }>;
18
+ export {};