@remotion/studio 4.0.479 → 4.0.481

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 (57) hide show
  1. package/dist/components/AudioWaveform.js +19 -11
  2. package/dist/components/Button.d.ts +1 -0
  3. package/dist/components/Button.js +2 -2
  4. package/dist/components/CurrentCompositionSideEffects.d.ts +0 -3
  5. package/dist/components/CurrentCompositionSideEffects.js +1 -37
  6. package/dist/components/Editor.js +2 -5
  7. package/dist/components/EditorContent.js +2 -1
  8. package/dist/components/ExpandedTracksProvider.d.ts +1 -0
  9. package/dist/components/ExpandedTracksProvider.js +81 -7
  10. package/dist/components/GlobalKeybindings.d.ts +3 -1
  11. package/dist/components/GlobalKeybindings.js +104 -10
  12. package/dist/components/InspectorPanel/SequenceSelectionInspector.js +4 -3
  13. package/dist/components/InspectorPanel/inspector-selection.d.ts +3 -3
  14. package/dist/components/InspectorPanel/inspector-selection.js +7 -6
  15. package/dist/components/InspectorPanel/styles.d.ts +1 -0
  16. package/dist/components/InspectorPanel/styles.js +19 -1
  17. package/dist/components/InspectorPanel.js +3 -3
  18. package/dist/components/InspectorSequenceSection.d.ts +3 -0
  19. package/dist/components/InspectorSequenceSection.js +12 -2
  20. package/dist/components/KeyboardShortcutsExplainer.js +10 -2
  21. package/dist/components/ResetZoomButton.d.ts +2 -1
  22. package/dist/components/ResetZoomButton.js +5 -1
  23. package/dist/components/SelectedOutlineElement.js +39 -0
  24. package/dist/components/SelectedOutlineOverlay.js +3 -1
  25. package/dist/components/Timeline/Timeline.js +9 -9
  26. package/dist/components/Timeline/TimelineEffectItem.js +1 -1
  27. package/dist/components/Timeline/TimelineEffectPropItem.js +1 -1
  28. package/dist/components/Timeline/TimelineExpandArrowButton.js +42 -2
  29. package/dist/components/Timeline/TimelineExpandedRow.js +2 -2
  30. package/dist/components/Timeline/TimelineExpandedSection.js +8 -9
  31. package/dist/components/Timeline/TimelineRowChrome.d.ts +2 -0
  32. package/dist/components/Timeline/TimelineRowChrome.js +5 -3
  33. package/dist/components/Timeline/TimelineSelection.d.ts +22 -2
  34. package/dist/components/Timeline/TimelineSelection.js +276 -90
  35. package/dist/components/Timeline/TimelineSequenceItem.js +61 -2
  36. package/dist/components/Timeline/TimelineSequencePropItem.js +1 -1
  37. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.js +6 -4
  38. package/dist/components/Timeline/find-track-for-node-path-info.js +2 -2
  39. package/dist/components/Timeline/get-node-keyframes.d.ts +7 -0
  40. package/dist/components/Timeline/get-node-keyframes.js +26 -1
  41. package/dist/components/Timeline/reset-selected-timeline-props.js +15 -2
  42. package/dist/components/Timeline/timeline-expanded-filter.d.ts +12 -0
  43. package/dist/components/Timeline/timeline-expanded-filter.js +38 -0
  44. package/dist/components/Timeline/use-expanded-track-keyframe-rows.js +50 -18
  45. package/dist/components/Timeline/use-timeline-expanded-tree.d.ts +1 -0
  46. package/dist/components/Timeline/use-timeline-expanded-tree.js +27 -0
  47. package/dist/components/Timeline/use-timeline-height.js +51 -7
  48. package/dist/components/Timeline/use-timeline-keyframe-drag.js +12 -6
  49. package/dist/components/TopPanel.js +1 -1
  50. package/dist/components/selected-outline-measurement.js +48 -14
  51. package/dist/error-overlay/remotion-overlay/ErrorLoader.js +8 -1
  52. package/dist/esm/{chunk-fge2mq5p.js → chunk-4rq5gt8c.js} +16552 -15859
  53. package/dist/esm/internals.mjs +16552 -15859
  54. package/dist/esm/previewEntry.mjs +4055 -3360
  55. package/dist/esm/renderEntry.mjs +1 -1
  56. package/dist/helpers/migrate-expanded-tracks-for-subscription-key.js +3 -3
  57. package/package.json +11 -11
