@remotion/studio 4.0.469 → 4.0.471

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 (101) hide show
  1. package/dist/Studio.js +1 -1
  2. package/dist/api/save-render-output.js +3 -12
  3. package/dist/components/AudioWaveform.js +19 -2
  4. package/dist/components/ContextMenu.d.ts +7 -2
  5. package/dist/components/ContextMenu.js +53 -9
  6. package/dist/components/EditorContent.js +5 -4
  7. package/dist/components/Menu/MenuItem.d.ts +1 -1
  8. package/dist/components/MenuBuildIndicator.js +0 -1
  9. package/dist/components/NewComposition/InputDragger.js +1 -0
  10. package/dist/components/Preview.js +6 -2
  11. package/dist/components/SelectedOutlineOverlay.d.ts +49 -0
  12. package/dist/components/SelectedOutlineOverlay.js +710 -0
  13. package/dist/components/Timeline/Timeline.js +102 -13
  14. package/dist/components/Timeline/TimelineDeleteKeybindings.d.ts +2 -0
  15. package/dist/components/Timeline/TimelineDeleteKeybindings.js +101 -0
  16. package/dist/components/Timeline/TimelineDragHandler.js +20 -244
  17. package/dist/components/Timeline/{TimelineEffectGroupRow.d.ts → TimelineEffectItem.d.ts} +1 -1
  18. package/dist/components/Timeline/TimelineEffectItem.js +323 -0
  19. package/dist/components/Timeline/{TimelineEffectFieldRow.d.ts → TimelineEffectPropItem.d.ts} +2 -1
  20. package/dist/components/Timeline/{TimelineEffectFieldRow.js → TimelineEffectPropItem.js} +97 -8
  21. package/dist/components/Timeline/TimelineExpandArrowButton.js +5 -1
  22. package/dist/components/Timeline/TimelineExpandedKeyframeRow.js +37 -5
  23. package/dist/components/Timeline/TimelineExpandedRow.d.ts +1 -0
  24. package/dist/components/Timeline/TimelineExpandedRow.js +9 -7
  25. package/dist/components/Timeline/TimelineExpandedSection.d.ts +1 -0
  26. package/dist/components/Timeline/TimelineExpandedSection.js +2 -2
  27. package/dist/components/Timeline/TimelineInOutDragHandler.d.ts +2 -0
  28. package/dist/components/Timeline/TimelineInOutDragHandler.js +324 -0
  29. package/dist/components/Timeline/TimelineInOutPointer.js +3 -1
  30. package/dist/components/Timeline/TimelineInOutPointerHandle.d.ts +1 -0
  31. package/dist/components/Timeline/TimelineInOutPointerHandle.js +5 -4
  32. package/dist/components/Timeline/TimelineKeyframeControls.d.ts +17 -0
  33. package/dist/components/Timeline/TimelineKeyframeControls.js +222 -0
  34. package/dist/components/Timeline/TimelineKeyframeDiamond.js +7 -6
  35. package/dist/components/Timeline/TimelineKeyframedValue.d.ts +8 -0
  36. package/dist/components/Timeline/TimelineKeyframedValue.js +36 -0
  37. package/dist/components/Timeline/TimelineList.js +3 -15
  38. package/dist/components/Timeline/TimelineRowChrome.d.ts +6 -1
  39. package/dist/components/Timeline/TimelineRowChrome.js +25 -7
  40. package/dist/components/Timeline/TimelineSelection.d.ts +53 -9
  41. package/dist/components/Timeline/TimelineSelection.js +305 -48
  42. package/dist/components/Timeline/TimelineSequence.d.ts +2 -0
  43. package/dist/components/Timeline/TimelineSequence.js +18 -6
  44. package/dist/components/Timeline/TimelineSequenceFrame.js +1 -0
  45. package/dist/components/Timeline/{TimelineListItem.d.ts → TimelineSequenceItem.d.ts} +2 -1
  46. package/dist/components/Timeline/{TimelineListItem.js → TimelineSequenceItem.js} +140 -34
  47. package/dist/components/Timeline/{TimelineFieldRow.d.ts → TimelineSequencePropItem.d.ts} +2 -1
  48. package/dist/components/Timeline/{TimelineFieldRow.js → TimelineSequencePropItem.js} +81 -5
  49. package/dist/components/Timeline/TimelineTimeIndicators.js +0 -1
  50. package/dist/components/Timeline/TimelineTrack.js +3 -1
  51. package/dist/components/Timeline/TimelineTranslateField.js +14 -22
  52. package/dist/components/Timeline/call-add-keyframe.d.ts +23 -0
  53. package/dist/components/Timeline/call-add-keyframe.js +54 -0
  54. package/dist/components/Timeline/call-delete-keyframe.d.ts +37 -0
  55. package/dist/components/Timeline/call-delete-keyframe.js +122 -0
  56. package/dist/components/Timeline/delete-selected-keyframe.d.ts +21 -0
  57. package/dist/components/Timeline/delete-selected-keyframe.js +92 -0
  58. package/dist/components/Timeline/delete-selected-timeline-item.d.ts +17 -0
  59. package/dist/components/Timeline/delete-selected-timeline-item.js +178 -0
  60. package/dist/components/Timeline/duplicate-selected-timeline-item.d.ts +13 -0
  61. package/dist/components/Timeline/duplicate-selected-timeline-item.js +66 -0
  62. package/dist/components/Timeline/find-track-for-node-path-info.d.ts +7 -0
  63. package/dist/components/Timeline/find-track-for-node-path-info.js +13 -0
  64. package/dist/components/Timeline/get-keyframe-navigation.d.ts +9 -0
  65. package/dist/components/Timeline/get-keyframe-navigation.js +26 -0
  66. package/dist/components/Timeline/parse-keyframe-field-from-node-path.d.ts +8 -0
  67. package/dist/components/Timeline/parse-keyframe-field-from-node-path.js +26 -0
  68. package/dist/components/Timeline/reset-selected-timeline-props.d.ts +38 -0
  69. package/dist/components/Timeline/reset-selected-timeline-props.js +143 -0
  70. package/dist/components/Timeline/save-sequence-prop.d.ts +14 -2
  71. package/dist/components/Timeline/save-sequence-prop.js +42 -7
  72. package/dist/components/Timeline/sequence-props-subscription-store.d.ts +3 -2
  73. package/dist/components/Timeline/sequence-props-subscription-store.js +2 -1
  74. package/dist/components/Timeline/timeline-row-layout.d.ts +1 -0
  75. package/dist/components/Timeline/timeline-row-layout.js +2 -1
  76. package/dist/components/Timeline/timeline-scroll-logic.js +3 -3
  77. package/dist/components/Timeline/timeline-translate-utils.d.ts +2 -0
  78. package/dist/components/Timeline/timeline-translate-utils.js +20 -0
  79. package/dist/components/Timeline/use-expanded-track-keyframe-rows.js +10 -3
  80. package/dist/components/Timeline/use-sequence-props-subscription.js +2 -1
  81. package/dist/components/composition-menu-items.js +32 -1
  82. package/dist/error-overlay/remotion-overlay/OpenInEditor.js +0 -1
  83. package/dist/esm/{chunk-1mp51e0w.js → chunk-z0z9d4r0.js} +10422 -5958
  84. package/dist/esm/internals.mjs +10422 -5958
  85. package/dist/esm/previewEntry.mjs +10419 -5953
  86. package/dist/esm/renderEntry.mjs +3 -1
  87. package/dist/helpers/format-file-location.d.ts +9 -0
  88. package/dist/helpers/format-file-location.js +27 -0
  89. package/dist/helpers/get-box-quads-polyfill-internals.d.ts +82 -0
  90. package/dist/helpers/get-box-quads-polyfill-internals.js +2395 -0
  91. package/dist/helpers/get-box-quads-ponyfill.d.ts +10 -0
  92. package/dist/helpers/get-box-quads-ponyfill.js +23 -0
  93. package/dist/helpers/get-left-of-timeline-slider.js +1 -1
  94. package/dist/helpers/get-timeline-sequence-layout.js +10 -11
  95. package/dist/helpers/open-in-editor.d.ts +20 -2
  96. package/dist/helpers/open-in-editor.js +53 -30
  97. package/dist/helpers/use-menu-structure.js +8 -17
  98. package/dist/renderEntry.js +2 -2
  99. package/dist/state/z-index.js +5 -2
  100. package/package.json +10 -10
  101. package/dist/components/Timeline/TimelineEffectGroupRow.js +0 -153
