@remotion/studio 4.0.519 → 4.0.521

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 (130) hide show
  1. package/dist/api/shut-down-studio.d.ts +6 -0
  2. package/dist/api/shut-down-studio.js +23 -0
  3. package/dist/api/visual-control.js +2 -2
  4. package/dist/components/AudioWaveform.js +16 -7
  5. package/dist/components/Canvas.js +114 -48
  6. package/dist/components/CaptionInspector.d.ts +1 -0
  7. package/dist/components/CaptionInspector.js +43 -2
  8. package/dist/components/CaptionTextEditor.js +1 -1
  9. package/dist/components/CompositionSelector.js +41 -34
  10. package/dist/components/CompositionSelectorItem.d.ts +7 -0
  11. package/dist/components/CompositionSelectorItem.js +344 -73
  12. package/dist/components/CurrentCompositionSideEffects.js +5 -0
  13. package/dist/components/ElementInstallConfirmation.d.ts +2 -23
  14. package/dist/components/ElementInstallConfirmation.js +89 -65
  15. package/dist/components/ElementLibraryModal.js +3 -1
  16. package/dist/components/FpsCounter.js +1 -1
  17. package/dist/components/FramePersistor.js +1 -1
  18. package/dist/components/InlineCaptionInspector.js +37 -1
  19. package/dist/components/InspectorPanel/ElementLibraryButton.js +3 -18
  20. package/dist/components/InspectorPanel/SequenceSelectionInspector.js +4 -3
  21. package/dist/components/InspectorPanel/use-composition-actions.js +5 -8
  22. package/dist/components/InspectorSequenceSection.js +1 -4
  23. package/dist/components/Menu/MenuSubItem.js +1 -1
  24. package/dist/components/ModalContainer.d.ts +1 -0
  25. package/dist/components/ModalContainer.js +2 -2
  26. package/dist/components/ModalHeader.js +5 -1
  27. package/dist/components/Modals.js +1 -1
  28. package/dist/components/NewComposition/NewComposition.js +2 -2
  29. package/dist/components/PlayPause.js +2 -13
  30. package/dist/components/PreviewToolbar.js +1 -1
  31. package/dist/components/QuickSwitcher/QuickSwitcherContent.js +29 -15
  32. package/dist/components/QuickSwitcher/QuickSwitcherResult.d.ts +14 -1
  33. package/dist/components/QuickSwitcher/QuickSwitcherResult.js +64 -9
  34. package/dist/components/QuickSwitcher/asset-search.d.ts +0 -1
  35. package/dist/components/QuickSwitcher/asset-search.js +1 -16
  36. package/dist/components/QuickSwitcher/composition-search.d.ts +16 -0
  37. package/dist/components/QuickSwitcher/composition-search.js +136 -0
  38. package/dist/components/RenderButton.js +2 -2
  39. package/dist/components/RenderModal/RenderStatusModal.js +7 -1
  40. package/dist/components/RenderModal/ServerRenderModal.js +1 -1
  41. package/dist/components/RenderModal/WebRenderModal.js +1 -1
  42. package/dist/components/RenderQueue/ClientRenderQueueProcessor.js +1 -1
  43. package/dist/components/ResetZoomButton.js +17 -2
  44. package/dist/components/SelectedOutlineElement.js +10 -30
  45. package/dist/components/SettingsModal.js +8 -5
  46. package/dist/components/TimeValue.js +36 -7
  47. package/dist/components/Timeline/Timeline.d.ts +1 -1
  48. package/dist/components/Timeline/Timeline.js +33 -20
  49. package/dist/components/Timeline/TimelineAssetField.d.ts +0 -1
  50. package/dist/components/Timeline/TimelineAssetField.js +17 -9
  51. package/dist/components/Timeline/TimelineCollapseToggle.js +1 -1
  52. package/dist/components/Timeline/TimelineDeleteKeybindings.js +5 -90
  53. package/dist/components/Timeline/TimelineDragHandler.js +18 -8
  54. package/dist/components/Timeline/TimelineFontWeightField.d.ts +11 -0
  55. package/dist/components/Timeline/TimelineFontWeightField.js +65 -0
  56. package/dist/components/Timeline/TimelineInOutDragHandler.js +2 -3
  57. package/dist/components/Timeline/TimelineLayerChildren.d.ts +21 -0
  58. package/dist/components/Timeline/TimelineLayerChildren.js +129 -0
  59. package/dist/components/Timeline/TimelineNumberField.js +13 -5
  60. package/dist/components/Timeline/TimelinePinchZoom.js +14 -4
  61. package/dist/components/Timeline/TimelinePlayCursorSyncer.js +8 -4
  62. package/dist/components/Timeline/TimelinePrimitiveFieldValue.js +4 -0
  63. package/dist/components/Timeline/TimelineSequence.js +54 -73
  64. package/dist/components/Timeline/TimelineSequenceItem.js +16 -36
  65. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.js +22 -10
  66. package/dist/components/Timeline/TimelineSlider.js +2 -2
  67. package/dist/components/Timeline/TimelineTickFormatProvider.d.ts +8 -0
  68. package/dist/components/Timeline/TimelineTickFormatProvider.js +16 -0
  69. package/dist/components/Timeline/TimelineTimeIndicators.js +7 -3
  70. package/dist/components/Timeline/TimelineVideoInfo.js +7 -3
  71. package/dist/components/Timeline/TimelineWidthProvider.js +15 -8
  72. package/dist/components/Timeline/TimelineZoomControls.js +25 -8
  73. package/dist/components/Timeline/delete-selected-timeline-item.js +2 -2
  74. package/dist/components/Timeline/timeline-field-display-utils.js +1 -0
  75. package/dist/components/Timeline/timeline-scroll-logic.d.ts +1 -0
  76. package/dist/components/Timeline/timeline-scroll-logic.js +21 -17
  77. package/dist/components/Timeline/use-delete-timeline-items.d.ts +1 -0
  78. package/dist/components/Timeline/use-delete-timeline-items.js +118 -0
  79. package/dist/components/Timeline/use-timeline-asset-drop.js +8 -14
  80. package/dist/components/UpdateStatusContext.d.ts +5 -0
  81. package/dist/components/UpdateStatusContext.js +53 -2
  82. package/dist/components/UpdatesSettings.js +25 -4
  83. package/dist/components/WebMcp.js +87 -8
  84. package/dist/components/ZoomPersistor.js +2 -2
  85. package/dist/components/composition-drag-data.d.ts +36 -0
  86. package/dist/components/composition-drag-data.js +232 -0
  87. package/dist/components/composition-selector-drag-data.d.ts +27 -0
  88. package/dist/components/composition-selector-drag-data.js +80 -0
  89. package/dist/components/drop-handler-data.d.ts +2 -1
  90. package/dist/components/drop-handler-data.js +6 -4
  91. package/dist/components/effect-drag-and-drop.d.ts +2 -5
  92. package/dist/components/handle-drop.js +6 -2
  93. package/dist/components/import-assets.d.ts +7 -14
  94. package/dist/components/import-assets.js +11 -1
  95. package/dist/components/parse-caption-file.d.ts +5 -0
  96. package/dist/components/parse-caption-file.js +63 -0
  97. package/dist/error-overlay/current-error.d.ts +13 -0
  98. package/dist/error-overlay/current-error.js +21 -0
  99. package/dist/error-overlay/remotion-overlay/ErrorLoader.js +5 -1
  100. package/dist/esm/chunk-emw4k5b0.js +99527 -0
  101. package/dist/esm/{chunk-1fkq5bfq.js → chunk-vkrvnx2h.js} +20116 -16283
  102. package/dist/esm/index.mjs +27 -9
  103. package/dist/esm/internals.mjs +17408 -13426
  104. package/dist/esm/previewEntry.mjs +17285 -13216
  105. package/dist/esm/renderEntry.mjs +1 -1
  106. package/dist/helpers/create-folder-tree.js +2 -5
  107. package/dist/helpers/document-title.d.ts +2 -0
  108. package/dist/helpers/document-title.js +14 -3
  109. package/dist/helpers/get-preview-file-type.js +22 -3
  110. package/dist/helpers/get-timeline-max-zoom.d.ts +34 -28
  111. package/dist/helpers/get-timeline-max-zoom.js +46 -29
  112. package/dist/helpers/inserted-element-selection.d.ts +2 -1
  113. package/dist/helpers/install-fiber-sequence-order-observer.d.ts +37 -0
  114. package/dist/helpers/install-fiber-sequence-order-observer.js +109 -0
  115. package/dist/helpers/open-in-editor.js +2 -1
  116. package/dist/helpers/use-menu-structure.js +2 -2
  117. package/dist/helpers/use-runtime-values.d.ts +1 -1
  118. package/dist/helpers/use-runtime-values.js +5 -4
  119. package/dist/helpers/use-sync-external-store.d.ts +3 -0
  120. package/dist/helpers/use-sync-external-store.js +5 -0
  121. package/dist/index.d.ts +1 -0
  122. package/dist/index.js +3 -1
  123. package/dist/previewEntry.js +2 -0
  124. package/dist/state/modals.d.ts +16 -2
  125. package/dist/state/timeline-sequence-hover.js +3 -2
  126. package/dist/state/timeline-zoom.d.ts +0 -1
  127. package/dist/state/timeline-zoom.js +13 -6
  128. package/package.json +17 -17
  129. package/dist/helpers/sort-by-nonce-history.d.ts +0 -1
  130. package/dist/helpers/sort-by-nonce-history.js +0 -6
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @description Shuts down the Remotion Studio.
3
+ * @see [Documentation](https://www.remotion.dev/docs/studio/shut-down-studio)
4
+ */
5
+ import type { ShutdownStudioResponse } from '@remotion/studio-shared';
6
+ export declare const shutDownStudio: () => Promise<ShutdownStudioResponse>;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ /**
3
+ * @description Shuts down the Remotion Studio.
4
+ * @see [Documentation](https://www.remotion.dev/docs/studio/shut-down-studio)
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.shutDownStudio = void 0;
8
+ const remotion_1 = require("remotion");
9
+ const call_api_1 = require("../components/call-api");
10
+ const browser_studio_operations_1 = require("../helpers/browser-studio-operations");
11
+ const shutDownStudio = () => {
12
+ if (!(0, remotion_1.getRemotionEnvironment)().isStudio) {
13
+ throw new Error('shutDownStudio() is only available in the Studio');
14
+ }
15
+ if ((0, browser_studio_operations_1.getBrowserStudioOperations)() !== null) {
16
+ throw new Error('shutDownStudio() is not supported in Browser Studio');
17
+ }
18
+ if (window.remotion_isReadOnlyStudio) {
19
+ throw new Error('shutDownStudio() is not available in read-only Studio');
20
+ }
21
+ return (0, call_api_1.callApi)('/api/shutdown-studio', {});
22
+ };
23
+ exports.shutDownStudio = shutDownStudio;
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.visualControl = void 0;
4
- const react_1 = require("react");
5
4
  const remotion_1 = require("remotion");
5
+ const use_sync_external_store_1 = require("../helpers/use-sync-external-store");
6
6
  const visual_control_store_1 = require("../visual-controls/visual-control-store");
7
7
  const VisualControls_1 = require("../visual-controls/VisualControls");
8
8
  /**
@@ -12,7 +12,7 @@ const visualControl = (key, value, schema) => {
12
12
  // Subscribe to store changes so the calling component
13
13
  // re-renders when a visual control value is edited in the sidebar.
14
14
  // eslint-disable-next-line react-hooks/rules-of-hooks
15
- (0, react_1.useSyncExternalStore)(visual_control_store_1.visualControlStore.subscribe, visual_control_store_1.visualControlStore.getSnapshot, visual_control_store_1.visualControlStore.getSnapshot);
15
+ (0, use_sync_external_store_1.useSyncExternalStore)(visual_control_store_1.visualControlStore.subscribe, visual_control_store_1.visualControlStore.getSnapshot, visual_control_store_1.visualControlStore.getSnapshot);
16
16
  if ((0, remotion_1.getRemotionEnvironment)().isRendering) {
17
17
  return value;
18
18
  }
@@ -39,6 +39,7 @@ const timeline_utils_1 = require("@remotion/timeline-utils");
39
39
  const react_1 = __importStar(require("react"));
40
40
  const remotion_1 = require("remotion");
41
41
  const colors_1 = require("../helpers/colors");
42
+ const get_timeline_max_zoom_1 = require("../helpers/get-timeline-max-zoom");
42
43
  const timeline_layout_1 = require("../helpers/timeline-layout");
43
44
  const EMPTY_PEAKS = new Float32Array(0);
44
45
  const getContainerStyle = (height) => {
@@ -49,17 +50,15 @@ const getContainerStyle = (height) => {
49
50
  position: 'relative',
50
51
  width: '100%',
51
52
  height,
53
+ overflow: 'hidden',
52
54
  };
53
55
  };
54
56
  const waveformCanvasStyle = {
55
57
  pointerEvents: 'none',
56
- width: '100%',
57
- height: '100%',
58
+ flexShrink: 0,
58
59
  };
59
60
  const volumeCanvasStyle = {
60
61
  position: 'absolute',
61
- width: '100%',
62
- height: '100%',
63
62
  };
64
63
  const parseVolume = (volume) => {
65
64
  if (typeof volume === 'number') {
@@ -74,6 +73,7 @@ const AudioWaveformInner = ({ src, height, startFrom, durationInFrames, displayO
74
73
  if (vidConf === null) {
75
74
  throw new Error('Expected video config');
76
75
  }
76
+ const waveformSampleRate = Math.ceil(vidConf.fps * get_timeline_max_zoom_1.TIMELINE_FRAME_WIDTH_AT_MAX_ZOOM);
77
77
  const waveformCanvas = (0, react_1.useRef)(null);
78
78
  const volumeCanvas = (0, react_1.useRef)(null);
79
79
  const shouldRenderVolumeOverlay = doesVolumeChange && typeof volume === 'string';
@@ -102,10 +102,11 @@ const AudioWaveformInner = ({ src, height, startFrom, durationInFrames, displayO
102
102
  setError(null);
103
103
  return (0, timeline_utils_1.subscribeToWaveformPeaks)({
104
104
  src,
105
+ waveformSampleRate,
105
106
  onPeaks: (p) => setPeaks(p),
106
107
  onError: (err) => setError(err),
107
108
  });
108
- }, [src]);
109
+ }, [src, waveformSampleRate]);
109
110
  const portionPeaks = (0, react_1.useMemo)(() => {
110
111
  if (!peaks) {
111
112
  return null;
@@ -119,6 +120,7 @@ const AudioWaveformInner = ({ src, height, startFrom, durationInFrames, displayO
119
120
  peaks,
120
121
  playbackRate,
121
122
  startFrom,
123
+ waveformSampleRate,
122
124
  });
123
125
  }, [
124
126
  displayDurationInFrames,
@@ -129,6 +131,7 @@ const AudioWaveformInner = ({ src, height, startFrom, durationInFrames, displayO
129
131
  playbackRate,
130
132
  startFrom,
131
133
  vidConf.fps,
134
+ waveformSampleRate,
132
135
  ]);
133
136
  // Drawing must happen in a layout effect: when the timeline zooms, the
134
137
  // canvas CSS box resizes in the same commit, and painting the bitmap
@@ -141,14 +144,17 @@ const AudioWaveformInner = ({ src, height, startFrom, durationInFrames, displayO
141
144
  const pixelRatio = window.devicePixelRatio;
142
145
  const h = Math.ceil(height * pixelRatio);
143
146
  const w = Math.ceil(visualizationWidth * pixelRatio);
147
+ const drawingWidth = visualizationWidth * pixelRatio;
144
148
  canvasElement.width = w;
145
149
  canvasElement.height = h;
150
+ canvasElement.style.width = w / pixelRatio + 'px';
151
+ canvasElement.style.height = h / pixelRatio + 'px';
146
152
  (0, timeline_utils_1.drawBars)({
147
153
  canvas: canvasElement,
148
154
  peaks: portionPeaks !== null && portionPeaks !== void 0 ? portionPeaks : EMPTY_PEAKS,
149
155
  color: colors_1.WHITE_ALPHA_60,
150
156
  volume: visibleVolume,
151
- width: w,
157
+ width: drawingWidth,
152
158
  });
153
159
  }, [height, portionPeaks, visibleVolume, visualizationWidth]);
154
160
  (0, react_1.useLayoutEffect)(() => {
@@ -162,12 +168,15 @@ const AudioWaveformInner = ({ src, height, startFrom, durationInFrames, displayO
162
168
  const pixelRatio = window.devicePixelRatio;
163
169
  const h = Math.ceil(height * pixelRatio);
164
170
  const w = Math.ceil(visualizationWidth * pixelRatio);
171
+ const drawingWidth = visualizationWidth * pixelRatio;
165
172
  const context = volumeCanvasElement.getContext('2d');
166
173
  if (!context) {
167
174
  return;
168
175
  }
169
176
  volumeCanvasElement.width = w;
170
177
  volumeCanvasElement.height = h;
178
+ volumeCanvasElement.style.width = w / pixelRatio + 'px';
179
+ volumeCanvasElement.style.height = h / pixelRatio + 'px';
171
180
  context.clearRect(0, 0, w, h);
172
181
  if (!Array.isArray(visibleVolume)) {
173
182
  return;
@@ -177,7 +186,7 @@ const AudioWaveformInner = ({ src, height, startFrom, durationInFrames, displayO
177
186
  visibleVolume.forEach((v, index) => {
178
187
  const x = visibleVolume.length <= 1
179
188
  ? 0
180
- : (index / (visibleVolume.length - 1)) * w;
189
+ : (index / (visibleVolume.length - 1)) * drawingWidth;
181
190
  const y = (1 - v) * (h - timeline_layout_1.TIMELINE_BORDER * 2 * pixelRatio) + pixelRatio;
182
191
  if (index === 0) {
183
192
  context.moveTo(x, y);
@@ -29,6 +29,7 @@ const editor_zoom_gestures_1 = require("../state/editor-zoom-gestures");
29
29
  const modals_1 = require("../state/modals");
30
30
  const call_api_1 = require("./call-api");
31
31
  const canvas_capture_drop_1 = require("./canvas-capture-drop");
32
+ const composition_drag_data_1 = require("./composition-drag-data");
32
33
  const composition_drop_preview_1 = require("./composition-drop-preview");
33
34
  const drop_handler_data_1 = require("./drop-handler-data");
34
35
  const EditorGuides_1 = __importDefault(require("./EditorGuides"));
@@ -37,7 +38,6 @@ const use_is_ruler_visible_1 = require("./EditorRuler/use-is-ruler-visible");
37
38
  const effect_drag_and_drop_1 = require("./effect-drag-and-drop");
38
39
  const element_install_api_1 = require("./element-install-api");
39
40
  const element_install_request_1 = require("./element-install-request");
40
- const ElementInstallConfirmation_1 = require("./ElementInstallConfirmation");
41
41
  const handle_drop_1 = require("./handle-drop");
42
42
  const import_assets_1 = require("./import-assets");
43
43
  const layout_1 = require("./layout");
@@ -47,7 +47,19 @@ const ResetZoomButton_1 = require("./ResetZoomButton");
47
47
  const SvgImportDialog_1 = require("./SvgImportDialog");
48
48
  const imperative_state_1 = require("./Timeline/imperative-state");
49
49
  const use_resolved_stack_1 = require("./Timeline/use-resolved-stack");
50
- const elementInstallDependencyIgnoreList = ['react', 'react-dom', 'remotion'];
50
+ const getCanvasDragPreviewMetadata = (mimeTypes) => {
51
+ var _a, _b, _c;
52
+ const composition = (0, composition_drag_data_1.getCompositionDragPreviewMetadata)(mimeTypes);
53
+ if (composition !== null) {
54
+ return {
55
+ ...composition,
56
+ width: (_a = composition.width) !== null && _a !== void 0 ? _a : undefined,
57
+ height: (_b = composition.height) !== null && _b !== void 0 ? _b : undefined,
58
+ durationInFrames: (_c = composition.durationInFrames) !== null && _c !== void 0 ? _c : undefined,
59
+ };
60
+ }
61
+ return studio_protocol_1.StudioProtocolInternals.getDragPreviewMetadata(mimeTypes);
62
+ };
51
63
  const getContainerStyle = (editorZoomGestures) => ({
52
64
  flex: 1,
53
65
  display: 'flex',
@@ -140,10 +152,8 @@ const Canvas = ({ canvasContent, size }) => {
140
152
  : null;
141
153
  const [isAddingAsset, setIsAddingAsset] = (0, react_1.useState)(false);
142
154
  const [compositionDropPreview, setCompositionDropPreview] = (0, react_1.useState)(null);
143
- const [installingElementName, setInstallingElementName] = (0, react_1.useState)(null);
144
155
  const [pendingElementInstallRequests, setPendingElementInstallRequests] = (0, react_1.useState)([]);
145
156
  const [activeElementInstallRequest, setActiveElementInstallRequest] = (0, react_1.useState)(null);
146
- const [elementInstallReview, setElementInstallReview] = (0, react_1.useState)(null);
147
157
  const lastFocusedAtRef = (0, react_1.useRef)(typeof document === 'undefined' || document.hasFocus() ? Date.now() : null);
148
158
  const [assetResolution, setAssetResolution] = (0, react_1.useState)(null);
149
159
  const metadataRequestRef = (0, react_1.useRef)(0);
@@ -601,16 +611,6 @@ const Canvas = ({ canvasContent, size }) => {
601
611
  updateElementInstallTarget(event.requestId);
602
612
  });
603
613
  }, [subscribeToEvent, updateElementInstallTarget]);
604
- (0, react_1.useEffect)(() => {
605
- if (installingElementName === null) {
606
- return;
607
- }
608
- const previousTitle = document.title;
609
- document.title = `📦 Install ${installingElementName} - Remotion Studio`;
610
- return () => {
611
- document.title = previousTitle;
612
- };
613
- }, [installingElementName]);
614
614
  (0, react_1.useEffect)(() => {
615
615
  if (previewServerClientId === null) {
616
616
  return;
@@ -623,24 +623,103 @@ const Canvas = ({ canvasContent, size }) => {
623
623
  (0, element_install_request_1.enqueueElementInstallRequest)(event.request);
624
624
  });
625
625
  }, [previewServerClientId, subscribeToEvent]);
626
+ (0, react_1.useEffect)(() => {
627
+ const onMessage = (event) => {
628
+ var _a, _b;
629
+ const elementLibrary = document.querySelector('iframe[data-remotion-element-library]');
630
+ if (event.source !== (elementLibrary === null || elementLibrary === void 0 ? void 0 : elementLibrary.contentWindow) ||
631
+ !studio_protocol_1.StudioProtocolInternals.isAllowedStudioProtocolPageOrigin(event.origin)) {
632
+ return;
633
+ }
634
+ const payload = studio_protocol_1.StudioProtocolInternals.parseStudioProtocolIframeInstallRequest(event.data);
635
+ const responsePort = event.ports[0];
636
+ if (payload === null || responsePort === undefined) {
637
+ return;
638
+ }
639
+ const canInstall = canReceiveElementInstallRequest &&
640
+ compositionFile !== null &&
641
+ currentCompositionId !== null &&
642
+ previewServerClientId !== null;
643
+ const request = canInstall
644
+ ? {
645
+ clientId: previewServerClientId,
646
+ compositionFile,
647
+ compositionId: currentCompositionId,
648
+ createdAt: Date.now(),
649
+ element: {
650
+ ...payload.element,
651
+ durationInFrames: (_a = payload.element.durationInFrames) !== null && _a !== void 0 ? _a : null,
652
+ installationMode: (_b = payload.element.installationMode) !== null && _b !== void 0 ? _b : null,
653
+ },
654
+ from: null,
655
+ id: crypto.randomUUID(),
656
+ position: null,
657
+ source: { origin: event.origin, type: 'studio-protocol' },
658
+ }
659
+ : null;
660
+ const result = canInstall
661
+ ? {
662
+ success: true,
663
+ status: 'awaiting-confirmation',
664
+ target: {
665
+ compositionId: currentCompositionId,
666
+ projectName: window.remotion_projectName,
667
+ studioOrigin: window.location.origin,
668
+ studioVersion: window.remotion_version,
669
+ },
670
+ }
671
+ : {
672
+ success: false,
673
+ code: 'no-installable-target',
674
+ message: 'Focus a composition in a Remotion Studio that is not read-only, then try again.',
675
+ };
676
+ const timeout = window.setTimeout(() => responsePort.close(), 1000);
677
+ responsePort.onmessage = () => {
678
+ window.clearTimeout(timeout);
679
+ responsePort.close();
680
+ if (request !== null) {
681
+ (0, element_install_request_1.enqueueElementInstallRequest)(request);
682
+ }
683
+ };
684
+ responsePort.postMessage(result);
685
+ };
686
+ window.addEventListener('message', onMessage);
687
+ return () => window.removeEventListener('message', onMessage);
688
+ }, [
689
+ canReceiveElementInstallRequest,
690
+ compositionFile,
691
+ currentCompositionId,
692
+ previewServerClientId,
693
+ ]);
626
694
  (0, react_1.useEffect)(() => {
627
695
  return (0, element_install_request_1.subscribeToElementInstallRequests)((request) => {
628
- const requestWithFrom = request.source.type === 'drag-and-drop' || request.from !== null
696
+ var _a, _b;
697
+ const isDragAndDrop = request.source.type === 'drag-and-drop';
698
+ const requestWithDefaults = isDragAndDrop
629
699
  ? request
630
700
  : {
631
701
  ...request,
632
- from: (0, import_assets_1.getFromForDrop)({
702
+ from: (_a = request.from) !== null && _a !== void 0 ? _a : (0, import_assets_1.getFromForDrop)({
633
703
  durationInFrames: request.element.durationInFrames,
634
704
  from: (0, imperative_state_1.getCurrentFrame)(),
635
705
  preferCompositionStart: true,
636
706
  }),
707
+ position: (_b = request.position) !== null && _b !== void 0 ? _b : (0, import_assets_1.getElementPositionForDrop)({
708
+ dimensions: request.element.dimensions,
709
+ dropPosition: contentDimensions === null || contentDimensions === 'none'
710
+ ? null
711
+ : {
712
+ centerX: contentDimensions.width / 2,
713
+ centerY: contentDimensions.height / 2,
714
+ },
715
+ }),
637
716
  };
638
717
  setPendingElementInstallRequests((requests) => [
639
718
  ...requests,
640
- requestWithFrom,
719
+ requestWithDefaults,
641
720
  ]);
642
721
  });
643
- }, []);
722
+ }, [contentDimensions]);
644
723
  (0, react_1.useEffect)(() => {
645
724
  var _a;
646
725
  var _b;
@@ -680,6 +759,10 @@ const Canvas = ({ canvasContent, size }) => {
680
759
  setActiveElementInstallRequest(nextRequest);
681
760
  setPendingElementInstallRequests(remainingRequests);
682
761
  }, [activeElementInstallRequest, pendingElementInstallRequests]);
762
+ const closeElementInstallDialog = (0, react_1.useCallback)(() => {
763
+ setSelectedModal(null);
764
+ setActiveElementInstallRequest(null);
765
+ }, [setSelectedModal]);
683
766
  (0, react_1.useEffect)(() => {
684
767
  if (activeElementInstallRequest === null) {
685
768
  return;
@@ -687,7 +770,6 @@ const Canvas = ({ canvasContent, size }) => {
687
770
  let canceled = false;
688
771
  const handleInstallRequest = async () => {
689
772
  var _a;
690
- setInstallingElementName(activeElementInstallRequest.element.displayName);
691
773
  try {
692
774
  const [newPreflight, currentPreflight] = await Promise.all([
693
775
  (0, element_install_api_1.prepareElementInstall)({
@@ -711,17 +793,11 @@ const Canvas = ({ canvasContent, size }) => {
711
793
  }
712
794
  if (!newPreflight.success) {
713
795
  (0, NotificationCenter_1.showNotification)(`Could not review Element installation: ${newPreflight.reason}`, 4000);
714
- setInstallingElementName(null);
715
796
  setActiveElementInstallRequest(null);
716
797
  return;
717
798
  }
718
799
  const declaredDependencies = Array.from(new Map(activeElementInstallRequest.element.dependencies.map((dependency) => [dependency.name, dependency])).values());
719
- const missingPackages = (0, install_required_package_1.getMissingPackages)(declaredDependencies).map((dependency) => dependency.name);
720
- const ignoredDependencies = declaredDependencies.filter((dependency) => elementInstallDependencyIgnoreList.includes(dependency.name) &&
721
- !missingPackages.includes(dependency.name));
722
- const dependenciesToReview = declaredDependencies
723
- .filter((dependency) => !ignoredDependencies.includes(dependency))
724
- .map((dependency) => dependency.version === null
800
+ const missingPackages = (0, install_required_package_1.getMissingPackages)(declaredDependencies).map((dependency) => dependency.version === null
725
801
  ? dependency.name
726
802
  : `${dependency.name}@${dependency.version}`);
727
803
  const { source } = activeElementInstallRequest;
@@ -735,12 +811,13 @@ const Canvas = ({ canvasContent, size }) => {
735
811
  const currentPlan = currentPreflight.success
736
812
  ? currentPreflight.plan
737
813
  : null;
738
- setSelectedModal(null);
739
- setElementInstallReview({
814
+ setSelectedModal({
815
+ type: 'element-install',
740
816
  currentPlan,
741
- dependenciesToReview,
742
817
  missingPackages,
743
818
  newPlan: newPreflight.plan,
819
+ onClose: closeElementInstallDialog,
820
+ request: activeElementInstallRequest,
744
821
  sourceIsUnverified,
745
822
  sourceLabel,
746
823
  });
@@ -750,7 +827,6 @@ const Canvas = ({ canvasContent, size }) => {
750
827
  return;
751
828
  }
752
829
  (0, NotificationCenter_1.showNotification)(`Could not review Element installation: ${error instanceof Error ? error.message : String(error)}`, 4000);
753
- setInstallingElementName(null);
754
830
  setActiveElementInstallRequest(null);
755
831
  }
756
832
  };
@@ -758,12 +834,11 @@ const Canvas = ({ canvasContent, size }) => {
758
834
  return () => {
759
835
  canceled = true;
760
836
  };
761
- }, [activeElementInstallRequest, setSelectedModal]);
762
- const closeElementInstallDialog = (0, react_1.useCallback)(() => {
763
- setElementInstallReview(null);
764
- setInstallingElementName(null);
765
- setActiveElementInstallRequest(null);
766
- }, []);
837
+ }, [
838
+ activeElementInstallRequest,
839
+ closeElementInstallDialog,
840
+ setSelectedModal,
841
+ ]);
767
842
  const onDragOver = (0, react_1.useCallback)((event) => {
768
843
  var _a;
769
844
  var _b;
@@ -787,7 +862,7 @@ const Canvas = ({ canvasContent, size }) => {
787
862
  setCompositionDropPreview(null);
788
863
  return;
789
864
  }
790
- const metadata = studio_protocol_1.StudioProtocolInternals.getDragPreviewMetadata((_b = (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.types) !== null && _b !== void 0 ? _b : []);
865
+ const metadata = getCanvasDragPreviewMetadata((_b = (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.types) !== null && _b !== void 0 ? _b : []);
791
866
  if (((metadata === null || metadata === void 0 ? void 0 : metadata.type) !== 'composition' && (metadata === null || metadata === void 0 ? void 0 : metadata.type) !== 'element') ||
792
867
  metadata.width === undefined ||
793
868
  metadata.height === undefined) {
@@ -918,7 +993,7 @@ const Canvas = ({ canvasContent, size }) => {
918
993
  event.stopPropagation();
919
994
  setIsAddingAsset(true);
920
995
  try {
921
- const metadata = studio_protocol_1.StudioProtocolInternals.getDragPreviewMetadata((_d = (_b = event.dataTransfer) === null || _b === void 0 ? void 0 : _b.types) !== null && _d !== void 0 ? _d : []);
996
+ const metadata = getCanvasDragPreviewMetadata((_d = (_b = event.dataTransfer) === null || _b === void 0 ? void 0 : _b.types) !== null && _d !== void 0 ? _d : []);
922
997
  const isComposition = (metadata === null || metadata === void 0 ? void 0 : metadata.type) === 'composition';
923
998
  let dropPosition = getDropPosition({
924
999
  addFitPadding,
@@ -1122,15 +1197,6 @@ const Canvas = ({ canvasContent, size }) => {
1122
1197
  };
1123
1198
  }, [compositionDropPreview, contentDimensions, previewSize, size]);
1124
1199
  return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
1125
- jsx_runtime_1.jsxs("div", { ref: canvas_ref_1.canvasRef, style: getContainerStyle(editorZoomGestures), children: [size ? (jsx_runtime_1.jsx(Preview_1.VideoPreview, { canvasContent: canvasContent, contentDimensions: contentDimensions, canvasSize: size, assetMetadata: assetResolution, onRetryAssetMetadata: fetchMetadata })) : null, compositionDropPreviewStyle === null ? null : (jsx_runtime_1.jsx("div", { className: "css-reset", "data-testid": "composition-drop-preview", style: compositionDropPreviewStyle })), isFit ? null : (jsx_runtime_1.jsx("div", { style: resetZoom, className: "css-reset", children: jsx_runtime_1.jsx(ResetZoomButton_1.ResetZoomButton, { onClick: onReset }) })), editorShowGuides && canvasContent.type === 'composition' && (jsx_runtime_1.jsx(EditorGuides_1.default, { canvasSize: size, contentDimensions: contentDimensions, assetMetadata: assetResolution }))] }), areRulersVisible && (jsx_runtime_1.jsx(EditorRuler_1.EditorRulers, { canCreateGuides: canvasContent.type === 'composition', contentDimensions: contentDimensions, canvasSize: size, assetMetadata: assetResolution, containerRef: canvas_ref_1.canvasRef })), activeElementInstallRequest === null ||
1126
- elementInstallReview === null ? null : (jsx_runtime_1.jsx(ElementInstallConfirmation_1.ElementInstallConfirmation, { currentCompositionMetadata: config === null ||
1127
- currentCompositionId !== activeElementInstallRequest.compositionId
1128
- ? null
1129
- : {
1130
- durationInFrames: config.durationInFrames,
1131
- fps: config.fps,
1132
- height: config.height,
1133
- width: config.width,
1134
- }, currentPlan: elementInstallReview.currentPlan, dependenciesToReview: elementInstallReview.dependenciesToReview, missingPackages: elementInstallReview.missingPackages, newPlan: elementInstallReview.newPlan, onClose: closeElementInstallDialog, request: activeElementInstallRequest, sourceIsUnverified: elementInstallReview.sourceIsUnverified, sourceLabel: elementInstallReview.sourceLabel, usesBrowserDependencyResolution: (0, browser_studio_operations_1.getBrowserStudioOperations)() !== null }))] }));
1200
+ jsx_runtime_1.jsxs("div", { ref: canvas_ref_1.canvasRef, style: getContainerStyle(editorZoomGestures), children: [size ? (jsx_runtime_1.jsx(Preview_1.VideoPreview, { canvasContent: canvasContent, contentDimensions: contentDimensions, canvasSize: size, assetMetadata: assetResolution, onRetryAssetMetadata: fetchMetadata })) : null, compositionDropPreviewStyle === null ? null : (jsx_runtime_1.jsx("div", { className: "css-reset", "data-testid": "composition-drop-preview", style: compositionDropPreviewStyle })), isFit ? null : (jsx_runtime_1.jsx("div", { style: resetZoom, className: "css-reset", children: jsx_runtime_1.jsx(ResetZoomButton_1.ResetZoomButton, { onClick: onReset }) })), editorShowGuides && canvasContent.type === 'composition' && (jsx_runtime_1.jsx(EditorGuides_1.default, { canvasSize: size, contentDimensions: contentDimensions, assetMetadata: assetResolution }))] }), areRulersVisible && (jsx_runtime_1.jsx(EditorRuler_1.EditorRulers, { canCreateGuides: canvasContent.type === 'composition', contentDimensions: contentDimensions, canvasSize: size, assetMetadata: assetResolution, containerRef: canvas_ref_1.canvasRef }))] }));
1135
1201
  };
1136
1202
  exports.Canvas = Canvas;
@@ -6,6 +6,7 @@ export declare const CaptionInspector: React.FC<{
6
6
  readonly onTextChange: (captions: Caption[]) => void;
7
7
  readonly onTextSave: ((captions: Caption[]) => void) | null;
8
8
  readonly onTextCancel: (() => void) | null;
9
+ readonly onReplaceCaptions: ((captions: Caption[]) => void) | null;
9
10
  readonly onToggle: () => void;
10
11
  readonly readOnly: boolean;
11
12
  readonly readOnlyTitle: string | null;
@@ -2,11 +2,19 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CaptionInspector = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
5
6
  const colors_1 = require("../helpers/colors");
7
+ const upload_1 = require("../icons/upload");
8
+ const Button_1 = require("./Button");
6
9
  const CaptionTextEditor_1 = require("./CaptionTextEditor");
7
10
  const CollapsibleInspectorSectionHeader_1 = require("./InspectorPanel/CollapsibleInspectorSectionHeader");
8
11
  const common_1 = require("./InspectorPanel/common");
9
12
  const styles_1 = require("./InspectorPanel/styles");
13
+ const NotificationCenter_1 = require("./Notifications/NotificationCenter");
14
+ const parse_caption_file_1 = require("./parse-caption-file");
15
+ const importTooltip = `Import captions
16
+
17
+ Supports Remotion Caption[] JSON. Files are processed locally.`;
10
18
  const readOnlyStatus = {
11
19
  color: colors_1.LIGHT_TEXT,
12
20
  fontFamily: 'sans-serif',
@@ -15,8 +23,41 @@ const readOnlyStatus = {
15
23
  lineHeight: '16px',
16
24
  marginLeft: 12,
17
25
  };
18
- const CaptionInspector = ({ captions, expanded, onTextChange, onTextSave, onTextCancel, onToggle, readOnly, readOnlyTitle, }) => {
26
+ const CaptionInspector = ({ captions, expanded, onTextChange, onTextSave, onTextCancel, onReplaceCaptions, onToggle, readOnly, readOnlyTitle, }) => {
27
+ const fileInput = (0, react_1.useRef)(null);
28
+ const [isImporting, setIsImporting] = (0, react_1.useState)(false);
29
+ const importCaptions = (0, react_1.useCallback)(async (event) => {
30
+ var _a;
31
+ const file = (_a = event.currentTarget.files) === null || _a === void 0 ? void 0 : _a[0];
32
+ event.currentTarget.value = '';
33
+ if (!file || onReplaceCaptions === null) {
34
+ return;
35
+ }
36
+ setIsImporting(true);
37
+ try {
38
+ onReplaceCaptions((0, parse_caption_file_1.parseCaptionFile)({
39
+ fileName: file.name,
40
+ contents: await file.text(),
41
+ }));
42
+ }
43
+ catch (error) {
44
+ (0, NotificationCenter_1.showNotification)(`Could not import ${file.name}: ${error instanceof Error ? error.message : String(error)}`, 5000);
45
+ }
46
+ finally {
47
+ setIsImporting(false);
48
+ }
49
+ }, [onReplaceCaptions]);
19
50
  return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
20
- jsx_runtime_1.jsx(common_1.InspectorSectionHeader, { children: jsx_runtime_1.jsx(CollapsibleInspectorSectionHeader_1.CollapsibleInspectorSectionHeader, { action: jsx_runtime_1.jsx("div", { style: styles_1.sectionHeaderEnd, children: readOnly ? (jsx_runtime_1.jsx("div", { style: readOnlyStatus, title: readOnlyTitle !== null && readOnlyTitle !== void 0 ? readOnlyTitle : undefined, children: "Read only" })) : null }), expanded: expanded, label: "Captions", onToggle: onToggle }) }), expanded ? (jsx_runtime_1.jsx(CaptionTextEditor_1.CaptionTextEditor, { captions: captions, onChange: onTextChange, onSave: onTextSave, onCancel: onTextCancel, readOnly: readOnly })) : null] }));
51
+ jsx_runtime_1.jsx(common_1.InspectorSectionHeader, { children: jsx_runtime_1.jsx(CollapsibleInspectorSectionHeader_1.CollapsibleInspectorSectionHeader, { action: jsx_runtime_1.jsxs("div", { style: styles_1.sectionHeaderEnd, children: [onReplaceCaptions === null ? null : (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
52
+ jsx_runtime_1.jsx("input", { ref: fileInput, accept: ".json", "aria-label": "Import captions file", hidden: true, onChange: importCaptions, type: "file" }), jsx_runtime_1.jsxs(Button_1.Button, { buttonContainerStyle: {
53
+ alignItems: 'center',
54
+ display: 'flex',
55
+ gap: 4,
56
+ }, disabled: isImporting, onClick: () => {
57
+ var _a;
58
+ return (_a = fileInput.current) === null || _a === void 0 ? void 0 : _a.click();
59
+ }, size: "condensed", title: importTooltip, children: [
60
+ jsx_runtime_1.jsx(upload_1.UploadIcon, { "aria-hidden": "true", color: colors_1.CURRENT_COLOR, focusable: "false", style: { height: 12, width: 12 } }), isImporting ? 'Importing…' : 'Import'] })
61
+ ] })), readOnly ? (jsx_runtime_1.jsx("div", { style: readOnlyStatus, title: readOnlyTitle !== null && readOnlyTitle !== void 0 ? readOnlyTitle : undefined, children: "Read only" })) : null] }), expanded: expanded, label: "Captions", onToggle: onToggle }) }), expanded ? (jsx_runtime_1.jsx(CaptionTextEditor_1.CaptionTextEditor, { captions: captions, onChange: onTextChange, onSave: onTextSave, onCancel: onTextCancel, readOnly: readOnly })) : null] }));
21
62
  };
22
63
  exports.CaptionInspector = CaptionInspector;
@@ -119,7 +119,7 @@ const CaptionTextEditor = ({ captions, onChange, onSave, onCancel, readOnly }) =
119
119
  ? `Remove page break after caption ${index + 1}`
120
120
  : `Add page break after caption ${index + 1}`;
121
121
  return (jsx_runtime_1.jsxs("div", { style: row, children: [
122
- jsx_runtime_1.jsxs("div", { style: timing, children: [formatMilliseconds(caption.startMs), " \u2192", ' ', formatMilliseconds(caption.endMs), " ms"] }), jsx_runtime_1.jsx(RemInput_1.RemotionInput, { "data-caption-index": index, disabled: readOnly, onBlur: (event) => {
122
+ jsx_runtime_1.jsxs("div", { style: timing, children: [formatMilliseconds(caption.startMs), " \u2192", ' ', formatMilliseconds(caption.endMs), " ms"] }), jsx_runtime_1.jsx(RemInput_1.RemotionInput, { "aria-label": `Caption ${index + 1}`, "data-caption-index": index, disabled: readOnly, onBlur: (event) => {
123
123
  if (cancelledBlurIndexes.current.delete(index)) {
124
124
  return;
125
125
  }