@remotion/studio 4.0.476 → 4.0.478

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 (74) hide show
  1. package/dist/components/Canvas.js +40 -1
  2. package/dist/components/CanvasIfSizeIsAvailable.js +2 -6
  3. package/dist/components/ControlButton.d.ts +1 -0
  4. package/dist/components/ControlButton.js +7 -2
  5. package/dist/components/EditorGuides/Guide.js +151 -21
  6. package/dist/components/EditorRuler/Ruler.js +20 -24
  7. package/dist/components/EditorRuler/index.js +26 -13
  8. package/dist/components/InlineAction.js +1 -0
  9. package/dist/components/MenuToolbar.d.ts +1 -0
  10. package/dist/components/MenuToolbar.js +4 -1
  11. package/dist/components/OutlineToggle.js +1 -1
  12. package/dist/components/SelectedOutlineElement.d.ts +17 -0
  13. package/dist/components/SelectedOutlineElement.js +1009 -0
  14. package/dist/components/SelectedOutlineOverlay.d.ts +4 -210
  15. package/dist/components/SelectedOutlineOverlay.js +68 -1637
  16. package/dist/components/SelectedOutlineUvControls.js +1 -1
  17. package/dist/components/ShowGuidesProvider.js +4 -4
  18. package/dist/components/Timeline/SubscribeToNodePaths.d.ts +2 -1
  19. package/dist/components/Timeline/SubscribeToNodePaths.js +2 -1
  20. package/dist/components/Timeline/Timeline.js +3 -1
  21. package/dist/components/Timeline/TimelineClipboardKeybindings.js +9 -10
  22. package/dist/components/Timeline/TimelineDeleteKeybindings.js +15 -4
  23. package/dist/components/Timeline/TimelineKeyframeEasingLine.js +7 -11
  24. package/dist/components/Timeline/TimelineList.js +1 -1
  25. package/dist/components/Timeline/TimelineSelection.d.ts +27 -13
  26. package/dist/components/Timeline/TimelineSelection.js +47 -28
  27. package/dist/components/Timeline/TimelineSequence.js +156 -3
  28. package/dist/components/Timeline/TimelineSequenceFrame.d.ts +1 -0
  29. package/dist/components/Timeline/TimelineSequenceFrame.js +17 -6
  30. package/dist/components/Timeline/TimelineSequenceItem.d.ts +1 -0
  31. package/dist/components/Timeline/TimelineSequenceItem.js +90 -130
  32. package/dist/components/Timeline/TimelineVideoInfo.d.ts +1 -0
  33. package/dist/components/Timeline/TimelineVideoInfo.js +93 -8
  34. package/dist/components/Timeline/delete-selected-timeline-item.js +4 -0
  35. package/dist/components/Timeline/duplicate-selected-timeline-item.d.ts +8 -2
  36. package/dist/components/Timeline/duplicate-selected-timeline-item.js +32 -3
  37. package/dist/components/Timeline/get-sequence-context-menu-items.d.ts +20 -0
  38. package/dist/components/Timeline/get-sequence-context-menu-items.js +160 -0
  39. package/dist/components/Timeline/sequence-props-subscription-store.d.ts +2 -1
  40. package/dist/components/Timeline/sequence-props-subscription-store.js +11 -3
  41. package/dist/components/Timeline/should-clear-selection-on-pointer-down.d.ts +2 -0
  42. package/dist/components/Timeline/should-clear-selection-on-pointer-down.js +16 -2
  43. package/dist/components/Timeline/timeline-video-filmstrip-times.d.ts +17 -0
  44. package/dist/components/Timeline/timeline-video-filmstrip-times.js +22 -0
  45. package/dist/components/Timeline/update-selected-easing.d.ts +4 -6
  46. package/dist/components/Timeline/use-sequence-props-subscription.d.ts +2 -1
  47. package/dist/components/Timeline/use-sequence-props-subscription.js +3 -1
  48. package/dist/components/Timeline/use-timeline-keyframe-drag.d.ts +37 -1
  49. package/dist/components/Timeline/use-timeline-keyframe-drag.js +282 -1
  50. package/dist/components/import-assets.d.ts +45 -8
  51. package/dist/components/import-assets.js +227 -12
  52. package/dist/components/selected-outline-drag.d.ts +140 -0
  53. package/dist/components/selected-outline-drag.js +475 -0
  54. package/dist/components/selected-outline-measurement.d.ts +67 -0
  55. package/dist/components/selected-outline-measurement.js +355 -0
  56. package/dist/components/selected-outline-types.d.ts +121 -0
  57. package/dist/components/selected-outline-types.js +15 -0
  58. package/dist/components/selected-outline-uv.d.ts +1 -0
  59. package/dist/components/selected-outline-uv.js +12 -0
  60. package/dist/error-overlay/remotion-overlay/Overlay.js +3 -0
  61. package/dist/esm/{chunk-0atarw3p.js → chunk-hrw9799x.js} +12812 -11386
  62. package/dist/esm/internals.mjs +12812 -11386
  63. package/dist/esm/previewEntry.mjs +21059 -19629
  64. package/dist/esm/renderEntry.mjs +1 -1
  65. package/dist/helpers/editor-guide-selection.d.ts +31 -0
  66. package/dist/helpers/editor-guide-selection.js +58 -0
  67. package/dist/helpers/editor-ruler.d.ts +3 -3
  68. package/dist/helpers/editor-ruler.js +16 -18
  69. package/dist/helpers/get-preview-file-type.js +1 -1
  70. package/dist/helpers/ruler-canvas-size.d.ts +5 -0
  71. package/dist/helpers/ruler-canvas-size.js +17 -0
  72. package/dist/state/editor-guides.d.ts +2 -2
  73. package/dist/state/editor-guides.js +2 -2
  74. package/package.json +11 -11
