@remotion/studio 4.0.471 → 4.0.472

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 (91) hide show
  1. package/dist/components/AssetSelector.js +10 -1
  2. package/dist/components/Canvas.js +98 -0
  3. package/dist/components/CompositionSelectorItem.d.ts +1 -0
  4. package/dist/components/CompositionSelectorItem.js +12 -4
  5. package/dist/components/ContextMenu.js +54 -46
  6. package/dist/components/Editor.js +14 -6
  7. package/dist/components/Modals.js +3 -1
  8. package/dist/components/NewComposition/CodemodFooter.js +2 -2
  9. package/dist/components/NewComposition/DeleteFolder.d.ts +6 -0
  10. package/dist/components/NewComposition/DeleteFolder.js +39 -0
  11. package/dist/components/NewComposition/RenameFolder.d.ts +6 -0
  12. package/dist/components/NewComposition/RenameFolder.js +60 -0
  13. package/dist/components/SelectedOutlineOverlay.d.ts +81 -4
  14. package/dist/components/SelectedOutlineOverlay.js +405 -54
  15. package/dist/components/Splitter/SplitterContainer.js +9 -0
  16. package/dist/components/Splitter/SplitterHandle.js +63 -70
  17. package/dist/components/Timeline/Timeline.js +47 -2
  18. package/dist/components/Timeline/TimelineArrayField.d.ts +9 -0
  19. package/dist/components/Timeline/TimelineArrayField.js +210 -0
  20. package/dist/components/Timeline/TimelineBooleanField.d.ts +2 -2
  21. package/dist/components/Timeline/TimelineBooleanField.js +2 -2
  22. package/dist/components/Timeline/TimelineClipboardKeybindings.d.ts +20 -0
  23. package/dist/components/Timeline/TimelineClipboardKeybindings.js +265 -0
  24. package/dist/components/Timeline/TimelineColorField.d.ts +2 -2
  25. package/dist/components/Timeline/TimelineColorField.js +2 -8
  26. package/dist/components/Timeline/TimelineEffectItem.js +2 -2
  27. package/dist/components/Timeline/TimelineEffectPropItem.js +95 -25
  28. package/dist/components/Timeline/TimelineEnumField.d.ts +2 -2
  29. package/dist/components/Timeline/TimelineEnumField.js +3 -3
  30. package/dist/components/Timeline/TimelineKeyframeControls.d.ts +4 -3
  31. package/dist/components/Timeline/TimelineKeyframeControls.js +37 -23
  32. package/dist/components/Timeline/TimelineKeyframedValue.d.ts +7 -2
  33. package/dist/components/Timeline/TimelineKeyframedValue.js +22 -8
  34. package/dist/components/Timeline/TimelineLayerEye.d.ts +1 -0
  35. package/dist/components/Timeline/TimelineLayerEye.js +8 -3
  36. package/dist/components/Timeline/TimelineNumberField.d.ts +2 -2
  37. package/dist/components/Timeline/TimelineNumberField.js +7 -11
  38. package/dist/components/Timeline/TimelinePrimitiveFieldValue.d.ts +17 -0
  39. package/dist/components/Timeline/TimelinePrimitiveFieldValue.js +53 -0
  40. package/dist/components/Timeline/TimelineRotationField.d.ts +2 -2
  41. package/dist/components/Timeline/TimelineRotationField.js +41 -24
  42. package/dist/components/Timeline/TimelineRowChrome.js +8 -7
  43. package/dist/components/Timeline/TimelineScaleField.d.ts +20 -0
  44. package/dist/components/Timeline/TimelineScaleField.js +314 -0
  45. package/dist/components/Timeline/TimelineSchemaField.d.ts +3 -2
  46. package/dist/components/Timeline/TimelineSchemaField.js +8 -42
  47. package/dist/components/Timeline/TimelineSelection.js +3 -2
  48. package/dist/components/Timeline/TimelineSequence.d.ts +1 -0
  49. package/dist/components/Timeline/TimelineSequence.js +51 -10
  50. package/dist/components/Timeline/TimelineSequenceFrame.js +1 -0
  51. package/dist/components/Timeline/TimelineSequenceItem.js +7 -7
  52. package/dist/components/Timeline/TimelineSequencePropItem.js +82 -21
  53. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.d.ts +58 -0
  54. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.js +528 -0
  55. package/dist/components/Timeline/TimelineTrack.js +1 -1
  56. package/dist/components/Timeline/TimelineTranslateField.d.ts +2 -2
  57. package/dist/components/Timeline/TimelineTranslateField.js +21 -25
  58. package/dist/components/Timeline/TimelineUvCoordinateField.d.ts +2 -2
  59. package/dist/components/Timeline/TimelineUvCoordinateField.js +20 -26
  60. package/dist/components/Timeline/call-add-keyframe.js +2 -0
  61. package/dist/components/Timeline/get-node-keyframes.d.ts +5 -2
  62. package/dist/components/Timeline/get-node-keyframes.js +38 -5
  63. package/dist/components/Timeline/get-timeline-keyframes.js +4 -4
  64. package/dist/components/Timeline/reset-selected-timeline-props.js +19 -6
  65. package/dist/components/Timeline/timeline-field-utils.d.ts +1 -0
  66. package/dist/components/Timeline/timeline-field-utils.js +5 -1
  67. package/dist/components/Timeline/timeline-translate-utils.js +6 -2
  68. package/dist/components/Timeline/use-expanded-track-keyframe-rows.js +7 -0
  69. package/dist/components/TopPanel.d.ts +1 -1
  70. package/dist/components/folder-menu-items.d.ts +12 -0
  71. package/dist/components/folder-menu-items.js +147 -0
  72. package/dist/components/import-assets.d.ts +6 -0
  73. package/dist/components/import-assets.js +157 -0
  74. package/dist/esm/{chunk-z0z9d4r0.js → chunk-48grt472.js} +8936 -5886
  75. package/dist/esm/internals.mjs +8936 -5886
  76. package/dist/esm/previewEntry.mjs +8748 -5698
  77. package/dist/esm/renderEntry.mjs +1 -1
  78. package/dist/helpers/calculate-timeline.js +7 -3
  79. package/dist/helpers/create-folder-tree.js +1 -0
  80. package/dist/helpers/detect-file-type.d.ts +69 -0
  81. package/dist/helpers/detect-file-type.js +278 -0
  82. package/dist/helpers/get-folder-id.d.ts +4 -0
  83. package/dist/helpers/get-folder-id.js +7 -0
  84. package/dist/helpers/get-timeline-sequence-sort-key.d.ts +2 -0
  85. package/dist/helpers/timeline-layout.js +5 -1
  86. package/dist/helpers/validate-folder-rename.d.ts +6 -0
  87. package/dist/helpers/validate-folder-rename.js +19 -0
  88. package/dist/state/modals.d.ts +10 -0
  89. package/dist/state/scale-lock.d.ts +18 -0
  90. package/dist/state/scale-lock.js +59 -0
  91. package/package.json +10 -10