@@ -0,0 +1,10 @@
1
+ export type GetBoxQuadsBox = 'margin' | 'border' | 'padding' | 'content';
2
+ export type GetBoxQuadsPonyfillOptions = {
3
+ readonly box?: GetBoxQuadsBox;
4
+ readonly relativeTo?: Element;
5
+ };
6
+ /**
7
+ * Returns border/margin/padding/content box quads for an element.
8
+ * Uses the native API when available, otherwise the getBoxQuads ponyfill.
9
+ */
10
+ export declare const getBoxQuadsPonyfill: (element: Element, options?: GetBoxQuadsPonyfillOptions | undefined) => readonly DOMQuad[] | null;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getBoxQuadsPonyfill = void 0;
4
+ const get_box_quads_polyfill_internals_js_1 = require("./get-box-quads-polyfill-internals.js");
5
+ const hasNativeGetBoxQuads = (element) => {
6
+ return (typeof element.getBoxQuads === 'function');
7
+ };
8
+ /**
9
+ * Returns border/margin/padding/content box quads for an element.
10
+ * Uses the native API when available, otherwise the getBoxQuads ponyfill.
11
+ */
12
+ const getBoxQuadsPonyfill = (element, options) => {
13
+ try {
14
+ if (hasNativeGetBoxQuads(element)) {
15
+ return element.getBoxQuads(options);
16
+ }
17
+ return (0, get_box_quads_polyfill_internals_js_1.getBoxQuads)(element, options);
18
+ }
19
+ catch (_a) {
20
+ return null;
21
+ }
22
+ };
23
+ exports.getBoxQuadsPonyfill = getBoxQuadsPonyfill;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getXPositionOfItemInTimelineImperatively = void 0;
4
4
  const timeline_layout_1 = require("./timeline-layout");
