@remotion/studio 4.0.470 → 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 (112) 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.d.ts +7 -2
  6. package/dist/components/ContextMenu.js +91 -43
  7. package/dist/components/Editor.js +14 -6
  8. package/dist/components/Modals.js +3 -1
  9. package/dist/components/NewComposition/CodemodFooter.js +2 -2
  10. package/dist/components/NewComposition/DeleteFolder.d.ts +6 -0
  11. package/dist/components/NewComposition/DeleteFolder.js +39 -0
  12. package/dist/components/NewComposition/RenameFolder.d.ts +6 -0
  13. package/dist/components/NewComposition/RenameFolder.js +60 -0
  14. package/dist/components/Preview.js +2 -1
  15. package/dist/components/SelectedOutlineOverlay.d.ts +109 -1
  16. package/dist/components/SelectedOutlineOverlay.js +489 -73
  17. package/dist/components/Splitter/SplitterContainer.js +9 -0
  18. package/dist/components/Splitter/SplitterHandle.js +63 -70
  19. package/dist/components/Timeline/Timeline.js +121 -1
  20. package/dist/components/Timeline/TimelineArrayField.d.ts +9 -0
  21. package/dist/components/Timeline/TimelineArrayField.js +210 -0
  22. package/dist/components/Timeline/TimelineBooleanField.d.ts +2 -2
  23. package/dist/components/Timeline/TimelineBooleanField.js +2 -2
  24. package/dist/components/Timeline/TimelineClipboardKeybindings.d.ts +20 -0
  25. package/dist/components/Timeline/TimelineClipboardKeybindings.js +265 -0
  26. package/dist/components/Timeline/TimelineColorField.d.ts +2 -2
  27. package/dist/components/Timeline/TimelineColorField.js +2 -8
  28. package/dist/components/Timeline/TimelineDeleteKeybindings.js +15 -0
  29. package/dist/components/Timeline/TimelineDragHandler.js +1 -0
  30. package/dist/components/Timeline/TimelineEffectItem.js +159 -6
  31. package/dist/components/Timeline/TimelineEffectPropItem.js +95 -25
  32. package/dist/components/Timeline/TimelineEnumField.d.ts +2 -2
  33. package/dist/components/Timeline/TimelineEnumField.js +3 -3
  34. package/dist/components/Timeline/TimelineKeyframeControls.d.ts +4 -3
  35. package/dist/components/Timeline/TimelineKeyframeControls.js +52 -33
  36. package/dist/components/Timeline/TimelineKeyframedValue.d.ts +7 -2
  37. package/dist/components/Timeline/TimelineKeyframedValue.js +22 -8
  38. package/dist/components/Timeline/TimelineLayerEye.d.ts +1 -0
  39. package/dist/components/Timeline/TimelineLayerEye.js +8 -3
  40. package/dist/components/Timeline/TimelineNumberField.d.ts +2 -2
  41. package/dist/components/Timeline/TimelineNumberField.js +7 -11
  42. package/dist/components/Timeline/TimelinePrimitiveFieldValue.d.ts +17 -0
  43. package/dist/components/Timeline/TimelinePrimitiveFieldValue.js +53 -0
  44. package/dist/components/Timeline/TimelineRotationField.d.ts +2 -2
  45. package/dist/components/Timeline/TimelineRotationField.js +41 -24
  46. package/dist/components/Timeline/TimelineRowChrome.d.ts +3 -0
  47. package/dist/components/Timeline/TimelineRowChrome.js +11 -10
  48. package/dist/components/Timeline/TimelineScaleField.d.ts +20 -0
  49. package/dist/components/Timeline/TimelineScaleField.js +314 -0
  50. package/dist/components/Timeline/TimelineSchemaField.d.ts +3 -2
  51. package/dist/components/Timeline/TimelineSchemaField.js +8 -42
  52. package/dist/components/Timeline/TimelineSelection.js +3 -2
  53. package/dist/components/Timeline/TimelineSequence.d.ts +1 -0
  54. package/dist/components/Timeline/TimelineSequence.js +51 -10
  55. package/dist/components/Timeline/TimelineSequenceFrame.js +1 -0
  56. package/dist/components/Timeline/TimelineSequenceItem.js +97 -7
  57. package/dist/components/Timeline/TimelineSequencePropItem.js +82 -21
  58. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.d.ts +58 -0
  59. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.js +528 -0
  60. package/dist/components/Timeline/TimelineTrack.js +1 -1
  61. package/dist/components/Timeline/TimelineTranslateField.d.ts +2 -2
  62. package/dist/components/Timeline/TimelineTranslateField.js +21 -25
  63. package/dist/components/Timeline/TimelineUvCoordinateField.d.ts +2 -2
  64. package/dist/components/Timeline/TimelineUvCoordinateField.js +20 -26
  65. package/dist/components/Timeline/call-add-keyframe.js +2 -0
  66. package/dist/components/Timeline/call-delete-keyframe.d.ts +16 -0
  67. package/dist/components/Timeline/call-delete-keyframe.js +86 -14
  68. package/dist/components/Timeline/delete-selected-keyframe.d.ts +10 -0
  69. package/dist/components/Timeline/delete-selected-keyframe.js +48 -7
  70. package/dist/components/Timeline/delete-selected-timeline-item.js +6 -11
  71. package/dist/components/Timeline/get-node-keyframes.d.ts +5 -2
  72. package/dist/components/Timeline/get-node-keyframes.js +38 -5
  73. package/dist/components/Timeline/get-timeline-keyframes.js +4 -4
  74. package/dist/components/Timeline/reset-selected-timeline-props.d.ts +38 -0
  75. package/dist/components/Timeline/reset-selected-timeline-props.js +156 -0
  76. package/dist/components/Timeline/sequence-props-subscription-store.d.ts +3 -2
  77. package/dist/components/Timeline/sequence-props-subscription-store.js +2 -1
  78. package/dist/components/Timeline/timeline-field-utils.d.ts +1 -0
  79. package/dist/components/Timeline/timeline-field-utils.js +5 -1
  80. package/dist/components/Timeline/timeline-scroll-logic.js +3 -3
  81. package/dist/components/Timeline/timeline-translate-utils.js +6 -2
  82. package/dist/components/Timeline/use-expanded-track-keyframe-rows.js +7 -0
  83. package/dist/components/Timeline/use-sequence-props-subscription.js +2 -1
  84. package/dist/components/TopPanel.d.ts +1 -1
  85. package/dist/components/folder-menu-items.d.ts +12 -0
  86. package/dist/components/folder-menu-items.js +147 -0
  87. package/dist/components/import-assets.d.ts +6 -0
  88. package/dist/components/import-assets.js +157 -0
  89. package/dist/esm/{chunk-dny42qnq.js → chunk-48grt472.js} +9717 -5925
  90. package/dist/esm/internals.mjs +9717 -5925
  91. package/dist/esm/previewEntry.mjs +9531 -5737
  92. package/dist/esm/renderEntry.mjs +1 -1
  93. package/dist/helpers/calculate-timeline.js +7 -3
  94. package/dist/helpers/create-folder-tree.js +1 -0
  95. package/dist/helpers/detect-file-type.d.ts +69 -0
  96. package/dist/helpers/detect-file-type.js +278 -0
  97. package/dist/helpers/get-folder-id.d.ts +4 -0
  98. package/dist/helpers/get-folder-id.js +7 -0
  99. package/dist/helpers/get-left-of-timeline-slider.js +1 -1
  100. package/dist/helpers/get-timeline-sequence-layout.js +10 -11
  101. package/dist/helpers/get-timeline-sequence-sort-key.d.ts +2 -0
  102. package/dist/helpers/open-in-editor.d.ts +19 -1
  103. package/dist/helpers/open-in-editor.js +42 -4
  104. package/dist/helpers/timeline-layout.js +5 -1
  105. package/dist/helpers/use-menu-structure.js +0 -1
  106. package/dist/helpers/validate-folder-rename.d.ts +6 -0
  107. package/dist/helpers/validate-folder-rename.js +19 -0
  108. package/dist/state/modals.d.ts +10 -0
  109. package/dist/state/scale-lock.d.ts +18 -0
  110. package/dist/state/scale-lock.js +59 -0
  111. package/dist/state/z-index.js +5 -2
  112. 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) {
@@ -41,7 +41,12 @@ const calculate_timeline_1 = require("../../helpers/calculate-timeline");
41
41
  const client_id_1 = require("../../helpers/client-id");
42
42
  const colors_1 = require("../../helpers/colors");
43
43
  const is_current_selected_still_1 = require("../../helpers/is-current-selected-still");
44
+ const open_in_editor_1 = require("../../helpers/open-in-editor");
45
+ const call_api_1 = require("../call-api");
46
+ const ContextMenu_1 = require("../ContextMenu");
47
+ const import_assets_1 = require("../import-assets");
44
48
  const is_menu_item_1 = require("../Menu/is-menu-item");
49
+ const NotificationCenter_1 = require("../Notifications/NotificationCenter");
45
50
  const SplitterContainer_1 = require("../Splitter/SplitterContainer");
46
51
  const SplitterElement_1 = require("../Splitter/SplitterElement");
47
52
  const SplitterHandle_1 = require("../Splitter/SplitterHandle");
@@ -63,6 +68,7 @@ const TimelineSlider_1 = require("./TimelineSlider");
63
68
  const TimelineTimeIndicators_1 = require("./TimelineTimeIndicators");
64
69
  const TimelineTracks_1 = require("./TimelineTracks");
65
70
  const TimelineWidthProvider_1 = require("./TimelineWidthProvider");
71
+ const use_resolved_stack_1 = require("./use-resolved-stack");
66
72
  const container = {
67
73
  minHeight: '100%',
68
74
  flex: 1,
@@ -73,7 +79,28 @@ const container = {
73
79
  };
74
80
  const noop = () => undefined;
75
81
  const TimelineClearSelectionArea = ({ children }) => {
82
+ var _a, _b;
76
83
  const { clearSelection } = (0, TimelineSelection_1.useTimelineSelection)();
84
+ const { compositions, canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
85
+ const videoConfig = remotion_1.Internals.useUnsafeVideoConfig();
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';
90
+ const currentCompositionId = (canvasContent === null || canvasContent === void 0 ? void 0 : canvasContent.type) === 'composition' ? canvasContent.compositionId : null;
91
+ const currentComposition = (0, react_1.useMemo)(() => {
92
+ var _a;
93
+ if (currentCompositionId === null) {
94
+ return null;
95
+ }
96
+ return ((_a = compositions.find((composition) => composition.id === currentCompositionId)) !== null && _a !== void 0 ? _a : null);
97
+ }, [compositions, currentCompositionId]);
98
+ const resolvedCompositionLocation = (0, use_resolved_stack_1.useResolvedStack)((_a = currentComposition === null || currentComposition === void 0 ? void 0 : currentComposition.stack) !== null && _a !== void 0 ? _a : null);
99
+ const compositionFile = (_b = resolvedCompositionLocation === null || resolvedCompositionLocation === void 0 ? void 0 : resolvedCompositionLocation.source) !== null && _b !== void 0 ? _b : null;
100
+ const compositionComponentInfo = (0, open_in_editor_1.useCachedCompositionComponentInfo)({
101
+ compositionFile,
102
+ compositionId: currentCompositionId,
103
+ });
77
104
  // Selection-triggering click handlers in children call e.stopPropagation(),
78
105
  // so any pointerdown that bubbles up here is by definition on empty space
79
106
  // and should clear the current selection.
@@ -83,7 +110,100 @@ const TimelineClearSelectionArea = ({ children }) => {
83
110
  }
84
111
  clearSelection();
85
112
  }, [clearSelection]);
86
- return (jsx_runtime_1.jsx("div", { ref: timeline_refs_1.timelineVerticalScroll, style: container, className: 'css-reset ' + is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, onPointerDown: onPointerDown, children: children }));
113
+ const canInsertSolid = previewConnected &&
114
+ (compositionComponentInfo === null || compositionComponentInfo === void 0 ? void 0 : compositionComponentInfo.canAddSequence) === true &&
115
+ currentCompositionId !== null &&
116
+ compositionFile !== null &&
117
+ videoConfig !== null &&
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;
125
+ const insertSolid = (0, react_1.useCallback)(async () => {
126
+ if (!canInsertSolid ||
127
+ currentCompositionId === null ||
128
+ compositionFile === null ||
129
+ videoConfig === null) {
130
+ return;
131
+ }
132
+ setIsAddingSolid(true);
133
+ try {
134
+ const result = await (0, call_api_1.callApi)('/api/insert-jsx-element', {
135
+ compositionFile,
136
+ compositionId: currentCompositionId,
137
+ element: {
138
+ type: 'solid',
139
+ width: videoConfig.width,
140
+ height: videoConfig.height,
141
+ },
142
+ });
143
+ if (result.success) {
144
+ (0, NotificationCenter_1.showNotification)('Added <Solid> to source file', 2000);
145
+ return;
146
+ }
147
+ (0, NotificationCenter_1.showNotification)(result.reason, 4000);
148
+ }
149
+ catch (err) {
150
+ (0, NotificationCenter_1.showNotification)(err.message, 4000);
151
+ }
152
+ finally {
153
+ setIsAddingSolid(false);
154
+ }
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]);
178
+ const contextMenuItems = (0, react_1.useMemo)(() => {
179
+ return [
180
+ {
181
+ type: 'item',
182
+ id: 'insert-solid',
183
+ label: 'Add <Solid>',
184
+ value: 'insert-solid',
185
+ onClick: insertSolid,
186
+ keyHint: null,
187
+ leftItem: null,
188
+ subMenu: null,
189
+ quickSwitcherLabel: null,
190
+ disabled: !canInsertSolid,
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
+ },
204
+ ];
205
+ }, [insertSolid, canInsertSolid, insertAsset, canInsertAsset]);
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 }));
87
207
  };
88
208
  const TimelineInner = () => {
89
209
  var _a;
@@ -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;