@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.
- package/dist/components/Canvas.js +197 -42
- package/dist/components/CanvasOrLoading.js +5 -0
- package/dist/components/Editor.js +10 -2
- package/dist/components/RenderErrorContext.d.ts +4 -0
- package/dist/components/RenderErrorContext.js +7 -0
- package/dist/components/RenderModal/RenderModalJSONPropsEditor.js +7 -5
- package/dist/components/RenderModal/SchemaEditor/SchemaEditor.js +5 -1
- package/dist/components/RenderModal/SchemaEditor/SchemaErrorMessages.js +5 -3
- package/dist/components/Timeline/Timeline.js +8 -5
- package/dist/components/Timeline/TimelineListItem.js +71 -8
- package/dist/components/Timeline/TimelinePinchZoom.d.ts +2 -0
- package/dist/components/Timeline/TimelinePinchZoom.js +240 -0
- package/dist/components/Timeline/TimelineSlider.js +22 -8
- package/dist/components/Timeline/TimelineVideoInfo.js +1 -18
- package/dist/components/Timeline/TimelineZoomControls.js +6 -3
- package/dist/components/Timeline/timeline-scroll-logic.d.ts +13 -1
- package/dist/components/Timeline/timeline-scroll-logic.js +20 -6
- package/dist/components/Timeline/use-sequence-props-subscription.d.ts +1 -1
- package/dist/components/Timeline/use-sequence-props-subscription.js +25 -6
- package/dist/error-overlay/react-overlay/listen-to-runtime-errors.js +1 -5
- package/dist/error-overlay/remotion-overlay/CopyStackTrace.d.ts +5 -0
- package/dist/error-overlay/remotion-overlay/CopyStackTrace.js +50 -0
- package/dist/error-overlay/remotion-overlay/ErrorDisplay.js +16 -13
- package/dist/error-overlay/remotion-overlay/MediaPlaybackErrorExplainer.d.ts +4 -0
- package/dist/error-overlay/remotion-overlay/MediaPlaybackErrorExplainer.js +87 -0
- package/dist/esm/{chunk-cpv44d93.js → chunk-bqd9dhnk.js} +3532 -2899
- package/dist/esm/internals.mjs +3532 -2899
- package/dist/esm/previewEntry.mjs +3727 -3098
- package/dist/esm/renderEntry.mjs +1 -1
- package/dist/helpers/frame-database.d.ts +2 -2
- package/dist/helpers/frame-database.js +36 -25
- package/dist/helpers/get-effective-translation.d.ts +13 -1
- package/dist/helpers/get-effective-translation.js +45 -1
- package/dist/helpers/use-max-media-duration.js +1 -1
- package/dist/state/timeline-zoom.d.ts +5 -1
- package/dist/state/timeline-zoom.js +21 -16
- package/package.json +9 -9
- package/dist/error-overlay/remotion-overlay/CompositionIdsDropdown.d.ts +0 -5
- package/dist/error-overlay/remotion-overlay/CompositionIdsDropdown.js +0 -108
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TimelinePinchZoom = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const remotion_1 = require("remotion");
|
|
6
|
+
const is_current_selected_still_1 = require("../../helpers/is-current-selected-still");
|
|
7
|
+
const editor_zoom_gestures_1 = require("../../state/editor-zoom-gestures");
|
|
8
|
+
const timeline_zoom_1 = require("../../state/timeline-zoom");
|
|
9
|
+
const timeline_refs_1 = require("./timeline-refs");
|
|
10
|
+
const timeline_scroll_logic_1 = require("./timeline-scroll-logic");
|
|
11
|
+
/**
|
|
12
|
+
* Maps wheel deltaY to zoom delta. Must be large enough that typical ctrl+wheel
|
|
13
|
+
* pinch steps change `TimelineZoomCtx` zoom by at least one 0.1 step after
|
|
14
|
+
* `Math.round(z * 10) / 10` in `timeline-zoom.tsx` (0.005 was too small).
|
|
15
|
+
*/
|
|
16
|
+
const ZOOM_WHEEL_DELTA = 0.06;
|
|
17
|
+
const TimelinePinchZoom = () => {
|
|
18
|
+
const isVideoComposition = (0, is_current_selected_still_1.useIsVideoComposition)();
|
|
19
|
+
const videoConfig = remotion_1.Internals.useUnsafeVideoConfig();
|
|
20
|
+
const { canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
|
|
21
|
+
const { zoom, setZoom } = (0, react_1.useContext)(timeline_zoom_1.TimelineZoomCtx);
|
|
22
|
+
const { editorZoomGestures } = (0, react_1.useContext)(editor_zoom_gestures_1.EditorZoomGesturesContext);
|
|
23
|
+
const zoomRef = (0, react_1.useRef)(zoom);
|
|
24
|
+
zoomRef.current = zoom;
|
|
25
|
+
const pinchBaseZoomRef = (0, react_1.useRef)(null);
|
|
26
|
+
const suppressWheelFromWebKitPinchRef = (0, react_1.useRef)(false);
|
|
27
|
+
const touchPinchRef = (0, react_1.useRef)(null);
|
|
28
|
+
const onWheel = (0, react_1.useCallback)((e) => {
|
|
29
|
+
if (!editorZoomGestures || !isVideoComposition) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const { ctrlKey, metaKey, clientX, deltaY, deltaMode } = e;
|
|
33
|
+
const wantsToZoom = ctrlKey || metaKey;
|
|
34
|
+
if (!wantsToZoom) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (suppressWheelFromWebKitPinchRef.current && wantsToZoom) {
|
|
38
|
+
e.preventDefault();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!videoConfig || videoConfig.durationInFrames < 2) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (!canvasContent || canvasContent.type !== 'composition') {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const scrollEl = timeline_refs_1.scrollableRef.current;
|
|
48
|
+
if (!scrollEl) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
const anchorContentX = (0, timeline_scroll_logic_1.viewportClientXToScrollContentX)({
|
|
53
|
+
clientX,
|
|
54
|
+
scrollEl,
|
|
55
|
+
});
|
|
56
|
+
let scaledDeltaY = deltaY;
|
|
57
|
+
if (deltaMode === WheelEvent.DOM_DELTA_LINE) {
|
|
58
|
+
scaledDeltaY *= 16;
|
|
59
|
+
}
|
|
60
|
+
else if (deltaMode === WheelEvent.DOM_DELTA_PAGE) {
|
|
61
|
+
scaledDeltaY *= scrollEl.clientHeight;
|
|
62
|
+
}
|
|
63
|
+
setZoom(canvasContent.compositionId, (z) => z - scaledDeltaY * ZOOM_WHEEL_DELTA, { anchorFrame: null, anchorContentX });
|
|
64
|
+
}, [
|
|
65
|
+
editorZoomGestures,
|
|
66
|
+
isVideoComposition,
|
|
67
|
+
videoConfig,
|
|
68
|
+
canvasContent,
|
|
69
|
+
setZoom,
|
|
70
|
+
]);
|
|
71
|
+
const supportsWebKitPinch = typeof window !== 'undefined' && 'GestureEvent' in window;
|
|
72
|
+
(0, react_1.useEffect)(() => {
|
|
73
|
+
const el = timeline_refs_1.timelineVerticalScroll.current;
|
|
74
|
+
if (!el) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
el.addEventListener('wheel', onWheel, { passive: false });
|
|
78
|
+
return () => {
|
|
79
|
+
el.removeEventListener('wheel', onWheel);
|
|
80
|
+
};
|
|
81
|
+
}, [onWheel]);
|
|
82
|
+
(0, react_1.useEffect)(() => {
|
|
83
|
+
const el = timeline_refs_1.timelineVerticalScroll.current;
|
|
84
|
+
if (!el || !editorZoomGestures || !supportsWebKitPinch) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const endWebKitPinch = () => {
|
|
88
|
+
pinchBaseZoomRef.current = null;
|
|
89
|
+
suppressWheelFromWebKitPinchRef.current = false;
|
|
90
|
+
};
|
|
91
|
+
const onGestureStart = (event) => {
|
|
92
|
+
var _a;
|
|
93
|
+
const e = event;
|
|
94
|
+
if (!isVideoComposition) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (!videoConfig || videoConfig.durationInFrames < 2) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (!canvasContent || canvasContent.type !== 'composition') {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const scrollEl = timeline_refs_1.scrollableRef.current;
|
|
104
|
+
if (!scrollEl) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
suppressWheelFromWebKitPinchRef.current = true;
|
|
109
|
+
pinchBaseZoomRef.current = (_a = zoomRef.current[canvasContent.compositionId]) !== null && _a !== void 0 ? _a : timeline_zoom_1.TIMELINE_MIN_ZOOM;
|
|
110
|
+
};
|
|
111
|
+
const onGestureChange = (event) => {
|
|
112
|
+
const e = event;
|
|
113
|
+
const base = pinchBaseZoomRef.current;
|
|
114
|
+
if (base === null ||
|
|
115
|
+
!isVideoComposition ||
|
|
116
|
+
!videoConfig ||
|
|
117
|
+
videoConfig.durationInFrames < 2) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (!canvasContent || canvasContent.type !== 'composition') {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const scrollEl = timeline_refs_1.scrollableRef.current;
|
|
124
|
+
if (!scrollEl) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
const anchorContentX = (0, timeline_scroll_logic_1.viewportClientXToScrollContentX)({
|
|
129
|
+
clientX: e.clientX,
|
|
130
|
+
scrollEl,
|
|
131
|
+
});
|
|
132
|
+
setZoom(canvasContent.compositionId, () => base * e.scale, {
|
|
133
|
+
anchorFrame: null,
|
|
134
|
+
anchorContentX,
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
const onGestureEnd = () => {
|
|
138
|
+
endWebKitPinch();
|
|
139
|
+
};
|
|
140
|
+
el.addEventListener('gesturestart', onGestureStart, { passive: false });
|
|
141
|
+
el.addEventListener('gesturechange', onGestureChange, { passive: false });
|
|
142
|
+
el.addEventListener('gestureend', onGestureEnd);
|
|
143
|
+
el.addEventListener('gesturecancel', onGestureEnd);
|
|
144
|
+
return () => {
|
|
145
|
+
el.removeEventListener('gesturestart', onGestureStart);
|
|
146
|
+
el.removeEventListener('gesturechange', onGestureChange);
|
|
147
|
+
el.removeEventListener('gestureend', onGestureEnd);
|
|
148
|
+
el.removeEventListener('gesturecancel', onGestureEnd);
|
|
149
|
+
};
|
|
150
|
+
}, [
|
|
151
|
+
editorZoomGestures,
|
|
152
|
+
supportsWebKitPinch,
|
|
153
|
+
isVideoComposition,
|
|
154
|
+
videoConfig,
|
|
155
|
+
canvasContent,
|
|
156
|
+
setZoom,
|
|
157
|
+
]);
|
|
158
|
+
(0, react_1.useEffect)(() => {
|
|
159
|
+
const el = timeline_refs_1.timelineVerticalScroll.current;
|
|
160
|
+
if (!el || !editorZoomGestures) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const onTouchStart = (event) => {
|
|
164
|
+
var _a;
|
|
165
|
+
if (event.touches.length !== 2) {
|
|
166
|
+
touchPinchRef.current = null;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (!isVideoComposition ||
|
|
170
|
+
!videoConfig ||
|
|
171
|
+
videoConfig.durationInFrames < 2) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (!canvasContent || canvasContent.type !== 'composition') {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const [t0, t1] = [event.touches[0], event.touches[1]];
|
|
178
|
+
const initialDistance = Math.hypot(t1.clientX - t0.clientX, t1.clientY - t0.clientY);
|
|
179
|
+
if (initialDistance < 1e-6) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
touchPinchRef.current = {
|
|
183
|
+
initialDistance,
|
|
184
|
+
initialZoom: (_a = zoomRef.current[canvasContent.compositionId]) !== null && _a !== void 0 ? _a : timeline_zoom_1.TIMELINE_MIN_ZOOM,
|
|
185
|
+
};
|
|
186
|
+
};
|
|
187
|
+
const onTouchMove = (event) => {
|
|
188
|
+
const pinch = touchPinchRef.current;
|
|
189
|
+
if (pinch === null ||
|
|
190
|
+
event.touches.length !== 2 ||
|
|
191
|
+
!videoConfig ||
|
|
192
|
+
videoConfig.durationInFrames < 2) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (!canvasContent || canvasContent.type !== 'composition') {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const scrollEl = timeline_refs_1.scrollableRef.current;
|
|
199
|
+
if (!scrollEl) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
event.preventDefault();
|
|
203
|
+
const [t0, t1] = [event.touches[0], event.touches[1]];
|
|
204
|
+
const dist = Math.hypot(t1.clientX - t0.clientX, t1.clientY - t0.clientY);
|
|
205
|
+
const ratio = dist / pinch.initialDistance;
|
|
206
|
+
const clientX = (t0.clientX + t1.clientX) / 2;
|
|
207
|
+
const anchorContentX = (0, timeline_scroll_logic_1.viewportClientXToScrollContentX)({
|
|
208
|
+
clientX,
|
|
209
|
+
scrollEl,
|
|
210
|
+
});
|
|
211
|
+
setZoom(canvasContent.compositionId, () => pinch.initialZoom * ratio, {
|
|
212
|
+
anchorFrame: null,
|
|
213
|
+
anchorContentX,
|
|
214
|
+
});
|
|
215
|
+
};
|
|
216
|
+
const onTouchEnd = (event) => {
|
|
217
|
+
if (event.touches.length < 2) {
|
|
218
|
+
touchPinchRef.current = null;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
el.addEventListener('touchstart', onTouchStart, { passive: true });
|
|
222
|
+
el.addEventListener('touchmove', onTouchMove, { passive: false });
|
|
223
|
+
el.addEventListener('touchend', onTouchEnd);
|
|
224
|
+
el.addEventListener('touchcancel', onTouchEnd);
|
|
225
|
+
return () => {
|
|
226
|
+
el.removeEventListener('touchstart', onTouchStart);
|
|
227
|
+
el.removeEventListener('touchmove', onTouchMove);
|
|
228
|
+
el.removeEventListener('touchend', onTouchEnd);
|
|
229
|
+
el.removeEventListener('touchcancel', onTouchEnd);
|
|
230
|
+
};
|
|
231
|
+
}, [
|
|
232
|
+
editorZoomGestures,
|
|
233
|
+
isVideoComposition,
|
|
234
|
+
videoConfig,
|
|
235
|
+
canvasContent,
|
|
236
|
+
setZoom,
|
|
237
|
+
]);
|
|
238
|
+
return null;
|
|
239
|
+
};
|
|
240
|
+
exports.TimelinePinchZoom = TimelinePinchZoom;
|
|
@@ -5,6 +5,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
const remotion_1 = require("remotion");
|
|
7
7
|
const get_left_of_timeline_slider_1 = require("../../helpers/get-left-of-timeline-slider");
|
|
8
|
+
const timeline_zoom_1 = require("../../state/timeline-zoom");
|
|
8
9
|
const imperative_state_1 = require("./imperative-state");
|
|
9
10
|
const timeline_refs_1 = require("./timeline-refs");
|
|
10
11
|
const TimelineSliderHandle_1 = require("./TimelineSliderHandle");
|
|
@@ -32,20 +33,33 @@ const TimelineSlider = () => {
|
|
|
32
33
|
};
|
|
33
34
|
exports.TimelineSlider = TimelineSlider;
|
|
34
35
|
const Inner = () => {
|
|
36
|
+
var _a;
|
|
35
37
|
const videoConfig = (0, remotion_1.useVideoConfig)();
|
|
36
38
|
const timelinePosition = remotion_1.Internals.Timeline.useTimelinePosition();
|
|
37
39
|
const ref = (0, react_1.useRef)(null);
|
|
38
40
|
const timelineWidth = (0, react_1.useContext)(TimelineWidthProvider_1.TimelineWidthContext);
|
|
41
|
+
const { zoom: zoomMap } = (0, react_1.useContext)(timeline_zoom_1.TimelineZoomCtx);
|
|
42
|
+
const { canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
|
|
39
43
|
if (timelineWidth === null) {
|
|
40
44
|
throw new Error('Unexpectedly did not have timeline width');
|
|
41
45
|
}
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
const zoomLevel = (canvasContent === null || canvasContent === void 0 ? void 0 : canvasContent.type) === 'composition'
|
|
47
|
+
? ((_a = zoomMap[canvasContent.compositionId]) !== null && _a !== void 0 ? _a : timeline_zoom_1.TIMELINE_MIN_ZOOM)
|
|
48
|
+
: timeline_zoom_1.TIMELINE_MIN_ZOOM;
|
|
49
|
+
(0, react_1.useLayoutEffect)(() => {
|
|
50
|
+
var _a;
|
|
51
|
+
const el = ref.current;
|
|
52
|
+
const measuredWidth = (_a = timeline_refs_1.sliderAreaRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth;
|
|
53
|
+
if (!el || measuredWidth === undefined || measuredWidth === 0) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
el.style.transform = `translateX(${(0, get_left_of_timeline_slider_1.getXPositionOfItemInTimelineImperatively)(timelinePosition, videoConfig.durationInFrames, measuredWidth)}px)`;
|
|
57
|
+
}, [
|
|
58
|
+
timelinePosition,
|
|
59
|
+
videoConfig.durationInFrames,
|
|
60
|
+
timelineWidth,
|
|
61
|
+
zoomLevel,
|
|
62
|
+
]);
|
|
49
63
|
(0, react_1.useImperativeHandle)(exports.redrawTimelineSliderFast, () => {
|
|
50
64
|
return {
|
|
51
65
|
draw: (frame, width) => {
|
|
@@ -76,7 +90,7 @@ const Inner = () => {
|
|
|
76
90
|
current.removeEventListener('scroll', onScroll);
|
|
77
91
|
};
|
|
78
92
|
}, []);
|
|
79
|
-
return (jsx_runtime_1.jsxs("div", { ref: ref, style:
|
|
93
|
+
return (jsx_runtime_1.jsxs("div", { ref: ref, style: container, children: [
|
|
80
94
|
jsx_runtime_1.jsx("div", { style: line }), jsx_runtime_1.jsx(TimelineSliderHandle_1.TimelineSliderHandle, {})
|
|
81
95
|
] }));
|
|
82
96
|
};
|
|
@@ -151,11 +151,6 @@ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore,
|
|
|
151
151
|
const ref = (0, react_1.useRef)(null);
|
|
152
152
|
const [error, setError] = (0, react_1.useState)(null);
|
|
153
153
|
const aspectRatio = (0, react_1.useRef)((0, frame_database_1.getAspectRatioFromCache)(src));
|
|
154
|
-
(0, react_1.useEffect)(() => {
|
|
155
|
-
return () => {
|
|
156
|
-
(0, frame_database_1.clearFramesForSrc)(src);
|
|
157
|
-
};
|
|
158
|
-
}, [src]);
|
|
159
154
|
// for rendering frames
|
|
160
155
|
(0, react_1.useEffect)(() => {
|
|
161
156
|
if (error) {
|
|
@@ -201,11 +196,9 @@ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore,
|
|
|
201
196
|
if (unfilled.length === 0) {
|
|
202
197
|
return () => {
|
|
203
198
|
current.removeChild(canvas);
|
|
204
|
-
(0, frame_database_1.clearOldFrames)();
|
|
205
199
|
};
|
|
206
200
|
}
|
|
207
201
|
}
|
|
208
|
-
(0, frame_database_1.clearOldFrames)();
|
|
209
202
|
(0, extract_frames_1.extractFrames)({
|
|
210
203
|
timestampsInSeconds: ({ track, }) => {
|
|
211
204
|
aspectRatio.current = track.width / track.height;
|
|
@@ -234,14 +227,7 @@ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore,
|
|
|
234
227
|
}
|
|
235
228
|
frame = undefined;
|
|
236
229
|
const databaseKey = (0, frame_database_1.makeFrameDatabaseKey)(src, transformed.timestamp);
|
|
237
|
-
|
|
238
|
-
if (existingFrame) {
|
|
239
|
-
existingFrame.frame.close();
|
|
240
|
-
}
|
|
241
|
-
frame_database_1.frameDatabase.set(databaseKey, {
|
|
242
|
-
frame: transformed,
|
|
243
|
-
lastUsed: Date.now(),
|
|
244
|
-
});
|
|
230
|
+
(0, frame_database_1.addFrameToCache)(databaseKey, transformed);
|
|
245
231
|
if (aspectRatio.current === null) {
|
|
246
232
|
throw new Error('Aspect ratio is not set');
|
|
247
233
|
}
|
|
@@ -288,9 +274,6 @@ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore,
|
|
|
288
274
|
})
|
|
289
275
|
.catch((e) => {
|
|
290
276
|
setError(e);
|
|
291
|
-
})
|
|
292
|
-
.finally(() => {
|
|
293
|
-
(0, frame_database_1.clearOldFrames)();
|
|
294
277
|
});
|
|
295
278
|
return () => {
|
|
296
279
|
controller.abort();
|
|
@@ -33,19 +33,22 @@ const TimelineZoomControls = () => {
|
|
|
33
33
|
if (canvasContent === null || canvasContent.type !== 'composition') {
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
-
setZoom(canvasContent.compositionId, (z) => Math.max(timeline_zoom_1.TIMELINE_MIN_ZOOM, z - 0.2));
|
|
36
|
+
setZoom(canvasContent.compositionId, (z) => Math.max(timeline_zoom_1.TIMELINE_MIN_ZOOM, z - 0.2), { anchorFrame: null, anchorContentX: null });
|
|
37
37
|
}, [canvasContent, setZoom]);
|
|
38
38
|
const onPlusClicked = (0, react_1.useCallback)(() => {
|
|
39
39
|
if (canvasContent === null || canvasContent.type !== 'composition') {
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
|
-
setZoom(canvasContent.compositionId, (z) => Math.min(timeline_zoom_1.TIMELINE_MAX_ZOOM, z + 0.2));
|
|
42
|
+
setZoom(canvasContent.compositionId, (z) => Math.min(timeline_zoom_1.TIMELINE_MAX_ZOOM, z + 0.2), { anchorFrame: null, anchorContentX: null });
|
|
43
43
|
}, [canvasContent, setZoom]);
|
|
44
44
|
const onChange = (0, react_1.useCallback)((e) => {
|
|
45
45
|
if (canvasContent === null || canvasContent.type !== 'composition') {
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
|
-
setZoom(canvasContent.compositionId, () => Number(e.target.value)
|
|
48
|
+
setZoom(canvasContent.compositionId, () => Number(e.target.value), {
|
|
49
|
+
anchorFrame: null,
|
|
50
|
+
anchorContentX: null,
|
|
51
|
+
});
|
|
49
52
|
}, [canvasContent, setZoom]);
|
|
50
53
|
const isStill = (0, is_current_selected_still_1.useIsStill)();
|
|
51
54
|
if (isStill ||
|
|
@@ -35,9 +35,21 @@ export declare const getFrameFromX: ({ clientX, durationInFrames, width, extrapo
|
|
|
35
35
|
width: number;
|
|
36
36
|
extrapolate: "clamp" | "extend";
|
|
37
37
|
}) => number;
|
|
38
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Horizontal position inside the scrollable timeline content (0 … scrollWidth)
|
|
40
|
+
* for a viewport `clientX`, so pinch-anchoring matches the pointer (not a
|
|
41
|
+
* rounded frame index).
|
|
42
|
+
*/
|
|
43
|
+
export declare const viewportClientXToScrollContentX: ({ clientX, scrollEl, }: {
|
|
44
|
+
clientX: number;
|
|
45
|
+
scrollEl: HTMLDivElement;
|
|
46
|
+
}) => number;
|
|
47
|
+
export declare const zoomAndPreserveCursor: ({ oldZoom, newZoom, currentFrame, currentDurationInFrames, anchorFrame, anchorContentX, }: {
|
|
39
48
|
oldZoom: number;
|
|
40
49
|
newZoom: number;
|
|
41
50
|
currentFrame: number;
|
|
42
51
|
currentDurationInFrames: number;
|
|
52
|
+
anchorFrame: number | null;
|
|
53
|
+
/** Prefer this over `anchorFrame` when not null (subpixel-accurate anchor). */
|
|
54
|
+
anchorContentX: number | null;
|
|
43
55
|
}) => void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.zoomAndPreserveCursor = exports.getFrameFromX = exports.getFrameWhileScrollingRight = exports.getFrameIncrementFromWidth = exports.getScrollPositionForCursorOnRightEdge = exports.getScrollPositionForCursorOnLeftEdge = exports.scrollToTimelineXOffset = exports.ensureFrameIsInViewport = exports.isCursorInViewport = exports.getFrameWhileScrollingLeft = exports.canScrollTimelineIntoDirection = void 0;
|
|
3
|
+
exports.zoomAndPreserveCursor = exports.viewportClientXToScrollContentX = exports.getFrameFromX = exports.getFrameWhileScrollingRight = exports.getFrameIncrementFromWidth = exports.getScrollPositionForCursorOnRightEdge = exports.getScrollPositionForCursorOnLeftEdge = exports.scrollToTimelineXOffset = exports.ensureFrameIsInViewport = exports.isCursorInViewport = exports.getFrameWhileScrollingLeft = exports.canScrollTimelineIntoDirection = void 0;
|
|
4
4
|
const remotion_1 = require("remotion");
|
|
5
5
|
const timeline_layout_1 = require("../../helpers/timeline-layout");
|
|
6
6
|
const timeline_refs_1 = require("./timeline-refs");
|
|
@@ -200,9 +200,18 @@ const getFrameFromX = ({ clientX, durationInFrames, width, extrapolate, }) => {
|
|
|
200
200
|
return frame;
|
|
201
201
|
};
|
|
202
202
|
exports.getFrameFromX = getFrameFromX;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
/**
|
|
204
|
+
* Horizontal position inside the scrollable timeline content (0 … scrollWidth)
|
|
205
|
+
* for a viewport `clientX`, so pinch-anchoring matches the pointer (not a
|
|
206
|
+
* rounded frame index).
|
|
207
|
+
*/
|
|
208
|
+
const viewportClientXToScrollContentX = ({ clientX, scrollEl, }) => {
|
|
209
|
+
const rect = scrollEl.getBoundingClientRect();
|
|
210
|
+
const clampedClientX = Math.min(Math.max(clientX, rect.left), rect.right);
|
|
211
|
+
return clampedClientX + scrollEl.scrollLeft - rect.left;
|
|
212
|
+
};
|
|
213
|
+
exports.viewportClientXToScrollContentX = viewportClientXToScrollContentX;
|
|
214
|
+
const zoomAndPreserveCursor = ({ oldZoom, newZoom, currentFrame, currentDurationInFrames, anchorFrame, anchorContentX, }) => {
|
|
206
215
|
const ratio = newZoom / oldZoom;
|
|
207
216
|
if (ratio === 1) {
|
|
208
217
|
return;
|
|
@@ -212,9 +221,14 @@ const zoomAndPreserveCursor = ({ oldZoom, newZoom, currentFrame, currentDuration
|
|
|
212
221
|
return;
|
|
213
222
|
}
|
|
214
223
|
const frameIncrement = getFrameIncrement(currentDurationInFrames);
|
|
215
|
-
const
|
|
224
|
+
const frameForScroll = anchorFrame !== null && anchorFrame !== void 0 ? anchorFrame : currentFrame;
|
|
225
|
+
const prevCursorPosition = anchorContentX !== null
|
|
226
|
+
? Math.min(Math.max(anchorContentX, 0), current.scrollWidth)
|
|
227
|
+
: frameIncrement * frameForScroll + timeline_layout_1.TIMELINE_PADDING;
|
|
216
228
|
const newCursorPosition = ratio * (prevCursorPosition - timeline_layout_1.TIMELINE_PADDING) + timeline_layout_1.TIMELINE_PADDING;
|
|
217
229
|
current.scrollLeft += newCursorPosition - prevCursorPosition;
|
|
218
|
-
|
|
230
|
+
// Playhead position is synced in `TimelineSlider` `useLayoutEffect` using
|
|
231
|
+
// measured `sliderAreaRef.clientWidth` so it matches layout after zoom
|
|
232
|
+
// (avoids fighting React `style` with stale `timelineWidth` during pinch).
|
|
219
233
|
};
|
|
220
234
|
exports.zoomAndPreserveCursor = zoomAndPreserveCursor;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { SequenceNodePath } from '@remotion/studio-shared';
|
|
2
2
|
import type { TSequence } from 'remotion';
|
|
3
3
|
import type { OriginalPosition } from '../../error-overlay/react-overlay/utils/get-source-map';
|
|
4
|
-
export declare const useSequencePropsSubscription: (sequence: TSequence, originalLocation: OriginalPosition | null) => SequenceNodePath | null;
|
|
4
|
+
export declare const useSequencePropsSubscription: (sequence: TSequence, originalLocation: OriginalPosition | null, visualModeEnabled: boolean) => SequenceNodePath | null;
|
|
@@ -6,7 +6,7 @@ const remotion_1 = require("remotion");
|
|
|
6
6
|
const client_id_1 = require("../../helpers/client-id");
|
|
7
7
|
const timeline_layout_1 = require("../../helpers/timeline-layout");
|
|
8
8
|
const call_api_1 = require("../call-api");
|
|
9
|
-
const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
9
|
+
const useSequencePropsSubscription = (sequence, originalLocation, visualModeEnabled) => {
|
|
10
10
|
var _a;
|
|
11
11
|
var _b, _c, _d, _e;
|
|
12
12
|
const { setCodeValues } = (0, react_1.useContext)(remotion_1.Internals.VisualModeOverridesContext);
|
|
@@ -44,7 +44,12 @@ const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
|
44
44
|
const currentLocationColumn = (0, react_1.useRef)(locationColumn);
|
|
45
45
|
currentLocationColumn.current = locationColumn;
|
|
46
46
|
const nodePathRef = (0, react_1.useRef)(null);
|
|
47
|
+
const [nodePath, setNodePath] = (0, react_1.useState)(null);
|
|
47
48
|
const isMountedRef = (0, react_1.useRef)(true);
|
|
49
|
+
const setNodePathBoth = (0, react_1.useCallback)((next) => {
|
|
50
|
+
nodePathRef.current = next;
|
|
51
|
+
setNodePath(next);
|
|
52
|
+
}, []);
|
|
48
53
|
(0, react_1.useEffect)(() => {
|
|
49
54
|
isMountedRef.current = true;
|
|
50
55
|
return () => {
|
|
@@ -52,12 +57,18 @@ const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
|
52
57
|
};
|
|
53
58
|
}, []);
|
|
54
59
|
(0, react_1.useEffect)(() => {
|
|
60
|
+
if (!visualModeEnabled) {
|
|
61
|
+
setPropStatusesForSequence(null);
|
|
62
|
+
setNodePathBoth(null);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
55
65
|
if (!clientId ||
|
|
56
66
|
!locationSource ||
|
|
57
67
|
!locationLine ||
|
|
58
68
|
locationColumn === null ||
|
|
59
69
|
!schemaKeysString) {
|
|
60
70
|
setPropStatusesForSequence(null);
|
|
71
|
+
setNodePathBoth(null);
|
|
61
72
|
return;
|
|
62
73
|
}
|
|
63
74
|
const keys = schemaKeysString.split(',');
|
|
@@ -75,16 +86,16 @@ const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
|
75
86
|
return;
|
|
76
87
|
}
|
|
77
88
|
if (result.canUpdate) {
|
|
78
|
-
|
|
89
|
+
setNodePathBoth(result.nodePath);
|
|
79
90
|
setPropStatusesForSequence(result.props);
|
|
80
91
|
}
|
|
81
92
|
else {
|
|
82
|
-
|
|
93
|
+
setNodePathBoth(null);
|
|
83
94
|
setPropStatusesForSequence(null);
|
|
84
95
|
}
|
|
85
96
|
})
|
|
86
97
|
.catch((err) => {
|
|
87
|
-
|
|
98
|
+
setNodePathBoth(null);
|
|
88
99
|
remotion_1.Internals.Log.error(err);
|
|
89
100
|
setPropStatusesForSequence(null);
|
|
90
101
|
});
|
|
@@ -95,7 +106,7 @@ const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
|
95
106
|
if (!isMountedRef.current) {
|
|
96
107
|
setPropStatusesForSequence(null);
|
|
97
108
|
}
|
|
98
|
-
|
|
109
|
+
setNodePathBoth(null);
|
|
99
110
|
if (currentNodePath) {
|
|
100
111
|
(0, call_api_1.callApi)('/api/unsubscribe-from-sequence-props', {
|
|
101
112
|
fileName: locationSource,
|
|
@@ -107,14 +118,19 @@ const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
|
107
118
|
}
|
|
108
119
|
};
|
|
109
120
|
}, [
|
|
121
|
+
visualModeEnabled,
|
|
110
122
|
clientId,
|
|
111
123
|
locationSource,
|
|
112
124
|
locationLine,
|
|
113
125
|
locationColumn,
|
|
114
126
|
schemaKeysString,
|
|
115
127
|
setPropStatusesForSequence,
|
|
128
|
+
setNodePathBoth,
|
|
116
129
|
]);
|
|
117
130
|
(0, react_1.useEffect)(() => {
|
|
131
|
+
if (!visualModeEnabled) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
118
134
|
if (!locationSource || !locationLine || locationColumn === null) {
|
|
119
135
|
return;
|
|
120
136
|
}
|
|
@@ -132,6 +148,7 @@ const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
|
132
148
|
}
|
|
133
149
|
else {
|
|
134
150
|
setPropStatusesForSequence(null);
|
|
151
|
+
setNodePathBoth(null);
|
|
135
152
|
}
|
|
136
153
|
};
|
|
137
154
|
const unsub = subscribeToEvent('sequence-props-updated', listener);
|
|
@@ -139,12 +156,14 @@ const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
|
139
156
|
unsub();
|
|
140
157
|
};
|
|
141
158
|
}, [
|
|
159
|
+
visualModeEnabled,
|
|
142
160
|
locationSource,
|
|
143
161
|
locationLine,
|
|
144
162
|
locationColumn,
|
|
145
163
|
subscribeToEvent,
|
|
146
164
|
setPropStatusesForSequence,
|
|
165
|
+
setNodePathBoth,
|
|
147
166
|
]);
|
|
148
|
-
return
|
|
167
|
+
return nodePath;
|
|
149
168
|
};
|
|
150
169
|
exports.useSequencePropsSubscription = useSequencePropsSubscription;
|
|
@@ -48,11 +48,7 @@ const crashWithFrames = (crash) => (error) => {
|
|
|
48
48
|
function listenToRuntimeErrors(crash) {
|
|
49
49
|
const crashWithFramesRunTime = crashWithFrames(crash);
|
|
50
50
|
(0, unhandled_error_1.register)(window, (error) => {
|
|
51
|
-
return crashWithFramesRunTime(
|
|
52
|
-
message: error.message,
|
|
53
|
-
stack: error.stack,
|
|
54
|
-
name: error.name,
|
|
55
|
-
});
|
|
51
|
+
return crashWithFramesRunTime(error);
|
|
56
52
|
});
|
|
57
53
|
(0, unhandled_rejection_1.register)(window, (error) => {
|
|
58
54
|
return crashWithFramesRunTime(error);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CopyStackTrace = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const Button_1 = require("../../components/Button");
|
|
7
|
+
const use_keybinding_1 = require("../../helpers/use-keybinding");
|
|
8
|
+
const ShortcutHint_1 = require("./ShortcutHint");
|
|
9
|
+
const CopyStackTrace = ({ canHaveKeyboardShortcuts, errorText }) => {
|
|
10
|
+
const [copyState, setCopyState] = (0, react_1.useState)('idle');
|
|
11
|
+
const handleCopyToClipboard = (0, react_1.useCallback)(() => {
|
|
12
|
+
navigator.clipboard
|
|
13
|
+
.writeText(errorText)
|
|
14
|
+
.then(() => {
|
|
15
|
+
setCopyState('copied');
|
|
16
|
+
setTimeout(() => setCopyState('idle'), 2000);
|
|
17
|
+
})
|
|
18
|
+
.catch(() => {
|
|
19
|
+
setCopyState('failed');
|
|
20
|
+
setTimeout(() => setCopyState('idle'), 2000);
|
|
21
|
+
});
|
|
22
|
+
}, [errorText]);
|
|
23
|
+
const { registerKeybinding } = (0, use_keybinding_1.useKeybinding)();
|
|
24
|
+
(0, react_1.useEffect)(() => {
|
|
25
|
+
if (!canHaveKeyboardShortcuts) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const { unregister } = registerKeybinding({
|
|
29
|
+
event: 'keydown',
|
|
30
|
+
key: 't',
|
|
31
|
+
callback: handleCopyToClipboard,
|
|
32
|
+
commandCtrlKey: true,
|
|
33
|
+
preventDefault: true,
|
|
34
|
+
triggerIfInputFieldFocused: false,
|
|
35
|
+
keepRegisteredWhenNotHighestContext: false,
|
|
36
|
+
});
|
|
37
|
+
return () => unregister();
|
|
38
|
+
}, [canHaveKeyboardShortcuts, handleCopyToClipboard, registerKeybinding]);
|
|
39
|
+
const label = (0, react_1.useMemo)(() => {
|
|
40
|
+
if (copyState === 'copied') {
|
|
41
|
+
return 'Copied!';
|
|
42
|
+
}
|
|
43
|
+
if (copyState === 'failed') {
|
|
44
|
+
return 'Failed!';
|
|
45
|
+
}
|
|
46
|
+
return 'Copy Stacktrace';
|
|
47
|
+
}, [copyState]);
|
|
48
|
+
return (jsx_runtime_1.jsxs(Button_1.Button, { onClick: handleCopyToClipboard, children: [label, ' ', copyState === 'idle' && canHaveKeyboardShortcuts ? (jsx_runtime_1.jsx(ShortcutHint_1.ShortcutHint, { cmdOrCtrl: true, keyToPress: "t" })) : null] }));
|
|
49
|
+
};
|
|
50
|
+
exports.CopyStackTrace = CopyStackTrace;
|