@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
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useTimelineRowContainsSelection = exports.useTimelineKeyframeSelection = exports.useTimelineRowSelection = exports.useTimelineSelection = exports.TimelineSelectionProvider = exports.SELECTION_ENABLED = exports.TIMELINE_SELECTED_TRACK_HIGHLIGHT_STYLE = exports.getTimelineColor = exports.getTimelineSelectedLabelStyle = exports.TIMELINE_SELECTED_LABEL_HORIZONTAL_PADDING = exports.TIMELINE_SELECTED_LABEL_TEXT = exports.TIMELINE_SELECTED_LABEL_BACKGROUND = exports.TIMELINE_SELECTED_BACKGROUND = void 0;
3
+ exports.useTimelineRowContainsSelection = exports.useTimelineKeyframeSelection = exports.useTimelineRowSelection = exports.useCurrentTimelineSelectionStateAsRef = exports.useTimelineSelection = exports.TimelineSelectionProvider = exports.TimelineSelectAllKeybindings = exports.getTimelineSequenceSelectionKey = exports.getSelectableTimelineSequenceSelections = exports.getTimelineSelectionFromNodePathInfo = exports.getTimelineSelectionAfterInteraction = exports.isTimelineSelectionModifierEvent = exports.ENABLE_OUTLINES = exports.TIMELINE_TOP_DRAG = exports.SELECTION_ENABLED = exports.getTimelineSelectedTrackHighlightStyle = exports.getTimelineColor = exports.getTimelineSelectedLabelStyle = exports.TIMELINE_SELECTED_LABEL_HORIZONTAL_PADDING = exports.TIMELINE_SELECTED_LABEL_TEXT = exports.TIMELINE_SELECTED_LABEL_BACKGROUND = exports.TIMELINE_SELECTED_BACKGROUND = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const studio_shared_1 = require("@remotion/studio-shared");
6
6
  const react_1 = require("react");
7
7
  const client_id_1 = require("../../helpers/client-id");
8
8
  const timeline_layout_1 = require("../../helpers/timeline-layout");
9
9
  const timeline_node_path_key_1 = require("../../helpers/timeline-node-path-key");
10
+ const use_keybinding_1 = require("../../helpers/use-keybinding");
11
+ const TimelineDeleteKeybindings_1 = require("./TimelineDeleteKeybindings");
10
12
  exports.TIMELINE_SELECTED_BACKGROUND = '#3B3F42';
11
13
  exports.TIMELINE_SELECTED_LABEL_BACKGROUND = '#B0B0B0';
12
14
  exports.TIMELINE_SELECTED_LABEL_TEXT = 'black';
@@ -31,99 +33,351 @@ const getTimelineColor = (selected, subcategory) => {
31
33
  : 'rgba(255, 255, 255, 0.8)';
32
34
  };
33
35
  exports.getTimelineColor = getTimelineColor;
34
- exports.TIMELINE_SELECTED_TRACK_HIGHLIGHT_STYLE = {
36
+ const getTimelineSelectedTrackHighlightStyle = (timelineWidth) => ({
35
37
  backgroundColor: exports.TIMELINE_SELECTED_BACKGROUND,
36
38
  bottom: 0,
37
39
  left: -timeline_layout_1.TIMELINE_PADDING,
38
40
  pointerEvents: 'none',
39
41
  position: 'absolute',
40
- right: -timeline_layout_1.TIMELINE_PADDING,
41
42
  top: 0,
42
- };
43
+ width: timelineWidth,
44
+ });
45
+ exports.getTimelineSelectedTrackHighlightStyle = getTimelineSelectedTrackHighlightStyle;
43
46
  exports.SELECTION_ENABLED = false;
