@remotion/studio 4.0.517 → 4.0.518

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 (52) hide show
  1. package/dist/components/AssetSelector.js +62 -21
  2. package/dist/components/AssetSelectorItem.d.ts +2 -1
  3. package/dist/components/AssetSelectorItem.js +41 -10
  4. package/dist/components/CaptionTextEditor.js +32 -8
  5. package/dist/components/CompositionContextButton.js +2 -1
  6. package/dist/components/CompositionSelector.js +1 -1
  7. package/dist/components/EditorContent.js +6 -3
  8. package/dist/components/ElementInstallConfirmation.d.ts +5 -0
  9. package/dist/components/ElementInstallConfirmation.js +25 -1
  10. package/dist/components/ElementLibraryModal.js +3 -1
  11. package/dist/components/InlineCaptionInspector.js +13 -2
  12. package/dist/components/Modals.js +49 -0
  13. package/dist/components/PlaybackRateSelector.js +2 -5
  14. package/dist/components/QuickSwitcher/ExplorerQuickSwitcherTrigger.d.ts +2 -0
  15. package/dist/components/QuickSwitcher/ExplorerQuickSwitcherTrigger.js +28 -8
  16. package/dist/components/SelectedOutlineKeyboardControls.d.ts +2 -2
  17. package/dist/components/SelectedOutlineOverlay.d.ts +1 -3
  18. package/dist/components/SelectedOutlineOverlay.js +21 -17
  19. package/dist/components/SelectedOutlineRenderer.d.ts +1 -1
  20. package/dist/components/SelectedOutlineRenderer.js +9 -78
  21. package/dist/components/SidebarRenderButton.js +2 -1
  22. package/dist/components/WebMcp.d.ts +2 -0
  23. package/dist/components/WebMcp.js +945 -0
  24. package/dist/components/composition-menu-items.js +26 -0
  25. package/dist/components/selected-outline-geometry.d.ts +2 -21
  26. package/dist/components/selected-outline-geometry.js +5 -16
  27. package/dist/components/selected-outline-measurement.d.ts +1 -29
  28. package/dist/components/selected-outline-measurement.js +1 -231
  29. package/dist/components/selected-outline-types.d.ts +2 -15
  30. package/dist/components/selected-outline-uv.js +3 -106
  31. package/dist/esm/{chunk-gzqm3g2m.js → chunk-7prjt5n5.js} +4782 -5611
  32. package/dist/esm/internals.mjs +4782 -5611
  33. package/dist/esm/previewEntry.mjs +4790 -5619
  34. package/dist/esm/renderEntry.mjs +1 -1
  35. package/dist/helpers/create-folder-tree.js +16 -1
  36. package/dist/helpers/format-file-location.d.ts +8 -0
  37. package/dist/helpers/format-file-location.js +14 -3
  38. package/dist/helpers/get-timeline-max-zoom.d.ts +8 -0
  39. package/dist/helpers/get-timeline-max-zoom.js +17 -6
  40. package/dist/helpers/hoverable.d.ts +1 -0
  41. package/dist/helpers/hoverable.js +4 -1
  42. package/dist/icons/enter.d.ts +4 -0
  43. package/dist/icons/enter.js +10 -0
  44. package/dist/state/playbackrate.d.ts +1 -0
  45. package/dist/state/playbackrate.js +4 -1
  46. package/package.json +14 -14
  47. package/dist/components/selected-outline-order.d.ts +0 -12
  48. package/dist/components/selected-outline-order.js +0 -251
  49. package/dist/helpers/get-box-quads-polyfill-internals.d.ts +0 -82
  50. package/dist/helpers/get-box-quads-polyfill-internals.js +0 -2403
  51. package/dist/helpers/get-box-quads-ponyfill.d.ts +0 -10
  52. package/dist/helpers/get-box-quads-ponyfill.js +0 -23
