@remotion/studio 4.0.443 → 4.0.445

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 (39) hide show
  1. package/dist/components/Canvas.js +197 -42
  2. package/dist/components/CanvasOrLoading.js +5 -0
  3. package/dist/components/Editor.js +10 -2
  4. package/dist/components/RenderErrorContext.d.ts +4 -0
  5. package/dist/components/RenderErrorContext.js +7 -0
  6. package/dist/components/RenderModal/RenderModalJSONPropsEditor.js +7 -5
  7. package/dist/components/RenderModal/SchemaEditor/SchemaEditor.js +5 -1
  8. package/dist/components/RenderModal/SchemaEditor/SchemaErrorMessages.js +5 -3
  9. package/dist/components/Timeline/Timeline.js +8 -5
  10. package/dist/components/Timeline/TimelineListItem.js +71 -8
  11. package/dist/components/Timeline/TimelinePinchZoom.d.ts +2 -0
  12. package/dist/components/Timeline/TimelinePinchZoom.js +240 -0
  13. package/dist/components/Timeline/TimelineSlider.js +22 -8
  14. package/dist/components/Timeline/TimelineVideoInfo.js +1 -18
  15. package/dist/components/Timeline/TimelineZoomControls.js +6 -3
  16. package/dist/components/Timeline/timeline-scroll-logic.d.ts +13 -1
  17. package/dist/components/Timeline/timeline-scroll-logic.js +20 -6
  18. package/dist/components/Timeline/use-sequence-props-subscription.d.ts +1 -1
  19. package/dist/components/Timeline/use-sequence-props-subscription.js +25 -6
  20. package/dist/error-overlay/react-overlay/listen-to-runtime-errors.js +1 -5
  21. package/dist/error-overlay/remotion-overlay/CopyStackTrace.d.ts +5 -0
  22. package/dist/error-overlay/remotion-overlay/CopyStackTrace.js +50 -0
  23. package/dist/error-overlay/remotion-overlay/ErrorDisplay.js +16 -13
  24. package/dist/error-overlay/remotion-overlay/MediaPlaybackErrorExplainer.d.ts +4 -0
  25. package/dist/error-overlay/remotion-overlay/MediaPlaybackErrorExplainer.js +87 -0
  26. package/dist/esm/{chunk-cpv44d93.js → chunk-bqd9dhnk.js} +3532 -2899
  27. package/dist/esm/internals.mjs +3532 -2899
  28. package/dist/esm/previewEntry.mjs +3727 -3098
  29. package/dist/esm/renderEntry.mjs +1 -1
  30. package/dist/helpers/frame-database.d.ts +2 -2
  31. package/dist/helpers/frame-database.js +36 -25
  32. package/dist/helpers/get-effective-translation.d.ts +13 -1
  33. package/dist/helpers/get-effective-translation.js +45 -1
  34. package/dist/helpers/use-max-media-duration.js +1 -1
  35. package/dist/state/timeline-zoom.d.ts +5 -1
  36. package/dist/state/timeline-zoom.js +21 -16
  37. package/package.json +9 -9
  38. package/dist/error-overlay/remotion-overlay/CompositionIdsDropdown.d.ts +0 -5
  39. package/dist/error-overlay/remotion-overlay/CompositionIdsDropdown.js +0 -108
@@ -21,13 +21,14 @@ const use_is_ruler_visible_1 = require("./EditorRuler/use-is-ruler-visible");
21
21
  const layout_1 = require("./layout");
22
22
  const Preview_1 = require("./Preview");
23
23
  const ResetZoomButton_1 = require("./ResetZoomButton");