44
- const TimelineSelectionContext = (0, react_1.createContext)({
47
+ exports.TIMELINE_TOP_DRAG = false;
48
+ exports.ENABLE_OUTLINES = false;
49
+ const isTimelineSelectionModifierEvent = ({ shiftKey, metaKey, ctrlKey, }) => {
50
+ return shiftKey || metaKey || ctrlKey;
51
+ };
52
+ exports.isTimelineSelectionModifierEvent = isTimelineSelectionModifierEvent;
53
+ const getTimelineSelectionType = (item) => item.type;
54
+ const getTimelineSelectionAnchor = (selectedItems, previousAnchor, targetType) => {
55
+ if (previousAnchor &&
56
+ getTimelineSelectionType(previousAnchor) === targetType) {
57
+ return previousAnchor;
58
+ }
59
+ for (let i = selectedItems.length - 1; i >= 0; i--) {
60
+ const candidate = selectedItems[i];
61
+ if (getTimelineSelectionType(candidate) === targetType) {
62
+ return candidate;
63
+ }
64
+ }
65
+ return null;
66
+ };
67
+ const getRangeSelection = ({ anchor, clickedItem, allSelectableItems, }) => {
68
+ const anchorKey = getTimelineSelectionKey(anchor);
69
+ const clickedKey = getTimelineSelectionKey(clickedItem);
70
+ const orderedOfType = allSelectableItems.filter((item) => getTimelineSelectionType(item) === clickedItem.type);
71
+ const anchorIndex = orderedOfType.findIndex((item) => getTimelineSelectionKey(item) === anchorKey);
72
+ const clickedIndex = orderedOfType.findIndex((item) => getTimelineSelectionKey(item) === clickedKey);
73
+ if (anchorIndex === -1 || clickedIndex === -1) {
74
+ return [clickedItem];
75
+ }
76
+ const [from, to] = anchorIndex < clickedIndex
77
+ ? [anchorIndex, clickedIndex]
78
+ : [clickedIndex, anchorIndex];
79
+ return orderedOfType.slice(from, to + 1);
80
+ };
81
+ const getTimelineSelectionAfterInteraction = ({ currentState, clickedItem, interaction, allSelectableItems, }) => {
82
+ const { selectedItems, anchor: previousAnchor } = currentState;
83
+ const clickedType = getTimelineSelectionType(clickedItem);
84
+ const nextAnchor = getTimelineSelectionAnchor(selectedItems, previousAnchor, clickedType);
85
+ const clickedKey = getTimelineSelectionKey(clickedItem);
86
+ if (interaction.shiftKey && nextAnchor) {
87
+ return {
88
+ selectedItems: getRangeSelection({
89
+ anchor: nextAnchor,
90
+ clickedItem,
91
+ allSelectableItems,
92
+ }),
93
+ anchor: nextAnchor,
94
+ };
95
+ }
96
+ if (interaction.toggleKey) {
97
+ const sameTypeItems = selectedItems.filter((item) => getTimelineSelectionType(item) === clickedType);
98
+ const existingKeySet = new Set(sameTypeItems.map(getTimelineSelectionKey));
99
+ if (existingKeySet.has(clickedKey)) {
100
+ const toggledSelection = sameTypeItems.filter((item) => getTimelineSelectionKey(item) !== clickedKey);
101
+ return {
102
+ selectedItems: toggledSelection,
103
+ anchor: toggledSelection.length === 0 ? null : clickedItem,
104
+ };
105
+ }
106
+ const selectableOrderMap = new Map(allSelectableItems
107
+ .filter((item) => getTimelineSelectionType(item) === clickedType)
108
+ .map((item, index) => [getTimelineSelectionKey(item), index]));
109
+ const extendedSelection = [...sameTypeItems, clickedItem].sort((a, b) => {
110
+ var _a, _b;
111
+ return (((_a = selectableOrderMap.get(getTimelineSelectionKey(a))) !== null && _a !== void 0 ? _a : 0) -
112
+ ((_b = selectableOrderMap.get(getTimelineSelectionKey(b))) !== null && _b !== void 0 ? _b : 0));
113
+ });
114
+ return {
115
+ selectedItems: extendedSelection,
116
+ anchor: clickedItem,
117
+ };
118
+ }
119
+ return {
120
+ selectedItems: [clickedItem],
121
+ anchor: clickedItem,
122
+ };
123
+ };
124
+ exports.getTimelineSelectionAfterInteraction = getTimelineSelectionAfterInteraction;
125
+ const defaultTimelineSelectionContextValue = {
45
126
  canSelect: false,
127
+ selectedItems: [],
46
128
  isSelected: () => false,
47
129
  selectItem: () => undefined,
130
+ selectItems: () => undefined,
131
+ registerSelectableItem: () => () => undefined,
48
132
  containsSelection: () => false,
49
133
  clearSelection: () => undefined,
50
- });
134
+ };
135
+ const TimelineSelectionContext = (0, react_1.createContext)(defaultTimelineSelectionContextValue);
136
+ const CurrentTimelineSelectionContext = (0, react_1.createContext)(null);
137
+ const parseEffectIndex = (effectIndex) => {
138
+ const parsed = Number(effectIndex);
139
+ if (!Number.isInteger(parsed) || parsed < 0) {
140
+ return null;
141
+ }
142
+ return parsed;
143
+ };
144
+ const getTimelineSelectionFromNodePathInfo = (nodePathInfo) => {
145
+ if (nodePathInfo === null) {
146
+ return null;
147
+ }
148
+ const { auxiliaryKeys } = nodePathInfo;
149
+ if (auxiliaryKeys.length === 0) {
150
+ return { type: 'sequence', nodePathInfo };
151
+ }
152
+ if (auxiliaryKeys.length === 2 && auxiliaryKeys[0] === 'controls') {
153
+ return { type: 'sequence-prop', nodePathInfo, key: auxiliaryKeys[1] };
154
+ }
155
+ if (auxiliaryKeys.length === 1 && auxiliaryKeys[0] === 'effects') {
156
+ return { type: 'sequence-all-effects', nodePathInfo };
157
+ }
158
+ if (auxiliaryKeys[0] === 'effects') {
159
+ const effectIndex = parseEffectIndex(auxiliaryKeys[1]);
160
+ if (effectIndex === null) {
161
+ return null;
162
+ }
163
+ if (auxiliaryKeys.length === 2) {
164
+ return { type: 'sequence-effect', nodePathInfo, i: effectIndex };
165
+ }
166
+ if (auxiliaryKeys.length === 3) {
167
+ return {
168
+ type: 'sequence-effect-prop',
169
+ nodePathInfo,
170
+ i: effectIndex,
171
+ key: auxiliaryKeys[2],
172
+ };
173
+ }
174
+ }
175
+ return null;
176
+ };
177
+ exports.getTimelineSelectionFromNodePathInfo = getTimelineSelectionFromNodePathInfo;
51
178
  const getTimelineSelectionKey = (item) => {
52
- const rowKey = (0, timeline_node_path_key_1.timelineNodePathInfoToKey)(item.nodePathInfo);
53
- if (item.type === 'row') {
54
- return rowKey;
179
+ const sequenceKey = (0, exports.getTimelineSequenceSelectionKey)(item.nodePathInfo);
180
+ switch (item.type) {
181
+ case 'sequence':
182
+ return `${sequenceKey}.sequence`;
183
+ case 'sequence-prop':
184
+ return `${sequenceKey}.sequence-prop.${item.key}`;
185
+ case 'sequence-all-effects':
186
+ return `${sequenceKey}.sequence-all-effects`;
187
+ case 'sequence-effect':
188
+ return `${sequenceKey}.sequence-effect.${item.i}`;
189
+ case 'sequence-effect-prop':
190
+ return `${sequenceKey}.sequence-effect-prop.${item.i}.${item.key}`;
191
+ case 'keyframe':
192
+ return `${(0, timeline_node_path_key_1.timelineNodePathInfoToKey)(item.nodePathInfo)}.keyframe.${item.frame}`;
193
+ default:
194
+ throw new Error(`Unexpected timeline selection type: ${item}`);
195
+ }
196
+ };
197
+ const nodePathDescendsFrom = (descendant, ancestor) => {
198
+ if ((0, studio_shared_1.stringifySequenceExpandedRowKey)(descendant.sequenceSubscriptionKey) !==
199
+ (0, studio_shared_1.stringifySequenceExpandedRowKey)(ancestor.sequenceSubscriptionKey)) {
200
+ return false;
55
201
  }
56
- return `${rowKey}.keyframe.${item.frame}`;
202
+ if (descendant.index !== ancestor.index) {
203
+ return false;
204
+ }
205
+ // Must be strictly deeper than `ancestor` (i.e. a descendant), not the same row.
206
+ if (descendant.auxiliaryKeys.length <= ancestor.auxiliaryKeys.length) {
207
+ return false;
208
+ }
209
+ return ancestor.auxiliaryKeys.every((key, i) => descendant.auxiliaryKeys[i] === key);
210
+ };
211
+ const getSelectableTimelineSequenceSelections = (tracks) => {
212
+ return tracks.flatMap((track) => {
213
+ if (track.nodePathInfo === null ||
214
+ track.nodePathInfo.auxiliaryKeys.length > 0) {
215
+ return [];
216
+ }
217
+ return [{ type: 'sequence', nodePathInfo: track.nodePathInfo }];
218
+ });
219
+ };
220
+ exports.getSelectableTimelineSequenceSelections = getSelectableTimelineSequenceSelections;
221
+ const getTimelineSequenceSelectionKey = (nodePathInfo) => (0, timeline_node_path_key_1.timelineNodePathInfoToKey)({ ...nodePathInfo, auxiliaryKeys: [] });
222
+ exports.getTimelineSequenceSelectionKey = getTimelineSequenceSelectionKey;
223
+ const TimelineSelectAllKeybindings = ({ timeline }) => {
224
+ const keybindings = (0, use_keybinding_1.useKeybinding)();
225
+ const { canSelect } = (0, exports.useTimelineSelection)();
226
+ const currentSelection = (0, exports.useCurrentTimelineSelectionStateAsRef)();
227
+ const selectableSequenceSelections = (0, react_1.useMemo)(() => (0, exports.getSelectableTimelineSequenceSelections)(timeline), [timeline]);
228
+ const selectableSequenceSelectionsRef = (0, react_1.useRef)(selectableSequenceSelections);
229
+ selectableSequenceSelectionsRef.current = selectableSequenceSelections;
230
+ (0, react_1.useEffect)(() => {
231
+ if (!canSelect) {
232
+ return;
233
+ }
234
+ const selectAll = keybindings.registerKeybinding({
235
+ event: 'keydown',
236
+ key: 'a',
237
+ callback: () => {
238
+ const latestSelectableSequenceSelections = selectableSequenceSelectionsRef.current;
239
+ if (latestSelectableSequenceSelections.length === 0) {
240
+ return;
241
+ }
242
+ currentSelection.current.selectItems(latestSelectableSequenceSelections);
243
+ },
244
+ commandCtrlKey: true,
245
+ preventDefault: true,
246
+ triggerIfInputFieldFocused: false,
247
+ keepRegisteredWhenNotHighestContext: false,
248
+ });
249
+ return () => {
250
+ selectAll.unregister();
251
+ };
252
+ }, [canSelect, currentSelection, keybindings]);
253
+ return null;
57
254
  };
255
+ exports.TimelineSelectAllKeybindings = TimelineSelectAllKeybindings;
58
256
  const TimelineSelectionProvider = ({ children }) => {
59
257
  const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
60
258
  const canSelect = exports.SELECTION_ENABLED &&
61
259
  previewServerState.type === 'connected' &&
62
260
  !window.remotion_isReadOnlyStudio;
63
- const [selectedItem, setSelectedItem] = (0, react_1.useState)(null);
261
+ const [selectedItems, setSelectedItems] = (0, react_1.useState)([]);
262
+ const selectionAnchor = (0, react_1.useRef)(null);
263
+ const selectableItemsOrder = (0, react_1.useRef)(new Map());
264
+ const selectableItems = (0, react_1.useRef)(new Map());
265
+ const registrationCounter = (0, react_1.useRef)(0);
64
266
  (0, react_1.useEffect)(() => {
65
267
  if (!canSelect) {
66
- setSelectedItem(null);
268
+ setSelectedItems([]);
67
269
  }
68
270
  }, [canSelect]);
271
+ const selectedKeys = (0, react_1.useMemo)(() => new Set(selectedItems.map(getTimelineSelectionKey)), [selectedItems]);
69
272
  const isSelected = (0, react_1.useCallback)((item) => {
70
- return selectedItem === null
71
- ? false
72
- : getTimelineSelectionKey(selectedItem) ===
73
- getTimelineSelectionKey(item);
74
- }, [selectedItem]);
75
- const selectItem = (0, react_1.useCallback)((item) => {
273
+ return selectedKeys.has(getTimelineSelectionKey(item));
274
+ }, [selectedKeys]);
275
+ const selectItem = (0, react_1.useCallback)((item, interaction = {
276
+ shiftKey: false,
277
+ toggleKey: false,
278
+ }) => {
279
+ if (!canSelect) {
280
+ return;
281
+ }
282
+ setSelectedItems((currentSelectedItems) => {
283
+ const orderedSelectableItems = [
284
+ ...selectableItems.current.values(),
285
+ ].sort((a, b) => {
286
+ var _a, _b;
287
+ return (((_a = selectableItemsOrder.current.get(getTimelineSelectionKey(a))) !== null && _a !== void 0 ? _a : 0) -
288
+ ((_b = selectableItemsOrder.current.get(getTimelineSelectionKey(b))) !== null && _b !== void 0 ? _b : 0));
289
+ });
290
+ const nextState = (0, exports.getTimelineSelectionAfterInteraction)({
291
+ currentState: {
292
+ selectedItems: currentSelectedItems,
293
+ anchor: selectionAnchor.current,
294
+ },
295
+ clickedItem: item,
296
+ interaction,
297
+ allSelectableItems: orderedSelectableItems,
298
+ });
299
+ selectionAnchor.current = nextState.anchor;
300
+ return nextState.selectedItems;
301
+ });
302
+ }, [canSelect]);
303
+ const selectItems = (0, react_1.useCallback)((items) => {
76
304
  if (!canSelect) {
77
305
  return;
78
306
  }
79
- setSelectedItem(item);
307
+ selectionAnchor.current =
308
+ items.length === 0 ? null : items[items.length - 1];
309
+ setSelectedItems(items);
80
310
  }, [canSelect]);
311
+ const registerSelectableItem = (0, react_1.useCallback)((item) => {
312
+ const key = getTimelineSelectionKey(item);
313
+ const registrationOrder = registrationCounter.current;
314
+ registrationCounter.current += 1;
315
+ selectableItems.current.set(key, item);
316
+ selectableItemsOrder.current.set(key, registrationOrder);
317
+ return () => {
318
+ selectableItems.current.delete(key);
319
+ selectableItemsOrder.current.delete(key);
320
+ };
321
+ }, []);
81
322
  const clearSelection = (0, react_1.useCallback)(() => {
82
- setSelectedItem(null);
323
+ selectionAnchor.current = null;
324
+ setSelectedItems([]);
83
325
  }, []);
84
326
  const containsSelection = (0, react_1.useCallback)((nodePathInfo) => {
85
- if (selectedItem === null) {
86
- return false;
87
- }
88
- const selectedNodePath = selectedItem.nodePathInfo;
89
- if ((0, studio_shared_1.stringifySequenceExpandedRowKey)(selectedNodePath.sequenceSubscriptionKey) !==
90
- (0, studio_shared_1.stringifySequenceExpandedRowKey)(nodePathInfo.sequenceSubscriptionKey)) {
91
- return false;
92
- }
93
- if (selectedNodePath.index !== nodePathInfo.index) {
94
- return false;
95
- }
96
- // Selection must be strictly deeper than this node (i.e. a descendant),
97
- // not the same row.
98
- if (selectedNodePath.auxiliaryKeys.length <=
99
- nodePathInfo.auxiliaryKeys.length) {
100
- return false;
101
- }
102
- return nodePathInfo.auxiliaryKeys.every((key, i) => selectedNodePath.auxiliaryKeys[i] === key);
103
- }, [selectedItem]);
327
+ return selectedItems.some((selected) => nodePathDescendsFrom(selected.nodePathInfo, nodePathInfo));
328
+ }, [selectedItems]);
104
329
  const value = (0, react_1.useMemo)(() => ({
105
330
  canSelect,
331
+ selectedItems,
332
+ isSelected,
333
+ selectItem,
334
+ selectItems,
335
+ registerSelectableItem,
336
+ containsSelection,
337
+ clearSelection,
338
+ }), [
339
+ canSelect,
340
+ selectedItems,
106
341
  isSelected,
107
342
  selectItem,
343
+ selectItems,
344
+ registerSelectableItem,
108
345
  containsSelection,
109
346
  clearSelection,
110
- }), [canSelect, isSelected, selectItem, containsSelection, clearSelection]);
111
- return (jsx_runtime_1.jsx(TimelineSelectionContext.Provider, { value: value, children: children }));
347
+ ]);
348
+ const currentSelection = (0, react_1.useRef)(value);
349
+ currentSelection.current = value;
350
+ return (jsx_runtime_1.jsx(CurrentTimelineSelectionContext.Provider, { value: currentSelection, children: jsx_runtime_1.jsxs(TimelineSelectionContext.Provider, { value: value, children: [children, jsx_runtime_1.jsx(TimelineDeleteKeybindings_1.TimelineDeleteKeybindings, {})
351
+ ] }) }));
112
352
  };