@@ -8,6 +8,7 @@ const remotion_1 = require("remotion");
8
8
  const timeline_layout_1 = require("../../helpers/timeline-layout");
9
9
  const AudioWaveform_1 = require("../AudioWaveform");
10
10
  const get_timeline_video_info_widths_1 = require("./get-timeline-video-info-widths");
11
+ const timeline_video_filmstrip_times_1 = require("./timeline-video-filmstrip-times");
11
12
  const outerStyle = {
12
13
  width: '100%',
13
14
  height: '100%',
@@ -22,7 +23,8 @@ const filmstripContainerStyle = {
22
23
  fontSize: 10,
23
24
  fontFamily: 'Arial, Helvetica',
24
25
  };
25
- const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore, durationInFrames, playbackRate, volume, doesVolumeChange, premountWidth, postmountWidth, loopDisplay, }) => {
26
+ const MAX_FROZEN_FRAME_CACHE_DEVIATION = timeline_utils_1.WEBCODECS_TIMESCALE * 0.05;
27
+ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore, durationInFrames, playbackRate, volume, doesVolumeChange, premountWidth, postmountWidth, loopDisplay, frozenMediaFrame, }) => {
26
28
  const { fps } = (0, remotion_1.useVideoConfig)();
27
29
  const ref = (0, react_1.useRef)(null);
28
30
  const [error, setError] = (0, react_1.useState)(null);
@@ -53,6 +55,94 @@ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore,
53
55
  return;
54
56
  }
55
57
  current.appendChild(canvas);