5
5
  const getXPositionOfItemInTimelineImperatively = (frame, duration, width) => {
6
- const proportion = frame / (duration - 1);
6
+ const proportion = frame / duration;
7
7
  return proportion * (width - timeline_layout_1.TIMELINE_PADDING * 2) + timeline_layout_1.TIMELINE_PADDING;
8
8
  };
9
9
  exports.getXPositionOfItemInTimelineImperatively = getXPositionOfItemInTimelineImperatively;
@@ -3,35 +3,34 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getTimelineSequenceLayout = exports.SEQUENCE_BORDER_WIDTH = void 0;
4
4
  const timeline_layout_1 = require("./timeline-layout");
5
5
  exports.SEQUENCE_BORDER_WIDTH = 1;
6
- const getWidthOfTrack = ({ durationInFrames, lastFrame, windowWidth, spatialDuration, nonNegativeMarginLeft, }) => {
6
+ const getWidthOfTrack = ({ durationInFrames, timelineDuration, windowWidth, spatialDuration, nonNegativeMarginLeft, }) => {
7
7
  const fullWidth = windowWidth - timeline_layout_1.TIMELINE_PADDING * 2;
8
- const base = durationInFrames === Infinity || lastFrame === 0
8
+ const base = durationInFrames === Infinity || timelineDuration <= 0
9
9
  ? fullWidth
10
- : (spatialDuration / lastFrame) * fullWidth;
10
+ : (spatialDuration / timelineDuration) * fullWidth;
11
11
  return Math.max(0, base - exports.SEQUENCE_BORDER_WIDTH + nonNegativeMarginLeft);
12
12
  };
13
13
  const getTimelineSequenceLayout = ({ durationInFrames, startFrom, maxMediaDuration, startFromMedia, video, windowWidth, premountDisplay, postmountDisplay, }) => {
14
14
  var _a;
15
15
  const maxMediaSequenceDuration = (maxMediaDuration !== null && maxMediaDuration !== void 0 ? maxMediaDuration : Infinity) - startFromMedia;
16
16
  const timelineDuration = (_a = video.durationInFrames) !== null && _a !== void 0 ? _a : 1;
17
- const lastFrame = timelineDuration - 1;
18
17
  const spatialDuration = Math.max(0, Math.min(maxMediaSequenceDuration, durationInFrames, timelineDuration - startFrom));
19
- // Unclipped spatial duration: without the lastFrame - startFrom constraint
18
+ // Unclipped spatial duration: without the timeline-end constraint
20
19
  const naturalSpatialDuration = Math.max(0, Math.min(maxMediaSequenceDuration, durationInFrames));
21
- const marginLeft = lastFrame === 0
20
+ const marginLeft = timelineDuration <= 0
22
21
  ? 0
23
- : (startFrom / lastFrame) * (windowWidth - timeline_layout_1.TIMELINE_PADDING * 2);
22
+ : (startFrom / timelineDuration) * (windowWidth - timeline_layout_1.TIMELINE_PADDING * 2);
24
23
  const nonNegativeMarginLeft = Math.min(marginLeft, 0);
25
24
  const width = getWidthOfTrack({
26
25
  durationInFrames,
27
- lastFrame,
26
+ timelineDuration,
28
27
  nonNegativeMarginLeft,
29
28
  spatialDuration,
30
29
  windowWidth,
31
30
  });
32
31
  const naturalWidth = getWidthOfTrack({
33
32
  durationInFrames,
34
- lastFrame,
33
+ timelineDuration,
35
34
  nonNegativeMarginLeft,
36
35
  spatialDuration: naturalSpatialDuration,
37
36
  windowWidth,
@@ -39,7 +38,7 @@ const getTimelineSequenceLayout = ({ durationInFrames, startFrom, maxMediaDurati
39
38
  const premountWidth = premountDisplay
40
39
  ? getWidthOfTrack({
41
40
  durationInFrames: premountDisplay,
42
- lastFrame,
41
+ timelineDuration,
43
42
  nonNegativeMarginLeft,
44
43
  spatialDuration: premountDisplay,
45
44
  windowWidth,
@@ -48,7 +47,7 @@ const getTimelineSequenceLayout = ({ durationInFrames, startFrom, maxMediaDurati
48
47
  const postmountWidth = postmountDisplay
49
48
  ? getWidthOfTrack({
50
49
  durationInFrames: postmountDisplay,
51
- lastFrame,
50
+ timelineDuration,
52
51
  nonNegativeMarginLeft,
53
52
  spatialDuration: postmountDisplay,
54
53
  windowWidth,
@@ -1,7 +1,24 @@
1
- import type { SymbolicatedStackFrame } from '@remotion/studio-shared';
1
+ import type { CompositionComponentInfoResponse, SymbolicatedStackFrame } from '@remotion/studio-shared';
2
2
  import type { OriginalPosition } from '../error-overlay/react-overlay/utils/get-source-map';
3
- export declare const openInEditor: (stack: SymbolicatedStackFrame) => Promise<Response>;
3
+ export declare const openInEditor: (stack: SymbolicatedStackFrame) => Promise<import("@remotion/studio-shared").OpenInEditorResponse>;
4
4
  export declare const openOriginalPositionInEditor: (originalPosition: OriginalPosition) => Promise<void>;
5
+ type ResolvedCompositionComponentInfo = {
6
+ location: CompositionComponentInfoResponse['location'];
7
+ canAddSequence: boolean;
8
+ };
9
+ export declare const subscribeToCompositionComponentInfo: (listener: () => void) => () => void;
10
+ export declare const getCachedCompositionComponentInfo: ({ compositionFile, compositionId, }: {
11
+ compositionFile: string;
12
+ compositionId: string;
13
+ }) => ResolvedCompositionComponentInfo | null;
14
+ export declare const useCachedCompositionComponentInfo: ({ compositionFile, compositionId, }: {
15
+ compositionFile: string | null;
16
+ compositionId: string | null;
17
+ }) => ResolvedCompositionComponentInfo | null;
18
+ export declare const loadCompositionComponentInfo: ({ compositionFile, compositionId, }: {
19
+ compositionFile: string;
20
+ compositionId: string;
21
+ }) => Promise<ResolvedCompositionComponentInfo>;
5
22
  export declare const preloadCompositionComponentInfo: ({ compositionFile, compositionId, }: {
6
23
  compositionFile: string;
7
24
  compositionId: string;
@@ -10,3 +27,4 @@ export declare const openCompositionComponentInEditor: ({ compositionFile, compo
10
27
  compositionFile: string;
11
28
  compositionId: string;
12
29
  }) => Promise<void>;
30
+ export {};
@@ -1,22 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.openCompositionComponentInEditor = exports.preloadCompositionComponentInfo = exports.openOriginalPositionInEditor = exports.openInEditor = void 0;
3
+ exports.openCompositionComponentInEditor = exports.preloadCompositionComponentInfo = exports.loadCompositionComponentInfo = exports.useCachedCompositionComponentInfo = exports.getCachedCompositionComponentInfo = exports.subscribeToCompositionComponentInfo = exports.openOriginalPositionInEditor = exports.openInEditor = void 0;
4
+ const react_1 = require("react");
5
+ const call_api_1 = require("../components/call-api");
4
6
  const openInEditor = (stack) => {
5
7
  const { originalFileName, originalLineNumber, originalColumnNumber, originalFunctionName, originalScriptCode, } = stack;
6
- return fetch(`/api/open-in-editor`, {
7
- method: 'post',
8
- headers: {
9
- 'content-type': 'application/json',
8
+ return (0, call_api_1.callApi)('/api/open-in-editor', {
9
+ stack: {
10
+ originalFileName,
11
+ originalLineNumber,
12
+ originalColumnNumber,
13
+ originalFunctionName,
14
+ originalScriptCode,
10
15
  },
11
- body: JSON.stringify({
12
- stack: {
13
- originalFileName,
14
- originalLineNumber,
15
- originalColumnNumber,
16
- originalFunctionName,
17
- originalScriptCode,
18
- },
19
- }),
20
16
  });
21
17
  };
22
18
  exports.openInEditor = openInEditor;
@@ -31,9 +27,40 @@ const openOriginalPositionInEditor = async (originalPosition) => {
31
27
  };
32
28
  exports.openOriginalPositionInEditor = openOriginalPositionInEditor;
33
29
  const componentResolutionCache = new Map();
30
+ const componentResolutionResults = new Map();
31
+ const componentResolutionListeners = new Set();
34
32
  const getComponentResolutionCacheKey = ({ compositionFile, compositionId, }) => {
35
33
  return `${compositionFile}::${compositionId}`;
36
34
  };
35
+ const notifyComponentResolutionListeners = () => {
36
+ for (const listener of componentResolutionListeners) {
37
+ listener();
38
+ }
39
+ };
40
+ const subscribeToCompositionComponentInfo = (listener) => {
41
+ componentResolutionListeners.add(listener);
42
+ return () => {
43
+ componentResolutionListeners.delete(listener);
44
+ };
45
+ };
46
+ exports.subscribeToCompositionComponentInfo = subscribeToCompositionComponentInfo;
47
+ const getCachedCompositionComponentInfo = ({ compositionFile, compositionId, }) => {
48
+ var _a;
49
+ return ((_a = componentResolutionResults.get(getComponentResolutionCacheKey({ compositionFile, compositionId }))) !== null && _a !== void 0 ? _a : null);
50
+ };
51
+ exports.getCachedCompositionComponentInfo = getCachedCompositionComponentInfo;
52
+ const useCachedCompositionComponentInfo = ({ compositionFile, compositionId, }) => {
53
+ return (0, react_1.useSyncExternalStore)(exports.subscribeToCompositionComponentInfo, () => {
54
+ if (compositionFile === null || compositionId === null) {
55
+ return null;
56
+ }
57
+ return (0, exports.getCachedCompositionComponentInfo)({
58
+ compositionFile,
59
+ compositionId,
60
+ });
61
+ }, () => null);
62
+ };
63
+ exports.useCachedCompositionComponentInfo = useCachedCompositionComponentInfo;
37
64
  const loadCompositionComponentInfo = async ({ compositionFile, compositionId, }) => {
38
65
  const cacheKey = getComponentResolutionCacheKey({
39
66
  compositionFile,
@@ -44,24 +71,17 @@ const loadCompositionComponentInfo = async ({ compositionFile, compositionId, })
44
71
  return existing;
45
72
  }
46
73
  const promise = (async () => {
47
- const response = await fetch(`/api/composition-component-info`, {
48
- method: 'post',
49
- headers: {
50
- 'content-type': 'application/json',
51
- },
52
- body: JSON.stringify({
53
- compositionFile,
54
- compositionId,
55
- }),
74
+ const body = await (0, call_api_1.callApi)('/api/composition-component-info', {
75
+ compositionFile,
76
+ compositionId,
56
77
  });
57
- const body = (await response.json());
58
- if (!body.success) {
59
- throw new Error(body.error);
60
- }
61
- return {
78
+ const result = {
62
79
  location: body.location,
63
80
  canAddSequence: body.canAddSequence,
64
81
  };
82
+ componentResolutionResults.set(cacheKey, result);
83
+ notifyComponentResolutionListeners();
84
+ return result;
65
85
  })();
66
86
  componentResolutionCache.set(cacheKey, promise);
67
87
  try {
@@ -69,18 +89,21 @@ const loadCompositionComponentInfo = async ({ compositionFile, compositionId, })
69
89
  }
70
90
  catch (err) {
71
91
  componentResolutionCache.delete(cacheKey);
92
+ componentResolutionResults.delete(cacheKey);
93
+ notifyComponentResolutionListeners();
72
94
  throw err;
73
95
  }
74
96
  };
97
+ exports.loadCompositionComponentInfo = loadCompositionComponentInfo;
75
98
  const preloadCompositionComponentInfo = ({ compositionFile, compositionId, }) => {
76
- loadCompositionComponentInfo({
99
+ (0, exports.loadCompositionComponentInfo)({
77
100
  compositionFile,
78
101
  compositionId,
79
102
  }).catch(() => undefined);
80
103
  };
81
104
  exports.preloadCompositionComponentInfo = preloadCompositionComponentInfo;
82
105
  const openCompositionComponentInEditor = async ({ compositionFile, compositionId, }) => {
83
- const info = await loadCompositionComponentInfo({
106
+ const info = await (0, exports.loadCompositionComponentInfo)({
84
107
  compositionFile,
85
108
  compositionId,
86
109
  });
@@ -114,7 +114,6 @@ const getFileMenu = ({ readOnlyStudio, closeMenu, previewServerState, setSelecte
114
114
  originalFunctionName: null,
115
115
  originalScriptCode: null,
116
116
  })
117
- .then((res) => res.json())
118
117
  .then(({ success }) => {
119
118
  if (!success) {
120
119
  (0, NotificationCenter_1.showNotification)(`Could not open ${window.remotion_editorName}`, 2000);
@@ -172,7 +171,6 @@ const useMenuStructure = (closeMenu, readOnlyStudio) => {
172
171
  const resolvedCompositionLocation = (0, use_resolved_stack_1.useResolvedStack)((_a = currentComposition === null || currentComposition === void 0 ? void 0 : currentComposition.stack) !== null && _a !== void 0 ? _a : null);
173
172
  (0, react_1.useEffect)(() => {
174
173
  if (type !== 'connected' ||
175
- !window.remotion_editorName ||
176
174
  !currentComposition ||
177
175
  !(resolvedCompositionLocation === null || resolvedCompositionLocation === void 0 ? void 0 : resolvedCompositionLocation.source)) {
178
176
  return;
@@ -664,20 +662,12 @@ const useMenuStructure = (closeMenu, readOnlyStudio) => {
664
662
  type: 'item',
665
663
  quickSwitcherLabel: 'Open spring() Editor',
666
664
  },
667
- ].filter(remotion_1.Internals.truthy),
668
- quickSwitcherLabel: null,
669
- },
670
- readOnlyStudio || remotion_packageManager === 'unknown'
671
- ? null
672
- : {
673
- id: 'install',
674
- label: 'Packages',
675
- leaveLeftPadding: false,
676
- items: [
677
- {
665
+ readOnlyStudio || remotion_packageManager === 'unknown'
666
+ ? null
667
+ : {
678
668
  id: 'install-packages',
679
669
  value: 'install-packages',
680
- label: 'Install...',
670
+ label: 'Install package',
681
671
  onClick: () => {
682
672
  closeMenu();
683
673
  setSelectedModal({
@@ -689,10 +679,11 @@ const useMenuStructure = (closeMenu, readOnlyStudio) => {
689
679
  keyHint: null,
690
680
  leftItem: null,
691
681
  subMenu: null,
692
- quickSwitcherLabel: `Install packages`,
682
+ quickSwitcherLabel: `Install package`,
693
683
  },
694
- ],
695
- },
684
+ ].filter(remotion_1.Internals.truthy),
685
+ quickSwitcherLabel: null,
686
+ },
696
687
  {
697
688
  id: 'help',
698
689
  label: 'Help',
@@ -186,13 +186,13 @@ const renderContent = (Root) => {
186
186
  defaultPixelFormat: bundleMode.compositionDefaultPixelFormat,
187
187
  defaultProResProfile: bundleMode.compositionDefaultProResProfile,
188
188
  defaultSampleRate: bundleMode.compositionDefaultSampleRate,
189
- }, initialCompositions: [], children: jsx_runtime_1.jsx(remotion_1.Internals.RemotionRootContexts, { frameState: null, audioEnabled: window.remotion_audioEnabled, videoEnabled: window.remotion_videoEnabled, logLevel: (_a = window.remotion_logLevel) !== null && _a !== void 0 ? _a : 'info', numberOfAudioTags: 0, audioLatencyHint: (_b = window.remotion_audioLatencyHint) !== null && _b !== void 0 ? _b : 'playback', children: jsx_runtime_1.jsxs(remotion_1.Internals.RenderAssetManagerProvider, { collectAssets: null, children: [
189
+ }, initialCompositions: [], children: jsx_runtime_1.jsx(remotion_1.Internals.RemotionRootContexts, { frameState: null, audioEnabled: window.remotion_audioEnabled, videoEnabled: window.remotion_videoEnabled, logLevel: (_a = window.remotion_logLevel) !== null && _a !== void 0 ? _a : 'info', numberOfAudioTags: 0, audioLatencyHint: (_b = window.remotion_audioLatencyHint) !== null && _b !== void 0 ? _b : 'playback', previewSampleRate: window.remotion_previewSampleRate, children: jsx_runtime_1.jsxs(remotion_1.Internals.RenderAssetManagerProvider, { collectAssets: null, children: [
190
190
  jsx_runtime_1.jsx(Root, {}), jsx_runtime_1.jsx(GetVideoComposition, { state: bundleMode })
191
191
  ] }) }) }));
192
192
  renderToDOM(markup);
193
193
  }
194
194
  if (bundleMode.type === 'evaluation') {
195
- const markup = (jsx_runtime_1.jsx(remotion_1.Internals.CompositionManagerProvider, { initialCanvasContent: null, onlyRenderComposition: null, currentCompositionMetadata: null, initialCompositions: [], children: jsx_runtime_1.jsx(remotion_1.Internals.RemotionRootContexts, { frameState: null, audioEnabled: window.remotion_audioEnabled, videoEnabled: window.remotion_videoEnabled, logLevel: (_c = window.remotion_logLevel) !== null && _c !== void 0 ? _c : 'info', numberOfAudioTags: 0, audioLatencyHint: (_d = window.remotion_audioLatencyHint) !== null && _d !== void 0 ? _d : 'playback', children: jsx_runtime_1.jsx(remotion_1.Internals.RenderAssetManagerProvider, { collectAssets: null, children: jsx_runtime_1.jsx(Root, {}) }) }) }));
195
+ const markup = (jsx_runtime_1.jsx(remotion_1.Internals.CompositionManagerProvider, { initialCanvasContent: null, onlyRenderComposition: null, currentCompositionMetadata: null, initialCompositions: [], children: jsx_runtime_1.jsx(remotion_1.Internals.RemotionRootContexts, { frameState: null, audioEnabled: window.remotion_audioEnabled, videoEnabled: window.remotion_videoEnabled, logLevel: (_c = window.remotion_logLevel) !== null && _c !== void 0 ? _c : 'info', numberOfAudioTags: 0, audioLatencyHint: (_d = window.remotion_audioLatencyHint) !== null && _d !== void 0 ? _d : 'playback', previewSampleRate: window.remotion_previewSampleRate, children: jsx_runtime_1.jsx(remotion_1.Internals.RenderAssetManagerProvider, { collectAssets: null, children: jsx_runtime_1.jsx(Root, {}) }) }) }));
196
196
  renderToDOM(markup);
197
197
  }
198
198
  if (bundleMode.type === 'index') {
@@ -70,7 +70,10 @@ const HigherZIndex = ({ children, onEscape, onOutsideClick, disabled }) => {
70
70
  // If a menu is opened, then this component will also still receive the pointerdown event.
71
71
  // However we may not interpret it as a outside click, so we need to wait for the next tick
72
72
  requestAnimationFrame(() => {
73
- window.addEventListener('pointerdown', listener);
73
+ // The third argument `true` registers a capture-phase listener. Some Studio
74
+ // elements stop pointerdown propagation while still being outside the menu,
75
+ // so bubbling listeners would miss those clicks and keep the menu open.
76
+ window.addEventListener('pointerdown', listener, true);
74
77
  });
75
78
  return () => {
76
79
  if (onUp) {
@@ -78,7 +81,7 @@ const HigherZIndex = ({ children, onEscape, onOutsideClick, disabled }) => {
78
81
  window.removeEventListener('pointerup', onUp, { once: true });
79
82
  }
80
83
  onUp = null;
81
- return window.removeEventListener('pointerdown', listener);
84
+ return window.removeEventListener('pointerdown', listener, true);
82
85
  };
83
86
  }, [currentIndex, disabled, highestContext.highestIndex, onOutsideClick]);
84
87
  const value = (0, react_1.useMemo)(() => {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/studio"
4
4
  },
5
5
  "name": "@remotion/studio",
6
- "version": "4.0.469",
6
+ "version": "4.0.471",
7
7
  "description": "APIs for interacting with the Remotion Studio",
8
8
  "main": "dist",
9
9
  "scripts": {
@@ -25,14 +25,14 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "semver": "7.5.3",
28
- "remotion": "4.0.469",
29
- "@remotion/player": "4.0.469",
30
- "@remotion/media-utils": "4.0.469",
31
- "@remotion/renderer": "4.0.469",
32
- "@remotion/web-renderer": "4.0.469",
33
- "@remotion/studio-shared": "4.0.469",
34
- "@remotion/timeline-utils": "4.0.469",
35
- "@remotion/zod-types": "4.0.469",
28
+ "remotion": "4.0.471",
29
+ "@remotion/player": "4.0.471",
30
+ "@remotion/media-utils": "4.0.471",
31
+ "@remotion/renderer": "4.0.471",
32
+ "@remotion/web-renderer": "4.0.471",
33
+ "@remotion/studio-shared": "4.0.471",
34
+ "@remotion/timeline-utils": "4.0.471",
35
+ "@remotion/zod-types": "4.0.471",
36
36
  "@jridgewell/trace-mapping": "0.3.31",
37
37
  "mediabunny": "1.45.0",
38
38
  "memfs": "3.4.3",
@@ -43,7 +43,7 @@
43
43
  "react": "19.2.3",
44
44
  "react-dom": "19.2.3",
45
45
  "@types/semver": "7.5.3",
46
- "@remotion/eslint-config-internal": "4.0.469",
46
+ "@remotion/eslint-config-internal": "4.0.471",
47
47
  "eslint": "9.19.0",
48
48
  "@typescript/native-preview": "7.0.0-dev.20260217.1"
49
49
  },
@@ -1,153 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TimelineEffectGroupRow = void 0;
4
- const jsx_runtime_1 = require("react/jsx-runtime");
5
- const react_1 = require("react");
6
- const remotion_1 = require("remotion");
7
- const client_id_1 = require("../../helpers/client-id");
8
- const timeline_layout_1 = require("../../helpers/timeline-layout");
9
- const call_api_1 = require("../call-api");
10
- const ContextMenu_1 = require("../ContextMenu");
11
- const NotificationCenter_1 = require("../Notifications/NotificationCenter");
12
- const save_effect_prop_1 = require("./save-effect-prop");
13
- const TimelineExpandArrowButton_1 = require("./TimelineExpandArrowButton");
14
- const TimelineLayerEye_1 = require("./TimelineLayerEye");
15
- const TimelineRowChrome_1 = require("./TimelineRowChrome");
16
- const TimelineSelection_1 = require("./TimelineSelection");
17
- const rowLabel = {
18
- fontSize: 12,
19
- color: 'rgba(255, 255, 255, 0.8)',
20
- userSelect: 'none',
21
- };
22
- const TimelineEffectGroupRow = ({ label, nodePathInfo, effectIndex, effectSchema, documentationLink, nodePath, validatedLocation, rowDepth, getIsExpanded, toggleTrack, }) => {
23
- var _a;
24
- var _b;
25
- const [labelHovered, setLabelHovered] = (0, react_1.useState)(false);
26
- const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
27
- const previewConnected = previewServerState.type === 'connected';
28
- const { codeValues } = (0, react_1.useContext)(remotion_1.Internals.VisualModeCodeValuesContext);
29
- const { setCodeValues } = (0, react_1.useContext)(remotion_1.Internals.VisualModeSettersContext);
30
- const selection = (0, TimelineSelection_1.useTimelineRowSelection)(nodePathInfo);
31
- const effectStatus = (0, react_1.useMemo)(() => remotion_1.Internals.getEffectCodeValuesCtx({
32
- codeValues,
33
- nodePath,
34
- effectIndex,
35
- }), [codeValues, nodePath, effectIndex]);
36
- const disabledStatus = effectStatus.type === 'can-update-effect'
37
- ? ((_b = (_a = effectStatus.props) === null || _a === void 0 ? void 0 : _a.disabled) !== null && _b !== void 0 ? _b : null)
38
- : null;
39
- const isDisabled = (0, react_1.useMemo)(() => {
40
- if (disabledStatus && disabledStatus.canUpdate) {
41
- return Boolean(disabledStatus.codeValue);
42
- }
43
- return false;
44
- }, [disabledStatus]);
45
- const canToggle = previewConnected && disabledStatus !== null && disabledStatus.canUpdate;
46
- const deleteDisabled = !previewConnected ||
47
- effectStatus.type !== 'can-update-effect' ||
48
- !validatedLocation.source;
49
- const onDeleteEffectFromSource = (0, react_1.useCallback)(async () => {
50
- if (deleteDisabled) {
51
- return;
52
- }
53
- try {
54
- const result = await (0, call_api_1.callApi)('/api/delete-effect', {
55
- fileName: validatedLocation.source,
56
- sequenceNodePath: nodePath,
57
- effectIndex,
58
- });
59
- if (result.success) {
60
- (0, NotificationCenter_1.showNotification)('Removed effect from source file', 2000);
61
- }
62
- else {
63
- (0, NotificationCenter_1.showNotification)(result.reason, 4000);
64
- }
65
- }
66
- catch (err) {
67
- (0, NotificationCenter_1.showNotification)(err.message, 4000);
68
- }
69
- }, [deleteDisabled, effectIndex, nodePath, validatedLocation.source]);
70
- const contextMenuValues = (0, react_1.useMemo)(() => {
71
- if (!previewConnected) {
72
- return [];
73
- }
74
- return [
75
- {
76
- type: 'item',
77
- id: 'delete-effect',
78
- keyHint: null,
79
- label: 'Delete',
80
- leftItem: null,
81
- disabled: deleteDisabled,
82
- onClick: () => {
83
- if (deleteDisabled) {
84
- return;
85
- }
86
- onDeleteEffectFromSource();
87
- },
88
- quickSwitcherLabel: null,
89
- subMenu: null,
90
- value: 'delete-effect',
91
- },
92
- ];
93
- }, [deleteDisabled, onDeleteEffectFromSource, previewConnected]);
94
- const onToggle = (0, react_1.useCallback)((type) => {
95
- if (!canToggle || previewServerState.type !== 'connected') {
96
- return;
97
- }
98
- const newValue = type !== 'enable';
99
- const fieldSchema = effectSchema.disabled;
100
- const defaultValue = fieldSchema && fieldSchema.type === 'boolean'
101
- ? JSON.stringify(fieldSchema.default)
102
- : null;
103
- (0, save_effect_prop_1.saveEffectProp)({
104
- fileName: validatedLocation.source,
105
- nodePath,
106
- effectIndex,
107
- fieldKey: 'disabled',
108
- value: newValue,
109
- defaultValue,
110
- schema: effectSchema,
111
- setCodeValues,
112
- clientId: previewServerState.clientId,
113
- });
114
- }, [
115
- canToggle,
116
- effectIndex,
117
- effectSchema,
118
- nodePath,
119
- previewServerState,
120
- setCodeValues,
121
- validatedLocation.source,
122
- ]);
123
- const isExpanded = getIsExpanded(nodePathInfo);
124
- const rowStyle = (0, react_1.useMemo)(() => ({
125
- height: timeline_layout_1.TREE_GROUP_ROW_HEIGHT,
126
- }), []);
127
- const labelStyle = (0, react_1.useMemo)(() => {
128
- const hoverEffect = labelHovered && documentationLink !== null;
129
- return {
130
- ...rowLabel,
131
- ...(0, TimelineSelection_1.getTimelineSelectedLabelStyle)(selection.selected, true),
132
- alignSelf: 'stretch',
133
- alignItems: 'center',
134
- color: (0, TimelineSelection_1.getTimelineColor)(selection.selected, true),
135
- display: 'flex',
136
- flex: 1,
137
- minWidth: 0,
138
- paddingRight: timeline_layout_1.EXPANDED_SECTION_PADDING_RIGHT,
139
- textDecoration: hoverEffect ? 'underline' : 'none',
140
- textUnderlineOffset: 2,
141
- cursor: hoverEffect ? 'pointer' : undefined,
142
- };
143
- }, [documentationLink, labelHovered, selection.selected]);
144
- const onClickLabel = (0, react_1.useCallback)(() => {
145
- if (documentationLink === null) {
146
- return;
147
- }
148
- window.open(documentationLink, '_blank', 'noopener,noreferrer');
149
- }, [documentationLink]);
150
- const row = (jsx_runtime_1.jsx(TimelineRowChrome_1.TimelineRowChrome, { depth: rowDepth, eye: canToggle ? (jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEye, { type: "effect", hidden: isDisabled, onInvoked: onToggle })) : (jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEyeSpacer, {})), arrow: jsx_runtime_1.jsx(TimelineExpandArrowButton_1.TimelineExpandArrowButton, { isExpanded: isExpanded, onClick: () => toggleTrack(nodePathInfo), label: `${label} section`, disabled: false }), style: rowStyle, selected: selection.selected, selectable: selection.selectable, onSelect: selection.onSelect, showSelectedBackground: true, containsSelection: false, outerHeight: null, children: jsx_runtime_1.jsx("span", { onPointerEnter: () => setLabelHovered(true), onPointerLeave: () => setLabelHovered(false), onClick: onClickLabel, title: documentationLink ? `Open documentation: ${documentationLink}` : label, style: labelStyle, children: label }) }));
151
- return previewConnected ? (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { values: contextMenuValues, onOpen: selection.selectable ? selection.onSelect : null, children: row })) : (row);
152
- };
153
- exports.TimelineEffectGroupRow = TimelineEffectGroupRow;