113
353
  exports.TimelineSelectionProvider = TimelineSelectionProvider;
114
354
  const useTimelineSelection = () => {
115
355
  return (0, react_1.useContext)(TimelineSelectionContext);
116
356
  };
117
357
  exports.useTimelineSelection = useTimelineSelection;
358
+ const useCurrentTimelineSelectionStateAsRef = () => {
359
+ const currentSelection = (0, react_1.useContext)(CurrentTimelineSelectionContext);
360
+ if (currentSelection === null) {
361
+ throw new Error('useCurrentTimelineSelectionStateAsRef must be used inside TimelineSelectionProvider');
362
+ }
363
+ return currentSelection;
364
+ };
365
+ exports.useCurrentTimelineSelectionStateAsRef = useCurrentTimelineSelectionStateAsRef;
118
366
  const useTimelineRowSelection = (nodePathInfo) => {
119
- const { canSelect, isSelected, selectItem } = (0, exports.useTimelineSelection)();
120
- const selectionItem = (0, react_1.useMemo)(() => nodePathInfo === null ? null : { type: 'row', nodePathInfo }, [nodePathInfo]);
367
+ const { canSelect, isSelected, selectItem, registerSelectableItem } = (0, exports.useTimelineSelection)();
368
+ const selectionItem = (0, react_1.useMemo)(() => (0, exports.getTimelineSelectionFromNodePathInfo)(nodePathInfo), [nodePathInfo]);
369
+ (0, react_1.useEffect)(() => {
370
+ if (selectionItem === null) {
371
+ return;
372
+ }
373
+ return registerSelectableItem(selectionItem);
374
+ }, [registerSelectableItem, selectionItem]);
121
375
  const selected = selectionItem === null ? false : isSelected(selectionItem);
122
- const onSelect = (0, react_1.useCallback)(() => {
376
+ const onSelect = (0, react_1.useCallback)((interaction) => {
123
377
  if (selectionItem === null) {
124
378
  return;
125
379
  }
126
- selectItem(selectionItem);
380
+ selectItem(selectionItem, interaction);
127
381
  }, [selectItem, selectionItem]);
128
382
  return {
129
383
  onSelect,
@@ -133,15 +387,18 @@ const useTimelineRowSelection = (nodePathInfo) => {
133
387
  };
134
388
  exports.useTimelineRowSelection = useTimelineRowSelection;
135
389
  const useTimelineKeyframeSelection = (nodePathInfo, frame) => {
136
- const { canSelect, isSelected, selectItem } = (0, exports.useTimelineSelection)();
390
+ const { canSelect, isSelected, selectItem, registerSelectableItem } = (0, exports.useTimelineSelection)();
137
391
  const selectionItem = (0, react_1.useMemo)(() => ({
138
392
  type: 'keyframe',
139
393
  nodePathInfo,
140
394
  frame,
141
395
  }), [nodePathInfo, frame]);
396
+ (0, react_1.useEffect)(() => {
397
+ return registerSelectableItem(selectionItem);
398
+ }, [registerSelectableItem, selectionItem]);
142
399
  const selected = isSelected(selectionItem);
143
- const onSelect = (0, react_1.useCallback)(() => {
144
- selectItem(selectionItem);
400
+ const onSelect = (0, react_1.useCallback)((interaction) => {
401
+ selectItem(selectionItem, interaction);
145
402
  }, [selectItem, selectionItem]);
146
403
  return {
147
404
  onSelect,
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { TSequence } from 'remotion';
3
+ import type { SequenceNodePathInfo } from '../../helpers/get-timeline-sequence-sort-key';
3
4
  export declare const TimelineSequence: React.NamedExoticComponent<{
4
5
  readonly s: TSequence;
6
+ readonly nodePathInfo: SequenceNodePathInfo | null;
5
7
  }>;
@@ -44,21 +44,32 @@ const use_max_media_duration_1 = require("../../helpers/use-max-media-duration")
44
44
  const AudioWaveform_1 = require("../AudioWaveform");
45
45
  const LoopedTimelineIndicators_1 = require("./LoopedTimelineIndicators");
46
46
  const TimelineImageInfo_1 = require("./TimelineImageInfo");
47
+ const TimelineSelection_1 = require("./TimelineSelection");
47
48
  const TimelineSequenceFrame_1 = require("./TimelineSequenceFrame");
48
49
  const TimelineVideoInfo_1 = require("./TimelineVideoInfo");
49
50
  const TimelineWidthProvider_1 = require("./TimelineWidthProvider");
50
51
  const AUDIO_GRADIENT = 'linear-gradient(rgb(16 171 58), rgb(43 165 63) 60%)';
51
52
  const VIDEO_GRADIENT = 'linear-gradient(to top, #8e44ad, #9b59b6)';
52
53
  const IMAGE_GRADIENT = 'linear-gradient(to top, #2980b9, #3498db)';
53
- const TimelineSequenceFn = ({ s }) => {
54
+ const TimelineSequenceFn = ({ s, nodePathInfo }) => {
54
55
  const windowWidth = (0, react_1.useContext)(TimelineWidthProvider_1.TimelineWidthContext);
55
56
  if (windowWidth === null) {
56
57
  return null;
57
58
  }
58
- return jsx_runtime_1.jsx(TimelineSequenceInner, { windowWidth: windowWidth, s: s });
59
+ return (jsx_runtime_1.jsx(TimelineSequenceInner, { windowWidth: windowWidth, s: s, nodePathInfo: nodePathInfo }));
59
60
  };
60
- const TimelineSequenceCurrentFrame = ({ s, displayDurationInFrames, premountWidth, postmountWidth, style, children, }) => {
61
+ const TimelineSequenceCurrentFrame = ({ s, displayDurationInFrames, premountWidth, postmountWidth, style, children, nodePathInfo, }) => {
61
62
  var _a, _b;
63
+ const { onSelect, selectable } = (0, TimelineSelection_1.useTimelineRowSelection)(nodePathInfo);
64
+ const onPointerDown = (0, react_1.useCallback)((e) => {
65
+ if (e.button === 0) {
66
+ e.stopPropagation();
67
+ onSelect({
68
+ shiftKey: e.shiftKey,
69
+ toggleKey: e.metaKey || e.ctrlKey,
70
+ });
71
+ }
72
+ }, [onSelect]);
62
73
  const frame = (0, remotion_1.useCurrentFrame)();
63
74
  const relativeFrame = frame - s.from;
64
75
  const relativeFrameWithPremount = relativeFrame + ((_a = s.premountDisplay) !== null && _a !== void 0 ? _a : 0);
@@ -75,9 +86,10 @@ const TimelineSequenceCurrentFrame = ({ s, displayDurationInFrames, premountWidt
75
86
  return {
76
87
  ...style,
77
88
  opacity: isInRange ? 1 : 0.5,
89
+ ...(TimelineSelection_1.TIMELINE_TOP_DRAG ? { cursor: 'pointer' } : {}),
78
90
  };
79
91
  }, [isInRange, style]);
80
- return (jsx_runtime_1.jsxs("div", { style: actualStyle, title: s.displayName, children: [premountWidth ? (jsx_runtime_1.jsx("div", { style: {
92
+ return (jsx_runtime_1.jsxs("div", { style: actualStyle, title: s.displayName, onPointerDown: selectable ? onPointerDown : undefined, children: [premountWidth ? (jsx_runtime_1.jsx("div", { style: {
81
93
  width: premountWidth,
82
94
  height: '100%',
83
95
  background: `repeating-linear-gradient(
@@ -111,7 +123,7 @@ const TimelineSequenceCurrentFrame = ({ s, displayDurationInFrames, premountWidt
111
123
  alignItems: 'center',
112
124
  }, children: jsx_runtime_1.jsx(TimelineSequenceFrame_1.TimelineSequenceFrame, { premounted: isPremounting, postmounted: isPostmounting ? s.duration - 1 : null, roundedFrame: roundedFrame }) })) : null] }));
113
125
  };
114
- const TimelineSequenceInner = ({ s, windowWidth }) => {
126
+ const TimelineSequenceInner = ({ s, windowWidth, nodePathInfo }) => {
115
127
  // If a duration is 1, it is essentially a still and it should have width 0
116
128
  // Some compositions may not be longer than their media duration,
117
129
  // if that is the case, it needs to be asynchronously determined
@@ -165,6 +177,6 @@ const TimelineSequenceInner = ({ s, windowWidth }) => {
165
177
  if (maxMediaDuration === null && !s.loopDisplay) {
166
178
  return null;
167
179
  }
168
- return (jsx_runtime_1.jsxs(TimelineSequenceCurrentFrame, { s: s, displayDurationInFrames: displayDurationInFrames, premountWidth: premountWidth, postmountWidth: postmountWidth, style: style, children: [s.type === 'audio' ? (jsx_runtime_1.jsx(AudioWaveform_1.AudioWaveform, { src: s.src, height: timeline_layout_1.TIMELINE_LAYER_HEIGHT_AUDIO, doesVolumeChange: s.doesVolumeChange, visualizationWidth: width, startFrom: s.startMediaFrom, durationInFrames: s.duration, volume: s.volume, playbackRate: s.playbackRate, loopDisplay: s.loopDisplay })) : null, s.type === 'video' ? (jsx_runtime_1.jsx(TimelineVideoInfo_1.TimelineVideoInfo, { src: s.src, visualizationWidth: width, naturalWidth: naturalWidth, trimBefore: s.startMediaFrom, durationInFrames: s.duration, playbackRate: s.playbackRate, volume: s.volume, doesVolumeChange: s.doesVolumeChange, premountWidth: premountWidth !== null && premountWidth !== void 0 ? premountWidth : 0, postmountWidth: postmountWidth !== null && postmountWidth !== void 0 ? postmountWidth : 0, loopDisplay: s.loopDisplay })) : null, s.type === 'image' ? (jsx_runtime_1.jsx(TimelineImageInfo_1.TimelineImageInfo, { src: s.src, visualizationWidth: width })) : null, s.loopDisplay === undefined ? null : (jsx_runtime_1.jsx(LoopedTimelineIndicators_1.LoopedTimelineIndicator, { loops: s.loopDisplay.numberOfTimes }))] }));
180
+ return (jsx_runtime_1.jsxs(TimelineSequenceCurrentFrame, { s: s, displayDurationInFrames: displayDurationInFrames, premountWidth: premountWidth, postmountWidth: postmountWidth, style: style, nodePathInfo: nodePathInfo, children: [s.type === 'audio' ? (jsx_runtime_1.jsx(AudioWaveform_1.AudioWaveform, { src: s.src, height: timeline_layout_1.TIMELINE_LAYER_HEIGHT_AUDIO, doesVolumeChange: s.doesVolumeChange, visualizationWidth: width, startFrom: s.startMediaFrom, durationInFrames: s.duration, volume: s.volume, playbackRate: s.playbackRate, loopDisplay: s.loopDisplay })) : null, s.type === 'video' ? (jsx_runtime_1.jsx(TimelineVideoInfo_1.TimelineVideoInfo, { src: s.src, visualizationWidth: width, naturalWidth: naturalWidth, trimBefore: s.startMediaFrom, durationInFrames: s.duration, playbackRate: s.playbackRate, volume: s.volume, doesVolumeChange: s.doesVolumeChange, premountWidth: premountWidth !== null && premountWidth !== void 0 ? premountWidth : 0, postmountWidth: postmountWidth !== null && postmountWidth !== void 0 ? postmountWidth : 0, loopDisplay: s.loopDisplay })) : null, s.type === 'image' ? (jsx_runtime_1.jsx(TimelineImageInfo_1.TimelineImageInfo, { src: s.src, visualizationWidth: width })) : null, s.loopDisplay === undefined ? null : (jsx_runtime_1.jsx(LoopedTimelineIndicators_1.LoopedTimelineIndicator, { loops: s.loopDisplay.numberOfTimes }))] }));
169
181
  };
170
182
  exports.TimelineSequence = react_1.default.memo(TimelineSequenceFn);
@@ -8,6 +8,7 @@ const relativeFrameStyle = {
8
8
  color: 'white',
9
9
  opacity: 0.5,
10
10
  whiteSpace: 'nowrap',
11
+ pointerEvents: 'none',
11
12
  };
12
13
  const TimelineSequenceFrame = ({ roundedFrame, premounted, postmounted }) => {
13
14
  return (jsx_runtime_1.jsx("div", { style: relativeFrameStyle, children: premounted
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
2
  import type { TSequence } from 'remotion';
3
3
  import type { SequenceNodePathInfo } from '../../helpers/get-timeline-sequence-sort-key';
4
- export declare const TimelineListItem: React.FC<{
4
+ export declare const TimelineSequenceItem: React.FC<{
5
5
  readonly sequence: TSequence;
6
6
  readonly nestedDepth: number;
7
7
  readonly nodePathInfo: SequenceNodePathInfo | null;
8
+ readonly keyframeDisplayOffset: number;
8
9
  }>;