@@ -48,6 +48,7 @@ const use_asset_drag_events_1 = __importStar(require("../helpers/use-asset-drag-
48
48
  const folders_1 = require("../state/folders");
49
49
  const z_index_1 = require("../state/z-index");
50
50
  const AssetSelectorItem_1 = require("./AssetSelectorItem");
51
+ const import_assets_1 = require("./import-assets");
51
52
  const styles_1 = require("./Menu/styles");
52
53
  const NotificationCenter_1 = require("./Notifications/NotificationCenter");
53
54
  const ExplorerQuickSwitcherTrigger_1 = require("./QuickSwitcher/ExplorerQuickSwitcherTrigger");
@@ -97,6 +98,28 @@ const AssetSelector = ({ readOnlyStudio }) => {
97
98
  const assetTree = (0, react_1.useMemo)(() => {
98
99
  return (0, create_folder_tree_1.buildAssetFolderStructure)(staticFiles, null, assetFoldersExpanded);
99
100
  }, [assetFoldersExpanded, staticFiles]);
101
+ const writeFilesToPublicFolder = (0, react_1.useCallback)(async ({ files, assetPath, }) => {
102
+ const makePath = (file) => {
103
+ return [assetPath, file.name].filter(Boolean).join('/');
104
+ };
105
+ const differentExistingFile = files.find((file) => {
106
+ const filePath = makePath(file);
107
+ return staticFiles.some((staticFile) => staticFile.name === filePath &&
108
+ staticFile.sizeInBytes !== file.size);
109
+ });
110
+ if (differentExistingFile) {
111
+ (0, NotificationCenter_1.showNotification)(`File with name ${makePath(differentExistingFile)} already exists and is different`, 4000);
112
+ return false;
113
+ }
114
+ for (const file of files) {
115
+ const body = await file.arrayBuffer();
116
+ await (0, write_static_file_1.writeStaticFile)({
117
+ contents: body,
118
+ filePath: makePath(file),
119
+ });
120
+ }
121
+ return true;
122
+ }, [staticFiles]);
100
123
  const toggleFolder = (0, react_1.useCallback)((folderName, parentName) => {
101
124
  setAssetFoldersExpanded((p) => {
102
125
  const key = [parentName, folderName].filter(Boolean).join('/');
@@ -145,25 +168,7 @@ const AssetSelector = ({ readOnlyStudio }) => {
145
168
  setDropLocation(null);
146
169
  return;
147
170
  }
148
- const makePath = (file) => {
149
- return [assetPath, file.name].filter(Boolean).join('/');
150
- };
151
- const differentExistingFile = Array.from(files).find((file) => {
152
- const filePath = makePath(file);
153
- return staticFiles.some((staticFile) => staticFile.name === filePath &&
154
- staticFile.sizeInBytes !== file.size);
155
- });
156
- if (differentExistingFile) {
157
- (0, NotificationCenter_1.showNotification)(`File with name ${makePath(differentExistingFile)} already exists and is different`, 4000);
158
- return;
159
- }
160
- for (const file of files) {
161
- const body = await file.arrayBuffer();
162
- await (0, write_static_file_1.writeStaticFile)({
163
- contents: body,
164
- filePath: makePath(file),
165
- });
166
- }
171
+ await writeFilesToPublicFolder({ files, assetPath });
167
172
  }
168
173
  catch (error) {
169
174
  (0, NotificationCenter_1.showNotification)(`Error during upload: ${error}`, 3000);
@@ -171,9 +176,45 @@ const AssetSelector = ({ readOnlyStudio }) => {
171
176
  finally {
172
177
  setDropLocation(null);
173
178
  }
174
- }, [dropLocation, staticFiles]);
179
+ }, [dropLocation, writeFilesToPublicFolder]);
180
+ const uploadAssets = (0, react_1.useCallback)(async () => {
181
+ try {
182
+ const files = await (0, import_assets_1.pickFilesToImport)();
183
+ if (files.length === 0) {
184
+ return;
185
+ }
186
+ const wereWritten = await writeFilesToPublicFolder({
187
+ files,
188
+ assetPath: null,
189
+ });
190
+ if (wereWritten) {
191
+ (0, NotificationCenter_1.showNotification)(files.length === 1
192
+ ? `Uploaded ${files[0].name} to public folder`
193
+ : `Uploaded ${files.length} assets to public folder`, 3000);
194
+ }
195
+ }
196
+ catch (error) {
197
+ (0, NotificationCenter_1.showNotification)(`Error during upload: ${error}`, 3000);
198
+ }
199
+ }, [writeFilesToPublicFolder]);
200
+ const getAssetActions = (0, react_1.useCallback)(() => {
201
+ return [
202
+ {
203
+ id: 'upload-assets',
204
+ keyHint: null,
205
+ label: 'Upload...',
206
+ leftItem: null,
207
+ onClick: uploadAssets,
208
+ quickSwitcherLabel: 'Upload assets...',
209
+ subMenu: null,
210
+ type: 'item',
211
+ value: 'upload-assets',
212
+ disabled: !shouldAllowUpload,
213
+ },
214
+ ];
215
+ }, [shouldAllowUpload, uploadAssets]);
175
216
  return (jsx_runtime_1.jsxs("div", { "data-asset-selector": true, style: container, onDragOver: shouldAllowUpload ? onDragOver : undefined, onDrop: shouldAllowUpload ? onDrop : undefined, children: [
176
- jsx_runtime_1.jsx(ExplorerQuickSwitcherTrigger_1.ExplorerQuickSwitcherTrigger, { mode: "assets", showShortcut: true, tabIndex: tabIndex }), staticFiles.length === 0 ? (publicFolderExists ? (jsx_runtime_1.jsx("div", { style: emptyState, children: jsx_runtime_1.jsxs("div", { style: label, children: ["To add assets, place a file in the", ' ', jsx_runtime_1.jsx("code", { style: styles_1.inlineCodeSnippet, children: "public" }),
217
+ jsx_runtime_1.jsx(ExplorerQuickSwitcherTrigger_1.ExplorerQuickSwitcherTrigger, { mode: "assets", showShortcut: true, tabIndex: tabIndex, getActions: getAssetActions }), staticFiles.length === 0 ? (publicFolderExists ? (jsx_runtime_1.jsx("div", { style: emptyState, children: jsx_runtime_1.jsxs("div", { style: label, children: ["To add assets, place a file in the", ' ', jsx_runtime_1.jsx("code", { style: styles_1.inlineCodeSnippet, children: "public" }),
177
218
  " folder of your project or drag and drop a file here."] }) })) : (jsx_runtime_1.jsx("div", { style: emptyState, children: jsx_runtime_1.jsxs("div", { style: label, children: ["To add assets, create a folder called", ' ', jsx_runtime_1.jsx("code", { style: styles_1.inlineCodeSnippet, children: "public" }),
178
219
  " in the root of your project and place a file in it."] }) }))) : (jsx_runtime_1.jsx("div", { className: "__remotion-vertical-scrollbar", style: {
179
220
  ...list,
@@ -14,11 +14,12 @@ export declare const getCanDragAsset: ({ readOnlyStudio, relativePath, }: {
14
14
  readOnlyStudio: boolean;
15
15
  relativePath: string;
16
16
  }) => boolean;
17
- export declare const getAssetContextMenuItems: ({ relativePath, fileManagerName, copyFileName, copyStaticFilePath, openAssetInConvert, openAssetInExplorer, renameAsset, deleteAsset, fileExplorerAvailable, fileExplorerDisabled, mutationsDisabled, }: {
17
+ export declare const getAssetContextMenuItems: ({ relativePath, fileManagerName, copyFileName, copyStaticFilePath, copyAbsolutePath, openAssetInConvert, openAssetInExplorer, renameAsset, deleteAsset, fileExplorerAvailable, fileExplorerDisabled, mutationsDisabled, }: {
18
18
  relativePath: string;
19
19
  fileManagerName: string;
20
20
  copyFileName: () => void;
21
21
  copyStaticFilePath: () => void;
22
+ copyAbsolutePath: (() => void) | null;
22
23
  openAssetInConvert: () => void;
23
24
  openAssetInExplorer: () => void;
24
25
  renameAsset: () => void;
@@ -45,19 +45,21 @@ const colors_1 = require("../helpers/colors");
45
45
  const copy_text_1 = require("../helpers/copy-text");
46
46
  const get_file_manager_name_1 = require("../helpers/get-file-manager-name");
47
47
  const get_preview_file_type_1 = require("../helpers/get-preview-file-type");
48
+ const hoverable_1 = require("../helpers/hoverable");
48
49
  const open_in_remotion_convert_1 = require("../helpers/open-in-remotion-convert");
49
50
  const sidebar_scroll_into_view_1 = require("../helpers/sidebar-scroll-into-view");
50
51
  const url_state_1 = require("../helpers/url-state");
51
52
  const use_asset_drag_events_1 = __importStar(require("../helpers/use-asset-drag-events"));
52
53
  const use_image_metadata_1 = require("../helpers/use-image-metadata");
53
54
  const use_media_metadata_1 = require("../helpers/use-media-metadata");
54
- const clipboard_1 = require("../icons/clipboard");
55
+ const ellipsis_1 = require("../icons/ellipsis");
55
56
  const folder_1 = require("../icons/folder");
56
57
  const modals_1 = require("../state/modals");
57
58
  const AssetFileIcon_1 = require("./AssetFileIcon");
58
59
  const ContextMenu_1 = require("./ContextMenu");
59
60
  const import_assets_1 = require("./import-assets");
60
61
  const InlineAction_1 = require("./InlineAction");
62
+ const InlineDropdown_1 = require("./InlineDropdown");
61
63
  const layout_1 = require("./layout");
62
64
  const NotificationCenter_1 = require("./Notifications/NotificationCenter");
63
65
  const open_in_new_window_1 = require("./open-in-new-window");
@@ -102,6 +104,11 @@ const revealIconStyle = {
102
104
  height: 12,
103
105
  color: colors_1.CURRENT_COLOR,
104
106
  };
107
+ const ellipsisIconStyle = {
108
+ style: {
109
+ height: 12,
110
+ },
111
+ };
105
112
  const getAssetActionAvailability = ({ browserStudioCanMutateAssets, readOnlyStudio, connectionStatus, publicFolderExists, }) => {
106
113
  return {
107
114
  mutationsDisabled: browserStudioCanMutateAssets !== true &&
@@ -116,7 +123,7 @@ const getCanDragAsset = ({ readOnlyStudio, relativePath, }) => {
116
123
  return !readOnlyStudio && (0, import_assets_1.getAssetElementFromPath)(relativePath) !== null;
117
124
  };
118
125
  exports.getCanDragAsset = getCanDragAsset;
119
- const getAssetContextMenuItems = ({ relativePath, fileManagerName, copyFileName, copyStaticFilePath, openAssetInConvert, openAssetInExplorer, renameAsset, deleteAsset, fileExplorerAvailable, fileExplorerDisabled, mutationsDisabled, }) => {
126
+ const getAssetContextMenuItems = ({ relativePath, fileManagerName, copyFileName, copyStaticFilePath, copyAbsolutePath, openAssetInConvert, openAssetInExplorer, renameAsset, deleteAsset, fileExplorerAvailable, fileExplorerDisabled, mutationsDisabled, }) => {
120
127
  const previewFileType = (0, get_preview_file_type_1.getPreviewFileType)(relativePath);
121
128
  const canOpenInConvert = previewFileType === 'audio' || previewFileType === 'video';
122
129
  const items = [
@@ -160,6 +167,19 @@ const getAssetContextMenuItems = ({ relativePath, fileManagerName, copyFileName,
160
167
  type: 'item',
161
168
  value: 'copy-asset-static-file-path',
162
169
  },
170
+ copyAbsolutePath
171
+ ? {
172
+ id: 'copy-asset-absolute-path',
173
+ keyHint: null,
174
+ label: 'Copy absolute path',
175
+ leftItem: null,
176
+ onClick: copyAbsolutePath,
177
+ quickSwitcherLabel: 'Copy asset absolute path',
178
+ subMenu: null,
179
+ type: 'item',
180
+ value: 'copy-asset-absolute-path',
181
+ }
182
+ : null,
163
183
  {
164
184
  type: 'divider',
165
185
  id: 'asset-file-actions-divider',
@@ -370,8 +390,8 @@ const AssetSelectorItem = ({ item, tabIndex, level, parentFolder, readOnlyStudio
370
390
  const renderFileExplorerAction = (0, react_1.useCallback)((color) => {
371
391
  return jsx_runtime_1.jsx(folder_1.ExpandedFolderIcon, { style: revealIconStyle, color: color });
372
392
  }, []);
373
- const renderCopyAction = (0, react_1.useCallback)((color) => {
374
- return jsx_runtime_1.jsx(clipboard_1.ClipboardIcon, { style: revealIconStyle, color: color });
393
+ const renderContextMenuAction = (0, react_1.useCallback)((color) => {
394
+ return jsx_runtime_1.jsx(ellipsis_1.EllipsisIcon, { svgProps: ellipsisIconStyle, fill: color });
375
395
  }, []);
376
396
  const copyFileName = (0, react_1.useCallback)(() => {
377
397
  (0, copy_text_1.copyText)(item.name)
@@ -392,6 +412,19 @@ const AssetSelectorItem = ({ item, tabIndex, level, parentFolder, readOnlyStudio
392
412
  (0, NotificationCenter_1.showNotification)(`Could not copy: ${err.message}`, 2000);
393
413
  });
394
414
  }, [relativePath]);
415
+ const copyAbsolutePath = (0, react_1.useCallback)(() => {
416
+ if (window.remotion_publicFolderExists === null) {
417
+ return;
418
+ }
419
+ const content = `${window.remotion_publicFolderExists}/${relativePath}`;
420
+ (0, copy_text_1.copyText)(content)
421
+ .then(() => {
422
+ (0, NotificationCenter_1.showNotification)(`Copied '${content}' to clipboard`, 1000);
423
+ })
424
+ .catch((err) => {
425
+ (0, NotificationCenter_1.showNotification)(`Could not copy: ${err.message}`, 2000);
426
+ });
427
+ }, [relativePath]);
395
428
  const openAssetInConvert = (0, react_1.useCallback)(() => {
396
429
  (0, open_in_remotion_convert_1.openInRemotionConvert)({ relativePath });
397
430
  }, [relativePath]);
@@ -425,6 +458,7 @@ const AssetSelectorItem = ({ item, tabIndex, level, parentFolder, readOnlyStudio
425
458
  fileManagerName,
426
459
  copyFileName,
427
460
  copyStaticFilePath,
461
+ copyAbsolutePath: window.remotion_publicFolderExists === null ? null : copyAbsolutePath,
428
462
  openAssetInConvert,
429
463
  openAssetInExplorer,
430
464
  renameAsset,
@@ -436,6 +470,7 @@ const AssetSelectorItem = ({ item, tabIndex, level, parentFolder, readOnlyStudio
436
470
  }, [
437
471
  copyFileName,
438
472
  copyStaticFilePath,
473
+ copyAbsolutePath,
439
474
  deleteAsset,
440
475
  fileExplorerDisabled,
441
476
  fileManagerName,
@@ -449,13 +484,9 @@ const AssetSelectorItem = ({ item, tabIndex, level, parentFolder, readOnlyStudio
449
484
  e.stopPropagation();
450
485
  openAssetInExplorer();
451
486
  }, [openAssetInExplorer]);
452
- const copyToClipboard = (0, react_1.useCallback)((e) => {
453
- e.stopPropagation();
454
- copyStaticFilePath();
455
- }, [copyStaticFilePath]);
456
487
  return (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { getItems: getContextMenuItems, children: jsx_runtime_1.jsx(layout_1.Row, { align: "center", children: jsx_runtime_1.jsxs("div", { ref: rowRef, style: style, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, onClick: onClick, draggable: canDragAsset, onDragStart: onDragStart, onDragEnd: onDragEnd, tabIndex: tabIndex, title: item.name, children: [
457
488
  jsx_runtime_1.jsx(AssetFileIcon_1.AssetFileIcon, { fileType: previewFileType, style: iconStyle, color: hovered || selected ? colors_1.WHITE : colors_1.LIGHT_TEXT }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 1 }), jsx_runtime_1.jsx("div", { style: label, children: item.name }), hovered && !isDragging ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
458
- jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(InlineAction_1.InlineAction, { variant: null, title: "Copy staticFile() path", renderAction: renderCopyAction, onClick: copyToClipboard }), fileExplorerDisabled ? null : (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
459
- jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(InlineAction_1.InlineAction, { variant: null, title: `Show in ${fileManagerName}`, renderAction: renderFileExplorerAction, onClick: revealInExplorer })
489
+ jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(InlineDropdown_1.InlineDropdown, { variant: null, title: "More actions", renderAction: renderContextMenuAction, getItems: getContextMenuItems, style: hoverable_1.NO_HOVER_BACKGROUND_STYLE, className: hoverable_1.FOCUS_VISIBLE_ONLY_CLASS_NAME }), fileExplorerDisabled ? null : (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
490
+ jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(InlineAction_1.InlineAction, { variant: null, title: `Show in ${fileManagerName}`, renderAction: renderFileExplorerAction, onClick: revealInExplorer, style: hoverable_1.NO_HOVER_BACKGROUND_STYLE, className: hoverable_1.FOCUS_VISIBLE_ONLY_CLASS_NAME })
460
491
  ] }))] })) : null] }) }) }));
461
492
  };
@@ -4,6 +4,9 @@ exports.CaptionTextEditor = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const colors_1 = require("../helpers/colors");
7
+ const hoverable_1 = require("../helpers/hoverable");
8
+ const enter_1 = require("../icons/enter");
9
+ const InlineAction_1 = require("./InlineAction");
7
10
  const RemInput_1 = require("./NewComposition/RemInput");
8
11
  const container = {
9
12
  alignSelf: 'stretch',
@@ -21,8 +24,8 @@ const row = {
21
24
  alignItems: 'center',
22
25
  borderBottom: `1px solid ${colors_1.LINE_COLOR}`,
23
26
  display: 'grid',
24
- gap: 12,
25
- gridTemplateColumns: '100px minmax(0, 1fr)',
27
+ gap: 8,
28
+ gridTemplateColumns: '100px minmax(0, 1fr) 24px',
26
29
  padding: '5px 12px',
27
30
  };
28
31
  const timing = {
@@ -77,18 +80,33 @@ const CaptionTextEditor = ({ captions, onChange, onSave, onCancel, readOnly }) =
77
80
  (0, react_1.useEffect)(() => {
78
81
  return commitPending;
79
82
  }, [commitPending]);
80
- const updateText = (0, react_1.useCallback)((index, text) => {
81
- var _a;
82
- if (((_a = latestRef.current.captions[index]) === null || _a === void 0 ? void 0 : _a.text) === text) {
83
+ const updateCaption = (0, react_1.useCallback)((index, changes) => {
84
+ const currentCaption = latestRef.current.captions[index];
85
+ if (!currentCaption) {
83
86
  return;
84
87
  }
85
88
  const nextCaptions = latestRef.current.captions.map((caption, captionIndex) => {
86
- return captionIndex === index ? { ...caption, text } : caption;
89
+ return captionIndex === index ? { ...caption, ...changes } : caption;
87
90
  });
88
91
  latestRef.current.captions = nextCaptions;
89
92
  dirtyRef.current = true;
90
93
  onChange(nextCaptions);
91
94
  }, [onChange]);
95
+ const updateText = (0, react_1.useCallback)((index, text) => {
96
+ var _a;
97
+ if (((_a = latestRef.current.captions[index]) === null || _a === void 0 ? void 0 : _a.text) === text) {
98
+ return;
99
+ }
100
+ updateCaption(index, { text });
101
+ }, [updateCaption]);
102
+ const updatePageBreakAfter = (0, react_1.useCallback)((index, pageBreakAfter) => {
103
+ var _a;
104
+ if (Boolean((_a = latestRef.current.captions[index]) === null || _a === void 0 ? void 0 : _a.pageBreakAfter) ===
105
+ pageBreakAfter) {
106
+ return;
107
+ }
108
+ updateCaption(index, { pageBreakAfter });
109
+ }, [updateCaption]);
92
110
  const focusSibling = (0, react_1.useCallback)((index) => {
93
111
  var _a;
94
112
  const input = (_a = listRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(`[data-caption-index="${index}"]`);
@@ -96,6 +114,10 @@ const CaptionTextEditor = ({ captions, onChange, onSave, onCancel, readOnly }) =
96
114
  input === null || input === void 0 ? void 0 : input.scrollIntoView({ block: 'nearest' });
97
115
  }, []);
98
116
  return (jsx_runtime_1.jsx("div", { style: container, children: jsx_runtime_1.jsxs("div", { ref: listRef, style: list, children: [captions.length === 0 ? jsx_runtime_1.jsx("div", { style: empty, children: "No captions" }) : null, captionRows.map(({ caption, key }, index) => {
117
+ const hasPageBreakAfter = Boolean(caption.pageBreakAfter);
118
+ const pageBreakTitle = hasPageBreakAfter
119
+ ? `Remove page break after caption ${index + 1}`
120
+ : `Add page break after caption ${index + 1}`;
99
121
  return (jsx_runtime_1.jsxs("div", { style: row, children: [
100
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) => {
101
123
  if (cancelledBlurIndexes.current.delete(index)) {
@@ -125,8 +147,10 @@ const CaptionTextEditor = ({ captions, onChange, onSave, onCancel, readOnly }) =
125
147
  fontFamily: 'sans-serif',
126
148
  fontSize: 12,
127
149
  lineHeight: '16px',
128
- }, value: caption.text })
129
- ] }, key));
150
+ }, value: caption.text }), jsx_runtime_1.jsx(InlineAction_1.InlineAction, { "aria-pressed": hasPageBreakAfter, className: hoverable_1.FOCUS_VISIBLE_ONLY_CLASS_NAME, disabled: readOnly, onClick: () => {
151
+ updatePageBreakAfter(index, !hasPageBreakAfter);
152
+ commitPending();
153
+ }, renderAction: (color) => (jsx_runtime_1.jsx(enter_1.EnterIcon, { "aria-hidden": "true", color: hasPageBreakAfter ? colors_1.BLUE : color, focusable: "false", style: { height: 16, width: 16 } })), title: pageBreakTitle, variant: null })] }, key));
130
154
  })] }) }));
131
155
  };
132
156
  exports.CaptionTextEditor = CaptionTextEditor;
@@ -4,6 +4,7 @@ exports.CompositionContextButton = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const client_id_1 = require("../helpers/client-id");
7
+ const hoverable_1 = require("../helpers/hoverable");
7
8
  const ellipsis_1 = require("../icons/ellipsis");
8
9
  const InlineDropdown_1 = require("./InlineDropdown");
9
10
  const CompositionContextButton = ({ visible, getItems }) => {
@@ -22,6 +23,6 @@ const CompositionContextButton = ({ visible, getItems }) => {
22
23
  if (!visible || connectionStatus !== 'connected') {
23
24
  return null;
24
25
  }
25
- return (jsx_runtime_1.jsx(InlineDropdown_1.InlineDropdown, { renderAction: renderAction, getItems: getItems, variant: null }));
26
+ return (jsx_runtime_1.jsx(InlineDropdown_1.InlineDropdown, { renderAction: renderAction, getItems: getItems, variant: null, style: hoverable_1.NO_HOVER_BACKGROUND_STYLE, className: hoverable_1.FOCUS_VISIBLE_ONLY_CLASS_NAME }));
26
27
  };
27
28
  exports.CompositionContextButton = CompositionContextButton;
@@ -239,7 +239,7 @@ const CompositionSelector = () => {
239
239
  }
240
240
  }, [compositions, stopCompositionListAutoScroll]);
241
241
  return (jsx_runtime_1.jsxs("div", { style: container, children: [
242
- jsx_runtime_1.jsx(ContextMenu_1.ContextMenuForTarget, { triggerRef: listRef, getItems: getRootContextMenuItems }), jsx_runtime_1.jsx(ExplorerQuickSwitcherTrigger_1.ExplorerQuickSwitcherTrigger, { mode: "compositions", showShortcut: true, tabIndex: tabIndex }), jsx_runtime_1.jsx("div", { ref: listRef, className: "__remotion-vertical-scrollbar", style: list, onDragOverCapture: onCompositionListDragOverCapture, onDragEndCapture: stopCompositionListAutoScroll, onDragOver: onRootDragOver, onDragLeave: onRootDragLeave, onDropCapture: stopCompositionListAutoScroll, onDrop: onRootDrop, children: items.map((c) => {
242
+ jsx_runtime_1.jsx(ContextMenu_1.ContextMenuForTarget, { triggerRef: listRef, getItems: getRootContextMenuItems }), jsx_runtime_1.jsx(ExplorerQuickSwitcherTrigger_1.ExplorerQuickSwitcherTrigger, { mode: "compositions", showShortcut: true, tabIndex: tabIndex, getActions: getRootContextMenuItems }), jsx_runtime_1.jsx("div", { ref: listRef, className: "__remotion-vertical-scrollbar", style: list, onDragOverCapture: onCompositionListDragOverCapture, onDragEndCapture: stopCompositionListAutoScroll, onDragOver: onRootDragOver, onDragLeave: onRootDragLeave, onDropCapture: stopCompositionListAutoScroll, onDrop: onRootDrop, children: items.map((c) => {
243
243
  return (jsx_runtime_1.jsx(CompositionSelectorItem_1.CompositionSelectorItem, { level: 0, currentComposition: canvasContent && canvasContent.type === 'composition'
244
244
  ? canvasContent.compositionId
245
245
  : null, selectComposition: selectComposition, toggleFolder: toggleFolder, clearRootDragHover: clearRootDragHover, tabIndex: tabIndex, item: c }, c.key + c.type));
@@ -16,6 +16,7 @@ const Timeline_1 = require("./Timeline/Timeline");
16
16
  const TimelineEmptyState_1 = require("./Timeline/TimelineEmptyState");
17
17
  const TimelineKeyframeDragState_1 = require("./Timeline/TimelineKeyframeDragState");
18
18
  const TimelineSelection_1 = require("./Timeline/TimelineSelection");
19
+ const WebMcp_1 = require("./WebMcp");
19
20
  const noop = () => undefined;
20
21
  const container = {
21
22
  display: 'flex',
@@ -42,8 +43,10 @@ const EditorContent = ({ readOnlyStudio, children }) => {
42
43
  const content = (jsx_runtime_1.jsxs(SplitterContainer_1.SplitterContainer, { orientation: "horizontal", id: "top-to-bottom", maxFlex: 0.9, minFlex: 0.2, defaultFlex: 0.75, maxFlexerSize: null, minFlexerSize: null, maxAntiFlexerSize: null, minAntiFlexerSize: null, children: [
43
44
  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, {}) })
44
45
  ] }));
45
- return (jsx_runtime_1.jsx(TimelineSelection_1.TimelineSelectionProvider, { children: jsx_runtime_1.jsx(transform_3d_mode_1.Transform3DModeStateProvider, { children: jsx_runtime_1.jsxs(StudioClearSelectionArea, { children: [
46
- jsx_runtime_1.jsx(InitialCompositionLoader_1.InitialCompositionLoader, {}), jsx_runtime_1.jsx(MenuToolbar_1.MenuToolbar, { readOnlyStudio: readOnlyStudio }), jsx_runtime_1.jsx(GlobalKeybindings_1.GlobalKeybindings, {}), jsx_runtime_1.jsx(TimelineKeyframeDragState_1.TimelineKeyframeDragStateProvider, { children: content })
47
- ] }) }) }));
46
+ return (jsx_runtime_1.jsxs(TimelineSelection_1.TimelineSelectionProvider, { children: [
47
+ jsx_runtime_1.jsx(WebMcp_1.WebMcp, {}), jsx_runtime_1.jsx(transform_3d_mode_1.Transform3DModeStateProvider, { children: jsx_runtime_1.jsxs(StudioClearSelectionArea, { children: [
48
+ jsx_runtime_1.jsx(InitialCompositionLoader_1.InitialCompositionLoader, {}), jsx_runtime_1.jsx(MenuToolbar_1.MenuToolbar, { readOnlyStudio: readOnlyStudio }), jsx_runtime_1.jsx(GlobalKeybindings_1.GlobalKeybindings, {}), jsx_runtime_1.jsx(TimelineKeyframeDragState_1.TimelineKeyframeDragStateProvider, { children: content })
49
+ ] }) })
50
+ ] }));
48
51
  };
49
52
  exports.EditorContent = EditorContent;
@@ -1,4 +1,9 @@
1
1
  import React from 'react';
2
+ export declare const ElementLibraryAddConfirmation: React.FC<{
3
+ readonly displayName: string | null;
4
+ readonly origin: string;
5
+ readonly url: string;
6
+ }>;
2
7
  export declare const ElementInstallConfirmation: React.FC<{
3
8
  readonly displayName: string;
4
9
  readonly sourceLabel: string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ElementInstallConfirmation = void 0;
3
+ exports.ElementInstallConfirmation = exports.ElementLibraryAddConfirmation = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const colors_1 = require("../helpers/colors");
6
6
  const ValidationMessage_1 = require("./NewComposition/ValidationMessage");
@@ -130,6 +130,13 @@ const warningDescriptionStyle = {
130
130
  fontWeight: 400,
131
131
  lineHeight: 1.5,
132
132
  };
133
+ const browseElementsStyle = {
134
+ color: 'inherit',
135
+ fontFamily: 'inherit',
136
+ fontSize: 'inherit',
137
+ fontWeight: 600,
138
+ lineHeight: 'inherit',
139
+ };
133
140
  const sourceDetailsStyle = {
134
141
  paddingTop: 2,
135
142
  fontFamily: 'sans-serif',
@@ -171,6 +178,23 @@ const makeSourceControlsVisible = (sourceCode) => {
171
178
  return `\\u${(_a = character.codePointAt(0)) === null || _a === void 0 ? void 0 : _a.toString(16).padStart(4, '0')}`;
172
179
  });
173
180
  };
181
+ const ElementLibraryAddConfirmation = ({ displayName, origin, url }) => {
182
+ return (jsx_runtime_1.jsxs("div", { style: container, children: [
183
+ jsx_runtime_1.jsxs("dl", { style: metadataStyle, "aria-label": "Catalog details", children: [displayName === null ? null : (jsx_runtime_1.jsxs("div", { style: metadataRowStyle, children: [
184
+ jsx_runtime_1.jsx("dt", { style: metadataTermStyle, children: "Display name" }), jsx_runtime_1.jsx("dd", { style: metadataDescriptionStyle, children: displayName })
185
+ ] })), jsx_runtime_1.jsxs("div", { style: metadataRowStyle, children: [
186
+ jsx_runtime_1.jsx("dt", { style: metadataTermStyle, children: "Request source" }), jsx_runtime_1.jsx("dd", { style: metadataDescriptionStyle, children: origin })
187
+ ] }), jsx_runtime_1.jsxs("div", { style: metadataRowStyle, children: [
188
+ jsx_runtime_1.jsx("dt", { style: metadataTermStyle, children: "Catalog URL" }), jsx_runtime_1.jsx("dd", { style: metadataDescriptionStyle, children: jsx_runtime_1.jsx("code", { style: codeStyle, children: url }) })
189
+ ] })
190
+ ] }), jsx_runtime_1.jsxs("div", { style: warningStyle, children: [
191
+ jsx_runtime_1.jsx(ValidationMessage_1.WarningTriangle, { style: warningIconStyle }), jsx_runtime_1.jsxs("p", { style: warningDescriptionStyle, children: ["This adds the catalog to", ' ', jsx_runtime_1.jsx("strong", { style: browseElementsStyle, children: "Browse Elements" }),
192
+ " when nothing is selected on the canvas. It is saved in", ' ', jsx_runtime_1.jsx("code", { style: codeStyle, children: "remotion.config.ts" }),
193
+ "."] })
194
+ ] })
195
+ ] }));
196
+ };
197
+ exports.ElementLibraryAddConfirmation = ElementLibraryAddConfirmation;
174
198
  const ElementInstallConfirmation = ({ displayName, sourceLabel, sourceIsUnverified, compositionId, filePath, overwritesExistingFile, dependenciesToReview, missingPackages, sourceCode, usesBrowserDependencyResolution, }) => {
175
199
  return (jsx_runtime_1.jsxs("div", { style: container, children: [
176
200
  jsx_runtime_1.jsxs("dl", { style: metadataStyle, "aria-label": "Installation details", children: [
@@ -29,7 +29,9 @@ const ElementLibraryModal = ({ name, url }) => {
29
29
  // Studio is cross-origin isolated. A credentialless iframe may embed a
30
30
  // library that does not set Cross-Origin-Resource-Policy headers.
31
31
  iframe.setAttribute('credentialless', '');
32
- iframe.src = url;
32
+ const iframeUrl = new URL(url);
33
+ iframeUrl.searchParams.set('remotion-studio', 'true');
34
+ iframe.src = iframeUrl.toString();
33
35
  }, [url]);
34
36
  return (jsx_runtime_1.jsxs(DismissableModal_1.DismissableModal, { panelStyle: panelStyle, children: [
35
37
  jsx_runtime_1.jsx(ModalHeader_1.ModalHeader, { title: name }), jsx_runtime_1.jsx("iframe", { ref: iframeRef, allow: "local-network-access; loopback-network", style: iframeStyle, title: `${name} library` })
@@ -11,6 +11,7 @@ const serializeCaptions = (captions) => {
11
11
  return JSON.stringify(captions);
12
12
  };
13
13
  const getCaptionPatches = ({ previous, next, }) => {
14
+ var _a;
14
15
  if (previous.length !== next.length) {
15
16
  return null;
16
17
  }
@@ -20,11 +21,21 @@ const getCaptionPatches = ({ previous, next, }) => {
20
21
  if (!after) {
21
22
  return null;
22
23
  }
24
+ const changes = {};
23
25
  if (before.text !== after.text) {
26
+ changes.text = after.text;
27
+ }
28
+ if (Boolean(before.pageBreakAfter) !== Boolean(after.pageBreakAfter)) {
29
+ changes.pageBreakAfter = Boolean(after.pageBreakAfter);
30
+ }
31
+ if (Object.keys(changes).length > 0) {
24
32
  patches.push({
25
33
  index,
26
- before,
27
- changes: { text: after.text },
34
+ before: {
35
+ ...before,
36
+ pageBreakAfter: (_a = before.pageBreakAfter) !== null && _a !== void 0 ? _a : null,
37
+ },
38
+ changes,
28
39
  });
29
40
  }
30
41
  }
@@ -11,8 +11,10 @@ const client_id_1 = require("../helpers/client-id");
11
11
  const studio_runtime_config_1 = require("../helpers/studio-runtime-config");
12
12
  const modals_1 = require("../state/modals");
13
13
  const AskAiModal_1 = require("./AskAiModal");
14
+ const call_api_1 = require("./call-api");
14
15
  const ConfirmationDialog_1 = require("./ConfirmationDialog");
15
16
  const EffectPickerModal_1 = require("./EffectPickerModal");
17
+ const ElementInstallConfirmation_1 = require("./ElementInstallConfirmation");
16
18
  const ElementLibraryModal_1 = require("./ElementLibraryModal");
17
19
  const FixComputedValueModal_1 = require("./FixComputedValueModal");
18
20
  const DeleteComposition_1 = require("./NewComposition/DeleteComposition");
@@ -23,6 +25,7 @@ const NewFolder_1 = require("./NewComposition/NewFolder");
23
25
  const RenameComposition_1 = require("./NewComposition/RenameComposition");
24
26
  const RenameFolder_1 = require("./NewComposition/RenameFolder");
25
27
  const RenameStaticFile_1 = require("./NewComposition/RenameStaticFile");
28
+ const NotificationCenter_1 = require("./Notifications/NotificationCenter");
26
29
  const OverrideInputProps_1 = require("./OverrideInputProps");
27
30
  const QuickSwitcher_1 = __importDefault(require("./QuickSwitcher/QuickSwitcher"));
28
31
  const RenderStatusModal_1 = require("./RenderModal/RenderStatusModal");
@@ -38,6 +41,7 @@ const Modals = ({ readOnlyStudio }) => {
38
41
  const { previewServerState, subscribeToEvent } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
39
42
  const canRender = previewServerState.type === 'connected';
40
43
  const isBrowserStudio = (0, browser_studio_operations_1.getBrowserStudioOperations)() !== null;
44
+ const confirm = (0, ConfirmationDialog_1.useConfirmationDialog)();
41
45
  (0, react_1.useEffect)(() => {
42
46
  if (isBrowserStudio) {
43
47
  return;
@@ -53,6 +57,51 @@ const Modals = ({ readOnlyStudio }) => {
53
57
  });
54
58
  });
55
59
  }, [isBrowserStudio, setSelectedModal, subscribeToEvent]);
60
+ (0, react_1.useEffect)(() => {
61
+ if (isBrowserStudio) {
62
+ return;
63
+ }
64
+ return subscribeToEvent('element-library-add-request', (event) => {
65
+ if (event.type !== 'element-library-add-request') {
66
+ return;
67
+ }
68
+ (async () => {
69
+ const confirmed = await confirm({
70
+ title: 'Add Element catalog',
71
+ message: (jsx_runtime_1.jsx(ElementInstallConfirmation_1.ElementLibraryAddConfirmation, { displayName: event.displayName, origin: event.origin, url: event.url })),
72
+ confirmLabel: 'Add catalog',
73
+ cancelLabel: 'Cancel',
74
+ });
75
+ if (!confirmed) {
76
+ return;
77
+ }
78
+ if (previewServerState.type !== 'connected') {
79
+ (0, NotificationCenter_1.showNotification)('Could not add catalog: Studio disconnected', 4000);
80
+ return;
81
+ }
82
+ try {
83
+ const result = await (0, call_api_1.callApi)('/api/update-config', {
84
+ clientId: previewServerState.clientId,
85
+ updates: [
86
+ {
87
+ setter: 'addElementLibrary',
88
+ type: 'set',
89
+ value: event.displayName === null
90
+ ? { url: event.url }
91
+ : { url: event.url, displayName: event.displayName },
92
+ },
93
+ ],
94
+ });
95
+ if (!result.success) {
96
+ (0, NotificationCenter_1.showNotification)(`Could not add catalog: ${result.reason}`, 4000);
97
+ }
98
+ }
99
+ catch (error) {
100
+ (0, NotificationCenter_1.showNotification)(`Could not add catalog: ${error.message}`, 4000);
101
+ }
102
+ })();
103
+ });
104
+ }, [confirm, isBrowserStudio, previewServerState, subscribeToEvent]);
56
105
  return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [modalContextType && modalContextType.type === 'new-comp' && (jsx_runtime_1.jsx(NewComposition_1.NewComposition, { folderName: modalContextType.folderName, parentName: modalContextType.parentName, stack: modalContextType.stack, canvasCapture: modalContextType.canvasCapture })), modalContextType && modalContextType.type === 'new-folder' && (jsx_runtime_1.jsx(NewFolder_1.NewFolder, { parentName: modalContextType.parentName, stack: modalContextType.stack })), modalContextType && modalContextType.type === 'duplicate-comp' && (jsx_runtime_1.jsx(DuplicateComposition_1.DuplicateComposition, { compositionType: modalContextType.compositionType, compositionId: modalContextType.compositionId })), modalContextType && modalContextType.type === 'delete-comp' && (jsx_runtime_1.jsx(DeleteComposition_1.DeleteComposition, { compositionId: modalContextType.compositionId })), modalContextType && modalContextType.type === 'rename-comp' && (jsx_runtime_1.jsx(RenameComposition_1.RenameComposition, { compositionId: modalContextType.compositionId })), modalContextType && modalContextType.type === 'delete-folder' && (jsx_runtime_1.jsx(DeleteFolder_1.DeleteFolder, { folderName: modalContextType.folderName, parentName: modalContextType.parentName, stack: modalContextType.stack })), modalContextType && modalContextType.type === 'rename-folder' && (jsx_runtime_1.jsx(RenameFolder_1.RenameFolder, { folderName: modalContextType.folderName, parentName: modalContextType.parentName, stack: modalContextType.stack })), modalContextType && modalContextType.type === 'rename-static-file' && (jsx_runtime_1.jsx(RenameStaticFile_1.RenameStaticFileModal, { relativePath: modalContextType.relativePath })), modalContextType && modalContextType.type === 'input-props-override' && (jsx_runtime_1.jsx(OverrideInputProps_1.OverrideInputPropsModal, {})), modalContextType &&
57
106
  modalContextType.type === 'settings' &&
58
107
  (!isBrowserStudio ||
@@ -10,9 +10,6 @@ const Checkmark_1 = require("../icons/Checkmark");
10
10
  const playback_rate_1 = require("../icons/playback-rate");
11
11
  const playbackrate_1 = require("../state/playbackrate");
12
12
  const TimelineCombobox_1 = require("./TimelineCombobox");
13
- const commonPlaybackRates = [
14
- -4, -2, -1, -0.5, -0.25, 0.25, 0.5, 1, 1.5, 2, 4,
15
- ];
16
13
  const getPlaybackRateLabel = (playbackRate) => {
17
14
  return `${playbackRate}x`;
18
15
  };
@@ -23,7 +20,7 @@ const usePlaybackRateMenuItems = ({ playbackRate, setPlaybackRate, }) => {
23
20
  type: 'divider',
24
21
  id: 'divider',
25
22
  };
26
- const values = commonPlaybackRates.map((newPlaybackRate) => {
23
+ const values = playbackrate_1.commonPlaybackRates.map((newPlaybackRate) => {
27
24
  return {
28
25
  id: String(newPlaybackRate),
29
26
  label: getPlaybackRateLabel(newPlaybackRate),
@@ -41,7 +38,7 @@ const usePlaybackRateMenuItems = ({ playbackRate, setPlaybackRate, }) => {
41
38
  quickSwitcherLabel: null,
42
39
  };
43
40
  });
44
- const middle = Math.floor(commonPlaybackRates.length / 2);
41
+ const middle = Math.floor(playbackrate_1.commonPlaybackRates.length / 2);
45
42
  return [...values.slice(0, middle), divider, ...values.slice(middle)];
46
43
  }, [playbackRate, setPlaybackRate]);
47
44
  return {
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
+ import type { ComboboxValue } from '../NewComposition/ComboBox';
2
3
  import type { QuickSwitcherMode } from './NoResults';
3
4
  export declare const ExplorerQuickSwitcherTrigger: React.FC<{
4
5
  readonly mode: QuickSwitcherMode;
5
6
  readonly showShortcut: boolean;
6
7
  readonly tabIndex: number;
8
+ readonly getActions: () => ComboboxValue[];
7
9
  }>;