@@ -52,6 +52,12 @@ const waveformCanvasStyle = {
52
52
  const volumeCanvasStyle = {
53
53
  position: 'absolute',
54
54
  };
55
+ const parseVolume = (volume) => {
56
+ if (typeof volume === 'number') {
57
+ return volume;
58
+ }
59
+ return volume.split(',').map((v) => Number(v));
60
+ };
55
61
  const drawLoopedWaveform = ({ canvas, peaks, volume, visualizationWidth, loopWidth, }) => {
56
62
  const h = canvas.height;
57
63
  const w = Math.ceil(visualizationWidth);
@@ -95,6 +101,7 @@ const AudioWaveform = ({ src, height, startFrom, durationInFrames, visualization
95
101
  const hasTransferredCanvas = (0, react_1.useRef)(false);
96
102
  const latestRequestId = (0, react_1.useRef)(0);
97
103
  const shouldRenderVolumeOverlay = doesVolumeChange && typeof volume === 'string';
104
+ const parsedVolume = (0, react_1.useMemo)(() => parseVolume(volume), [volume]);
98
105
  (0, react_1.useEffect)(() => {
99
106
  if (canUseWorkerPath) {
100
107
  return;
@@ -198,7 +205,6 @@ const AudioWaveform = ({ src, height, startFrom, durationInFrames, visualization
198
205
  }
199
206
  const h = height;
200
207
  const w = Math.ceil(visualizationWidth);
201
- const vol = typeof volume === 'number' ? volume : 1;
202
208
  if (canUseWorkerPath) {
203
209
  const worker = waveformWorker.current;
204
210
  if (!worker || !hasTransferredCanvas.current) {
@@ -212,7 +218,7 @@ const AudioWaveform = ({ src, height, startFrom, durationInFrames, visualization
212
218
  src,
213
219
  width: w,
214
220
  height: h,
215
- volume: vol,
221
+ volume: parsedVolume,
216
222
  startFrom,
217
223
  durationInFrames,
218
224
  fps: vidConf.fps,
@@ -228,7 +234,7 @@ const AudioWaveform = ({ src, height, startFrom, durationInFrames, visualization
228
234
  drawLoopedWaveform({
229
235
  canvas: canvasElement,
230
236
  peaks: portionPeaks !== null && portionPeaks !== void 0 ? portionPeaks : EMPTY_PEAKS,
231
- volume: vol,
237
+ volume: parsedVolume,
232
238
  visualizationWidth,
233
239
  loopWidth: (0, timeline_utils_1.getLoopDisplayWidth)({
234
240
  visualizationWidth,
@@ -241,7 +247,7 @@ const AudioWaveform = ({ src, height, startFrom, durationInFrames, visualization
241
247
  canvas: canvasElement,
242
248
  peaks: portionPeaks !== null && portionPeaks !== void 0 ? portionPeaks : EMPTY_PEAKS,
243
249
  color: 'rgba(255, 255, 255, 0.6)',
244
- volume: vol,
250
+ volume: parsedVolume,
245
251
  width: w,
246
252
  });
247
253
  }
@@ -251,12 +257,12 @@ const AudioWaveform = ({ src, height, startFrom, durationInFrames, visualization
251
257
  height,
252
258
  loopDisplay,
253
259
  playbackRate,
260
+ parsedVolume,
254
261
  portionPeaks,
255
262
  src,
256
263
  startFrom,
257
264
  vidConf.fps,
258
265
  visualizationWidth,
259
- volume,
260
266
  waveformCanvasKey,
261
267
  ]);
262
268
  (0, react_1.useEffect)(() => {
@@ -275,11 +281,15 @@ const AudioWaveform = ({ src, height, startFrom, durationInFrames, visualization
275
281
  volumeCanvasElement.width = Math.ceil(visualizationWidth);
276
282
  volumeCanvasElement.height = h;
277
283
  context.clearRect(0, 0, visualizationWidth, h);
278
- const volumes = volume.split(',').map((v) => Number(v));
284
+ if (!Array.isArray(parsedVolume)) {
285
+ return;
286
+ }
279
287
  context.beginPath();
280
288
  context.moveTo(0, h);
281
- volumes.forEach((v, index) => {
282
- const x = (index / (volumes.length - 1)) * visualizationWidth;
289
+ parsedVolume.forEach((v, index) => {
290
+ const x = parsedVolume.length <= 1
291
+ ? 0
292
+ : (index / (parsedVolume.length - 1)) * visualizationWidth;
283
293
  const y = (1 - v) * (h - timeline_layout_1.TIMELINE_BORDER * 2) + 1;
284
294
  if (index === 0) {
285
295
  context.moveTo(x, y);
@@ -290,10 +300,8 @@ const AudioWaveform = ({ src, height, startFrom, durationInFrames, visualization
290
300
  });
291
301
  context.strokeStyle = colors_1.LIGHT_TRANSPARENT;
292
302
  context.stroke();
293
- }, [height, shouldRenderVolumeOverlay, visualizationWidth, volume]);
303
+ }, [height, parsedVolume, shouldRenderVolumeOverlay, visualizationWidth]);
294
304
  if (error) {
295
- // eslint-disable-next-line no-console
296
- console.error(error);
297
305
  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." }) }));
298
306
  }
299
307
  if (!canUseWorkerPath && !peaks) {
@@ -9,5 +9,6 @@ export type ButtonProps = {
9
9
  readonly autoFocus?: boolean;
10
10
  readonly title?: string;
11
11
  readonly id?: string;
12
+ readonly onPointerDown?: React.PointerEventHandler<HTMLButtonElement>;
12
13
  };
13
14
  export declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
@@ -14,7 +14,7 @@ const button = {
14
14
  color: 'white',
15
15
  flexDirection: 'row',
16
16
  };
17
- const ButtonRefForwardFunction = ({ children, onClick, title, disabled, size = 'default', style, id, autoFocus, buttonContainerStyle, }, ref) => {
17
+ const ButtonRefForwardFunction = ({ children, onClick, title, disabled, size = 'default', style, id, autoFocus, buttonContainerStyle, onPointerDown, }, ref) => {
18
18
  const combined = (0, react_1.useMemo)(() => {
19
19
  return {
20
20
  ...button,
@@ -33,6 +33,6 @@ const ButtonRefForwardFunction = ({ children, onClick, title, disabled, size = '
33
33
  ...(buttonContainerStyle !== null && buttonContainerStyle !== void 0 ? buttonContainerStyle : {}),
34
34
  };
35
35
  }, [buttonContainerStyle, disabled, size]);
36
- return (jsx_runtime_1.jsx("button", { ref: ref, id: id, style: combined, type: "button", disabled: disabled, onClick: onClick, autoFocus: autoFocus, title: title, children: jsx_runtime_1.jsx("div", { className: "css-reset", style: buttonContainer, children: children }) }));
36
+ return (jsx_runtime_1.jsx("button", { ref: ref, id: id, style: combined, type: "button", disabled: disabled, onClick: onClick, onPointerDown: onPointerDown, autoFocus: autoFocus, title: title, children: jsx_runtime_1.jsx("div", { className: "css-reset", style: buttonContainer, children: children }) }));
37
37
  };
38
38
  exports.Button = (0, react_1.forwardRef)(ButtonRefForwardFunction);
@@ -1,5 +1,2 @@
1
1
  import type React from 'react';
2
2
  export declare const TitleUpdater: React.FC;
3
- export declare const CurrentCompositionKeybindings: React.FC<{
4
- readonly readOnlyStudio: boolean;
5
- }>;
@@ -1,13 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CurrentCompositionKeybindings = exports.TitleUpdater = void 0;
3
+ exports.TitleUpdater = void 0;
4
4
  const react_1 = require("react");
5
5
  const remotion_1 = require("remotion");
6
- const client_id_1 = require("../helpers/client-id");
7
6
  const document_title_1 = require("../helpers/document-title");
8
- const show_browser_rendering_1 = require("../helpers/show-browser-rendering");
9
- const use_keybinding_1 = require("../helpers/use-keybinding");
10
- const NotificationCenter_1 = require("./Notifications/NotificationCenter");
11
7
  const context_1 = require("./RenderQueue/context");
12
8
  const TitleUpdater = () => {
13
9
  const renderQueue = (0, react_1.useContext)(context_1.RenderQueueContext);
@@ -38,35 +34,3 @@ const TitleUpdater = () => {
38
34
  return null;
39
35
  };
40
36
  exports.TitleUpdater = TitleUpdater;
41
- const CurrentCompositionKeybindings = ({ readOnlyStudio }) => {
42
- const keybindings = (0, use_keybinding_1.useKeybinding)();
43
- const video = remotion_1.Internals.useVideo();
44
- const { type } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx).previewServerState;
45
- const openRenderModal = (0, react_1.useCallback)(() => {
46
- if (!video) {
47
- return;
48
- }
49
- if (type !== 'connected' && !show_browser_rendering_1.SHOW_BROWSER_RENDERING && !readOnlyStudio) {
50
- (0, NotificationCenter_1.showNotification)('Studio server is offline', 2000);
51
- return;
52
- }
53
- const renderButton = document.getElementById('render-modal-button');
54
- renderButton.click();
55
- }, [readOnlyStudio, type, video]);
56
- (0, react_1.useEffect)(() => {
57
- const binding = keybindings.registerKeybinding({
58
- event: 'keydown',
59
- key: 'r',
60
- commandCtrlKey: false,
61
- callback: openRenderModal,
62
- preventDefault: true,
63
- triggerIfInputFieldFocused: false,
64
- keepRegisteredWhenNotHighestContext: false,
65
- });
66
- return () => {
67
- binding.unregister();
68
- };
69
- }, [keybindings, openRenderModal]);
70
- return null;
71
- };
72
- exports.CurrentCompositionKeybindings = CurrentCompositionKeybindings;
@@ -47,7 +47,6 @@ const z_index_1 = require("../state/z-index");
47
47
  const canvas_capture_enabled_1 = require("./canvas-capture-enabled");
48
48
  const EditorContent_1 = require("./EditorContent");
49
49
  const ForceSpecificCursor_1 = require("./ForceSpecificCursor");
50
- const GlobalKeybindings_1 = require("./GlobalKeybindings");
51
50
  const Modals_1 = require("./Modals");
52
51
  const NotificationCenter_1 = require("./Notifications/NotificationCenter");
53
52
  const RenderErrorContext_1 = require("./RenderErrorContext");
@@ -104,12 +103,10 @@ const Editor = ({ Root, readOnlyStudio }) => {
104
103
  const editor = (jsx_runtime_1.jsx(z_index_1.HigherZIndex, { onEscape: noop_1.noop, onOutsideClick: noop_1.noop, children: jsx_runtime_1.jsx(timeline_zoom_1.TimelineZoomContext, { children: jsx_runtime_1.jsxs(SequencePropsSubscriptionProvider_1.SequencePropsSubscriptionProvider, { children: [
105
104
  jsx_runtime_1.jsxs(remotion_1.Internals.CurrentScaleContext.Provider, { value: value, children: [
106
105
  jsx_runtime_1.jsx(ForceSpecificCursor_1.ForceSpecificCursor, {}), jsx_runtime_1.jsx(scale_lock_1.ScaleLockProvider, { children: jsx_runtime_1.jsxs("div", { style: background, children: [
107
- 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: [
108
- 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: setDrawRef, bufferStateDelayInMilliseconds: exports.BUFFER_STATE_DELAY_IN_MILLISECONDS, onMounted: onMounted, readOnlyStudio: readOnlyStudio }) }) }), jsx_runtime_1.jsx(GlobalKeybindings_1.GlobalKeybindings, {})
109
- ] })
106
+ jsx_runtime_1.jsx(remotion_1.Internals.CompositionRenderErrorContext.Provider, { value: compositionRenderErrorContextValue, children: canvasMounted ? jsx_runtime_1.jsx(MemoRoot, {}) : null }), jsx_runtime_1.jsx(remotion_1.Internals.CanUseRemotionHooksProvider, { children: 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: setDrawRef, bufferStateDelayInMilliseconds: exports.BUFFER_STATE_DELAY_IN_MILLISECONDS, onMounted: onMounted, readOnlyStudio: readOnlyStudio }) }) }) })
110
107
  ] }) })
111
108
  ] }), jsx_runtime_1.jsx(Modals_1.Modals, { readOnlyStudio: readOnlyStudio }), jsx_runtime_1.jsx(NotificationCenter_1.NotificationCenter, {})
112
109
  ] }) }) }));
113
- return canvas_capture_enabled_1.CANVAS_CAPTURE_ENABLED ? (jsx_runtime_1.jsx(StudioCanvasCapture_1.StudioCanvasCapture, { density: 2, children: editor })) : (editor);
110
+ return canvas_capture_enabled_1.CANVAS_CAPTURE_ENABLED ? (jsx_runtime_1.jsx(StudioCanvasCapture_1.StudioCanvasCapture, { density: 3, children: editor })) : (editor);
114
111
  };
115
112
  exports.Editor = Editor;
@@ -4,6 +4,7 @@ exports.EditorContent = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const remotion_1 = require("remotion");
7
+ const GlobalKeybindings_1 = require("./GlobalKeybindings");
7
8
  const InitialCompositionLoader_1 = require("./InitialCompositionLoader");
8
9
  const MenuToolbar_1 = require("./MenuToolbar");
9
10
  const SplitterContainer_1 = require("./Splitter/SplitterContainer");
@@ -38,7 +39,7 @@ const EditorContent = ({ readOnlyStudio, children }) => {
38
39
  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, {}) })
39
40
  ] }));
40
41
  return (jsx_runtime_1.jsx(TimelineSelection_1.TimelineSelectionProvider, { children: jsx_runtime_1.jsxs(StudioClearSelectionArea, { children: [
41
- jsx_runtime_1.jsx(InitialCompositionLoader_1.InitialCompositionLoader, {}), jsx_runtime_1.jsx(MenuToolbar_1.MenuToolbar, { readOnlyStudio: readOnlyStudio }), jsx_runtime_1.jsx(TimelineKeyframeDragState_1.TimelineKeyframeDragStateProvider, { children: content })
42
+ jsx_runtime_1.jsx(InitialCompositionLoader_1.InitialCompositionLoader, {}), jsx_runtime_1.jsx(MenuToolbar_1.MenuToolbar, { readOnlyStudio: readOnlyStudio }), jsx_runtime_1.jsx(GlobalKeybindings_1.GlobalKeybindings, { readOnlyStudio: readOnlyStudio }), jsx_runtime_1.jsx(TimelineKeyframeDragState_1.TimelineKeyframeDragStateProvider, { children: content })
42
43
  ] }) }));
43
44
  };
44
45
  exports.EditorContent = EditorContent;
@@ -6,6 +6,7 @@ type ExpandedTracksGetterContextValue = {
6
6
  readonly getIsExpanded: GetIsExpanded;
7
7
  };
8
8
  type ExpandedTracksSetterContextValue = {
9
+ readonly expandParentTracks: (nodePathInfo: SequenceNodePathInfo) => void;
9
10
  readonly toggleTrack: (nodePathInfo: SequenceNodePathInfo) => void;
10
11
  readonly migrateExpandedTracksForSubscriptionKey: (oldKey: SequencePropsSubscriptionKey, newKey: SequencePropsSubscriptionKey) => void;
11
12
  };
@@ -4,11 +4,46 @@ exports.ExpandedTracksProvider = exports.ExpandedTracksSetterContext = exports.E
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const migrate_expanded_tracks_for_subscription_key_1 = require("../helpers/migrate-expanded-tracks-for-subscription-key");
7
- const persist_boolean_map_1 = require("../helpers/persist-boolean-map");
8
7
  const timeline_node_path_key_1 = require("../helpers/timeline-node-path-key");
9
8
  const SESSION_STORAGE_KEY = 'remotion.editor.expandedTracks';
9
+ const onlyCollapsedTrackValues = (state) => {
10
+ const result = {};
11
+ for (const [key, value] of Object.entries(state)) {
12
+ if (value === false) {
13
+ result[key] = false;
14
+ }
15
+ }
16
+ return result;
17
+ };
10
18
  const loadExpandedTracks = () => {
11
- return (0, persist_boolean_map_1.loadPersistedBooleanMap)(SESSION_STORAGE_KEY);
19
+ if (typeof window === 'undefined') {
20
+ return {};
21
+ }
22
+ try {
23
+ const raw = window.sessionStorage.getItem(SESSION_STORAGE_KEY);
24
+ if (raw === null) {
25
+ return {};
26
+ }
27
+ const parsed = JSON.parse(raw);
28
+ if (!parsed || typeof parsed !== 'object') {
29
+ return {};
30
+ }
31
+ return onlyCollapsedTrackValues(parsed);
32
+ }
33
+ catch (_a) {
34
+ return {};
35
+ }
36
+ };
37
+ const persistExpandedTracks = (state) => {
38
+ if (typeof window === 'undefined') {
39
+ return;
40
+ }
41
+ try {
42
+ window.sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(onlyCollapsedTrackValues(state)));
43
+ }
44
+ catch (_a) {
45
+ // Ignore quota errors or disabled storage.
46
+ }
12
47
  };
13
48
  exports.ExpandedTracksGetterContext = (0, react_1.createContext)({
14
49
  getIsExpanded: () => {
@@ -16,6 +51,9 @@ exports.ExpandedTracksGetterContext = (0, react_1.createContext)({
16
51
  },
17
52
  });
18
53
  exports.ExpandedTracksSetterContext = (0, react_1.createContext)({
54
+ expandParentTracks: () => {
55
+ throw new Error('ExpandedTracksSetterContext not initialized');
56
+ },
19
57
  toggleTrack: () => {
20
58
  throw new Error('ExpandedTracksSetterContext not initialized');
21
59
  },
@@ -25,11 +63,46 @@ exports.ExpandedTracksSetterContext = (0, react_1.createContext)({
25
63
  });
26
64
  const ExpandedTracksProvider = ({ children }) => {
27
65
  const [expandedTracks, setExpandedTracks] = (0, react_1.useState)(loadExpandedTracks);
66
+ const expandParentTracks = (0, react_1.useCallback)((nodePathInfo) => {
67
+ const keysToExpand = [];
68
+ for (let i = 0; i < nodePathInfo.auxiliaryKeys.length; i++) {
69
+ keysToExpand.push((0, timeline_node_path_key_1.timelineNodePathInfoToKey)({
70
+ ...nodePathInfo,
71
+ auxiliaryKeys: nodePathInfo.auxiliaryKeys.slice(0, i),
72
+ }));
73
+ }
74
+ if (keysToExpand.length === 0) {
75
+ return;
76
+ }
77
+ setExpandedTracks((prev) => {
78
+ let changed = false;
79
+ const next = { ...prev };
80
+ for (const key of keysToExpand) {
81
+ if (next[key] === false) {
82
+ delete next[key];
83
+ changed = true;
84
+ }
85
+ }
86
+ if (!changed) {
87
+ return prev;
88
+ }
89
+ persistExpandedTracks(next);
90
+ return next;
91
+ });
92
+ }, []);
28
93
  const toggleTrack = (0, react_1.useCallback)((nodePathInfo) => {
29
94
  setExpandedTracks((prev) => {
95
+ var _a;
30
96
  const key = (0, timeline_node_path_key_1.timelineNodePathInfoToKey)(nodePathInfo);
31
- const next = (0, persist_boolean_map_1.toggleBooleanMapKey)(prev, key);
32
- (0, persist_boolean_map_1.persistBooleanMap)(SESSION_STORAGE_KEY, next);
97
+ const next = { ...prev };
98
+ const isExpanded = (_a = next[key]) !== null && _a !== void 0 ? _a : true;
99
+ if (isExpanded) {
100
+ next[key] = false;
101
+ }
102
+ else {
103
+ delete next[key];
104
+ }
105
+ persistExpandedTracks(next);
33
106
  return next;
34
107
  });
35
108
  }, []);
@@ -39,20 +112,21 @@ const ExpandedTracksProvider = ({ children }) => {
39
112
  if (!next) {
40
113
  return prev;
41
114
  }
42
- (0, persist_boolean_map_1.persistBooleanMap)(SESSION_STORAGE_KEY, next);
115
+ persistExpandedTracks(next);
43
116
  return next;
44
117
  });
45
118
  }, []);
46
119
  const getterValue = (0, react_1.useMemo)(() => ({
47
120
  getIsExpanded: (nodePathInfo) => {
48
121
  var _a;
49
- return (_a = expandedTracks[(0, timeline_node_path_key_1.timelineNodePathInfoToKey)(nodePathInfo)]) !== null && _a !== void 0 ? _a : false;
122
+ return (_a = expandedTracks[(0, timeline_node_path_key_1.timelineNodePathInfoToKey)(nodePathInfo)]) !== null && _a !== void 0 ? _a : true;
50
123
  },
51
124
  }), [expandedTracks]);
52
125
  const setterValue = (0, react_1.useMemo)(() => ({
126
+ expandParentTracks,
53
127
  toggleTrack,
54
128
  migrateExpandedTracksForSubscriptionKey: migrateExpandedTracks,
55
- }), [toggleTrack, migrateExpandedTracks]);
129
+ }), [expandParentTracks, toggleTrack, migrateExpandedTracks]);
56
130
  return (jsx_runtime_1.jsx(exports.ExpandedTracksSetterContext.Provider, { value: setterValue, children: jsx_runtime_1.jsx(exports.ExpandedTracksGetterContext.Provider, { value: getterValue, children: children }) }));
57
131
  };
58
132
  exports.ExpandedTracksProvider = ExpandedTracksProvider;
@@ -1,2 +1,4 @@
1
1
  import type React from 'react';
2
- export declare const GlobalKeybindings: React.FC;
2
+ export declare const GlobalKeybindings: React.FC<{
3
+ readonly readOnlyStudio: boolean;
4
+ }>;
@@ -2,18 +2,110 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GlobalKeybindings = void 0;
4
4
  const react_1 = require("react");
5
+ const remotion_1 = require("remotion");
6
+ const calculate_timeline_1 = require("../helpers/calculate-timeline");
7
+ const client_id_1 = require("../helpers/client-id");
8
+ const show_browser_rendering_1 = require("../helpers/show-browser-rendering");
9
+ const timeline_node_path_key_1 = require("../helpers/timeline-node-path-key");
5
10
  const use_keybinding_1 = require("../helpers/use-keybinding");
6
11
  const checkerboard_1 = require("../state/checkerboard");
7
12
  const modals_1 = require("../state/modals");
8
13
  const AskAiModal_1 = require("./AskAiModal");
9
14
  const CompositionSelector_1 = require("./CompositionSelector");
10
15
  const NotificationCenter_1 = require("./Notifications/NotificationCenter");
11
- const GlobalKeybindings = () => {
16
+ const TimelineSelection_1 = require("./Timeline/TimelineSelection");
17
+ const sequencePropShortcuts = {
18
+ p: 'style.translate',
19
+ r: 'style.rotate',
20
+ s: 'style.scale',
21
+ t: 'style.opacity',
22
+ };
23
+ const hasOwnProperty = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
24
+ const GlobalKeybindings = ({ readOnlyStudio }) => {
12
25
  const keybindings = (0, use_keybinding_1.useKeybinding)();
13
26
  const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
14
27
  const { setCheckerboard } = (0, react_1.useContext)(checkerboard_1.CheckerboardContext);
28
+ const currentSelection = (0, TimelineSelection_1.useCurrentTimelineSelectionStateAsRef)();
29
+ const { sequences } = (0, react_1.useContext)(remotion_1.Internals.SequenceManager);
30
+ const videoConfig = remotion_1.Internals.useUnsafeVideoConfig();
31
+ const { overrideIdToNodePathMappings } = (0, react_1.useContext)(remotion_1.Internals.OverrideIdsToNodePathsGettersContext);
32
+ const { type } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx).previewServerState;
15
33
  const { navigateToNextComposition, navigateToPreviousComposition } = (0, CompositionSelector_1.useCompositionNavigation)();
34
+ const video = remotion_1.Internals.useVideo();
35
+ const timeline = (0, react_1.useMemo)(() => {
36
+ if (videoConfig === null) {
37
+ return [];
38
+ }
39
+ return (0, calculate_timeline_1.calculateTimeline)({
40
+ sequences,
41
+ overrideIdsToNodePaths: overrideIdToNodePathMappings,
42
+ });
43
+ }, [overrideIdToNodePathMappings, sequences, videoConfig]);
44
+ const selectSequenceProp = (0, react_1.useCallback)((fieldKey) => {
45
+ const { selectedItems, selectItems } = currentSelection.current;
46
+ if (selectedItems.length !== 1) {
47
+ return false;
48
+ }
49
+ const [selection] = selectedItems;
50
+ if (selection.type !== 'sequence' && selection.type !== 'sequence-prop') {
51
+ return false;
52
+ }
53
+ const selectedTrackKey = (0, TimelineSelection_1.getTimelineSequenceSelectionKey)(selection.nodePathInfo);
54
+ const track = timeline.find((candidate) => candidate.nodePathInfo !== null &&
55
+ (0, timeline_node_path_key_1.timelineNodePathInfoToKey)(candidate.nodePathInfo) ===
56
+ selectedTrackKey);
57
+ if (!(track === null || track === void 0 ? void 0 : track.sequence.controls)) {
58
+ return false;
59
+ }
60
+ const { schema, currentRuntimeValueDotNotation } = track.sequence.controls;
61
+ if (!hasOwnProperty(schema, fieldKey) &&
62
+ !hasOwnProperty(currentRuntimeValueDotNotation, fieldKey)) {
63
+ return false;
64
+ }
65
+ selectItems([
66
+ {
67
+ type: 'sequence-prop',
68
+ nodePathInfo: {
69
+ ...selection.nodePathInfo,
70
+ auxiliaryKeys: ['controls', fieldKey],
71
+ },
72
+ key: fieldKey,
73
+ },
74
+ ]);
75
+ return true;
76
+ }, [currentSelection, timeline]);
77
+ const openRenderModal = (0, react_1.useCallback)(() => {
78
+ if (!video) {
79
+ return;
80
+ }
81
+ if (type !== 'connected' && !show_browser_rendering_1.SHOW_BROWSER_RENDERING && !readOnlyStudio) {
82
+ (0, NotificationCenter_1.showNotification)('Studio server is offline', 2000);
83
+ return;
84
+ }
85
+ const renderButton = document.getElementById('render-modal-button');
86
+ renderButton.click();
87
+ }, [readOnlyStudio, type, video]);
16
88
  (0, react_1.useEffect)(() => {
89
+ const onSequencePropKey = (event) => {
90
+ const key = event.key.toLowerCase();
91
+ const fieldKey = sequencePropShortcuts[key];
92
+ if (!fieldKey) {
93
+ return;
94
+ }
95
+ if (selectSequenceProp(fieldKey)) {
96
+ event.preventDefault();
97
+ return;
98
+ }
99
+ if (key === 't') {
100
+ setCheckerboard((c) => !c);
101
+ event.preventDefault();
102
+ return;
103
+ }
104
+ if (key === 'r') {
105
+ openRenderModal();
106
+ event.preventDefault();
107
+ }
108
+ };
17
109
  const nKey = keybindings.registerKeybinding({
18
110
  event: 'keypress',
19
111
  key: 'n',
@@ -65,17 +157,15 @@ const GlobalKeybindings = () => {
65
157
  preventDefault: true,
66
158
  })
67
159
  : null;
68
- const cKey = keybindings.registerKeybinding({
69
- event: 'keypress',
70
- key: 't',
71
- callback: () => {
72
- setCheckerboard((c) => !c);
73
- },
160
+ const sequencePropKeys = Object.keys(sequencePropShortcuts).map((key) => keybindings.registerKeybinding({
161
+ event: 'keydown',
162
+ key,
163
+ callback: onSequencePropKey,
74
164
  commandCtrlKey: false,
75
- preventDefault: true,
165
+ preventDefault: false,
76
166
  triggerIfInputFieldFocused: false,
77
167
  keepRegisteredWhenNotHighestContext: false,
78
- });
168
+ }));
79
169
  const questionMark = keybindings.registerKeybinding({
80
170
  event: 'keypress',
81
171
  key: '?',
@@ -111,7 +201,9 @@ const GlobalKeybindings = () => {
111
201
  });
112
202
  return () => {
113
203
  nKey.unregister();
114
- cKey.unregister();
204
+ for (const sequencePropKey of sequencePropKeys) {
205
+ sequencePropKey.unregister();
206
+ }
115
207
  questionMark.unregister();
116
208
  cmdKKey.unregister();
117
209
  cmdSKey.unregister();
@@ -121,6 +213,8 @@ const GlobalKeybindings = () => {
121
213
  };
122
214
  }, [
123
215
  keybindings,
216
+ openRenderModal,
217
+ selectSequenceProp,
124
218
  setCheckerboard,
125
219
  setSelectedModal,
126
220
  navigateToNextComposition,
@@ -62,9 +62,9 @@ const SequenceExpandedInspector = ({ track }) => {
62
62
  }
63
63
  window.open(documentationLink, '_blank', 'noopener,noreferrer');
64
64
  }, [documentationLink]);
65
- const titleStyle = (0, react_1.useMemo)(() => {
65
+ const subtitleStyle = (0, react_1.useMemo)(() => {
66
66
  return {
67
- ...styles_1.sequenceHeaderTitle,
67
+ ...styles_1.sequenceHeaderSubtitle,
68
68
  cursor: documentationLink ? 'pointer' : 'default',
69
69
  };
70
70
  }, [documentationLink]);
@@ -103,7 +103,8 @@ const SequenceExpandedInspector = ({ track }) => {
103
103
  return jsx_runtime_1.jsx(common_1.InspectorMessage, { children: "Sequence inspector unavailable" });
104
104
  }
105
105
  return (jsx_runtime_1.jsxs("div", { style: styles_1.selectedContainer, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, onPointerDown: selectSequenceOnInspectorPointerDown, children: [
106
- jsx_runtime_1.jsxs("div", { style: styles_1.sequenceHeader, children: [documentationLink ? (jsx_runtime_1.jsx("button", { type: "button", style: titleStyle, title: "Open component docs", onClick: openDocumentationLink, children: sequenceDisplayName })) : (jsx_runtime_1.jsx("div", { style: titleStyle, children: sequenceDisplayName })), jsx_runtime_1.jsx(InspectorSourceLocation_1.InspectorSourceLocation, { location: validatedLocation, canOpen: canOpenInEditor, onOpen: openFileLocation })
106
+ jsx_runtime_1.jsxs("div", { style: styles_1.sequenceHeader, children: [
107
+ jsx_runtime_1.jsx("div", { style: styles_1.sequenceHeaderTitle, children: sequenceDisplayName }), documentationLink ? (jsx_runtime_1.jsx("button", { type: "button", style: subtitleStyle, title: "Open component docs", onClick: openDocumentationLink, children: track.sequence.controls.componentName })) : (jsx_runtime_1.jsx("div", { style: subtitleStyle, children: track.sequence.controls.componentName })), jsx_runtime_1.jsx(InspectorSourceLocation_1.InspectorSourceLocation, { location: validatedLocation, canOpen: canOpenInEditor, onOpen: openFileLocation })
107
108
  ] }), jsx_runtime_1.jsx(InspectorSequenceSection_1.InspectorSequenceSection, { sequence: track.sequence, validatedLocation: validatedLocation, nodePathInfo: track.nodePathInfo, keyframeDisplayOffset: track.keyframeDisplayOffset, renderSectionHeader: (children) => (jsx_runtime_1.jsx(common_1.InspectorSectionHeader, { children: children })) })] }));
108
109
  };
109
110
  const SequenceSelectionInspector = ({ selection }) => {
@@ -3,8 +3,8 @@ export type SequenceSectionSelection = Extract<TimelineSelection, {
3
3
  type: 'sequence' | 'sequence-prop' | 'sequence-all-effects' | 'sequence-effect' | 'sequence-effect-prop';
4
4
  }>;
5
5
  export declare const isSequenceSectionSelection: (selection: TimelineSelection) => selection is SequenceSectionSelection;
6
- type SequencePropSelection = Extract<TimelineSelection, {
7
- type: 'sequence-prop' | 'sequence-effect-prop';
6
+ type SameSequenceInspectorSelection = Extract<TimelineSelection, {
7
+ type: 'sequence-prop' | 'sequence-effect' | 'sequence-effect-prop';
8
8
  }>;
9
- export declare const getSameSequencePropInspectorSelection: (selections: readonly TimelineSelection[]) => SequencePropSelection | null;
9
+ export declare const getSameSequenceInspectorSelection: (selections: readonly TimelineSelection[]) => SameSequenceInspectorSelection | null;
10
10
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getSameSequencePropInspectorSelection = exports.isSequenceSectionSelection = void 0;
3
+ exports.getSameSequenceInspectorSelection = exports.isSequenceSectionSelection = void 0;
4
4
  const TimelineSelection_1 = require("../Timeline/TimelineSelection");
5
5
  const isSequenceSectionSelection = (selection) => {
6
6
  return (selection.type === 'sequence' ||
@@ -10,18 +10,19 @@ const isSequenceSectionSelection = (selection) => {
10
10
  selection.type === 'sequence-effect-prop');
11
11
  };
12
12
  exports.isSequenceSectionSelection = isSequenceSectionSelection;
13
- const isSequencePropSelection = (selection) => {
13
+ const isSameSequenceInspectorSelection = (selection) => {
14
14
  return (selection.type === 'sequence-prop' ||
15
+ selection.type === 'sequence-effect' ||
15
16
  selection.type === 'sequence-effect-prop');
16
17
  };
17
- const getSameSequencePropInspectorSelection = (selections) => {
18
+ const getSameSequenceInspectorSelection = (selections) => {
18
19
  const firstSelection = selections[0];
19
- if (!firstSelection || !isSequencePropSelection(firstSelection)) {
20
+ if (!firstSelection || !isSameSequenceInspectorSelection(firstSelection)) {
20
21
  return null;
21
22
  }
22
23
  const rootSequenceKey = (0, TimelineSelection_1.getTimelineSequenceSelectionKey)(firstSelection.nodePathInfo);
23
24
  for (const selection of selections) {
24
- if (!isSequencePropSelection(selection)) {
25
+ if (!isSameSequenceInspectorSelection(selection)) {
25
26
  return null;
26
27
  }
27
28
  if ((0, TimelineSelection_1.getTimelineSequenceSelectionKey)(selection.nodePathInfo) !==
@@ -31,4 +32,4 @@ const getSameSequencePropInspectorSelection = (selections) => {
31
32
  }
32
33
  return firstSelection;
33
34
  };
34
- exports.getSameSequencePropInspectorSelection = getSameSequencePropInspectorSelection;
35
+ exports.getSameSequenceInspectorSelection = getSameSequenceInspectorSelection;
@@ -8,6 +8,7 @@ export declare const inspectorSectionDivider: React.CSSProperties;
8
8
  export declare const sectionHeader: React.CSSProperties;
9
9
  export declare const sequenceHeader: React.CSSProperties;
10
10
  export declare const sequenceHeaderTitle: React.CSSProperties;
11
+ export declare const sequenceHeaderSubtitle: React.CSSProperties;
11
12
  export declare const sectionHeaderRow: React.CSSProperties;
12
13
  export declare const sectionHeaderTitle: React.CSSProperties;
13
14
  export declare const sectionHeaderStart: React.CSSProperties;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.keyframeEditorValue = exports.keyframeEditorLabel = exports.keyframeEditorRow = exports.detailValue = exports.detailLabel = exports.detailRow = exports.guideDetailsContainer = exports.detailsContainer = exports.centeredMessage = exports.selectedContainer = exports.resolveLinkStyle = exports.defaultPropsWarningMessages = exports.defaultPropsWarningContainer = exports.sectionHeaderEnd = exports.sectionHeaderStart = exports.sectionHeaderTitle = exports.sectionHeaderRow = exports.sequenceHeaderTitle = exports.sequenceHeader = exports.sectionHeader = exports.inspectorSectionDivider = exports.compositionSection = exports.visualControlsSection = exports.defaultPropsSection = exports.scrollableContainer = exports.container = void 0;
3
+ exports.keyframeEditorValue = exports.keyframeEditorLabel = exports.keyframeEditorRow = exports.detailValue = exports.detailLabel = exports.detailRow = exports.guideDetailsContainer = exports.detailsContainer = exports.centeredMessage = exports.selectedContainer = exports.resolveLinkStyle = exports.defaultPropsWarningMessages = exports.defaultPropsWarningContainer = exports.sectionHeaderEnd = exports.sectionHeaderStart = exports.sectionHeaderTitle = exports.sectionHeaderRow = exports.sequenceHeaderSubtitle = exports.sequenceHeaderTitle = exports.sequenceHeader = exports.sectionHeader = exports.inspectorSectionDivider = exports.compositionSection = exports.visualControlsSection = exports.defaultPropsSection = exports.scrollableContainer = exports.container = void 0;
4
4
  const colors_1 = require("../../helpers/colors");
5
5
  const InspectorPanelLayout_1 = require("../InspectorPanelLayout");
6
6
  exports.container = {
@@ -62,6 +62,24 @@ exports.sequenceHeaderTitle = {
62
62
  textOverflow: 'ellipsis',
63
63
  whiteSpace: 'nowrap',
64
64
  };
65
+ exports.sequenceHeaderSubtitle = {
66
+ alignSelf: 'flex-start',
67
+ backgroundColor: colors_1.BACKGROUND,
68
+ border: 'none',
69
+ color: colors_1.LIGHT_TEXT,
70
+ display: 'inline-flex',
71
+ fontFamily: 'sans-serif',
72
+ fontSize: 12,
73
+ lineHeight: '18px',
74
+ margin: 0,
75
+ maxWidth: '100%',
76
+ minWidth: 0,
77
+ overflow: 'hidden',
78
+ padding: 0,
79
+ textAlign: 'left',
80
+ textOverflow: 'ellipsis',
81
+ whiteSpace: 'nowrap',
82
+ };
65
83
  exports.sectionHeaderRow = {
66
84
  alignItems: 'center',
67
85
  display: 'flex',