@remotion/studio 4.0.473 → 4.0.475

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 (123) hide show
  1. package/dist/components/AssetSelectorItem.js +30 -6
  2. package/dist/components/Canvas.js +77 -0
  3. package/dist/components/ColorPicker/ColorPicker.js +4 -31
  4. package/dist/components/CompositionSelectorItem.js +4 -4
  5. package/dist/components/Editor.js +4 -1
  6. package/dist/components/Modals.js +2 -2
  7. package/dist/components/NewComposition/ComboBox.js +1 -0
  8. package/dist/components/NewComposition/InputDragger.d.ts +1 -0
  9. package/dist/components/NewComposition/InputDragger.js +9 -6
  10. package/dist/components/PreviewToolbar.js +2 -2
  11. package/dist/components/SelectedOutlineOverlay.d.ts +61 -33
  12. package/dist/components/SelectedOutlineOverlay.js +813 -351
  13. package/dist/components/SelectedOutlineUvControls.d.ts +17 -0
  14. package/dist/components/SelectedOutlineUvControls.js +167 -0
  15. package/dist/components/StudioCanvasCapture.d.ts +5 -0
  16. package/dist/components/StudioCanvasCapture.js +40 -0
  17. package/dist/components/Timeline/EasingEditorModal.d.ts +11 -0
  18. package/dist/components/Timeline/EasingEditorModal.js +247 -0
  19. package/dist/components/Timeline/KeyframeSettingsModal.js +5 -4
  20. package/dist/components/Timeline/SequencePropsObserver.js +3 -3
  21. package/dist/components/Timeline/Timeline.js +10 -7
  22. package/dist/components/Timeline/TimelineClipboardKeybindings.d.ts +7 -7
  23. package/dist/components/Timeline/TimelineClipboardKeybindings.js +19 -16
  24. package/dist/components/Timeline/TimelineDeleteKeybindings.js +71 -40
  25. package/dist/components/Timeline/TimelineDragHandler.js +2 -2
  26. package/dist/components/Timeline/TimelineEffectItem.js +8 -9
  27. package/dist/components/Timeline/TimelineEffectPropItem.js +18 -18
  28. package/dist/components/Timeline/TimelineExpandedKeyframeRow.d.ts +1 -0
  29. package/dist/components/Timeline/TimelineExpandedKeyframeRow.js +7 -2
  30. package/dist/components/Timeline/TimelineExpandedSection.js +5 -5
  31. package/dist/components/Timeline/TimelineExpandedTrackKeyframes.js +1 -1
  32. package/dist/components/Timeline/TimelineHeightContainer.js +2 -0
  33. package/dist/components/Timeline/TimelineItemStack.js +3 -56
  34. package/dist/components/Timeline/TimelineKeyframeControls.d.ts +7 -0
  35. package/dist/components/Timeline/TimelineKeyframeControls.js +265 -68
  36. package/dist/components/Timeline/TimelineKeyframeDiamond.js +4 -3
  37. package/dist/components/Timeline/TimelineKeyframeEasingLine.d.ts +9 -0
  38. package/dist/components/Timeline/TimelineKeyframeEasingLine.js +245 -0
  39. package/dist/components/Timeline/TimelineKeyframeTracksContext.d.ts +7 -0
  40. package/dist/components/Timeline/TimelineKeyframeTracksContext.js +17 -0
  41. package/dist/components/Timeline/TimelineKeyframedValue.js +1 -1
  42. package/dist/components/Timeline/TimelineMediaInfo.js +4 -24
  43. package/dist/components/Timeline/TimelineNumberField.js +15 -7
  44. package/dist/components/Timeline/TimelinePrimitiveFieldValue.js +4 -0
  45. package/dist/components/Timeline/TimelineRotationField.js +22 -34
  46. package/dist/components/Timeline/TimelineScaleField.js +16 -12
  47. package/dist/components/Timeline/TimelineScrollable.js +19 -3
  48. package/dist/components/Timeline/TimelineSelection.d.ts +82 -3
  49. package/dist/components/Timeline/TimelineSelection.js +312 -30
  50. package/dist/components/Timeline/TimelineSequence.js +23 -15
  51. package/dist/components/Timeline/TimelineSequenceItem.js +48 -73
  52. package/dist/components/Timeline/TimelineSequenceName.js +3 -17
  53. package/dist/components/Timeline/TimelineSequencePropItem.js +37 -37
  54. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.d.ts +5 -5
  55. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.js +46 -44
  56. package/dist/components/Timeline/TimelineTimeIndicators.js +4 -2
  57. package/dist/components/Timeline/TimelineTransformOriginField.d.ts +11 -0
  58. package/dist/components/Timeline/TimelineTransformOriginField.js +138 -0
  59. package/dist/components/Timeline/TimelineTranslateField.js +24 -19
  60. package/dist/components/Timeline/TimelineUvCoordinateField.js +18 -12
  61. package/dist/components/Timeline/{apply-effect-response-to-code-values.d.ts → apply-effect-response-to-prop-statuses.d.ts} +1 -1
  62. package/dist/components/Timeline/{apply-effect-response-to-code-values.js → apply-effect-response-to-prop-statuses.js} +3 -3
  63. package/dist/components/Timeline/call-add-keyframe.d.ts +22 -5
  64. package/dist/components/Timeline/call-add-keyframe.js +71 -7
  65. package/dist/components/Timeline/call-delete-keyframe.d.ts +7 -7
  66. package/dist/components/Timeline/call-delete-keyframe.js +7 -7
  67. package/dist/components/Timeline/call-move-keyframe.d.ts +3 -3
  68. package/dist/components/Timeline/call-move-keyframe.js +3 -3
  69. package/dist/components/Timeline/call-update-keyframe-settings.d.ts +5 -5
  70. package/dist/components/Timeline/call-update-keyframe-settings.js +6 -6
  71. package/dist/components/Timeline/delete-selected-keyframe.d.ts +5 -5
  72. package/dist/components/Timeline/delete-selected-keyframe.js +5 -5
  73. package/dist/components/Timeline/delete-selected-timeline-item.d.ts +5 -5
  74. package/dist/components/Timeline/delete-selected-timeline-item.js +28 -16
  75. package/dist/components/Timeline/get-node-keyframes.d.ts +3 -3
  76. package/dist/components/Timeline/get-node-keyframes.js +4 -4
  77. package/dist/components/Timeline/get-timeline-easing-segments.d.ts +9 -0
  78. package/dist/components/Timeline/get-timeline-easing-segments.js +19 -0
  79. package/dist/components/Timeline/reset-selected-timeline-props.d.ts +7 -7
  80. package/dist/components/Timeline/reset-selected-timeline-props.js +23 -15
  81. package/dist/components/Timeline/save-effect-prop.d.ts +2 -2
  82. package/dist/components/Timeline/save-effect-prop.js +5 -5
  83. package/dist/components/Timeline/save-prop-queue.d.ts +3 -3
  84. package/dist/components/Timeline/save-prop-queue.js +3 -3
  85. package/dist/components/Timeline/save-sequence-prop.d.ts +3 -3
  86. package/dist/components/Timeline/save-sequence-prop.js +4 -4
  87. package/dist/components/Timeline/timeline-field-utils.d.ts +10 -0
  88. package/dist/components/Timeline/timeline-field-utils.js +26 -5
  89. package/dist/components/Timeline/timeline-rotation-utils.d.ts +2 -0
  90. package/dist/components/Timeline/timeline-rotation-utils.js +32 -0
  91. package/dist/components/Timeline/timeline-translate-utils.d.ts +1 -1
  92. package/dist/components/Timeline/timeline-translate-utils.js +4 -5
  93. package/dist/components/Timeline/transform-origin-utils.d.ts +24 -0
  94. package/dist/components/Timeline/transform-origin-utils.js +170 -0
  95. package/dist/components/Timeline/update-selected-easing.d.ts +35 -0
  96. package/dist/components/Timeline/update-selected-easing.js +133 -0
  97. package/dist/components/Timeline/use-expanded-track-keyframe-rows.d.ts +1 -0
  98. package/dist/components/Timeline/use-expanded-track-keyframe-rows.js +35 -7
  99. package/dist/components/Timeline/use-sequence-props-subscription.js +3 -3
  100. package/dist/components/Timeline/use-timeline-height.js +3 -3
  101. package/dist/components/Timeline/use-timeline-keyframe-drag.js +13 -11
  102. package/dist/components/canvas-capture-enabled.d.ts +1 -0
  103. package/dist/components/canvas-capture-enabled.js +4 -0
  104. package/dist/components/effect-drag-and-drop.d.ts +11 -0
  105. package/dist/components/effect-drag-and-drop.js +73 -0
  106. package/dist/components/import-assets.d.ts +15 -0
  107. package/dist/components/import-assets.js +63 -1
  108. package/dist/components/selected-outline-geometry.d.ts +20 -0
  109. package/dist/components/selected-outline-geometry.js +18 -0
  110. package/dist/components/selected-outline-uv.d.ts +46 -0
  111. package/dist/components/selected-outline-uv.js +240 -0
  112. package/dist/esm/{chunk-q0jkt0zq.js → chunk-qaqqvw4q.js} +8096 -5307
  113. package/dist/esm/internals.mjs +8096 -5307
  114. package/dist/esm/previewEntry.mjs +8106 -5317
  115. package/dist/esm/renderEntry.mjs +1 -1
  116. package/dist/helpers/colors.d.ts +0 -1
  117. package/dist/helpers/colors.js +1 -2
  118. package/dist/helpers/timeline-layout.d.ts +6 -6
  119. package/dist/helpers/timeline-layout.js +5 -5
  120. package/dist/state/modals.d.ts +2 -4
  121. package/package.json +11 -10
  122. package/dist/components/NewComposition/DeleteStaticFile.d.ts +0 -4
  123. package/dist/components/NewComposition/DeleteStaticFile.js +0 -44