58
+ const drawRepeatedFrame = (frame) => {
59
+ const thumbnailWidth = Math.max(1, frame.displayWidth / window.devicePixelRatio);
60
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
61
+ for (let x = 0; x < canvas.width; x += thumbnailWidth) {
62
+ ctx.drawImage(frame, x, 0, thumbnailWidth, timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT);
63
+ }
64
+ };
65
+ const getCachedFrozenFrame = (timestamp) => {
66
+ const prefix = (0, timeline_utils_1.getFrameDatabaseKeyPrefix)(src);
67
+ const keys = Array.from(timeline_utils_1.frameDatabase.keys()).filter((k) => k.startsWith(prefix));
68
+ let bestDistance = Infinity;
69
+ let bestFrame = null;
70
+ for (const key of keys) {
71
+ const frame = timeline_utils_1.frameDatabase.get(key);
72
+ if (!frame) {
73
+ continue;
74
+ }
75
+ const distance = Math.abs((0, timeline_utils_1.getTimestampFromFrameDatabaseKey)(key) - timestamp);
76
+ if (distance < bestDistance) {
77
+ bestDistance = distance;
78
+ bestFrame = frame.frame;
79
+ }
80
+ }
81
+ return bestDistance <= MAX_FROZEN_FRAME_CACHE_DEVIATION
82
+ ? bestFrame
83
+ : null;
84
+ };
85
+ const times = (0, timeline_video_filmstrip_times_1.getTimelineVideoFilmstripTimes)({
86
+ trimBefore,
87
+ durationInFrames,
88
+ playbackRate,
89
+ fps,
90
+ loopDisplay,
91
+ frozenMediaFrame,
92
+ });
93
+ if (times.type === 'frozen') {
94
+ const timestamp = times.timestampInSeconds * timeline_utils_1.WEBCODECS_TIMESCALE;
95
+ const cachedFrame = getCachedFrozenFrame(timestamp);
96
+ if (cachedFrame) {
97
+ drawRepeatedFrame(cachedFrame);
98
+ return () => {
99
+ current.removeChild(canvas);
100
+ };
101
+ }
102
+ (0, timeline_utils_1.extractFrames)({
103
+ timestampsInSeconds: ({ track, }) => {
104
+ aspectRatio.current = track.width / track.height;
105
+ timeline_utils_1.aspectRatioCache.set(src, aspectRatio.current);
106
+ return [times.timestampInSeconds];
107
+ },
108
+ src,
109
+ onVideoSample: (sample) => {
110
+ let frame;
111
+ try {
112
+ frame = sample.toVideoFrame();
113
+ const scale = (timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT / frame.displayHeight) *
114
+ window.devicePixelRatio;
115
+ const transformed = (0, timeline_utils_1.resizeVideoFrame)({
116
+ frame,
117
+ scale,
118
+ });
119
+ if (transformed !== frame) {
120
+ frame.close();
121
+ }
122
+ frame = undefined;
123
+ const databaseKey = (0, timeline_utils_1.makeFrameDatabaseKey)(src, transformed.timestamp);
124
+ (0, timeline_utils_1.addFrameToCache)(databaseKey, transformed);
125
+ drawRepeatedFrame(transformed);
126
+ }
127
+ catch (e) {
128
+ if (frame) {
129
+ frame.close();
130
+ }
131
+ throw e;
132
+ }
133
+ finally {
134
+ sample.close();
135
+ }
136
+ },
137
+ signal: controller.signal,
138
+ }).catch((e) => {
139
+ setError(e);
140
+ });
141
+ return () => {
142
+ controller.abort();
143
+ current.removeChild(canvas);
144
+ };
145
+ }
56
146
  const loopWidth = (0, timeline_utils_1.getLoopDisplayWidth)({
57
147
  visualizationWidth: mediaNaturalWidth,
58
148
  loopDisplay,
@@ -85,13 +175,7 @@ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore,
85
175
  };
86
176
  // desired-timestamp -> filled-timestamp
87
177
  const filledSlots = new Map();
88
- const fromSeconds = trimBefore / fps;
89
- const visibleDurationInFrames = shouldRepeatVideo && loopDisplay
90
- ? loopDisplay.durationInFrames
91
- : durationInFrames;
92
- // Trim is applied first, then playbackRate. Each composition frame
93
- // advances the source video by `playbackRate` source frames.
94
- const toSeconds = fromSeconds + (visibleDurationInFrames * playbackRate) / fps;
178
+ const { fromSeconds, toSeconds } = times;
95
179
  const targetWidth = shouldRepeatVideo
96
180
  ? targetCanvas.width
97
181
  : mediaNaturalWidth;
@@ -216,6 +300,7 @@ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore,
216
300
  durationInFrames,
217
301
  error,
218
302
  fps,
303
+ frozenMediaFrame,
219
304
  loopDisplay,
220
305
  mediaNaturalWidth,
221
306
  mediaVisualizationWidth,
@@ -107,6 +107,8 @@ const deleteSelectedTimelineItem = ({ selection, sequences, overrideIdsToNodePat
107
107
  return (_a = promise === null || promise === void 0 ? void 0 : promise.then(() => true)) !== null && _a !== void 0 ? _a : null;
108
108
  }
109
109
  switch (selection.type) {
110
+ case 'guide':
111
+ return null;
110
112
  case 'sequence':
111
113
  return deleteSequences([selection.nodePathInfo], confirm);
112
114
  case 'sequence-effect':
@@ -175,6 +177,8 @@ const deleteSelectedTimelineItems = ({ selections, sequences, overrideIdsToNodeP
175
177
  }
176
178
  assertTimelineSelectionsHaveSameType(selections);
177
179
  switch (firstSelection.type) {
180
+ case 'guide':
181
+ return null;
178
182
  case 'sequence':
179
183
  return deleteSequences(selections
180
184
  .filter(isSequenceRowSelection)
@@ -3,12 +3,18 @@ import type { ConfirmationDialogFunction } from '../ConfirmationDialog-types';
3
3
  import type { TimelineSelection } from './TimelineSelection';
4
4
  export declare const duplicateSequencesFromSource: (nodePathInfos: readonly SequenceNodePathInfo[], confirm: ConfirmationDialogFunction) => Promise<void>;
5
5
  export declare const isDuplicatableSequenceRowSelection: (selection: TimelineSelection) => selection is {
6
- readonly nodePathInfo: SequenceNodePathInfo;
7
- } & {
8
6
  readonly type: "sequence";
7
+ readonly nodePathInfo: SequenceNodePathInfo;
9
8
  } & {
10
9
  type: "sequence";
11
10
  };
11
+ export declare const isDuplicatableEffectSelection: (selection: TimelineSelection) => selection is {
12
+ readonly type: "sequence-effect";
13
+ readonly nodePathInfo: SequenceNodePathInfo;
14
+ readonly i: number;
15
+ } & {
16
+ type: "sequence-effect";
17
+ };
12
18
  export declare const duplicateSelectedTimelineItems: ({ selections, confirm, }: {
13
19
  selections: readonly TimelineSelection[];
14
20
  confirm: ConfirmationDialogFunction;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.duplicateSelectedTimelineItems = exports.isDuplicatableSequenceRowSelection = exports.duplicateSequencesFromSource = void 0;
3
+ exports.duplicateSelectedTimelineItems = exports.isDuplicatableEffectSelection = exports.isDuplicatableSequenceRowSelection = exports.duplicateSequencesFromSource = void 0;
4
4
  const call_api_1 = require("../call-api");
5
5
  const NotificationCenter_1 = require("../Notifications/NotificationCenter");
6
6
  const confirmDuplicatingProgrammaticallyDuplicatedSequences = (nodePathInfos, confirm) => {
@@ -61,11 +61,40 @@ const duplicateSequencesFromSource = (nodePathInfos, confirm) => {
61
61
  exports.duplicateSequencesFromSource = duplicateSequencesFromSource;
62
62
  const isDuplicatableSequenceRowSelection = (selection) => selection.type === 'sequence';
63
63
  exports.isDuplicatableSequenceRowSelection = isDuplicatableSequenceRowSelection;
64
+ const isDuplicatableEffectSelection = (selection) => selection.type === 'sequence-effect';
65
+ exports.isDuplicatableEffectSelection = isDuplicatableEffectSelection;
66
+ const duplicateEffectsFromSource = (effects) => {
67
+ return (0, call_api_1.callApi)('/api/duplicate-effect', effects.map((effect) => {
68
+ const nodePath = effect.nodePathInfo.sequenceSubscriptionKey;
69
+ return {
70
+ fileName: nodePath.absolutePath,
71
+ sequenceNodePath: nodePath,
72
+ effectIndex: effect.i,
73
+ };
74
+ }))
75
+ .then((result) => {
76
+ if (result.success) {
77
+ (0, NotificationCenter_1.showNotification)(effects.length === 1
78
+ ? 'Duplicated effect in source file'
79
+ : 'Duplicated effects in source files', 2000);
80
+ }
81
+ else {
82
+ (0, NotificationCenter_1.showNotification)(result.reason, 4000);
83
+ }
84
+ })
85
+ .catch((err) => {
86
+ (0, NotificationCenter_1.showNotification)(err.message, 4000);
87
+ });
88
+ };
64
89
  const duplicateSelectedTimelineItems = ({ selections, confirm, }) => {
65
90
  const sequenceSelections = selections.filter(exports.isDuplicatableSequenceRowSelection);
66
- if (sequenceSelections.length === 0) {
91
+ if (sequenceSelections.length > 0) {
92
+ return (0, exports.duplicateSequencesFromSource)(sequenceSelections.map((selection) => selection.nodePathInfo), confirm);
93
+ }
94
+ const effectSelections = selections.filter(exports.isDuplicatableEffectSelection);
95
+ if (effectSelections.length === 0) {
67
96
  return null;
68
97
  }
69
- return (0, exports.duplicateSequencesFromSource)(sequenceSelections.map((selection) => selection.nodePathInfo), confirm);
98
+ return duplicateEffectsFromSource(effectSelections);
70
99
  };
71
100
  exports.duplicateSelectedTimelineItems = duplicateSelectedTimelineItems;
@@ -0,0 +1,20 @@
1
+ import type { ResolvedStackLocation, TSequence } from 'remotion';
2
+ import type { ComboboxValue } from '../NewComposition/ComboBox';
3
+ import type { TimelineAssetLinkInfo } from './timeline-asset-link';
4
+ export declare const getSequenceContextMenuItems: ({ assetLinkInfo, canOpenInEditor, deleteDisabled, disableInteractivityDisabled, duplicateDisabled, fileLocation, includeSourceEditItems, onDeleteSequenceFromSource, onDisableSequenceInteractivity, onDuplicateSequenceFromSource, openInEditor, originalLocation, selectAsset, sequence, sourceActions, }: {
5
+ readonly assetLinkInfo: TimelineAssetLinkInfo | null;
6
+ readonly canOpenInEditor: boolean;
7
+ readonly deleteDisabled: boolean;
8
+ readonly disableInteractivityDisabled: boolean;
9
+ readonly duplicateDisabled: boolean;
10
+ readonly fileLocation: string | null;
11
+ readonly includeSourceEditItems: boolean;
12
+ readonly onDeleteSequenceFromSource: () => void;
13
+ readonly onDisableSequenceInteractivity: () => void;
14
+ readonly onDuplicateSequenceFromSource: () => void;
15
+ readonly openInEditor: () => void;
16
+ readonly originalLocation: ResolvedStackLocation | null;
17
+ readonly selectAsset: (src: string) => void;
18
+ readonly sequence: TSequence;
19
+ readonly sourceActions?: readonly ComboboxValue[] | undefined;
20
+ }) => ComboboxValue[];
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSequenceContextMenuItems = void 0;
4
+ const no_react_1 = require("remotion/no-react");
5
+ const NotificationCenter_1 = require("../Notifications/NotificationCenter");
6
+ const timeline_asset_link_1 = require("./timeline-asset-link");
7
+ const normalizeMenuDividers = (items) => {
8
+ var _a;
9
+ const normalized = [];
10
+ for (const item of items) {
11
+ if (item.type === 'divider') {
12
+ const previousItem = normalized[normalized.length - 1];
13
+ if (!previousItem || previousItem.type === 'divider') {
14
+ continue;
15
+ }
16
+ }
17
+ normalized.push(item);
18
+ }
19
+ if (((_a = normalized[normalized.length - 1]) === null || _a === void 0 ? void 0 : _a.type) === 'divider') {
20
+ normalized.pop();
21
+ }
22
+ return normalized;
23
+ };
24
+ const getSequenceContextMenuItems = ({ assetLinkInfo, canOpenInEditor, deleteDisabled, disableInteractivityDisabled, duplicateDisabled, fileLocation, includeSourceEditItems, onDeleteSequenceFromSource, onDisableSequenceInteractivity, onDuplicateSequenceFromSource, openInEditor, originalLocation, selectAsset, sequence, sourceActions = [], }) => {
25
+ const editorName = window.remotion_editorName;
26
+ const { documentationLink } = sequence;
27
+ const items = [
28
+ editorName
29
+ ? {
30
+ type: 'item',
31
+ id: 'show-in-editor',
32
+ keyHint: null,
33
+ label: `Show in ${editorName}`,
34
+ leftItem: null,
35
+ disabled: !canOpenInEditor || !originalLocation,
36
+ onClick: openInEditor,
37
+ quickSwitcherLabel: null,
38
+ subMenu: null,
39
+ value: 'show-in-editor',
40
+ }
41
+ : null,
42
+ {
43
+ type: 'item',
44
+ id: 'copy-file-location',
45
+ keyHint: null,
46
+ label: 'Copy file location',
47
+ leftItem: null,
48
+ disabled: !fileLocation,
49
+ onClick: () => {
50
+ if (!fileLocation) {
51
+ return;
52
+ }
53
+ navigator.clipboard
54
+ .writeText(fileLocation)
55
+ .then(() => {
56
+ (0, NotificationCenter_1.showNotification)('Copied file location to clipboard', 1000);
57
+ })
58
+ .catch((err) => {
59
+ (0, NotificationCenter_1.showNotification)(`Could not copy to clipboard: ${err.message}`, 1000);
60
+ });
61
+ },
62
+ quickSwitcherLabel: null,
63
+ subMenu: null,
64
+ value: 'copy-file-location',
65
+ },
66
+ documentationLink
67
+ ? {
68
+ type: 'item',
69
+ id: 'open-component-docs',
70
+ keyHint: null,
71
+ label: 'Open component docs',
72
+ leftItem: null,
73
+ disabled: false,
74
+ onClick: () => {
75
+ window.open(documentationLink, '_blank', 'noopener,noreferrer');
76
+ },
77
+ quickSwitcherLabel: null,
78
+ subMenu: null,
79
+ value: 'open-component-docs',
80
+ }
81
+ : null,
82
+ assetLinkInfo
83
+ ? {
84
+ type: 'item',
85
+ id: 'show-asset',
86
+ keyHint: null,
87
+ label: 'Show asset',
88
+ leftItem: null,
89
+ disabled: false,
90
+ onClick: () => {
91
+ (0, timeline_asset_link_1.openTimelineAssetLink)(assetLinkInfo, selectAsset);
92
+ },
93
+ quickSwitcherLabel: null,
94
+ subMenu: null,
95
+ value: 'show-asset',
96
+ }
97
+ : null,
98
+ documentationLink || assetLinkInfo
99
+ ? {
100
+ type: 'divider',
101
+ id: 'sequence-link-divider',
102
+ }
103
+ : null,
104
+ sourceActions.length > 0
105
+ ? {
106
+ type: 'divider',
107
+ id: 'sequence-source-actions-divider',
108
+ }
109
+ : null,
110
+ ...sourceActions,
111
+ {
112
+ type: 'item',
113
+ id: 'disable-interactivity',
114
+ keyHint: null,
115
+ label: 'Disable interactivity',
116
+ leftItem: null,
117
+ disabled: disableInteractivityDisabled,
118
+ onClick: onDisableSequenceInteractivity,
119
+ quickSwitcherLabel: null,
120
+ subMenu: null,
121
+ value: 'disable-interactivity',
122
+ },
123
+ includeSourceEditItems
124
+ ? {
125
+ type: 'item',
126
+ id: 'duplicate-sequence',
127
+ keyHint: null,
128
+ label: 'Duplicate',
129
+ leftItem: null,
130
+ disabled: duplicateDisabled,
131
+ onClick: onDuplicateSequenceFromSource,
132
+ quickSwitcherLabel: null,
133
+ subMenu: null,
134
+ value: 'duplicate-sequence',
135
+ }
136
+ : null,
137
+ includeSourceEditItems
138
+ ? {
139
+ type: 'divider',
140
+ id: 'sequence-duplicate-delete-divider',
141
+ }
142
+ : null,
143
+ includeSourceEditItems
144
+ ? {
145
+ type: 'item',
146
+ id: 'delete-sequence',
147
+ keyHint: null,
148
+ label: 'Delete',
149
+ leftItem: null,
150
+ disabled: deleteDisabled,
151
+ onClick: onDeleteSequenceFromSource,
152
+ quickSwitcherLabel: null,
153
+ subMenu: null,
154
+ value: 'delete-sequence',
155
+ }
156
+ : null,
157
+ ].filter(no_react_1.NoReactInternals.truthy);
158
+ return normalizeMenuDividers(items);
159
+ };
160
+ exports.getSequenceContextMenuItems = getSequenceContextMenuItems;
@@ -2,11 +2,12 @@ import type { SequenceNodePath, SequenceSchema } from 'remotion';
2
2
  import { callApi } from '../call-api';
3
3
  type SubscribeResult = Awaited<ReturnType<typeof callApi<'/api/subscribe-to-sequence-props'>>>;
4
4
  type ApplyResult = (result: SubscribeResult) => void;
5
- export declare const acquireSequencePropsSubscription: ({ fileName, line, column, schema, effects, nodePath, clientId, applyOnce, applyEach, }: {
5
+ export declare const acquireSequencePropsSubscription: ({ fileName, line, column, schema, componentIdentity, effects, nodePath, clientId, applyOnce, applyEach, }: {
6
6
  fileName: string;
7
7
  line: number;
8
8
  column: number;
9
9
  schema: SequenceSchema;
10
+ componentIdentity: string | null;
10
11
  effects: SequenceSchema[];
11
12
  nodePath: SequenceNodePath | null;
12
13
  clientId: string;
@@ -4,12 +4,19 @@ exports.acquireSequencePropsSubscription = void 0;
4
4
  const studio_shared_1 = require("@remotion/studio-shared");
5
5
  const remotion_1 = require("remotion");
6
6
  const call_api_1 = require("../call-api");
7
- const makeKey = (fileName, line, column, sequenceKeys, effectKeys) => `${fileName}\0${line}\0${column}\0${sequenceKeys.join('\0')}\0${effectKeys.map((keys) => keys.join('\0')).join('\0\0')}`;
7
+ const makeKey = ({ fileName, line, column, componentIdentity, sequenceKeys, effectKeys, }) => `${fileName}\0${line}\0${column}\0${componentIdentity !== null && componentIdentity !== void 0 ? componentIdentity : ''}\0${sequenceKeys.join('\0')}\0${effectKeys.map((keys) => keys.join('\0')).join('\0\0')}`;
8
8
  const entries = new Map();
9
- const acquireSequencePropsSubscription = ({ fileName, line, column, schema, effects, nodePath, clientId, applyOnce, applyEach, }) => {
9
+ const acquireSequencePropsSubscription = ({ fileName, line, column, schema, componentIdentity, effects, nodePath, clientId, applyOnce, applyEach, }) => {
10
10
  const sequenceKeys = (0, studio_shared_1.getAllSchemaKeys)(schema);
11
11
  const effectKeys = effects.map((effect) => (0, studio_shared_1.getAllSchemaKeys)(effect));
12
- const key = makeKey(fileName, line, column, sequenceKeys, effectKeys);
12
+ const key = makeKey({
13
+ fileName,
14
+ line,
15
+ column,
16
+ componentIdentity,
17
+ sequenceKeys,
18
+ effectKeys,
19
+ });
13
20
  let entry = entries.get(key);
14
21
  if (!entry) {
15
22
  const promise = (0, call_api_1.callApi)('/api/subscribe-to-sequence-props', {
@@ -17,6 +24,7 @@ const acquireSequencePropsSubscription = ({ fileName, line, column, schema, effe
17
24
  line,
18
25
  column,
19
26
  nodePath,
27
+ componentIdentity,
20
28
  keys: (0, studio_shared_1.getAllSchemaKeys)(schema),
21
29
  effects: effectKeys,
22
30
  clientId,
@@ -1,3 +1,5 @@
1
+ export declare const PREVENT_CLEAR_SELECTION_ON_POINTER_DOWN_ATTR = "data-remotion-prevent-selection-clear";
1
2
  export declare const shouldClearSelectionOnPointerDown: (event: {
2
3
  readonly button: number;
4
+ readonly target?: EventTarget | null | undefined;
3
5
  }) => boolean;
@@ -1,7 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.shouldClearSelectionOnPointerDown = void 0;
3
+ exports.shouldClearSelectionOnPointerDown = exports.PREVENT_CLEAR_SELECTION_ON_POINTER_DOWN_ATTR = void 0;
4
+ exports.PREVENT_CLEAR_SELECTION_ON_POINTER_DOWN_ATTR = 'data-remotion-prevent-selection-clear';
5
+ const isSelectionClearBlocked = (target) => {
6
+ if (target === null ||
7
+ target === undefined ||
8
+ typeof target !== 'object' ||
9
+ !('closest' in target) ||
10
+ typeof target.closest !== 'function') {
11
+ return false;
12
+ }
13
+ return (target.closest(`[${exports.PREVENT_CLEAR_SELECTION_ON_POINTER_DOWN_ATTR}]`) !== null);
14
+ };
4
15
  const shouldClearSelectionOnPointerDown = (event) => {
5
- return event.button === 0;
16
+ if (event.button !== 0) {
17
+ return false;
18
+ }
19
+ return !isSelectionClearBlocked(event.target);
6
20
  };
7
21
  exports.shouldClearSelectionOnPointerDown = shouldClearSelectionOnPointerDown;
@@ -0,0 +1,17 @@
1
+ import type { LoopDisplay } from 'remotion';
2
+ export type TimelineVideoFilmstripTimes = {
3
+ type: 'frozen';
4
+ timestampInSeconds: number;
5
+ } | {
6
+ type: 'range';
7
+ fromSeconds: number;
8
+ toSeconds: number;
9
+ };
10
+ export declare const getTimelineVideoFilmstripTimes: ({ trimBefore, durationInFrames, playbackRate, fps, loopDisplay, frozenMediaFrame, }: {
11
+ trimBefore: number;
12
+ durationInFrames: number;
13
+ playbackRate: number;
14
+ fps: number;
15
+ loopDisplay: LoopDisplay | undefined;
16
+ frozenMediaFrame: number | null;
17
+ }) => TimelineVideoFilmstripTimes;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTimelineVideoFilmstripTimes = void 0;
4
+ const timeline_utils_1 = require("@remotion/timeline-utils");
5
+ const getTimelineVideoFilmstripTimes = ({ trimBefore, durationInFrames, playbackRate, fps, loopDisplay, frozenMediaFrame, }) => {
6
+ if (frozenMediaFrame !== null) {
7
+ return {
8
+ type: 'frozen',
9
+ timestampInSeconds: Math.max(0, frozenMediaFrame / fps),
10
+ };
11
+ }
12
+ const fromSeconds = trimBefore / fps;
13
+ const visibleDurationInFrames = (0, timeline_utils_1.shouldTileLoopDisplay)(loopDisplay) && loopDisplay
14
+ ? loopDisplay.durationInFrames
15
+ : durationInFrames;
16
+ return {
17
+ type: 'range',
18
+ fromSeconds,
19
+ toSeconds: fromSeconds + (visibleDurationInFrames * playbackRate) / fps,
20
+ };
21
+ };
22
+ exports.getTimelineVideoFilmstripTimes = getTimelineVideoFilmstripTimes;
@@ -3,19 +3,17 @@ import type { SetPropStatuses } from './save-sequence-prop';
3
3
  import type { TimelineEasingSelection, TimelineSelection } from './TimelineSelection';
4
4
  export type EasingSelection = TimelineEasingSelection;
5
5
  export type TimelineEasingValue = 'linear' | [number, number, number, number];
6
- export declare const getEasingSelections: (selections: readonly TimelineSelection[]) => ({
7
- readonly nodePathInfo: import("../../helpers/get-timeline-sequence-sort-key").SequenceNodePathInfo;
8
- } & {
6
+ export declare const getEasingSelections: (selections: readonly TimelineSelection[]) => {
9
7
  readonly type: "easing";
8
+ readonly nodePathInfo: import("../../helpers/get-timeline-sequence-sort-key").SequenceNodePathInfo;
10
9
  readonly fromFrame: number;
11
10
  readonly toFrame: number;
12
11
  readonly segmentIndex: number;
13
- })[];
12
+ }[];
14
13
  export declare const getTimelineEasingValueForSelection: ({ selection, sequences, overrideIdsToNodePaths, propStatuses, }: {
15
14
  selection: {
16
- readonly nodePathInfo: import("../../helpers/get-timeline-sequence-sort-key").SequenceNodePathInfo;
17
- } & {
18
15
  readonly type: "easing";
16
+ readonly nodePathInfo: import("../../helpers/get-timeline-sequence-sort-key").SequenceNodePathInfo;
19
17
  readonly fromFrame: number;
20
18
  readonly toFrame: number;
21
19
  readonly segmentIndex: number;
@@ -1,7 +1,8 @@
1
1
  import type { SequenceSchema } from 'remotion';
2
2
  import type { OriginalPosition } from '../../error-overlay/react-overlay/utils/get-source-map';
3
- export declare const useSequencePropsSubscription: ({ originalLocation, overrideId, schema, effects, }: {
3
+ export declare const useSequencePropsSubscription: ({ originalLocation, overrideId, componentIdentity, schema, effects, }: {
4
4
  overrideId: string;
5
+ componentIdentity: string | null;
5
6
  schema: SequenceSchema;
6
7
  effects: SequenceSchema[];
7
8
  originalLocation: OriginalPosition | null;
@@ -7,7 +7,7 @@ const remotion_1 = require("remotion");
7
7
  const client_id_1 = require("../../helpers/client-id");
8
8
  const ExpandedTracksProvider_1 = require("../ExpandedTracksProvider");
9
9
  const sequence_props_subscription_store_1 = require("./sequence-props-subscription-store");
10
- const useSequencePropsSubscription = ({ originalLocation, overrideId, schema, effects, }) => {
10
+ const useSequencePropsSubscription = ({ originalLocation, overrideId, componentIdentity, schema, effects, }) => {
11
11
  var _a, _b, _c;
12
12
  const { setPropStatuses } = (0, react_1.useContext)(remotion_1.Internals.VisualModeSettersContext);
13
13
  const { setOverrideIdToNodePath } = (0, react_1.useContext)(remotion_1.Internals.OverrideIdsToNodePathsSettersContext);
@@ -50,6 +50,7 @@ const useSequencePropsSubscription = ({ originalLocation, overrideId, schema, ef
50
50
  line: locationLine,
51
51
  column: locationColumn,
52
52
  schema,
53
+ componentIdentity,
53
54
  effects,
54
55
  nodePath: (_b = nodePathAtResubscribe === null || nodePathAtResubscribe === void 0 ? void 0 : nodePathAtResubscribe.nodePath) !== null && _b !== void 0 ? _b : null,
55
56
  clientId,
@@ -85,6 +86,7 @@ const useSequencePropsSubscription = ({ originalLocation, overrideId, schema, ef
85
86
  };
86
87
  }, [
87
88
  clientId,
89
+ componentIdentity,
88
90
  effects,
89
91
  effectsSignature,
90
92
  locationColumn,
@@ -1,6 +1,30 @@
1
1
  import type React from 'react';
2
2
  import type { SequenceNodePathInfo } from '../../helpers/get-timeline-sequence-sort-key';
3
- import { type TimelineSelectionInteraction } from './TimelineSelection';
3
+ import { type TimelineDraggedKeyframe } from './TimelineKeyframeDragState';
4
+ import { type TimelineSelection, type TimelineSelectionInteraction } from './TimelineSelection';
5
+ export declare const getKeyframesForTimelineEasingDrag: ({ currentSelections, interaction, selectionItem, selected, }: {
6
+ readonly currentSelections: readonly TimelineSelection[];
7
+ readonly interaction: TimelineSelectionInteraction;
8
+ readonly selectionItem: {
9
+ readonly type: "easing";
10
+ readonly nodePathInfo: SequenceNodePathInfo;
11
+ readonly fromFrame: number;
12
+ readonly toFrame: number;
13
+ readonly segmentIndex: number;
14
+ };
15
+ readonly selected: boolean;
16
+ }) => ({
17
+ readonly type: "keyframe";
18
+ readonly nodePathInfo: SequenceNodePathInfo;
19
+ readonly frame: number;
20
+ } & {
21
+ type: "keyframe";
22
+ })[];
23
+ export declare const getTimelineSelectionsAfterEasingKeyframeDrag: ({ delta, selections, targets, }: {
24
+ readonly delta: number;
25
+ readonly selections: readonly TimelineSelection[];
26
+ readonly targets: readonly TimelineDraggedKeyframe[];
27
+ }) => TimelineSelection[];
4
28
  export declare const useTimelineKeyframeDrag: ({ frame, nodePathInfo, onSelect, selectable, selected, }: {
5
29
  readonly frame: number;
6
30
  readonly nodePathInfo: SequenceNodePathInfo;
@@ -8,3 +32,15 @@ export declare const useTimelineKeyframeDrag: ({ frame, nodePathInfo, onSelect,
8
32
  readonly selectable: boolean;
9
33
  readonly selected: boolean;
10
34
  }) => (e: React.PointerEvent<HTMLButtonElement>) => void;
35
+ export declare const useTimelineEasingKeyframeDrag: ({ onSelect, selectable, selected, selectionItem, }: {
36
+ readonly onSelect: (interaction?: TimelineSelectionInteraction | undefined) => void;
37
+ readonly selectable: boolean;
38
+ readonly selected: boolean;
39
+ readonly selectionItem: {
40
+ readonly type: "easing";
41
+ readonly nodePathInfo: SequenceNodePathInfo;
42
+ readonly fromFrame: number;
43
+ readonly toFrame: number;
44
+ readonly segmentIndex: number;
45
+ };
46
+ }) => (e: React.PointerEvent<HTMLButtonElement>) => void;