24
- const container = {
24
+ const getContainerStyle = (editorZoomGestures) => ({
25
25
  flex: 1,
26
26
  display: 'flex',
27
27
  overflow: 'hidden',
28
28
  position: 'relative',
29
29
  backgroundColor: colors_1.BACKGROUND,
30
- };
30
+ ...(editorZoomGestures ? { touchAction: 'none' } : {}),
31
+ });
31
32
  const resetZoom = {
32
33
  position: 'absolute',
33
34
  top: layout_1.SPACING_UNIT * 2,
@@ -37,6 +38,14 @@ const ZOOM_PX_FACTOR = 0.003;
37
38
  const Canvas = ({ canvasContent, size }) => {
38
39
  const { setSize, size: previewSize } = (0, react_1.useContext)(remotion_1.Internals.PreviewSizeContext);
39
40
  const { editorZoomGestures } = (0, react_1.useContext)(editor_zoom_gestures_1.EditorZoomGesturesContext);
41
+ const previewSnapshotRef = (0, react_1.useRef)({
42
+ previewSize,
43
+ canvasSize: size,
44
+ contentDimensions: null,
45
+ });
46
+ const pinchBaseZoomRef = (0, react_1.useRef)(null);
47
+ const suppressWheelFromWebKitPinchRef = (0, react_1.useRef)(false);
48
+ const touchPinchRef = (0, react_1.useRef)(null);
40
49
  const keybindings = (0, use_keybinding_1.useKeybinding)();
41
50
  const config = remotion_1.Internals.useUnsafeVideoConfig();
42
51
  const areRulersVisible = (0, use_is_ruler_visible_1.useIsRulerVisible)();
@@ -56,7 +65,13 @@ const Canvas = ({ canvasContent, size }) => {
56
65
  return null;
57
66
  }, [assetResolution, config, canvasContent]);
58
67
  const isFit = previewSize.size === 'auto';
68
+ previewSnapshotRef.current = {
69
+ previewSize,
70
+ canvasSize: size,
71
+ contentDimensions,
72
+ };
59
73
  const onWheel = (0, react_1.useCallback)((e) => {
74
+ const ev = e;
60
75
  if (!editorZoomGestures) {
61
76
  return;
62
77
  }
@@ -66,11 +81,15 @@ const Canvas = ({ canvasContent, size }) => {
66
81
  if (!contentDimensions || contentDimensions === 'none') {
67
82
  return;
68
83
  }
69
- const wantsToZoom = e.ctrlKey || e.metaKey;
84
+ const wantsToZoom = ev.ctrlKey || ev.metaKey;
70
85
  if (!wantsToZoom && isFit) {
71
86
  return;
72
87
  }
73
- e.preventDefault();
88
+ if (suppressWheelFromWebKitPinchRef.current && wantsToZoom) {
89
+ ev.preventDefault();
90
+ return;
91
+ }
92
+ ev.preventDefault();
74
93
  setSize((prevSize) => {
75
94
  const scale = remotion_1.Internals.calculateScale({
76
95
  canvasSize: size,
@@ -78,41 +97,20 @@ const Canvas = ({ canvasContent, size }) => {
78
97
  compositionWidth: contentDimensions.width,
79
98
  previewSize: prevSize.size,
80
99
  });
81
- // Zoom in/out
82
100
  if (wantsToZoom) {
83
101
  const oldSize = prevSize.size === 'auto' ? scale : prevSize.size;
84
102
  const smoothened = (0, smooth_zoom_1.smoothenZoom)(oldSize);
85
- const added = smoothened + e.deltaY * ZOOM_PX_FACTOR;
103
+ const added = smoothened + ev.deltaY * ZOOM_PX_FACTOR;
86
104
  const unsmoothened = (0, smooth_zoom_1.unsmoothenZoom)(added);
87
- const { centerX, centerY } = (0, get_effective_translation_1.getCenterPointWhileScrolling)({
88
- size,
89
- clientX: e.clientX,
90
- clientY: e.clientY,
91
- compositionWidth: contentDimensions.width,
92
- compositionHeight: contentDimensions.height,
93
- scale,
94
- translation: prevSize.translation,
105
+ return (0, get_effective_translation_1.applyZoomAroundFocalPoint)({
106
+ canvasSize: size,
107
+ contentDimensions,
108
+ previewSizeBefore: prevSize,
109
+ oldNumericSize: oldSize,
110
+ newNumericSize: unsmoothened,
111
+ clientX: ev.clientX,
112
+ clientY: ev.clientY,
95
113
  });
96
- const zoomDifference = unsmoothened - oldSize;
97
- const uvCoordinatesX = centerX / contentDimensions.width;
98
- const uvCoordinatesY = centerY / contentDimensions.height;
99
- const correctionLeft = -uvCoordinatesX * (zoomDifference * contentDimensions.width) +
100
- (1 - uvCoordinatesX) * zoomDifference * contentDimensions.width;
101
- const correctionTop = -uvCoordinatesY * (zoomDifference * contentDimensions.height) +
102
- (1 - uvCoordinatesY) * zoomDifference * contentDimensions.height;
103
- return {
104
- translation: (0, get_effective_translation_1.getEffectiveTranslation)({
105
- translation: {
106
- x: prevSize.translation.x - correctionLeft / 2,
107
- y: prevSize.translation.y - correctionTop / 2,
108
- },
109
- canvasSize: size,
110
- compositionHeight: contentDimensions.height,
111
- compositionWidth: contentDimensions.width,
112
- scale,
113
- }),
114
- size: unsmoothened,
115
- };
116
114
  }
117
115
  const effectiveTranslation = (0, get_effective_translation_1.getEffectiveTranslation)({
118
116
  translation: prevSize.translation,
@@ -121,13 +119,12 @@ const Canvas = ({ canvasContent, size }) => {
121
119
  compositionWidth: contentDimensions.width,
122
120
  scale,
123
121
  });
124
- // Pan
125
122
  return {
126
123
  ...prevSize,
127
124
  translation: (0, get_effective_translation_1.getEffectiveTranslation)({
128
125
  translation: {
129
- x: effectiveTranslation.x + e.deltaX,
130
- y: effectiveTranslation.y + e.deltaY,
126
+ x: effectiveTranslation.x + ev.deltaX,
127
+ y: effectiveTranslation.y + ev.deltaY,
131
128
  },
132
129
  canvasSize: size,
133
130
  compositionHeight: contentDimensions.height,
@@ -143,12 +140,170 @@ const Canvas = ({ canvasContent, size }) => {
143
140
  return;
144
141
  }
145
142
  current.addEventListener('wheel', onWheel, { passive: false });
146
- return () =>
147
- // @ts-expect-error
148
- current.removeEventListener('wheel', onWheel, {
143
+ return () => {
144
+ current.removeEventListener('wheel', onWheel);
145
+ };
146
+ }, [onWheel]);
147
+ const supportsWebKitPinch = typeof window !== 'undefined' && 'GestureEvent' in window;
148
+ (0, react_1.useEffect)(() => {
149
+ const { current } = canvas_ref_1.canvasRef;
150
+ if (!current || !editorZoomGestures || !supportsWebKitPinch) {
151
+ return;
152
+ }
153
+ const endWebKitPinch = () => {
154
+ pinchBaseZoomRef.current = null;
155
+ suppressWheelFromWebKitPinchRef.current = false;
156
+ };
157
+ const onGestureStart = (event) => {
158
+ const e = event;
159
+ const snap = previewSnapshotRef.current;
160
+ const canvasSz = snap.canvasSize;
161
+ const cdim = snap.contentDimensions;
162
+ if (!canvasSz || !cdim || cdim === 'none') {
163
+ return;
164
+ }
165
+ e.preventDefault();
166
+ suppressWheelFromWebKitPinchRef.current = true;
167
+ const fitted = remotion_1.Internals.calculateScale({
168
+ canvasSize: canvasSz,
169
+ compositionHeight: cdim.height,
170
+ compositionWidth: cdim.width,
171
+ previewSize: snap.previewSize.size,
172
+ });
173
+ pinchBaseZoomRef.current =
174
+ snap.previewSize.size === 'auto' ? fitted : snap.previewSize.size;
175
+ };
176
+ const onGestureChange = (event) => {
177
+ const e = event;
178
+ const base = pinchBaseZoomRef.current;
179
+ const snap = previewSnapshotRef.current;
180
+ const canvasSz = snap.canvasSize;
181
+ const cdim = snap.contentDimensions;
182
+ if (base === null || !canvasSz || !cdim || cdim === 'none') {
183
+ return;
184
+ }
185
+ const dimensions = cdim;
186
+ e.preventDefault();
187
+ setSize((prevSize) => {
188
+ const scale = remotion_1.Internals.calculateScale({
189
+ canvasSize: canvasSz,
190
+ compositionHeight: dimensions.height,
191
+ compositionWidth: dimensions.width,
192
+ previewSize: prevSize.size,
193
+ });
194
+ const oldNumeric = prevSize.size === 'auto' ? scale : prevSize.size;
195
+ return (0, get_effective_translation_1.applyZoomAroundFocalPoint)({
196
+ canvasSize: canvasSz,
197
+ contentDimensions: dimensions,
198
+ previewSizeBefore: prevSize,
199
+ oldNumericSize: oldNumeric,
200
+ newNumericSize: base * e.scale,
201
+ clientX: e.clientX,
202
+ clientY: e.clientY,
203
+ });
204
+ });
205
+ };
206
+ const onGestureEnd = () => {
207
+ endWebKitPinch();
208
+ };
209
+ current.addEventListener('gesturestart', onGestureStart, {
149
210
  passive: false,
150
211
  });
151
- }, [onWheel]);
212
+ current.addEventListener('gesturechange', onGestureChange, {
213
+ passive: false,
214
+ });
215
+ current.addEventListener('gestureend', onGestureEnd);
216
+ current.addEventListener('gesturecancel', onGestureEnd);
217
+ return () => {
218
+ current.removeEventListener('gesturestart', onGestureStart);
219
+ current.removeEventListener('gesturechange', onGestureChange);
220
+ current.removeEventListener('gestureend', onGestureEnd);
221
+ current.removeEventListener('gesturecancel', onGestureEnd);
222
+ };
223
+ }, [editorZoomGestures, setSize, supportsWebKitPinch]);
224
+ (0, react_1.useEffect)(() => {
225
+ const { current } = canvas_ref_1.canvasRef;
226
+ if (!current || !editorZoomGestures) {
227
+ return;
228
+ }
229
+ const onTouchStart = (event) => {
230
+ if (event.touches.length !== 2) {
231
+ touchPinchRef.current = null;
232
+ return;
233
+ }
234
+ const snap = previewSnapshotRef.current;
235
+ if (!snap.canvasSize ||
236
+ !snap.contentDimensions ||
237
+ snap.contentDimensions === 'none') {
238
+ return;
239
+ }
240
+ const [t0, t1] = [event.touches[0], event.touches[1]];
241
+ const initialDistance = Math.hypot(t1.clientX - t0.clientX, t1.clientY - t0.clientY);
242
+ if (initialDistance < 1e-6) {
243
+ return;
244
+ }
245
+ const fitted = remotion_1.Internals.calculateScale({
246
+ canvasSize: snap.canvasSize,
247
+ compositionHeight: snap.contentDimensions.height,
248
+ compositionWidth: snap.contentDimensions.width,
249
+ previewSize: snap.previewSize.size,
250
+ });
251
+ const initialZoom = snap.previewSize.size === 'auto' ? fitted : snap.previewSize.size;
252
+ touchPinchRef.current = { initialDistance, initialZoom };
253
+ };
254
+ const onTouchMove = (event) => {
255
+ const pinch = touchPinchRef.current;
256
+ const snap = previewSnapshotRef.current;
257
+ if (pinch === null ||
258
+ event.touches.length !== 2 ||
259
+ !snap.canvasSize ||
260
+ !snap.contentDimensions ||
261
+ snap.contentDimensions === 'none') {
262
+ return;
263
+ }
264
+ event.preventDefault();
265
+ const [t0, t1] = [event.touches[0], event.touches[1]];
266
+ const dist = Math.hypot(t1.clientX - t0.clientX, t1.clientY - t0.clientY);
267
+ const ratio = dist / pinch.initialDistance;
268
+ const clientX = (t0.clientX + t1.clientX) / 2;
269
+ const clientY = (t0.clientY + t1.clientY) / 2;
270
+ setSize((prevSize) => {
271
+ const canvasSz = snap.canvasSize;
272
+ const cdim = snap.contentDimensions;
273
+ const scale = remotion_1.Internals.calculateScale({
274
+ canvasSize: canvasSz,
275
+ compositionHeight: cdim.height,
276
+ compositionWidth: cdim.width,
277
+ previewSize: prevSize.size,
278
+ });
279
+ const oldNumeric = prevSize.size === 'auto' ? scale : prevSize.size;
280
+ return (0, get_effective_translation_1.applyZoomAroundFocalPoint)({
281
+ canvasSize: canvasSz,
282
+ contentDimensions: cdim,
283
+ previewSizeBefore: prevSize,
284
+ oldNumericSize: oldNumeric,
285
+ newNumericSize: pinch.initialZoom * ratio,
286
+ clientX,
287
+ clientY,
288
+ });
289
+ });
290
+ };
291
+ const onTouchEnd = (event) => {
292
+ if (event.touches.length < 2) {
293
+ touchPinchRef.current = null;
294
+ }
295
+ };
296
+ current.addEventListener('touchstart', onTouchStart, { passive: true });
297
+ current.addEventListener('touchmove', onTouchMove, { passive: false });
298
+ current.addEventListener('touchend', onTouchEnd);
299
+ current.addEventListener('touchcancel', onTouchEnd);
300
+ return () => {
301
+ current.removeEventListener('touchstart', onTouchStart);
302
+ current.removeEventListener('touchmove', onTouchMove);
303
+ current.removeEventListener('touchend', onTouchEnd);
304
+ current.removeEventListener('touchcancel', onTouchEnd);
305
+ };
306
+ }, [editorZoomGestures, setSize]);
152
307
  const onReset = (0, react_1.useCallback)(() => {
153
308
  setSize(() => {
154
309
  return {
@@ -263,6 +418,6 @@ const Canvas = ({ canvasContent, size }) => {
263
418
  fetchMetadata();
264
419
  }, [fetchMetadata]);
265
420
  return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
266
- jsx_runtime_1.jsxs("div", { ref: canvas_ref_1.canvasRef, style: container, children: [size ? (jsx_runtime_1.jsx(Preview_1.VideoPreview, { canvasContent: canvasContent, contentDimensions: contentDimensions, canvasSize: size, assetMetadata: assetResolution })) : null, isFit ? null : (jsx_runtime_1.jsx("div", { style: resetZoom, className: "css-reset", children: jsx_runtime_1.jsx(ResetZoomButton_1.ResetZoomButton, { onClick: onReset }) })), editorShowGuides && canvasContent.type === 'composition' && (jsx_runtime_1.jsx(EditorGuides_1.default, { canvasSize: size, contentDimensions: contentDimensions, assetMetadata: assetResolution }))] }), areRulersVisible && (jsx_runtime_1.jsx(EditorRuler_1.EditorRulers, { contentDimensions: contentDimensions, canvasSize: size, assetMetadata: assetResolution, containerRef: canvas_ref_1.canvasRef }))] }));
421
+ jsx_runtime_1.jsxs("div", { ref: canvas_ref_1.canvasRef, style: getContainerStyle(editorZoomGestures), children: [size ? (jsx_runtime_1.jsx(Preview_1.VideoPreview, { canvasContent: canvasContent, contentDimensions: contentDimensions, canvasSize: size, assetMetadata: assetResolution })) : null, isFit ? null : (jsx_runtime_1.jsx("div", { style: resetZoom, className: "css-reset", children: jsx_runtime_1.jsx(ResetZoomButton_1.ResetZoomButton, { onClick: onReset }) })), editorShowGuides && canvasContent.type === 'composition' && (jsx_runtime_1.jsx(EditorGuides_1.default, { canvasSize: size, contentDimensions: contentDimensions, assetMetadata: assetResolution }))] }), areRulersVisible && (jsx_runtime_1.jsx(EditorRuler_1.EditorRulers, { contentDimensions: contentDimensions, canvasSize: size, assetMetadata: assetResolution, containerRef: canvas_ref_1.canvasRef }))] }));
267
422
  };
268
423
  exports.Canvas = Canvas;
@@ -11,6 +11,7 @@ const Canvas_1 = require("./Canvas");
11
11
  const FramePersistor_1 = require("./FramePersistor");
12
12
  const is_menu_item_1 = require("./Menu/is-menu-item");
13
13
  const RefreshCompositionOverlay_1 = require("./RefreshCompositionOverlay");
14
+ const RenderErrorContext_1 = require("./RenderErrorContext");
14
15
  const RunningCalculateMetadata_1 = require("./RunningCalculateMetadata");
15
16
  const imperative_state_1 = require("./Timeline/imperative-state");
16
17
  const timeline_scroll_logic_1 = require("./Timeline/timeline-scroll-logic");
@@ -28,6 +29,7 @@ const CanvasOrLoading = ({ size }) => {
28
29
  const resolved = remotion_1.Internals.useResolvedVideoConfig(null);
29
30
  const { setZoom } = (0, react_1.useContext)(timeline_zoom_1.TimelineZoomCtx);
30
31
  const { canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
32
+ const { error: renderError } = (0, react_1.useContext)(RenderErrorContext_1.RenderErrorContext);
31
33
  (0, react_1.useEffect)(() => {
32
34
  if ((resolved === null || resolved === void 0 ? void 0 : resolved.type) !== 'success' &&
33
35
  (resolved === null || resolved === void 0 ? void 0 : resolved.type) !== 'success-and-refreshing') {
@@ -42,6 +44,9 @@ const CanvasOrLoading = ({ size }) => {
42
44
  });
43
45
  });
44
46
  }, [resolved, setZoom]);
47
+ if (renderError) {
48
+ return jsx_runtime_1.jsx(ErrorLoading, { error: renderError });
49
+ }
45
50
  if (!canvasContent) {
46
51
  const compname = window.location.pathname.replace('/', '');
47
52
  return (jsx_runtime_1.jsx("div", { style: container, className: "css-reset", children: jsx_runtime_1.jsxs("div", { style: RunningCalculateMetadata_1.loaderLabel, children: ["Composition with ID ", compname, " not found."] }) }));
@@ -48,6 +48,7 @@ const ForceSpecificCursor_1 = require("./ForceSpecificCursor");
48
48
  const GlobalKeybindings_1 = require("./GlobalKeybindings");
49
49
  const Modals_1 = require("./Modals");
50
50
  const NotificationCenter_1 = require("./Notifications/NotificationCenter");
51
+ const RenderErrorContext_1 = require("./RenderErrorContext");
51
52
  const TopPanel_1 = require("./TopPanel");
52
53
  const background = {
53
54
  backgroundColor: colors_1.BACKGROUND,
@@ -83,10 +84,17 @@ const Editor = ({ Root, readOnlyStudio }) => {
83
84
  const MemoRoot = (0, react_1.useMemo)(() => {
84
85
  return react_1.default.memo(Root);
85
86
  }, [Root]);
87
+ const [renderError, setRenderError] = (0, react_1.useState)(null);
88
+ const clearError = (0, react_1.useCallback)(() => {
89
+ setRenderError(null);
90
+ }, []);
91
+ const compositionRenderErrorContextValue = (0, react_1.useMemo)(() => ({ setError: setRenderError, clearError }), [clearError]);
92
+ const renderErrorContextValue = (0, react_1.useMemo)(() => ({ error: renderError }), [renderError]);
86
93
  return (jsx_runtime_1.jsx(z_index_1.HigherZIndex, { onEscape: noop_1.noop, onOutsideClick: noop_1.noop, children: jsx_runtime_1.jsxs(timeline_zoom_1.TimelineZoomContext, { children: [
87
94
  jsx_runtime_1.jsxs(remotion_1.Internals.CurrentScaleContext.Provider, { value: value, children: [
88
- jsx_runtime_1.jsx(ForceSpecificCursor_1.ForceSpecificCursor, {}), jsx_runtime_1.jsxs("div", { style: background, children: [canvasMounted ? jsx_runtime_1.jsx(MemoRoot, {}) : null, jsx_runtime_1.jsxs(remotion_1.Internals.CanUseRemotionHooksProvider, { children: [
89
- jsx_runtime_1.jsx(EditorContent_1.EditorContent, { readOnlyStudio: readOnlyStudio, children: jsx_runtime_1.jsx(TopPanel_1.TopPanel, { drawRef: canvas_ref_1.drawRef, bufferStateDelayInMilliseconds: exports.BUFFER_STATE_DELAY_IN_MILLISECONDS, onMounted: onMounted, readOnlyStudio: readOnlyStudio }) }), jsx_runtime_1.jsx(GlobalKeybindings_1.GlobalKeybindings, {})
95
+ jsx_runtime_1.jsx(ForceSpecificCursor_1.ForceSpecificCursor, {}), jsx_runtime_1.jsxs("div", { style: background, children: [
96
+ jsx_runtime_1.jsx(remotion_1.Internals.CompositionRenderErrorContext.Provider, { value: compositionRenderErrorContextValue, children: canvasMounted ? jsx_runtime_1.jsx(MemoRoot, {}) : null }), jsx_runtime_1.jsxs(remotion_1.Internals.CanUseRemotionHooksProvider, { children: [
97
+ jsx_runtime_1.jsx(RenderErrorContext_1.RenderErrorContext.Provider, { value: renderErrorContextValue, children: jsx_runtime_1.jsx(EditorContent_1.EditorContent, { readOnlyStudio: readOnlyStudio, children: jsx_runtime_1.jsx(TopPanel_1.TopPanel, { drawRef: canvas_ref_1.drawRef, bufferStateDelayInMilliseconds: exports.BUFFER_STATE_DELAY_IN_MILLISECONDS, onMounted: onMounted, readOnlyStudio: readOnlyStudio }) }) }), jsx_runtime_1.jsx(GlobalKeybindings_1.GlobalKeybindings, {})
90
98
  ] })
91
99
  ] })
92
100
  ] }), jsx_runtime_1.jsx(Modals_1.Modals, { readOnlyStudio: readOnlyStudio }), jsx_runtime_1.jsx(NotificationCenter_1.NotificationCenter, {})
@@ -0,0 +1,4 @@
1
+ export type RenderErrorContextType = {
2
+ error: Error | null;
3
+ };
4
+ export declare const RenderErrorContext: import("react").Context<RenderErrorContextType>;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RenderErrorContext = void 0;
4
+ const react_1 = require("react");
5
+ exports.RenderErrorContext = (0, react_1.createContext)({
6
+ error: null,
7
+ });
@@ -112,11 +112,13 @@ const RenderModalJSONPropsEditor = ({ setValue, value, defaultProps, onSave, ser
112
112
  };
113
113
  }, [subscribeToEvent, compositionId, schema]);
114
114
  (0, react_1.useEffect)(() => {
115
- if (localValue.validJSON && (0, deep_equal_1.deepEqual)(value, localValue.value)) {
116
- return;
117
- }
118
- setLocalValue(parseJS(value, schema));
119
- }, [value, schema, localValue]);
115
+ setLocalValue((prev) => {
116
+ if (prev.validJSON && (0, deep_equal_1.deepEqual)(value, prev.value)) {
117
+ return prev;
118
+ }
119
+ return parseJS(value, schema);
120
+ });
121
+ }, [value, schema]);
120
122
  const onPretty = (0, react_1.useCallback)(() => {
121
123
  if (!localValue.validJSON) {
122
124
  return;
@@ -7,6 +7,7 @@ const is_menu_item_1 = require("../../Menu/is-menu-item");
7
7
  const SchemaErrorMessages_1 = require("./SchemaErrorMessages");
8
8
  const scroll_to_default_props_path_1 = require("./scroll-to-default-props-path");
9
9
  const zod_schema_type_1 = require("./zod-schema-type");
10
+ const ZodDiscriminatedUnionEditor_1 = require("./ZodDiscriminatedUnionEditor");
10
11
  const ZodObjectEditor_1 = require("./ZodObjectEditor");
11
12
  const scrollable = {
12
13
  display: 'flex',
@@ -19,9 +20,12 @@ const SchemaEditor = ({ schema, value, setValue }) => {
19
20
  throw new Error('expected zod');
20
21
  }
21
22
  const typeName = (0, zod_schema_type_1.getZodSchemaType)(schema);
22
- if (typeName !== 'object') {
23
+ if (typeName !== 'object' && typeName !== 'discriminatedUnion') {
23
24
  return jsx_runtime_1.jsx(SchemaErrorMessages_1.TopLevelZodValue, { typeReceived: typeName });
24
25
  }
26
+ if (typeName === 'discriminatedUnion') {
27
+ return (jsx_runtime_1.jsx("div", { ref: scroll_to_default_props_path_1.defaultPropsEditorScrollableAreaRef, style: scrollable, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(ZodDiscriminatedUnionEditor_1.ZodDiscriminatedUnionEditor, { schema: schema, setValue: setValue, value: value, mayPad: true, jsonPath: [], onRemove: null }) }));
28
+ }
25
29
  return (jsx_runtime_1.jsx("div", { ref: scroll_to_default_props_path_1.defaultPropsEditorScrollableAreaRef, style: scrollable, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(ZodObjectEditor_1.ZodObjectEditor, { discriminatedUnionReplacement: null, value: value, setValue: setValue, jsonPath: [], schema: schema, onRemove: null, mayPad: true }) }));
26
30
  };
27
31
  exports.SchemaEditor = SchemaEditor;
@@ -78,9 +78,11 @@ const InvalidSchema = ({ zodValidationResult }) => {
78
78
  exports.InvalidSchema = InvalidSchema;
79
79
  const TopLevelZodValue = ({ typeReceived }) => {
80
80
  return (jsx_runtime_1.jsxs("div", { style: explainer, children: [
81
- jsx_runtime_1.jsxs("div", { style: errorExplanation, children: ["The top-level type of the schema must be a pure", ' ', jsx_runtime_1.jsx("code", { style: codeSnippet, children: "z.object" }),
82
- ". Instead got a schema of type", ' ', jsx_runtime_1.jsx("code", { style: codeSnippet, children: typeReceived })
83
- ] }), jsx_runtime_1.jsx(layout_1.Spacing, { y: 1 }), jsx_runtime_1.jsx("div", { style: errorExplanation, children: "Fix the schema by changing the top-level Zod type to an object." }), jsx_runtime_1.jsx(layout_1.Spacing, { y: 2, block: true }), jsx_runtime_1.jsx(Button_1.Button, { onClick: openDocs, children: "Learn more" })
81
+ jsx_runtime_1.jsxs("div", { style: errorExplanation, children: ["The top-level type of the schema must be", ' ', jsx_runtime_1.jsx("code", { style: codeSnippet, children: "z.object()" }),
82
+ " or", ' ', jsx_runtime_1.jsx("code", { style: codeSnippet, children: "z.discriminatedUnion()" }),
83
+ ". Instead got a schema of type ",
84
+ jsx_runtime_1.jsx("code", { style: codeSnippet, children: typeReceived })
85
+ ] }), jsx_runtime_1.jsx(layout_1.Spacing, { y: 1 }), jsx_runtime_1.jsx("div", { style: errorExplanation, children: "Fix the schema by changing the top-level Zod type to an object or discriminated union." }), jsx_runtime_1.jsx(layout_1.Spacing, { y: 2, block: true }), jsx_runtime_1.jsx(Button_1.Button, { onClick: openDocs, children: "Learn more" })
84
86
  ] }));
85
87
  };
86
88
  exports.TopLevelZodValue = TopLevelZodValue;
@@ -52,6 +52,7 @@ const timeline_refs_1 = require("./timeline-refs");
52
52
  const TimelineDragHandler_1 = require("./TimelineDragHandler");
53
53
  const TimelineInOutPointer_1 = require("./TimelineInOutPointer");
54
54
  const TimelineList_1 = require("./TimelineList");
55
+ const TimelinePinchZoom_1 = require("./TimelinePinchZoom");
55
56
  const TimelinePlayCursorSyncer_1 = require("./TimelinePlayCursorSyncer");
56
57
  const TimelineScrollable_1 = require("./TimelineScrollable");
57
58
  const TimelineSlider_1 = require("./TimelineSlider");
@@ -116,10 +117,12 @@ const TimelineInner = () => {
116
117
  overflowX: 'hidden',
117
118
  };
118
119
  }, [hasBeenCut, shown, expandedTracks, visualModeEnabled]);
119
- return (jsx_runtime_1.jsx("div", { ref: timeline_refs_1.timelineVerticalScroll, style: container, className: 'css-reset ' + is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(TimelineWidthProvider_1.TimelineWidthProvider, { children: jsx_runtime_1.jsx("div", { style: inner, children: jsx_runtime_1.jsxs(SplitterContainer_1.SplitterContainer, { orientation: "vertical", defaultFlex: 0.2, id: "names-to-timeline", maxFlex: 0.5, minFlex: 0.15, children: [
120
- jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { type: "flexer", sticky: jsx_runtime_1.jsx(TimelineTimeIndicators_1.TimelineTimePlaceholders, {}), children: jsx_runtime_1.jsx(TimelineList_1.TimelineList, { timeline: shown }) }), jsx_runtime_1.jsx(SplitterHandle_1.SplitterHandle, { onCollapse: noop, allowToCollapse: "none" }), jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { type: "anti-flexer", sticky: null, children: jsx_runtime_1.jsxs(TimelineScrollable_1.TimelineScrollable, { children: [
121
- jsx_runtime_1.jsx(TimelineTracks_1.TimelineTracks, { timeline: shown, hasBeenCut: hasBeenCut }), jsx_runtime_1.jsx(TimelineInOutPointer_1.TimelineInOutPointer, {}), jsx_runtime_1.jsx(TimelinePlayCursorSyncer_1.TimelinePlayCursorSyncer, {}), jsx_runtime_1.jsx(TimelineDragHandler_1.TimelineDragHandler, {}), jsx_runtime_1.jsx(TimelineTimeIndicators_1.TimelineTimeIndicators, {}), jsx_runtime_1.jsx(TimelineSlider_1.TimelineSlider, {})
122
- ] }) })
123
- ] }) }) }) }));
120
+ return (jsx_runtime_1.jsx("div", { ref: timeline_refs_1.timelineVerticalScroll, style: container, className: 'css-reset ' + is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsxs(TimelineWidthProvider_1.TimelineWidthProvider, { children: [
121
+ jsx_runtime_1.jsx(TimelinePinchZoom_1.TimelinePinchZoom, {}), jsx_runtime_1.jsx("div", { style: inner, children: jsx_runtime_1.jsxs(SplitterContainer_1.SplitterContainer, { orientation: "vertical", defaultFlex: 0.2, id: "names-to-timeline", maxFlex: 0.5, minFlex: 0.15, children: [
122
+ jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { type: "flexer", sticky: jsx_runtime_1.jsx(TimelineTimeIndicators_1.TimelineTimePlaceholders, {}), children: jsx_runtime_1.jsx(TimelineList_1.TimelineList, { timeline: shown }) }), jsx_runtime_1.jsx(SplitterHandle_1.SplitterHandle, { onCollapse: noop, allowToCollapse: "none" }), jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { type: "anti-flexer", sticky: null, children: jsx_runtime_1.jsxs(TimelineScrollable_1.TimelineScrollable, { children: [
123
+ jsx_runtime_1.jsx(TimelineTracks_1.TimelineTracks, { timeline: shown, hasBeenCut: hasBeenCut }), jsx_runtime_1.jsx(TimelineInOutPointer_1.TimelineInOutPointer, {}), jsx_runtime_1.jsx(TimelinePlayCursorSyncer_1.TimelinePlayCursorSyncer, {}), jsx_runtime_1.jsx(TimelineDragHandler_1.TimelineDragHandler, {}), jsx_runtime_1.jsx(TimelineTimeIndicators_1.TimelineTimeIndicators, {}), jsx_runtime_1.jsx(TimelineSlider_1.TimelineSlider, {})
124
+ ] }) })
125
+ ] }) })
126
+ ] }) }));
124
127
  };
125
128
  exports.Timeline = react_1.default.memo(TimelineInner);
@@ -7,7 +7,10 @@ const remotion_1 = require("remotion");
7
7
  const client_id_1 = require("../../helpers/client-id");
8
8
  const colors_1 = require("../../helpers/colors");
9
9
  const timeline_layout_1 = require("../../helpers/timeline-layout");
10
+ const call_api_1 = require("../call-api");
11
+ const ContextMenu_1 = require("../ContextMenu");
10
12
  const ExpandedTracksProvider_1 = require("../ExpandedTracksProvider");
13
+ const NotificationCenter_1 = require("../Notifications/NotificationCenter");
11
14
  const TimelineExpandedSection_1 = require("./TimelineExpandedSection");
12
15
  const TimelineLayerEye_1 = require("./TimelineLayerEye");
13
16
  const TimelineStack_1 = require("./TimelineStack");
@@ -39,13 +42,73 @@ const arrowButton = {
39
42
  const TimelineListItem = ({ nestedDepth, sequence, isCompact }) => {
40
43
  var _a, _b;
41
44
  const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
42
- const visualModeEnabled = Boolean(process.env.EXPERIMENTAL_VISUAL_MODE_ENABLED) &&
43
- previewServerState.type === 'connected';
45
+ const visualModeEnvEnabled = Boolean(process.env.EXPERIMENTAL_VISUAL_MODE_ENABLED);
46
+ const previewConnected = previewServerState.type === 'connected';
47
+ const visualModeActive = visualModeEnvEnabled && previewConnected;
44
48
  const { hidden, setHidden } = (0, react_1.useContext)(remotion_1.Internals.SequenceVisibilityToggleContext);
45
49
  const { expandedTracks, toggleTrack } = (0, react_1.useContext)(ExpandedTracksProvider_1.ExpandedTracksContext);
46
50
  const originalLocation = (0, use_resolved_stack_1.useResolvedStack)((_a = sequence.stack) !== null && _a !== void 0 ? _a : null);
47
- const nodePath = (0, use_sequence_props_subscription_1.useSequencePropsSubscription)(sequence, originalLocation);
48
- const isExpanded = visualModeEnabled && ((_b = expandedTracks[sequence.id]) !== null && _b !== void 0 ? _b : false);
51
+ const nodePath = (0, use_sequence_props_subscription_1.useSequencePropsSubscription)(sequence, originalLocation, visualModeActive);
52
+ const validatedLocation = (0, react_1.useMemo)(() => {
53
+ var _a;
54
+ if (!originalLocation ||
55
+ !originalLocation.source ||
56
+ !originalLocation.line) {
57
+ return null;
58
+ }
59
+ return {
60
+ source: originalLocation.source,
61
+ line: originalLocation.line,
62
+ column: (_a = originalLocation.column) !== null && _a !== void 0 ? _a : 0,
63
+ };
64
+ }, [originalLocation]);
65
+ const canDeleteFromSource = Boolean(nodePath && (validatedLocation === null || validatedLocation === void 0 ? void 0 : validatedLocation.source));
66
+ const deleteDisabled = (0, react_1.useMemo)(() => !previewConnected || !sequence.controls || !canDeleteFromSource, [previewConnected, sequence.controls, canDeleteFromSource]);
67
+ const onDeleteSequenceFromSource = (0, react_1.useCallback)(async () => {
68
+ if (!(validatedLocation === null || validatedLocation === void 0 ? void 0 : validatedLocation.source) || !nodePath) {
69
+ return;
70
+ }
71
+ try {
72
+ const result = await (0, call_api_1.callApi)('/api/delete-jsx-node', {
73
+ fileName: validatedLocation.source,
74
+ nodePath,
75
+ });
76
+ if (result.success) {
77
+ (0, NotificationCenter_1.showNotification)('Removed sequence from source file', 2000);
78
+ }
79
+ else {
80
+ (0, NotificationCenter_1.showNotification)(result.reason, 4000);
81
+ }
82
+ }
83
+ catch (err) {
84
+ (0, NotificationCenter_1.showNotification)(err.message, 4000);
85
+ }
86
+ }, [nodePath, validatedLocation === null || validatedLocation === void 0 ? void 0 : validatedLocation.source]);
87
+ const contextMenuValues = (0, react_1.useMemo)(() => {
88
+ if (!visualModeEnvEnabled) {
89
+ return [];
90
+ }
91
+ return [
92
+ {
93
+ type: 'item',
94
+ id: 'delete-sequence',
95
+ keyHint: null,
96
+ label: 'Delete',
97
+ leftItem: null,
98
+ disabled: deleteDisabled,
99
+ onClick: () => {
100
+ if (deleteDisabled) {
101
+ return;
102
+ }
103
+ onDeleteSequenceFromSource();
104
+ },
105
+ quickSwitcherLabel: null,
106
+ subMenu: null,
107
+ value: 'delete-sequence',
108
+ },
109
+ ];
110
+ }, [deleteDisabled, onDeleteSequenceFromSource, visualModeEnvEnabled]);
111
+ const isExpanded = visualModeActive && ((_b = expandedTracks[sequence.id]) !== null && _b !== void 0 ? _b : false);
49
112
  const onToggleExpand = (0, react_1.useCallback)(() => {
50
113
  toggleTrack(sequence.id);
51
114
  }, [sequence.id, toggleTrack]);
@@ -87,9 +150,9 @@ const TimelineListItem = ({ nestedDepth, sequence, isCompact }) => {
87
150
  transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
88
151
  };
89
152
  }, [isExpanded]);
90
- return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
91
- jsx_runtime_1.jsxs("div", { style: outer, children: [
92
- jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEye, { type: sequence.type === 'audio' ? 'speaker' : 'eye', hidden: isItemHidden, onInvoked: onToggleVisibility }), jsx_runtime_1.jsx("div", { style: padder }), sequence.parent && nestedDepth > 0 ? jsx_runtime_1.jsx("div", { style: space }) : null, visualModeEnabled ? (sequence.controls ? (jsx_runtime_1.jsx("button", { type: "button", style: arrowStyle, onClick: onToggleExpand, "aria-expanded": isExpanded, "aria-label": `${isExpanded ? 'Collapse' : 'Expand'} track`, children: jsx_runtime_1.jsx("svg", { width: "12", height: "12", viewBox: "0 0 8 8", style: { display: 'block' }, children: jsx_runtime_1.jsx("path", { d: "M2 1L6 4L2 7Z", fill: "white" }) }) })) : (jsx_runtime_1.jsx("div", { style: arrowButton }))) : null, jsx_runtime_1.jsx(TimelineStack_1.TimelineStack, { sequence: sequence, isCompact: isCompact, originalLocation: originalLocation })
93
- ] }), visualModeEnabled && isExpanded && sequence.controls ? (jsx_runtime_1.jsx(TimelineExpandedSection_1.TimelineExpandedSection, { sequence: sequence, originalLocation: originalLocation, nestedDepth: nestedDepth, nodePath: nodePath })) : null] }));
153
+ const trackRow = (jsx_runtime_1.jsxs("div", { style: outer, children: [
154
+ jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEye, { type: sequence.type === 'audio' ? 'speaker' : 'eye', hidden: isItemHidden, onInvoked: onToggleVisibility }), jsx_runtime_1.jsx("div", { style: padder }), sequence.parent && nestedDepth > 0 ? jsx_runtime_1.jsx("div", { style: space }) : null, visualModeActive ? (sequence.controls ? (jsx_runtime_1.jsx("button", { type: "button", style: arrowStyle, onClick: onToggleExpand, "aria-expanded": isExpanded, "aria-label": `${isExpanded ? 'Collapse' : 'Expand'} track`, children: jsx_runtime_1.jsx("svg", { width: "12", height: "12", viewBox: "0 0 8 8", style: { display: 'block' }, children: jsx_runtime_1.jsx("path", { d: "M2 1L6 4L2 7Z", fill: "white" }) }) })) : (jsx_runtime_1.jsx("div", { style: arrowButton }))) : null, jsx_runtime_1.jsx(TimelineStack_1.TimelineStack, { sequence: sequence, isCompact: isCompact, originalLocation: originalLocation })
155
+ ] }));
156
+ return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [visualModeEnvEnabled ? (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { values: contextMenuValues, children: trackRow })) : (trackRow), visualModeActive && isExpanded && sequence.controls ? (jsx_runtime_1.jsx(TimelineExpandedSection_1.TimelineExpandedSection, { sequence: sequence, originalLocation: originalLocation, nestedDepth: nestedDepth, nodePath: nodePath })) : null] }));
94
157
  };
95
158
  exports.TimelineListItem = TimelineListItem;
@@ -0,0 +1,2 @@
1
+ import { type FC } from 'react';
2
+ export declare const TimelinePinchZoom: FC;