@@ -0,0 +1,265 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TimelineClipboardKeybindings = exports.getSnapshotsFromSelection = exports.getPasteEffectsTarget = void 0;
4
+ const studio_shared_1 = require("@remotion/studio-shared");
5
+ const react_1 = require("react");
6
+ const remotion_1 = require("remotion");
7
+ const client_id_1 = require("../../helpers/client-id");
8
+ const use_keybinding_1 = require("../../helpers/use-keybinding");
9
+ const call_api_1 = require("../call-api");
10
+ const NotificationCenter_1 = require("../Notifications/NotificationCenter");
11
+ const TimelineSelection_1 = require("./TimelineSelection");
12
+ const makeClipboardText = (payload) => JSON.stringify(payload);
13
+ const makeTargetKey = (nodePath) => {
14
+ return JSON.stringify({
15
+ absolutePath: nodePath.absolutePath,
16
+ nodePath: nodePath.nodePath,
17
+ sequenceKeys: nodePath.sequenceKeys,
18
+ });
19
+ };
20
+ const getTargetSequenceNodePathInfo = (selection) => {
21
+ if (selection.type === 'sequence' ||
22
+ selection.type === 'sequence-effect' ||
23
+ selection.type === 'sequence-all-effects') {
24
+ return selection.nodePathInfo;
25
+ }
26
+ return null;
27
+ };
28
+ const getCopyType = (selection) => {
29
+ if (selection.type === 'sequence-effect') {
30
+ return 'effects-additive';
31
+ }
32
+ if (selection.type === 'sequence-all-effects') {
33
+ return 'effects-replacing';
34
+ }
35
+ return null;
36
+ };
37
+ const getPasteEffectsTarget = (selectedItems) => {
38
+ const targetNodePathInfos = selectedItems
39
+ .map(getTargetSequenceNodePathInfo)
40
+ .filter((targetNodePathInfo) => targetNodePathInfo !== null);
41
+ const uniqueTargetKeys = new Set(targetNodePathInfos.map((targetNodePathInfo) => makeTargetKey(targetNodePathInfo.sequenceSubscriptionKey)));
42
+ if (uniqueTargetKeys.size === 0) {
43
+ return { type: 'none' };
44
+ }
45
+ if (uniqueTargetKeys.size !== 1) {
46
+ return { type: 'multiple' };
47
+ }
48
+ const [nodePathInfo] = targetNodePathInfos;
49
+ if (!nodePathInfo) {
50
+ return { type: 'none' };
51
+ }
52
+ if (!nodePathInfo.supportsEffects) {
53
+ return { type: 'unsupported' };
54
+ }
55
+ return {
56
+ type: 'valid',
57
+ nodePathInfo,
58
+ };
59
+ };
60
+ exports.getPasteEffectsTarget = getPasteEffectsTarget;
61
+ const isClipboardInterpolationFunction = (value) => {
62
+ return (0, studio_shared_1.isKeyframeInterpolationFunction)(value);
63
+ };
64
+ const effectStatusToSnapshot = (effect) => {
65
+ if (effect.importPath === null) {
66
+ return null;
67
+ }
68
+ const params = {};
69
+ for (const [key, prop] of Object.entries(effect.props)) {
70
+ if (prop.status === 'computed') {
71
+ return null;
72
+ }
73
+ if (prop.status === 'static') {
74
+ if (prop.codeValue !== undefined) {
75
+ params[key] = {
76
+ type: 'static',
77
+ value: prop.codeValue,
78
+ };
79
+ }
80
+ continue;
81
+ }
82
+ if (!isClipboardInterpolationFunction(prop.interpolationFunction)) {
83
+ return null;
84
+ }
85
+ params[key] = {
86
+ type: 'keyframed',
87
+ interpolationFunction: prop.interpolationFunction,
88
+ keyframes: prop.keyframes,
89
+ easing: prop.easing,
90
+ clamping: prop.clamping,
91
+ ...(prop.posterize === undefined ? {} : { posterize: prop.posterize }),
92
+ };
93
+ }
94
+ return {
95
+ callee: effect.callee,
96
+ importPath: effect.importPath,
97
+ params,
98
+ };
99
+ };
100
+ const getSnapshotsFromSelection = ({ selection, codeValues, }) => {
101
+ if (selection.type !== 'sequence-effect' &&
102
+ selection.type !== 'sequence-all-effects') {
103
+ return null;
104
+ }
105
+ const { sequenceSubscriptionKey } = selection.nodePathInfo;
106
+ const sequenceStatus = codeValues[remotion_1.Internals.makeSequencePropsSubscriptionKey(sequenceSubscriptionKey)];
107
+ if (!sequenceStatus || !sequenceStatus.canUpdate) {
108
+ return null;
109
+ }
110
+ const effects = selection.type === 'sequence-effect'
111
+ ? sequenceStatus.effects.filter((effect) => effect.effectIndex === selection.i)
112
+ : sequenceStatus.effects;
113
+ if (selection.type === 'sequence-effect' && effects.length !== 1) {
114
+ return null;
115
+ }
116
+ const snapshots = [];
117
+ for (const effect of effects) {
118
+ if (!effect.canUpdate) {
119
+ return null;
120
+ }
121
+ const snapshot = effectStatusToSnapshot(effect);
122
+ if (snapshot === null) {
123
+ return null;
124
+ }
125
+ snapshots.push(snapshot);
126
+ }
127
+ return snapshots;
128
+ };
129
+ exports.getSnapshotsFromSelection = getSnapshotsFromSelection;
130
+ const TimelineClipboardKeybindings = () => {
131
+ const keybindings = (0, use_keybinding_1.useKeybinding)();
132
+ const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
133
+ const { canSelect } = (0, TimelineSelection_1.useTimelineSelection)();
134
+ const currentSelection = (0, TimelineSelection_1.useCurrentTimelineSelectionStateAsRef)();
135
+ const { codeValues } = (0, react_1.useContext)(remotion_1.Internals.VisualModeCodeValuesContext);
136
+ (0, react_1.useEffect)(() => {
137
+ if (!canSelect || previewServerState.type !== 'connected') {
138
+ return;
139
+ }
140
+ const { clientId } = previewServerState;
141
+ const copy = keybindings.registerKeybinding({
142
+ event: 'keydown',
143
+ key: 'c',
144
+ callback: (e) => {
145
+ const { selectedItems } = currentSelection.current;
146
+ if (selectedItems.length === 0) {
147
+ return;
148
+ }
149
+ const firstSelection = selectedItems[0];
150
+ const type = firstSelection ? getCopyType(firstSelection) : null;
151
+ if (type === null) {
152
+ return;
153
+ }
154
+ if (selectedItems.some((selection) => getCopyType(selection) !== type)) {
155
+ e.preventDefault();
156
+ (0, NotificationCenter_1.showNotification)('Cannot copy individual effects together with all effects', 3000);
157
+ return;
158
+ }
159
+ const snapshots = selectedItems.flatMap((selection) => {
160
+ const itemSnapshots = (0, exports.getSnapshotsFromSelection)({
161
+ selection,
162
+ codeValues,
163
+ });
164
+ return itemSnapshots !== null && itemSnapshots !== void 0 ? itemSnapshots : [null];
165
+ });
166
+ if (snapshots.some((snapshot) => snapshot === null)) {
167
+ e.preventDefault();
168
+ (0, NotificationCenter_1.showNotification)('Cannot copy effects because one of them contains values that cannot be copied', 3000);
169
+ return;
170
+ }
171
+ e.preventDefault();
172
+ navigator.clipboard
173
+ .writeText(makeClipboardText({
174
+ type,
175
+ version: 3,
176
+ remotionClipboard: 'effects',
177
+ effects: snapshots,
178
+ }))
179
+ .then(() => {
180
+ (0, NotificationCenter_1.showNotification)(snapshots.length === 1
181
+ ? 'Copied effect to clipboard'
182
+ : 'Copied effects to clipboard', 1000);
183
+ })
184
+ .catch((err) => {
185
+ (0, NotificationCenter_1.showNotification)(`Could not copy effects: ${err.message}`, 2000);
186
+ });
187
+ },
188
+ commandCtrlKey: true,
189
+ preventDefault: false,
190
+ triggerIfInputFieldFocused: false,
191
+ keepRegisteredWhenNotHighestContext: false,
192
+ });
193
+ const paste = keybindings.registerKeybinding({
194
+ event: 'keydown',
195
+ key: 'v',
196
+ callback: (e) => {
197
+ const { selectedItems } = currentSelection.current;
198
+ if (selectedItems.length === 0) {
199
+ return;
200
+ }
201
+ navigator.clipboard
202
+ .readText()
203
+ .then((text) => {
204
+ const result = (0, studio_shared_1.parseEffectClipboardDataResult)(text);
205
+ if (result.status === 'invalid') {
206
+ return;
207
+ }
208
+ e.preventDefault();
209
+ if (result.status === 'unsupported-version') {
210
+ (0, NotificationCenter_1.showNotification)('Cannot paste effects copied from a different Remotion Studio version', 4000);
211
+ return;
212
+ }
213
+ const { data: payload } = result;
214
+ const target = (0, exports.getPasteEffectsTarget)(selectedItems);
215
+ if (target.type === 'multiple') {
216
+ (0, NotificationCenter_1.showNotification)('Select one target sequence to paste effects', 3000);
217
+ return;
218
+ }
219
+ if (target.type === 'none') {
220
+ (0, NotificationCenter_1.showNotification)('Select a sequence to paste effects onto', 3000);
221
+ return;
222
+ }
223
+ if (target.type === 'unsupported') {
224
+ (0, NotificationCenter_1.showNotification)('This sequence does not support effects', 3000);
225
+ return;
226
+ }
227
+ const { sequenceSubscriptionKey: targetSequenceNodePath } = target.nodePathInfo;
228
+ return (0, call_api_1.callApi)('/api/paste-effects', {
229
+ targetFileName: targetSequenceNodePath.absolutePath,
230
+ targetSequenceNodePath,
231
+ type: payload.type,
232
+ effects: payload.effects,
233
+ clientId,
234
+ }).then((pasteResult) => {
235
+ if (pasteResult.success) {
236
+ (0, NotificationCenter_1.showNotification)('Pasted effects', 2000);
237
+ }
238
+ else {
239
+ (0, NotificationCenter_1.showNotification)(pasteResult.reason, 4000);
240
+ }
241
+ });
242
+ })
243
+ .catch((err) => {
244
+ (0, NotificationCenter_1.showNotification)(`Could not paste effects: ${err.message}`, 3000);
245
+ });
246
+ },
247
+ commandCtrlKey: true,
248
+ preventDefault: false,
249
+ triggerIfInputFieldFocused: false,
250
+ keepRegisteredWhenNotHighestContext: false,
251
+ });
252
+ return () => {
253
+ copy.unregister();
254
+ paste.unregister();
255
+ };
256
+ }, [
257
+ canSelect,
258
+ codeValues,
259
+ currentSelection,
260
+ keybindings,
261
+ previewServerState,
262
+ ]);
263
+ return null;
264
+ };
265
+ exports.TimelineClipboardKeybindings = TimelineClipboardKeybindings;
@@ -1,10 +1,10 @@
1
1
  import React from 'react';
