@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
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SplitterContainer = exports.containerColumn = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const player_1 = require("@remotion/player");
5
6
  const react_1 = require("react");
6
7
  const timeline_1 = require("../../state/timeline");
7
8
  const SplitterContext_1 = require("./SplitterContext");
@@ -46,6 +47,14 @@ const SplitterContainer = ({ orientation, children, defaultFlex, maxFlex, minFle
46
47
  persistFlex,
47
48
  ref,
48
49
  ]);
50
+ (0, react_1.useEffect)(() => {
51
+ const frame = requestAnimationFrame(() => {
52
+ player_1.PlayerInternals.updateAllElementsSizes();
53
+ });
54
+ return () => {
55
+ cancelAnimationFrame(frame);
56
+ };
57
+ });
49
58
  return (jsx_runtime_1.jsx(SplitterContext_1.SplitterContext.Provider, { value: value, children: jsx_runtime_1.jsx("div", { ref: ref, style: orientation === 'horizontal' ? exports.containerColumn : containerRow, children: children }) }));
50
59
  };
51
60
  exports.SplitterContainer = SplitterContainer;
@@ -18,97 +18,90 @@ const SplitterHandle = ({ allowToCollapse, onCollapse }) => {
18
18
  if (!context) {
19
19
  throw new Error('Cannot find splitter context');
20
20
  }
21
- const [lastPointerUp, setLastPointerUp] = (0, react_1.useState)(() => Date.now());
22
21
  const ref = (0, react_1.useRef)(null);
22
+ // Keep the latest props/context readable inside the long-lived pointerdown
23
+ // listener without re-subscribing it on every render.
24
+ const latest = (0, react_1.useRef)({ context, allowToCollapse, onCollapse });
25
+ latest.current = { context, allowToCollapse, onCollapse };
23
26
  (0, react_1.useEffect)(() => {
24
- if (context.isDragging.current) {
25
- return;
26
- }
27
27
  const { current } = ref;
28
28
  if (!current) {
29
29
  return;
30
30
  }
31
- const getNewValue = (e, clamp) => {
32
- if (!context.isDragging.current) {
33
- throw new Error('cannot get value if not dragging');
34
- }
35
- if (!context.ref.current) {
36
- throw new Error('domRect is not mounted');
37
- }
38
- const { width, height } = context.ref.current.getBoundingClientRect();
39
- const change = (() => {
40
- if (context.orientation === 'vertical') {
41
- return ((e.clientX - context.isDragging.current.x) /
42
- (width - exports.SPLITTER_HANDLE_SIZE));
43
- }
44
- return ((e.clientY - context.isDragging.current.y) /
45
- (height - exports.SPLITTER_HANDLE_SIZE));
46
- })();
47
- const newFlex = context.flexValue + change;
48
- if (clamp) {
49
- return Math.min(context.maxFlex, Math.max(context.minFlex, newFlex));
50
- }
51
- return newFlex;
52
- };
31
+ // Cleanup for the listeners that only exist for the duration of a drag.
32
+ let endDrag = null;
53
33
  const onPointerDown = (e) => {
54
- var _a;
55
34
  if (e.button !== 0) {
56
35
  return;
57
36
  }
58
- context.isDragging.current = {
59
- x: e.clientX,
60
- y: e.clientY,
37
+ // Capture the context and starting flex once, at drag start. The flex
38
+ // value updates on every pointermove, so it must not be re-read live.
39
+ const dragContext = latest.current.context;
40
+ const start = { x: e.clientX, y: e.clientY };
41
+ const startFlex = dragContext.flexValue;
42
+ dragContext.isDragging.current = start;
43
+ (0, ForceSpecificCursor_1.forceSpecificCursor)(dragContext.orientation === 'horizontal' ? 'row-resize' : 'col-resize');
44
+ current.classList.add('remotion-splitter-active');
45
+ const getNewValue = (ev, clamp) => {
46
+ if (!dragContext.ref.current) {
47
+ throw new Error('domRect is not mounted');
48
+ }
49
+ const { width, height } = dragContext.ref.current.getBoundingClientRect();
50
+ const change = dragContext.orientation === 'vertical'
51
+ ? (ev.clientX - start.x) / (width - exports.SPLITTER_HANDLE_SIZE)
52
+ : (ev.clientY - start.y) / (height - exports.SPLITTER_HANDLE_SIZE);
53
+ const newFlex = startFlex + change;
54
+ if (clamp) {
55
+ return Math.min(dragContext.maxFlex, Math.max(dragContext.minFlex, newFlex));
56
+ }
57
+ return newFlex;
61
58
  };
62
- (0, ForceSpecificCursor_1.forceSpecificCursor)(context.orientation === 'horizontal' ? 'row-resize' : 'col-resize');
63
- (_a = ref.current) === null || _a === void 0 ? void 0 : _a.classList.add('remotion-splitter-active');
64
- window.addEventListener('pointerup', (ev) => {
65
- if (!context.isDragging.current) {
59
+ endDrag = () => {
60
+ dragContext.isDragging.current = false;
61
+ (0, ForceSpecificCursor_1.stopForcingSpecificCursor)();
62
+ current.classList.remove('remotion-splitter-active');
63
+ window.removeEventListener('pointermove', onPointerMove);
64
+ window.removeEventListener('pointerup', onPointerUp);
65
+ endDrag = null;
66
+ player_1.PlayerInternals.updateAllElementsSizes();
67
+ };
68
+ const onPointerMove = (ev) => {
69
+ if (!dragContext.isDragging.current) {
66
70
  return;
67
71
  }
68
- context.persistFlex(getNewValue(ev, true));
69
- cleanup();
70
- setLastPointerUp(Date.now());
71
- }, { once: true });
72
- window.addEventListener('pointermove', onPointerMove);
73
- };
74
- const onPointerMove = (e) => {
75
- if (context.isDragging.current) {
76
- const val = getNewValue(e, true);
77
- context.setFlexValue(val);
78
- if (allowToCollapse === 'left') {
79
- const unclamped = getNewValue(e, false);
80
- if (unclamped < context.minFlex / 2) {
81
- cleanup();
82
- onCollapse();
83
- setLastPointerUp(Date.now());
72
+ dragContext.setFlexValue(getNewValue(ev, true));
73
+ const collapse = latest.current.allowToCollapse;
74
+ if (collapse === 'left') {
75
+ const unclamped = getNewValue(ev, false);
76
+ if (unclamped < dragContext.minFlex / 2) {
77
+ endDrag === null || endDrag === void 0 ? void 0 : endDrag();
78
+ latest.current.onCollapse();
84
79
  }
85
80
  }
86
- if (allowToCollapse === 'right') {
87
- const unclamped = 1 - getNewValue(e, false);
88
- if (unclamped < (1 - context.maxFlex) / 2) {
89
- cleanup();
90
- onCollapse();
91
- setLastPointerUp(Date.now());
81
+ else if (collapse === 'right') {
82
+ const unclamped = 1 - getNewValue(ev, false);
83
+ if (unclamped < (1 - dragContext.maxFlex) / 2) {
84
+ endDrag === null || endDrag === void 0 ? void 0 : endDrag();
85
+ latest.current.onCollapse();
92
86
  }
93
87
  }
94
- }
95
- };
96
- const cleanup = () => {
97
- var _a;
98
- context.isDragging.current = false;
99
- (0, ForceSpecificCursor_1.stopForcingSpecificCursor)();
100
- (_a = ref.current) === null || _a === void 0 ? void 0 : _a.classList.remove('remotion-splitter-active');
101
- current.removeEventListener('pointerdown', onPointerDown);
102
- window.removeEventListener('pointermove', onPointerMove);
103
- player_1.PlayerInternals.updateAllElementsSizes();
88
+ };
89
+ const onPointerUp = (ev) => {
90
+ if (!dragContext.isDragging.current) {
91
+ return;
92
+ }
93
+ dragContext.persistFlex(getNewValue(ev, true));
94
+ endDrag === null || endDrag === void 0 ? void 0 : endDrag();
95
+ };
96
+ window.addEventListener('pointermove', onPointerMove);
97
+ window.addEventListener('pointerup', onPointerUp);
104
98
  };
105
99
  current.addEventListener('pointerdown', onPointerDown);
106
100
  return () => {
107
- if (!context.isDragging.current) {
108
- cleanup();
109
- }
101
+ current.removeEventListener('pointerdown', onPointerDown);
102
+ endDrag === null || endDrag === void 0 ? void 0 : endDrag();
110
103
  };
111
- }, [allowToCollapse, context, context.flexValue, lastPointerUp, onCollapse]);
104
+ }, []);
112
105
  (0, react_1.useEffect)(() => {
113
106
  const { current } = ref;
114
107
  if (!current) {
@@ -44,6 +44,7 @@ const is_current_selected_still_1 = require("../../helpers/is-current-selected-s
44
44
  const open_in_editor_1 = require("../../helpers/open-in-editor");
45
45
  const call_api_1 = require("../call-api");
46
46
  const ContextMenu_1 = require("../ContextMenu");
47
+ const import_assets_1 = require("../import-assets");
47
48
  const is_menu_item_1 = require("../Menu/is-menu-item");
48
49
  const NotificationCenter_1 = require("../Notifications/NotificationCenter");
49
50
  const SplitterContainer_1 = require("../Splitter/SplitterContainer");
@@ -83,6 +84,9 @@ const TimelineClearSelectionArea = ({ children }) => {
83
84
  const { compositions, canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
84
85
  const videoConfig = remotion_1.Internals.useUnsafeVideoConfig();
85
86
  const [isAddingSolid, setIsAddingSolid] = (0, react_1.useState)(false);
87
+ const [isAddingAsset, setIsAddingAsset] = (0, react_1.useState)(false);
88
+ const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
89
+ const previewConnected = previewServerState.type === 'connected';
86
90
  const currentCompositionId = (canvasContent === null || canvasContent === void 0 ? void 0 : canvasContent.type) === 'composition' ? canvasContent.compositionId : null;
87
91
  const currentComposition = (0, react_1.useMemo)(() => {
88
92
  var _a;
@@ -106,11 +110,18 @@ const TimelineClearSelectionArea = ({ children }) => {
106
110
  }
107
111
  clearSelection();
108
112
  }, [clearSelection]);
109
- const canInsertSolid = (compositionComponentInfo === null || compositionComponentInfo === void 0 ? void 0 : compositionComponentInfo.canAddSequence) === true &&
113
+ const canInsertSolid = previewConnected &&
114
+ (compositionComponentInfo === null || compositionComponentInfo === void 0 ? void 0 : compositionComponentInfo.canAddSequence) === true &&
110
115
  currentCompositionId !== null &&
111
116
  compositionFile !== null &&
112
117
  videoConfig !== null &&
113
118
  !isAddingSolid;
119
+ const canInsertAsset = previewConnected &&
120
+ !window.remotion_isReadOnlyStudio &&
121
+ (compositionComponentInfo === null || compositionComponentInfo === void 0 ? void 0 : compositionComponentInfo.canAddSequence) === true &&
122
+ currentCompositionId !== null &&
123
+ compositionFile !== null &&
124
+ !isAddingAsset;
114
125
  const insertSolid = (0, react_1.useCallback)(async () => {
115
126
  if (!canInsertSolid ||
116
127
  currentCompositionId === null ||
@@ -142,6 +153,28 @@ const TimelineClearSelectionArea = ({ children }) => {
142
153
  setIsAddingSolid(false);
143
154
  }
144
155
  }, [canInsertSolid, compositionFile, currentCompositionId, videoConfig]);
156
+ const insertAsset = (0, react_1.useCallback)(async () => {
157
+ if (!canInsertAsset ||
158
+ currentCompositionId === null ||
159
+ compositionFile === null) {
160
+ return;
161
+ }
162
+ const files = await (0, import_assets_1.pickFilesToImport)();
163
+ if (files.length === 0) {
164
+ return;
165
+ }
166
+ setIsAddingAsset(true);
167
+ try {
168
+ await (0, import_assets_1.importAssets)({
169
+ files,
170
+ compositionFile,
171
+ compositionId: currentCompositionId,
172
+ });
173
+ }
174
+ finally {
175
+ setIsAddingAsset(false);
176
+ }
177
+ }, [canInsertAsset, compositionFile, currentCompositionId]);
145
178
  const contextMenuItems = (0, react_1.useMemo)(() => {
146
179
  return [
147
180
  {
@@ -156,8 +189,20 @@ const TimelineClearSelectionArea = ({ children }) => {
156
189
  quickSwitcherLabel: null,
157
190
  disabled: !canInsertSolid,
158
191
  },
192
+ {
193
+ type: 'item',
194
+ id: 'insert-asset',
195
+ label: 'Add asset',
196
+ value: 'insert-asset',
197
+ onClick: insertAsset,
198
+ keyHint: null,
199
+ leftItem: null,
200
+ subMenu: null,
201
+ quickSwitcherLabel: null,
202
+ disabled: !canInsertAsset,
203
+ },
159
204
  ];
160
- }, [insertSolid, canInsertSolid]);
205
+ }, [insertSolid, canInsertSolid, insertAsset, canInsertAsset]);
161
206
  return (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { ref: timeline_refs_1.timelineVerticalScroll, values: contextMenuItems, onOpen: null, style: container, className: 'css-reset ' + is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, onPointerDown: onPointerDown, children: children }));
162
207
  };
163
208
  const TimelineInner = () => {
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import type { SchemaFieldInfo, TimelineFieldOnDragValueChange, TimelineFieldOnSave } from '../../helpers/timeline-layout';
3
+ export declare const TimelineArrayField: React.FC<{
4
+ readonly field: SchemaFieldInfo;
5
+ readonly effectiveValue: unknown;
6
+ readonly onSave: TimelineFieldOnSave;
7
+ readonly onDragValueChange: TimelineFieldOnDragValueChange;
8
+ readonly onDragEnd: () => void;
9
+ }>;
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TimelineArrayField = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const TimelinePrimitiveFieldValue_1 = require("./TimelinePrimitiveFieldValue");
7
+ const container = {
8
+ display: 'flex',
9
+ flexDirection: 'column',
10
+ alignItems: 'flex-start',
11
+ minWidth: 0,
12
+ };
13
+ const itemContainer = {
14
+ display: 'flex',
15
+ alignItems: 'center',
16
+ gap: 6,
17
+ flexShrink: 0,
18
+ height: 22,
19
+ minWidth: 0,
20
+ };
21
+ const itemLabel = {
22
+ color: 'rgba(255, 255, 255, 0.45)',
23
+ fontSize: 11,
24
+ fontVariantNumeric: 'tabular-nums',
25
+ width: 18,
26
+ textAlign: 'right',
27
+ };
28
+ const button = {
29
+ background: 'rgba(255, 255, 255, 0.08)',
30
+ border: '1px solid rgba(255, 255, 255, 0.12)',
31
+ borderRadius: 3,
32
+ color: 'white',
33
+ cursor: 'pointer',
34
+ fontSize: 10,
35
+ height: 18,
36
+ lineHeight: '16px',
37
+ padding: '0 5px',
38
+ };
39
+ const disabledButton = {
40
+ ...button,
41
+ cursor: 'default',
42
+ opacity: 0.4,
43
+ };
44
+ const addButtonRow = {
45
+ ...itemContainer,
46
+ paddingLeft: 24,
47
+ };
48
+ const getFallbackItemValue = (field) => {
49
+ var _a;
50
+ if (field.newItemDefault !== undefined) {
51
+ return field.newItemDefault;
52
+ }
53
+ if (field.item.type === 'number' || field.item.type === 'rotation-degrees') {
54
+ return (_a = field.item.min) !== null && _a !== void 0 ? _a : 0;
55
+ }
56
+ if (field.item.type === 'boolean') {
57
+ return false;
58
+ }
59
+ if (field.item.type === 'rotation-css') {
60
+ return '0deg';
61
+ }
62
+ if (field.item.type === 'translate') {
63
+ return '0px 0px';
64
+ }
65
+ if (field.item.type === 'uv-coordinate') {
66
+ return [0.5, 0.5];
67
+ }
68
+ if (field.item.type === 'color') {
69
+ return '#000000';
70
+ }
71
+ if (field.item.type === 'enum') {
72
+ const [firstVariant] = field.item.variants;
73
+ return firstVariant;
74
+ }
75
+ throw new Error(`Unsupported array item field: ${JSON.stringify(field.item)}`);
76
+ };
77
+ const isUvCoordinateDefault = (value) => {
78
+ return (Array.isArray(value) &&
79
+ value.length === 2 &&
80
+ value.every((item) => typeof item === 'number'));
81
+ };
82
+ const makeItemFieldSchema = ({ field, defaultValue, }) => {
83
+ var _a;
84
+ if (field.item.type === 'number') {
85
+ return {
86
+ ...field.item,
87
+ default: typeof defaultValue === 'number' ? defaultValue : undefined,
88
+ hiddenFromList: false,
89
+ };
90
+ }
91
+ if (field.item.type === 'boolean') {
92
+ return {
93
+ ...field.item,
94
+ default: Boolean(defaultValue),
95
+ };
96
+ }
97
+ if (field.item.type === 'rotation-css') {
98
+ return {
99
+ ...field.item,
100
+ default: typeof defaultValue === 'string' ? defaultValue : undefined,
101
+ };
102
+ }
103
+ if (field.item.type === 'rotation-degrees') {
104
+ return {
105
+ ...field.item,
106
+ default: typeof defaultValue === 'number' ? defaultValue : undefined,
107
+ };
108
+ }
109
+ if (field.item.type === 'translate') {
110
+ return {
111
+ ...field.item,
112
+ default: typeof defaultValue === 'string' ? defaultValue : undefined,
113
+ };
114
+ }
115
+ if (field.item.type === 'uv-coordinate') {
116
+ return {
117
+ ...field.item,
118
+ default: isUvCoordinateDefault(defaultValue) ? defaultValue : undefined,
119
+ };
120
+ }
121
+ if (field.item.type === 'color') {
122
+ return {
123
+ ...field.item,
124
+ default: typeof defaultValue === 'string' ? defaultValue : undefined,
125
+ };
126
+ }
127
+ if (field.item.type === 'enum') {
128
+ return {
129
+ type: 'enum',
130
+ default: typeof defaultValue === 'string'
131
+ ? defaultValue
132
+ : ((_a = field.item.variants[0]) !== null && _a !== void 0 ? _a : ''),
133
+ variants: Object.fromEntries(field.item.variants.map((variant) => [variant, {}])),
134
+ };
135
+ }
136
+ throw new Error(`Unsupported array item field: ${JSON.stringify(field.item)}`);
137
+ };
138
+ const replaceAtIndex = (items, index, value) => items.map((item, i) => (i === index ? value : item));
139
+ const ItemEditor = ({ field, arrayField, items, index, onSave, onDragValueChange, onDragEnd, }) => {
140
+ var _a;
141
+ const fallback = (0, react_1.useMemo)(() => getFallbackItemValue(arrayField), [arrayField]);
142
+ const value = (_a = items[index]) !== null && _a !== void 0 ? _a : fallback;
143
+ const itemField = (0, react_1.useMemo)(() => {
144
+ const fieldSchema = makeItemFieldSchema({
145
+ field: arrayField,
146
+ defaultValue: fallback,
147
+ });
148
+ return {
149
+ ...field,
150
+ key: `${field.key}[${index}]`,
151
+ typeName: fieldSchema.type,
152
+ fieldSchema,
153
+ };
154
+ }, [arrayField, fallback, field, index]);
155
+ const propStatus = (0, react_1.useMemo)(() => ({
156
+ status: 'static',
157
+ codeValue: value,
158
+ }), [value]);
159
+ const onSaveItem = (0, react_1.useCallback)((next) => onSave(replaceAtIndex(items, index, next)), [index, items, onSave]);
160
+ const onDragItem = (0, react_1.useCallback)((next) => onDragValueChange(replaceAtIndex(items, index, next)), [index, items, onDragValueChange]);
161
+ return (jsx_runtime_1.jsx(TimelinePrimitiveFieldValue_1.TimelinePrimitiveFieldValue, { effectiveValue: value, field: itemField, onDragEnd: onDragEnd, onDragValueChange: onDragItem, onSave: onSaveItem, propStatus: propStatus, scaleLockNodePath: null }));
162
+ };
163
+ const TimelineArrayField = ({ field, effectiveValue, onSave, onDragValueChange, onDragEnd }) => {
164
+ var _a, _b;
165
+ const arrayField = field.fieldSchema;
166
+ if (arrayField.type !== 'array') {
167
+ throw new Error('TimelineArrayField rendered for non-array field');
168
+ }
169
+ const items = (0, react_1.useMemo)(() => {
170
+ if (Array.isArray(effectiveValue)) {
171
+ return effectiveValue;
172
+ }
173
+ return Array.isArray(arrayField.default) ? arrayField.default : [];
174
+ }, [arrayField.default, effectiveValue]);
175
+ const minLength = (_a = arrayField.minLength) !== null && _a !== void 0 ? _a : 0;
176
+ const maxLength = (_b = arrayField.maxLength) !== null && _b !== void 0 ? _b : Infinity;
177
+ const canAdd = items.length < maxLength;
178
+ const canRemove = items.length > minLength;
179
+ const itemKeys = (0, react_1.useRef)([]);
180
+ const nextItemKey = (0, react_1.useRef)(0);
181
+ while (itemKeys.current.length < items.length) {
182
+ itemKeys.current.push(`${field.key}-${nextItemKey.current}`);
183
+ nextItemKey.current++;
184
+ }
185
+ if (itemKeys.current.length > items.length) {
186
+ itemKeys.current.length = items.length;
187
+ }
188
+ const onAdd = (0, react_1.useCallback)(() => {
189
+ if (!canAdd) {
190
+ return;
191
+ }
192
+ itemKeys.current.push(`${field.key}-${nextItemKey.current}`);
193
+ nextItemKey.current++;
194
+ onSave([...items, getFallbackItemValue(arrayField)]);
195
+ }, [arrayField, canAdd, field.key, items, onSave]);
196
+ const onRemove = (0, react_1.useCallback)((index) => {
197
+ if (!canRemove) {
198
+ return;
199
+ }
200
+ itemKeys.current.splice(index, 1);
201
+ onSave(items.filter((_item, i) => i !== index));
202
+ }, [canRemove, items, onSave]);
203
+ return (jsx_runtime_1.jsxs("span", { style: container, children: [items.map((_item, index) => {
204
+ const itemKey = itemKeys.current[index];
205
+ return (jsx_runtime_1.jsxs("span", { style: itemContainer, children: [
206
+ jsx_runtime_1.jsx("span", { style: itemLabel, children: index }), jsx_runtime_1.jsx(ItemEditor, { arrayField: arrayField, field: field, index: index, items: items, onDragEnd: onDragEnd, onDragValueChange: onDragValueChange, onSave: onSave }), jsx_runtime_1.jsx("button", { disabled: !canRemove, onClick: () => onRemove(index), style: canRemove ? button : disabledButton, title: `Remove item ${index}`, type: "button", children: "-" })
207
+ ] }, itemKey));
208
+ }), canAdd ? (jsx_runtime_1.jsx("span", { style: addButtonRow, children: jsx_runtime_1.jsx("button", { type: "button", style: button, onClick: onAdd, title: "Add item", children: "+" }) })) : null] }));
209
+ };
210
+ exports.TimelineArrayField = TimelineArrayField;
@@ -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, TimelineFieldOnSave } from '../../helpers/timeline-layout';
4
4
  export declare const TimelineBooleanField: 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
  }>;
@@ -11,10 +11,10 @@ const TimelineBooleanField = ({ field, propStatus, effectiveValue, onSave }) =>
11
11
  const checked = Boolean(effectiveValue);
12
12
  const onChange = (0, react_1.useCallback)(() => {
13
13
  const newValue = !checked;
14
- if (propStatus.canUpdate && newValue !== propStatus.codeValue) {
14
+ if (newValue !== propStatus.codeValue) {
15
15
  onSave(newValue);
16
16
  }
17
17
  }, [propStatus, onSave, checked]);
18
- return (jsx_runtime_1.jsx("div", { style: checkboxContainer, children: jsx_runtime_1.jsx(Checkbox_1.Checkbox, { checked: checked, onChange: onChange, name: field.key, disabled: !propStatus.canUpdate, variant: "small" }) }));
18
+ return (jsx_runtime_1.jsx("div", { style: checkboxContainer, children: jsx_runtime_1.jsx(Checkbox_1.Checkbox, { checked: checked, onChange: onChange, name: field.key, disabled: false, variant: "small" }) }));
19
19
  };
20
20
  exports.TimelineBooleanField = TimelineBooleanField;
@@ -0,0 +1,20 @@
1
+ import { type EffectClipboardSnapshot } from '@remotion/studio-shared';
2
+ import type React from 'react';
3
+ import type { SequenceNodePathInfo } from '../../helpers/get-timeline-sequence-sort-key';
4
+ import { type TimelineSelection } from './TimelineSelection';
5
+ export type PasteEffectsTarget = {
6
+ readonly type: 'valid';
7
+ readonly nodePathInfo: SequenceNodePathInfo;
8
+ } | {
9
+ readonly type: 'none';
10
+ } | {
11
+ readonly type: 'multiple';
12
+ } | {
13
+ readonly type: 'unsupported';
14
+ };
15
+ export declare const getPasteEffectsTarget: (selectedItems: readonly TimelineSelection[]) => PasteEffectsTarget;
16
+ export declare const getSnapshotsFromSelection: ({ selection, codeValues, }: {
17
+ selection: TimelineSelection;
18
+ codeValues: import("remotion").CodeValues;
19
+ }) => EffectClipboardSnapshot[] | null;
20
+ export declare const TimelineClipboardKeybindings: React.FC;