@@ -1,5 +1,11 @@
1
1
  import React from 'react';
2
2
  import type { CanUpdateSequencePropStatus, DragOverrideValue, SequencePropsSubscriptionKey, SequenceSchema } from 'remotion';
3
+ import type { SequenceNodePathInfo } from '../../helpers/get-timeline-sequence-sort-key';
4
+ import { type TimelineSelection } from './TimelineSelection';
5
+ export declare const getSelectedKeyframeControlNodePathInfos: ({ clickedNodePathInfo, selectedItems, }: {
6
+ readonly clickedNodePathInfo: SequenceNodePathInfo;
7
+ readonly selectedItems: readonly TimelineSelection[];
8
+ }) => readonly SequenceNodePathInfo[];
3
9
  export declare const shouldShowTimelineKeyframeControls: ({ propStatus, selected, keyframable, }: {
4
10
  propStatus: CanUpdateSequencePropStatus | null;
5
11
  selected: boolean;
@@ -15,4 +21,5 @@ export declare const TimelineKeyframeControls: React.FC<{
15
21
  readonly dragOverrideValue: DragOverrideValue | undefined;
16
22
  readonly schema: SequenceSchema;
17
23
  readonly effectIndex: number | null;
24
+ readonly nodePathInfo: SequenceNodePathInfo;
18
25
  }>;
@@ -1,17 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TimelineKeyframeControls = exports.shouldShowTimelineKeyframeControls = void 0;
3
+ exports.TimelineKeyframeControls = exports.shouldShowTimelineKeyframeControls = exports.getSelectedKeyframeControlNodePathInfos = 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 remotion_1 = require("remotion");
8
8
  const client_id_1 = require("../../helpers/client-id");
9
9
  const colors_1 = require("../../helpers/colors");
10
+ const timeline_layout_1 = require("../../helpers/timeline-layout");
11
+ const timeline_node_path_key_1 = require("../../helpers/timeline-node-path-key");
10
12
  const call_add_keyframe_1 = require("./call-add-keyframe");
11
13
  const call_delete_keyframe_1 = require("./call-delete-keyframe");
12
14
  const get_keyframe_navigation_1 = require("./get-keyframe-navigation");
13
15
  const get_timeline_keyframes_1 = require("./get-timeline-keyframes");
14
16
  const TimelineKeyframeDiamondIcon_1 = require("./TimelineKeyframeDiamondIcon");
17
+ const TimelineKeyframeTracksContext_1 = require("./TimelineKeyframeTracksContext");
15
18
  const TimelineSelection_1 = require("./TimelineSelection");
16
19
  const controlsContainerStyle = {
17
20
  alignItems: 'center',
@@ -44,10 +47,184 @@ const diamondButtonStyle = {
44
47
  background: 'none',
45
48
  };
46
49
  const svgStyle = { display: 'block' };
47
- const getCurrentKeyframeValue = ({ propStatus, jsxFrame, defaultValue, dragOverrideValue, }) => {
50
+ const isKeyframeControlSelection = (selection) => {
51
+ return (selection.type === 'sequence-prop' ||
52
+ selection.type === 'sequence-effect-prop');
53
+ };
54
+ const getSelectedKeyframeControlNodePathInfos = ({ clickedNodePathInfo, selectedItems, }) => {
55
+ const clickedSelection = (0, TimelineSelection_1.getTimelineSelectionFromNodePathInfo)(clickedNodePathInfo);
56
+ if (clickedSelection === null ||
57
+ !isKeyframeControlSelection(clickedSelection)) {
58
+ return [clickedNodePathInfo];
59
+ }
60
+ const clickedSelectionKey = (0, TimelineSelection_1.getTimelineSelectionKey)(clickedSelection);
61
+ const selectedKeyframeControls = selectedItems.filter(isKeyframeControlSelection);
62
+ const clickedIsSelected = selectedKeyframeControls.some((selection) => (0, TimelineSelection_1.getTimelineSelectionKey)(selection) === clickedSelectionKey);
63
+ if (!clickedIsSelected || selectedKeyframeControls.length <= 1) {
64
+ return [clickedNodePathInfo];
65
+ }
66
+ return selectedKeyframeControls.map((selection) => selection.nodePathInfo);
67
+ };
68
+ exports.getSelectedKeyframeControlNodePathInfos = getSelectedKeyframeControlNodePathInfos;
69
+ const findFieldNode = (nodes, nodePathInfoKey) => {
70
+ for (const node of nodes) {
71
+ if ((0, timeline_node_path_key_1.timelineNodePathInfoToKey)(node.nodePathInfo) === nodePathInfoKey) {
72
+ return node.kind === 'field' ? node : null;
73
+ }
74
+ if (node.kind === 'group') {
75
+ const child = findFieldNode(node.children, nodePathInfoKey);
76
+ if (child !== null) {
77
+ return child;
78
+ }
79
+ }
80
+ }
81
+ return null;
82
+ };
83
+ const findTrackForNodePathInfo = ({ tracks, nodePathInfo, }) => {
84
+ var _a;
85
+ return ((_a = tracks.find((track) => {
86
+ if (track.nodePathInfo === null) {
87
+ return false;
88
+ }
89
+ return ((0, timeline_node_path_key_1.timelineNodePathInfoToKey)({
90
+ ...track.nodePathInfo,
91
+ auxiliaryKeys: [],
92
+ }) ===
93
+ (0, timeline_node_path_key_1.timelineNodePathInfoToKey)({
94
+ ...nodePathInfo,
95
+ auxiliaryKeys: [],
96
+ }));
97
+ })) !== null && _a !== void 0 ? _a : null);
98
+ };
99
+ const resolveKeyframeControlTarget = ({ nodePathInfo, tracks, propStatuses, getDragOverrides, getEffectDragOverrides, timelinePosition, }) => {
100
+ var _a;
101
+ var _b, _c, _d;
102
+ const track = findTrackForNodePathInfo({ tracks, nodePathInfo });
103
+ if (track === null ||
104
+ track.nodePathInfo === null ||
105
+ !track.sequence.controls) {
106
+ return null;
107
+ }
108
+ const tree = (0, timeline_layout_1.buildTimelineTree)({
109
+ sequence: track.sequence,
110
+ nodePathInfo: track.nodePathInfo,
111
+ getDragOverrides,
112
+ getEffectDragOverrides,
113
+ propStatuses,
114
+ });
115
+ const fieldNode = findFieldNode(tree, (0, timeline_node_path_key_1.timelineNodePathInfoToKey)(nodePathInfo));
116
+ if (fieldNode === null || fieldNode.field === null) {
117
+ return null;
118
+ }
119
+ const nodePath = nodePathInfo.sequenceSubscriptionKey;
120
+ if (fieldNode.field.kind === 'sequence-field') {
121
+ const sequencePropStatuses = remotion_1.Internals.getPropStatusesCtx(propStatuses, nodePath);
122
+ const sequenceSelectedPropStatus = (_b = sequencePropStatuses === null || sequencePropStatuses === void 0 ? void 0 : sequencePropStatuses[fieldNode.field.key]) !== null && _b !== void 0 ? _b : null;
123
+ if (sequenceSelectedPropStatus === null) {
124
+ return null;
125
+ }
126
+ return {
127
+ nodePathInfo,
128
+ fieldKey: fieldNode.field.key,
129
+ propStatus: sequenceSelectedPropStatus,
130
+ nodePath,
131
+ fileName: nodePath.absolutePath,
132
+ keyframeDisplayOffset: track.keyframeDisplayOffset,
133
+ sourceFrame: timelinePosition - track.keyframeDisplayOffset,
134
+ defaultValue: fieldNode.field.fieldSchema.default,
135
+ dragOverrideValue: ((_c = getDragOverrides(nodePath)) !== null && _c !== void 0 ? _c : {})[fieldNode.field.key],
136
+ schema: track.sequence.controls.schema,
137
+ effectIndex: null,
138
+ };
139
+ }
140
+ const effectStatus = remotion_1.Internals.getEffectPropStatusesCtx({
141
+ propStatuses,
142
+ nodePath,
143
+ effectIndex: fieldNode.field.effectIndex,
144
+ });
145
+ const effectSelectedPropStatus = effectStatus.type === 'can-update-effect'
146
+ ? ((_d = (_a = effectStatus.props) === null || _a === void 0 ? void 0 : _a[fieldNode.field.key]) !== null && _d !== void 0 ? _d : null)
147
+ : null;
148
+ if (effectSelectedPropStatus === null) {
149
+ return null;
150
+ }
151
+ return {
152
+ nodePathInfo,
153
+ fieldKey: fieldNode.field.key,
154
+ propStatus: effectSelectedPropStatus,
155
+ nodePath,
156
+ fileName: nodePath.absolutePath,
157
+ keyframeDisplayOffset: track.keyframeDisplayOffset,
158
+ sourceFrame: timelinePosition - track.keyframeDisplayOffset,
159
+ defaultValue: fieldNode.field.fieldSchema.default,
160
+ dragOverrideValue: getEffectDragOverrides(nodePath, fieldNode.field.effectIndex)[fieldNode.field.key],
161
+ schema: fieldNode.field.effectSchema,
162
+ effectIndex: fieldNode.field.effectIndex,
163
+ };
164
+ };
165
+ const hasTargetKeyframeAtCurrentFrame = (target) => {
166
+ if (!isKeyframedStatus(target.propStatus)) {
167
+ return false;
168
+ }
169
+ return (0, get_keyframe_navigation_1.hasKeyframeAtSourceFrame)(target.propStatus.keyframes, target.sourceFrame);
170
+ };
171
+ const getAddChange = (target) => {
172
+ if (target.propStatus.status === 'computed' ||
173
+ !(0, studio_shared_1.isSchemaFieldKeyframable)({ schema: target.schema, key: target.fieldKey }) ||
174
+ hasTargetKeyframeAtCurrentFrame(target)) {
175
+ return null;
176
+ }
177
+ const value = getCurrentKeyframeValue({
178
+ propStatus: target.propStatus,
179
+ jsxFrame: target.sourceFrame,
180
+ defaultValue: target.defaultValue,
181
+ dragOverrideValue: target.dragOverrideValue,
182
+ });
183
+ if (value === null) {
184
+ return null;
185
+ }
186
+ const change = {
187
+ fileName: target.fileName,
188
+ nodePath: target.nodePath,
189
+ fieldKey: target.fieldKey,
190
+ sourceFrame: target.sourceFrame,
191
+ value,
192
+ schema: target.schema,
193
+ };
194
+ if (target.effectIndex === null) {
195
+ return change;
196
+ }
197
+ return {
198
+ ...change,
199
+ effectIndex: target.effectIndex,
200
+ };
201
+ };
202
+ const getDeleteChange = (target) => {
203
+ if (!hasTargetKeyframeAtCurrentFrame(target)) {
204
+ return null;
205
+ }
206
+ const change = {
207
+ fileName: target.fileName,
208
+ nodePath: target.nodePath,
209
+ fieldKey: target.fieldKey,
210
+ sourceFrame: target.sourceFrame,
211
+ schema: target.schema,
212
+ };
213
+ if (target.effectIndex === null) {
214
+ return change;
215
+ }
216
+ return {
217
+ ...change,
218
+ effectIndex: target.effectIndex,
219
+ };
220
+ };
221
+ const hasEffectIndex = (change) => {
222
+ return 'effectIndex' in change;
223
+ };
224
+ function getCurrentKeyframeValue({ propStatus, jsxFrame, defaultValue, dragOverrideValue, }) {
48
225
  if (isKeyframedStatus(propStatus)) {
49
226
  return remotion_1.Internals.getEffectiveVisualModeValue({
50
- codeValue: propStatus,
227
+ propStatus,
51
228
  dragOverrideValue,
52
229
  frame: jsxFrame,
53
230
  defaultValue,
@@ -56,7 +233,7 @@ const getCurrentKeyframeValue = ({ propStatus, jsxFrame, defaultValue, dragOverr
56
233
  }
57
234
  if (propStatus.status === 'static') {
58
235
  return remotion_1.Internals.getEffectiveVisualModeValue({
59
- codeValue: propStatus,
236
+ propStatus,
60
237
  dragOverrideValue,
61
238
  frame: jsxFrame,
62
239
  defaultValue,
@@ -64,7 +241,7 @@ const getCurrentKeyframeValue = ({ propStatus, jsxFrame, defaultValue, dragOverr
64
241
  });
65
242
  }
66
243
  return null;
67
- };
244
+ }
68
245
  const shouldShowTimelineKeyframeControls = ({ propStatus, selected, keyframable, }) => {
69
246
  if (propStatus === null) {
70
247
  return false;
@@ -75,15 +252,19 @@ const shouldShowTimelineKeyframeControls = ({ propStatus, selected, keyframable,
75
252
  if (selected) {
76
253
  return true;
77
254
  }
78
- return TimelineSelection_1.SELECTION_ENABLED && isKeyframedStatus(propStatus);
255
+ return isKeyframedStatus(propStatus);
79
256
  };
80
257
  exports.shouldShowTimelineKeyframeControls = shouldShowTimelineKeyframeControls;
81
- const TimelineKeyframeControls = ({ fieldKey, propStatus, nodePath, fileName, keyframeDisplayOffset, defaultValue, dragOverrideValue, schema, effectIndex, }) => {
258
+ const TimelineKeyframeControls = ({ fieldKey, propStatus, nodePath, fileName, keyframeDisplayOffset, defaultValue, dragOverrideValue, schema, effectIndex, nodePathInfo, }) => {
82
259
  const videoConfig = (0, remotion_1.useVideoConfig)();
83
260
  const timelinePosition = remotion_1.Internals.Timeline.useTimelinePosition();
84
261
  const setFrame = remotion_1.Internals.useTimelineSetFrame();
85
- const { setCodeValues } = (0, react_1.useContext)(remotion_1.Internals.VisualModeSettersContext);
262
+ const { setPropStatuses } = (0, react_1.useContext)(remotion_1.Internals.VisualModeSettersContext);
263
+ const { propStatuses } = (0, react_1.useContext)(remotion_1.Internals.VisualModePropStatusesContext);
264
+ const { getDragOverrides, getEffectDragOverrides } = (0, react_1.useContext)(remotion_1.Internals.VisualModeDragOverridesContext);
86
265
  const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
266
+ const { selectedItems } = (0, TimelineSelection_1.useTimelineSelection)();
267
+ const tracks = (0, TimelineKeyframeTracksContext_1.useTimelineKeyframeTracks)();
87
268
  const clientId = previewServerState.type === 'connected'
88
269
  ? previewServerState.clientId
89
270
  : null;
@@ -95,12 +276,6 @@ const TimelineKeyframeControls = ({ fieldKey, propStatus, nodePath, fileName, ke
95
276
  }
96
277
  return (0, get_keyframe_navigation_1.hasKeyframeAtSourceFrame)(propStatus.keyframes, jsxFrame);
97
278
  }, [jsxFrame, propStatus]);
98
- const currentKeyframeValue = (0, react_1.useMemo)(() => getCurrentKeyframeValue({
99
- propStatus,
100
- jsxFrame,
101
- defaultValue,
102
- dragOverrideValue,
103
- }), [defaultValue, dragOverrideValue, jsxFrame, propStatus]);
104
279
  const previousDisplayFrame = (0, react_1.useMemo)(() => (0, get_keyframe_navigation_1.getPreviousKeyframeDisplayFrame)(keyframes, timelinePosition, videoConfig.durationInFrames), [keyframes, timelinePosition, videoConfig.durationInFrames]);
105
280
  const nextDisplayFrame = (0, react_1.useMemo)(() => (0, get_keyframe_navigation_1.getNextKeyframeDisplayFrame)(keyframes, timelinePosition, videoConfig.durationInFrames), [keyframes, timelinePosition, videoConfig.durationInFrames]);
106
281
  const keyframable = (0, studio_shared_1.isSchemaFieldKeyframable)({
@@ -110,6 +285,62 @@ const TimelineKeyframeControls = ({ fieldKey, propStatus, nodePath, fileName, ke
110
285
  const canAddKeyframe = keyframable;
111
286
  const canToggleKeyframe = propStatus.status !== 'computed' &&
112
287
  (hasKeyframeAtCurrentFrame || canAddKeyframe);
288
+ const selectedNodePathInfos = (0, react_1.useMemo)(() => (0, exports.getSelectedKeyframeControlNodePathInfos)({
289
+ clickedNodePathInfo: nodePathInfo,
290
+ selectedItems,
291
+ }), [nodePathInfo, selectedItems]);
292
+ const clickedTarget = (0, react_1.useMemo)(() => ({
293
+ nodePathInfo,
294
+ fieldKey,
295
+ propStatus,
296
+ nodePath,
297
+ fileName,
298
+ keyframeDisplayOffset,
299
+ sourceFrame: jsxFrame,
300
+ defaultValue,
301
+ dragOverrideValue,
302
+ schema,
303
+ effectIndex,
304
+ }), [
305
+ defaultValue,
306
+ dragOverrideValue,
307
+ effectIndex,
308
+ fieldKey,
309
+ fileName,
310
+ jsxFrame,
311
+ keyframeDisplayOffset,
312
+ nodePath,
313
+ nodePathInfo,
314
+ propStatus,
315
+ schema,
316
+ ]);
317
+ const keyframeToggleTargets = (0, react_1.useMemo)(() => {
318
+ const clickedNodePathInfoKey = (0, timeline_node_path_key_1.timelineNodePathInfoToKey)(nodePathInfo);
319
+ return selectedNodePathInfos.flatMap((selectedNodePathInfo) => {
320
+ if ((0, timeline_node_path_key_1.timelineNodePathInfoToKey)(selectedNodePathInfo) ===
321
+ clickedNodePathInfoKey) {
322
+ return [clickedTarget];
323
+ }
324
+ const target = resolveKeyframeControlTarget({
325
+ nodePathInfo: selectedNodePathInfo,
326
+ tracks,
327
+ propStatuses,
328
+ getDragOverrides,
329
+ getEffectDragOverrides,
330
+ timelinePosition,
331
+ });
332
+ return target === null ? [] : [target];
333
+ });
334
+ }, [
335
+ clickedTarget,
336
+ getDragOverrides,
337
+ getEffectDragOverrides,
338
+ nodePathInfo,
339
+ propStatuses,
340
+ selectedNodePathInfos,
341
+ timelinePosition,
342
+ tracks,
343
+ ]);
113
344
  const seekToDisplayFrame = (0, react_1.useCallback)((frame) => {
114
345
  setFrame((current) => {
115
346
  const next = { ...current, [videoConfig.id]: frame };
@@ -134,72 +365,38 @@ const TimelineKeyframeControls = ({ fieldKey, propStatus, nodePath, fileName, ke
134
365
  if (!clientId || !canToggleKeyframe) {
135
366
  return;
136
367
  }
137
- if (hasKeyframeAtCurrentFrame && isKeyframedStatus(propStatus)) {
138
- if (effectIndex === null) {
139
- await (0, call_delete_keyframe_1.callDeleteSequenceKeyframe)({
140
- fileName,
141
- nodePath,
142
- fieldKey,
143
- sourceFrame: jsxFrame,
144
- schema,
145
- setCodeValues,
146
- clientId,
147
- });
148
- return;
149
- }
150
- await (0, call_delete_keyframe_1.callDeleteEffectKeyframe)({
151
- fileName,
152
- nodePath,
153
- effectIndex,
154
- fieldKey,
155
- sourceFrame: jsxFrame,
156
- schema,
157
- setCodeValues,
368
+ if (hasKeyframeAtCurrentFrame) {
369
+ const deleteChanges = keyframeToggleTargets.flatMap((target) => {
370
+ const change = getDeleteChange(target);
371
+ return change === null ? [] : [change];
372
+ });
373
+ await (0, call_delete_keyframe_1.callDeleteKeyframes)({
374
+ sequenceKeyframes: deleteChanges.filter((change) => !hasEffectIndex(change)),
375
+ effectKeyframes: deleteChanges.filter((change) => hasEffectIndex(change)),
376
+ setPropStatuses,
158
377
  clientId,
159
378
  });
160
379
  return;
161
380
  }
162
- const value = currentKeyframeValue;
163
- if (value === null) {
164
- return;
165
- }
166
- if (effectIndex === null) {
167
- await (0, call_add_keyframe_1.callAddSequenceKeyframe)({
168
- fileName,
169
- nodePath,
170
- fieldKey,
171
- sourceFrame: jsxFrame,
172
- value,
173
- schema,
174
- setCodeValues,
175
- clientId,
176
- });
381
+ const addChanges = keyframeToggleTargets.flatMap((target) => {
382
+ const change = getAddChange(target);
383
+ return change === null ? [] : [change];
384
+ });
385
+ if (addChanges.length === 0) {
177
386
  return;
178
387
  }
179
- await (0, call_add_keyframe_1.callAddEffectKeyframe)({
180
- fileName,
181
- nodePath,
182
- effectIndex,
183
- fieldKey,
184
- sourceFrame: jsxFrame,
185
- value,
186
- schema,
187
- setCodeValues,
388
+ await (0, call_add_keyframe_1.callAddKeyframes)({
389
+ sequenceKeyframes: addChanges.filter((change) => !hasEffectIndex(change)),
390
+ effectKeyframes: addChanges.filter((change) => hasEffectIndex(change)),
391
+ setPropStatuses,
188
392
  clientId,
189
393
  });
190
394
  }, [
191
395
  canToggleKeyframe,
192
396
  clientId,
193
- effectIndex,
194
- fieldKey,
195
- fileName,
196
397
  hasKeyframeAtCurrentFrame,
197
- currentKeyframeValue,
198
- jsxFrame,
199
- nodePath,
200
- propStatus,
201
- schema,
202
- setCodeValues,
398
+ keyframeToggleTargets,
399
+ setPropStatuses,
203
400
  ]);
204
401
  const previousDisabled = previousDisplayFrame === null;
205
402
  const nextDisabled = nextDisplayFrame === null;
@@ -60,7 +60,9 @@ const diamondBase = {
60
60
  const TimelineKeyframeDiamondUnmemoized = ({ frame, rowHeight, nodePathInfo }) => {
61
61
  const videoConfig = (0, remotion_1.useVideoConfig)();
62
62
  const timelineWidth = (0, react_1.useContext)(TimelineWidthProvider_1.TimelineWidthContext);
63
- const { selected, onSelect, selectable } = (0, TimelineSelection_1.useTimelineKeyframeSelection)(nodePathInfo, frame);
63
+ const ref = (0, react_1.useRef)(null);
64
+ const { selected, onSelect, selectable, selectionItem } = (0, TimelineSelection_1.useTimelineKeyframeSelection)(nodePathInfo, frame);
65
+ (0, TimelineSelection_1.useTimelineMarqueeSelectableItem)(selectionItem, ref);
64
66
  const { isKeyframeDragging } = (0, TimelineKeyframeDragState_1.useTimelineKeyframeDragState)();
65
67
  const visuallySelected = selected || isKeyframeDragging({ nodePathInfo, frame });
66
68
  const style = (0, react_1.useMemo)(() => {
@@ -69,7 +71,6 @@ const TimelineKeyframeDiamondUnmemoized = ({ frame, rowHeight, nodePathInfo }) =
69
71
  }
70
72
  return {
71
73
  ...diamondBase,
72
- cursor: 'pointer',
73
74
  left: (0, get_left_of_timeline_slider_1.getXPositionOfItemInTimelineImperatively)(frame, videoConfig.durationInFrames, timelineWidth) - timeline_layout_1.TIMELINE_PADDING,
74
75
  pointerEvents: 'auto',
75
76
  top: rowHeight / 2,
@@ -86,6 +87,6 @@ const TimelineKeyframeDiamondUnmemoized = ({ frame, rowHeight, nodePathInfo }) =
86
87
  if (style === null) {
87
88
  return null;
88
89
  }
89
- return (jsx_runtime_1.jsx("button", { type: "button", style: style, title: `Keyframe at frame ${frame}`, "aria-label": `Select keyframe at frame ${frame}`, onPointerDown: selectable ? onPointerDown : undefined, children: jsx_runtime_1.jsx(TimelineKeyframeDiamondIcon_1.TimelineKeyframeDiamondIcon, { color: colors_1.LIGHT_TEXT, selected: visuallySelected, size: diamondSize }) }));
90
+ return (jsx_runtime_1.jsx("button", { ref: ref, [TimelineSelection_1.TIMELINE_MARQUEE_ITEM_ATTR]: true, type: "button", style: style, title: `Keyframe at frame ${frame}`, "aria-label": `Select keyframe at frame ${frame}`, onPointerDown: selectable ? onPointerDown : undefined, children: jsx_runtime_1.jsx(TimelineKeyframeDiamondIcon_1.TimelineKeyframeDiamondIcon, { color: colors_1.LIGHT_TEXT, selected: visuallySelected, size: diamondSize }) }));
90
91
  };
91
92
  exports.TimelineKeyframeDiamond = react_1.default.memo(TimelineKeyframeDiamondUnmemoized);
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import type { SequenceNodePathInfo } from '../../helpers/get-timeline-sequence-sort-key';
3
+ export declare const TimelineKeyframeEasingLine: React.NamedExoticComponent<{
4
+ readonly fromFrame: number;
5
+ readonly toFrame: number;
6
+ readonly rowHeight: number;
7
+ readonly nodePathInfo: SequenceNodePathInfo;
8
+ readonly segmentIndex: number;
9
+ }>;