2
- import type { CanUpdateSequencePropStatus } from 'remotion';
2
+ import type { CanUpdateSequencePropStatusStatic } from 'remotion';
3
3
  import type { SchemaFieldInfo, TimelineFieldOnDragValueChange, TimelineFieldOnSave } from '../../helpers/timeline-layout';
4
4
  export declare const TimelineColorField: React.FC<{
5
5
  readonly field: SchemaFieldInfo;
6
6
  readonly effectiveValue: unknown;
7
- readonly propStatus: CanUpdateSequencePropStatus;
7
+ readonly propStatus: CanUpdateSequencePropStatusStatic;
8
8
  readonly onSave: TimelineFieldOnSave;
9
9
  readonly onDragValueChange: TimelineFieldOnDragValueChange;
10
10
  readonly onDragEnd: () => void;
@@ -19,15 +19,9 @@ const TimelineColorField = ({ field, effectiveValue, propStatus, onSave, onDragV
19
19
  ? ((_a = field.fieldSchema.default) !== null && _a !== void 0 ? _a : '#000')
20
20
  : '';
21
21
  const onChange = (0, react_1.useCallback)((next) => {
22
- if (!propStatus.canUpdate) {
23
- return;
24
- }
25
22
  onDragValueChange(next);
26
- }, [onDragValueChange, propStatus.canUpdate]);
23
+ }, [onDragValueChange]);
27
24
  const onChangeComplete = (0, react_1.useCallback)((next) => {
28
- if (!propStatus.canUpdate) {
29
- return;
30
- }
31
25
  if (next !== propStatus.codeValue) {
32
26
  onSave(next);
33
27
  }
@@ -38,6 +32,6 @@ const TimelineColorField = ({ field, effectiveValue, propStatus, onSave, onDragV
38
32
  marginLeft: 5,
39
33
  };
40
34
  }, []);
41
- return (jsx_runtime_1.jsx("span", { style: containerStyle, children: jsx_runtime_1.jsx(ColorPicker_1.ColorPicker, { value: currentValue, status: "ok", onChange: onChange, onChangeComplete: onChangeComplete, width: SWATCH_WIDTH, height: SWATCH_HEIGHT, disabled: !propStatus.canUpdate, name: field.key, title: currentValue, style: swatchStyle }) }));
35
+ return (jsx_runtime_1.jsx("span", { style: containerStyle, children: jsx_runtime_1.jsx(ColorPicker_1.ColorPicker, { value: currentValue, status: "ok", onChange: onChange, onChangeComplete: onChangeComplete, width: SWATCH_WIDTH, height: SWATCH_HEIGHT, disabled: false, name: field.key, title: currentValue, style: swatchStyle }) }));
42
36
  };
43
37
  exports.TimelineColorField = TimelineColorField;
@@ -81,12 +81,12 @@ const TimelineEffectItem = ({ label, nodePathInfo, effectIndex, effectSchema, do
81
81
  ? ((_b = (_a = effectStatus.props) === null || _a === void 0 ? void 0 : _a.disabled) !== null && _b !== void 0 ? _b : null)
82
82
  : null;
83
83
  const isDisabled = (0, react_1.useMemo)(() => {
84
- if (disabledStatus && disabledStatus.canUpdate) {
84
+ if ((disabledStatus === null || disabledStatus === void 0 ? void 0 : disabledStatus.status) === 'static') {
85
85
  return Boolean(disabledStatus.codeValue);
86
86
  }
87
87
  return false;
88
88
  }, [disabledStatus]);
89
- const canToggle = previewConnected && disabledStatus !== null && disabledStatus.canUpdate;
89
+ const canToggle = previewConnected && (disabledStatus === null || disabledStatus === void 0 ? void 0 : disabledStatus.status) === 'static';
90
90
  const deleteDisabled = !previewConnected ||
91
91
  effectStatus.type !== 'can-update-effect' ||
92
92
  !validatedLocation.source;
@@ -8,6 +8,7 @@ const remotion_1 = require("remotion");
8
8
  const client_id_1 = require("../../helpers/client-id");
9
9
  const call_api_1 = require("../call-api");
10
10
  const ContextMenu_1 = require("../ContextMenu");
11
+ const call_add_keyframe_1 = require("./call-add-keyframe");
11
12
  const get_timeline_keyframes_1 = require("./get-timeline-keyframes");
12
13
  const save_effect_prop_1 = require("./save-effect-prop");
13
14
  const save_prop_queue_1 = require("./save-prop-queue");
@@ -21,6 +22,23 @@ const TimelineRowChrome_1 = require("./TimelineRowChrome");
21
22
  const TimelineSchemaField_1 = require("./TimelineSchemaField");
22
23
  const TimelineSelection_1 = require("./TimelineSelection");
23
24
  const fieldRowBase = {};
25
+ const isKeyframedStatus = (status) => {
26
+ return status.status === 'keyframed';
27
+ };
28
+ const isResettableStatus = ({ status, defaultValue, }) => {
29
+ var _a;
30
+ if (defaultValue === undefined) {
31
+ return false;
32
+ }
33
+ if (status.status === 'keyframed') {
34
+ return true;
35
+ }
36
+ if (status.status === 'computed') {
37
+ return false;
38
+ }
39
+ const effectiveCodeValue = (_a = status.codeValue) !== null && _a !== void 0 ? _a : defaultValue;
40
+ return JSON.stringify(effectiveCodeValue) !== JSON.stringify(defaultValue);
41
+ };
24
42
  const Value = ({ field, nodePath, validatedLocation, keyframeDisplayOffset }) => {
25
43
  var _a;
26
44
  var _b;
@@ -39,9 +57,25 @@ const Value = ({ field, nodePath, validatedLocation, keyframeDisplayOffset }) =>
39
57
  const propStatus = effectStatus.type === 'can-update-effect'
40
58
  ? ((_b = (_a = effectStatus.props) === null || _a === void 0 ? void 0 : _a[field.key]) !== null && _b !== void 0 ? _b : null)
41
59
  : null;
60
+ const timelinePosition = remotion_1.Internals.Timeline.useTimelinePosition();
61
+ const jsxFrame = timelinePosition - keyframeDisplayOffset;
42
62
  const onDragValueChange = (0, react_1.useCallback)((value) => {
43
- setEffectDragOverrides(nodePath, field.effectIndex, field.key, value);
44
- }, [setEffectDragOverrides, nodePath, field.effectIndex, field.key]);
63
+ const nextDragOverrideValue = propStatus !== null && isKeyframedStatus(propStatus)
64
+ ? remotion_1.Internals.makeKeyframedDragOverride({
65
+ status: propStatus,
66
+ frame: jsxFrame,
67
+ value,
68
+ })
69
+ : remotion_1.Internals.makeStaticDragOverride(value);
70
+ setEffectDragOverrides(nodePath, field.effectIndex, field.key, nextDragOverrideValue);
71
+ }, [
72
+ field.effectIndex,
73
+ field.key,
74
+ jsxFrame,
75
+ nodePath,
76
+ propStatus,
77
+ setEffectDragOverrides,
78
+ ]);
45
79
  const onDragEnd = (0, react_1.useCallback)(() => {
46
80
  clearEffectDragOverrides(nodePath, field.effectIndex);
47
81
  }, [clearEffectDragOverrides, nodePath, field.effectIndex]);
@@ -56,7 +90,7 @@ const Value = ({ field, nodePath, validatedLocation, keyframeDisplayOffset }) =>
56
90
  if (!propStatus) {
57
91
  return Promise.reject(new Error('Cannot save'));
58
92
  }
59
- if (!propStatus.canUpdate) {
93
+ if (propStatus.status !== 'static') {
60
94
  return Promise.reject(new Error('Cannot save'));
61
95
  }
62
96
  if (!clientId) {
@@ -106,6 +140,36 @@ const Value = ({ field, nodePath, validatedLocation, keyframeDisplayOffset }) =>
106
140
  setCodeValues,
107
141
  validatedLocation,
108
142
  ]);
143
+ const onSaveKeyframed = (0, react_1.useCallback)((value, sourceFrame) => {
144
+ if (!validatedLocation) {
145
+ return Promise.reject(new Error('Cannot save'));
146
+ }
147
+ if (!clientId) {
148
+ return Promise.reject(new Error('Not connected to studio server'));
149
+ }
150
+ return (0, call_add_keyframe_1.callAddEffectKeyframe)({
151
+ fileName: validatedLocation.source,
152
+ nodePath,
153
+ effectIndex: field.effectIndex,
154
+ fieldKey: field.key,
155
+ sourceFrame,
156
+ value,
157
+ schema: field.effectSchema,
158
+ setCodeValues,
159
+ clientId,
160
+ });
161
+ }, [
162
+ clientId,
163
+ field.effectIndex,
164
+ field.effectSchema,
165
+ field.key,
166
+ nodePath,
167
+ setCodeValues,
168
+ validatedLocation,
169
+ ]);
170
+ if (field.fieldSchema.type === 'scale') {
171
+ throw new Error(`Effects do not support scale fields: ${field.key}`);
172
+ }
109
173
  if (effectStatus.type === 'cannot-update-effect') {
110
174
  if (effectStatus.reason === 'computed') {
111
175
  return jsx_runtime_1.jsx(TimelineSchemaField_1.UnsupportedStatus, { label: "computed" });
@@ -127,22 +191,23 @@ const Value = ({ field, nodePath, validatedLocation, keyframeDisplayOffset }) =>
127
191
  }
128
192
  throw new Error(`Unsupported effect status: ${effectStatus.reason}`);
129
193
  }
130
- if (propStatus === null || !propStatus.canUpdate) {
131
- if ((propStatus === null || propStatus === void 0 ? void 0 : propStatus.reason) === 'keyframed') {
132
- return (jsx_runtime_1.jsx(TimelineKeyframedValue_1.TimelineKeyframedValue, { field: field, propStatus: propStatus, keyframeDisplayOffset: keyframeDisplayOffset }));
133
- }
134
- if ((propStatus === null || propStatus === void 0 ? void 0 : propStatus.reason) === 'computed') {
135
- return jsx_runtime_1.jsx(TimelineSchemaField_1.UnsupportedStatus, { label: (0, get_timeline_keyframes_1.getComputedStatusLabel)(propStatus) });
136
- }
194
+ if (propStatus === null) {
137
195
  return null;
138
196
  }
197
+ if (isKeyframedStatus(propStatus)) {
198
+ return (jsx_runtime_1.jsx(TimelineKeyframedValue_1.TimelineKeyframedValue, { field: field, propStatus: propStatus, keyframeDisplayOffset: keyframeDisplayOffset, dragOverrideValue: dragOverrideValue, onSave: onSaveKeyframed, onDragValueChange: onDragValueChange, onDragEnd: onDragEnd, scaleLockNodePath: nodePath }));
199
+ }
200
+ if (propStatus.status === 'computed') {
201
+ return jsx_runtime_1.jsx(TimelineSchemaField_1.UnsupportedStatus, { label: (0, get_timeline_keyframes_1.getComputedStatusLabel)(propStatus) });
202
+ }
139
203
  const effectiveValue = remotion_1.Internals.getEffectiveVisualModeValue({
140
204
  codeValue: propStatus,
141
205
  dragOverrideValue,
142
206
  defaultValue: field.fieldSchema.default,
207
+ frame: jsxFrame,
143
208
  shouldResortToDefaultValueIfUndefined: true,
144
209
  });
145
- return (jsx_runtime_1.jsx(TimelineSchemaField_1.TimelineFieldValue, { field: field, propStatus: propStatus, onSave: onSave, onDragValueChange: onDragValueChange, onDragEnd: onDragEnd, effectiveValue: effectiveValue }));
210
+ return (jsx_runtime_1.jsx(TimelineSchemaField_1.TimelineFieldValue, { field: field, propStatus: propStatus, onSave: onSave, onDragValueChange: onDragValueChange, onDragEnd: onDragEnd, effectiveValue: effectiveValue, scaleLockNodePath: null }));
146
211
  };
147
212
  const TimelineEffectPropItem = ({ field, validatedLocation, rowDepth, nodePath, nodePathInfo, keyframeDisplayOffset, }) => {
148
213
  var _a;
@@ -174,23 +239,28 @@ const TimelineEffectPropItem = ({ field, validatedLocation, rowDepth, nodePath,
174
239
  (0, TimelineKeyframeControls_1.shouldShowTimelineKeyframeControls)({
175
240
  propStatus,
176
241
  selected: selection.selected,
242
+ keyframable: (0, studio_shared_1.isSchemaFieldKeyframable)({
243
+ schema: field.effectSchema,
244
+ key: field.key,
245
+ }),
177
246
  }) ? (jsx_runtime_1.jsx(TimelineKeyframeControls_1.TimelineKeyframeControls, { fieldKey: field.key, propStatus: propStatus, nodePath: nodePath, fileName: validatedLocation.source, keyframeDisplayOffset: keyframeDisplayOffset, defaultValue: field.fieldSchema.default, dragOverrideValue: dragOverrideValue, schema: field.effectSchema, effectIndex: field.effectIndex })) : null;
178
- const isNonDefault = (0, react_1.useMemo)(() => {
179
- var _a;
180
- if (!propStatus || !propStatus.canUpdate) {
247
+ const canResetToDefault = (0, react_1.useMemo)(() => {
248
+ if (!propStatus || propStatus.status === 'computed') {
181
249
  return false;
182
250
  }
183
- const effectiveCodeValue = (_a = propStatus.codeValue) !== null && _a !== void 0 ? _a : field.fieldSchema.default;
184
- return (JSON.stringify(effectiveCodeValue) !==
185
- JSON.stringify(field.fieldSchema.default));
251
+ return isResettableStatus({
252
+ status: propStatus,
253
+ defaultValue: field.fieldSchema.default,
254
+ });
186
255
  }, [field.fieldSchema.default, propStatus]);
187
256
  const canPerformReset = previewServerState.type === 'connected' &&
188
257
  propStatus !== null &&
189
- propStatus.canUpdate;
258
+ propStatus.status !== 'computed';
259
+ const canShowReset = canPerformReset && field.fieldSchema.default !== undefined;
190
260
  const onReset = (0, react_1.useCallback)(() => {
191
- if (!canPerformReset ||
192
- previewServerState.type !== 'connected' ||
193
- !isNonDefault) {
261
+ if (!canShowReset ||
262
+ !canResetToDefault ||
263
+ previewServerState.type !== 'connected') {
194
264
  return;
195
265
  }
196
266
  const defaultValue = field.fieldSchema.default !== undefined
@@ -208,12 +278,12 @@ const TimelineEffectPropItem = ({ field, validatedLocation, rowDepth, nodePath,
208
278
  clientId: previewServerState.clientId,
209
279
  });
210
280
  }, [
211
- canPerformReset,
281
+ canResetToDefault,
282
+ canShowReset,
212
283
  field.effectIndex,
213
284
  field.effectSchema,
214
285
  field.fieldSchema.default,
215
286
  field.key,
216
- isNonDefault,
217
287
  nodePath,
218
288
  previewServerState,
219
289
  setCodeValues,
@@ -227,14 +297,14 @@ const TimelineEffectPropItem = ({ field, validatedLocation, rowDepth, nodePath,
227
297
  keyHint: null,
228
298
  label: 'Reset',
229
299
  leftItem: null,
230
- disabled: !canPerformReset,
300
+ disabled: !canShowReset,
231
301
  onClick: onReset,
232
302
  quickSwitcherLabel: null,
233
303
  subMenu: null,
234
304
  value: 'reset-effect-field',
235
305
  },
236
306
  ];
237
- }, [canPerformReset, onReset]);
307
+ }, [canShowReset, onReset]);
238
308
  const row = (jsx_runtime_1.jsxs(TimelineRowChrome_1.TimelineRowChrome, { depth: rowDepth, eye: jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEyeSpacer, {}), keyframeControls: keyframeControls, arrow: jsx_runtime_1.jsx(TimelineExpandArrowButton_1.TimelineExpandArrowSpacer, {}), style: style, selected: selection.selected, selectable: selection.selectable, onSelect: selection.onSelect, showSelectedBackground: true, containsSelection: false, outerHeight: null, children: [
239
309
  jsx_runtime_1.jsx(TimelineFieldLabel_1.TimelineFieldLabel, { rowDepth: rowDepth, selected: selection.selected, label: (_c = field.description) !== null && _c !== void 0 ? _c : field.key }), jsx_runtime_1.jsx("div", { style: timeline_field_row_layout_1.timelineFieldValueColumnStyle, children: jsx_runtime_1.jsx(Value, { field: field, nodePath: nodePath, validatedLocation: validatedLocation, keyframeDisplayOffset: keyframeDisplayOffset }) })
240
310
  ] }));
@@ -1,9 +1,9 @@
1
1
  import React from 'react';
2
- import type { CanUpdateSequencePropStatus } from 'remotion';
2
+ import type { CanUpdateSequencePropStatusStatic } from 'remotion';
3
3
  import type { SchemaFieldInfo, TimelineFieldOnDragValueChange, TimelineFieldOnSave } from '../../helpers/timeline-layout';
4
4
  export declare const TimelineEnumField: React.FC<{
5
5
  readonly field: SchemaFieldInfo;
6
- readonly propStatus: CanUpdateSequencePropStatus;
6
+ readonly propStatus: CanUpdateSequencePropStatusStatic;
7
7
  readonly effectiveValue: unknown;
8
8
  readonly onSave: TimelineFieldOnSave;
9
9
  readonly onDragValueChange: TimelineFieldOnDragValueChange;
@@ -15,7 +15,7 @@ const TimelineEnumField = ({ field, propStatus, effectiveValue, onSave, onDragVa
15
15
  const variantKeys = Object.keys(fieldSchema.variants);
16
16
  const current = String(effectiveValue !== null && effectiveValue !== void 0 ? effectiveValue : fieldSchema.default);
17
17
  const onSelect = (0, react_1.useCallback)((newValue) => {
18
- if (!propStatus.canUpdate || newValue === propStatus.codeValue) {
18
+ if (newValue === propStatus.codeValue) {
19
19
  return;
20
20
  }
21
21
  onDragValueChange(newValue);
@@ -34,9 +34,9 @@ const TimelineEnumField = ({ field, propStatus, effectiveValue, onSave, onDragVa
34
34
  leftItem: null,
35
35
  subMenu: null,
36
36
  quickSwitcherLabel: null,
37
- disabled: !propStatus.canUpdate,
37
+ disabled: false,
38
38
  }));
39
- }, [variantKeys, onSelect, propStatus]);
39
+ }, [variantKeys, onSelect]);
40
40
  return (jsx_runtime_1.jsx(ComboBox_1.Combobox, { small: true, title: field.key, selectedId: current, values: items, style: comboboxStyle }));
41
41
  };
42
42
  exports.TimelineEnumField = TimelineEnumField;
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
- import type { CanUpdateSequencePropStatus, SequencePropsSubscriptionKey, SequenceSchema } from 'remotion';
3
- export declare const shouldShowTimelineKeyframeControls: ({ propStatus, selected, }: {
2
+ import type { CanUpdateSequencePropStatus, DragOverrideValue, SequencePropsSubscriptionKey, SequenceSchema } from 'remotion';
3
+ export declare const shouldShowTimelineKeyframeControls: ({ propStatus, selected, keyframable, }: {
4
4
  propStatus: CanUpdateSequencePropStatus | null;
5
5
  selected: boolean;
6
+ keyframable: boolean;
6
7
  }) => boolean;
7
8
  export declare const TimelineKeyframeControls: React.FC<{
8
9
  readonly fieldKey: string;
@@ -11,7 +12,7 @@ export declare const TimelineKeyframeControls: React.FC<{
11
12
  readonly fileName: string;
12
13
  readonly keyframeDisplayOffset: number;
13
14
  readonly defaultValue: unknown;
14
- readonly dragOverrideValue: unknown | undefined;
15
+ readonly dragOverrideValue: DragOverrideValue | undefined;
15
16
  readonly schema: SequenceSchema;
16
17
  readonly effectIndex: number | null;
17
18
  }>;