@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,314 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TimelineScaleField = exports.getLinkedScale = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const no_react_1 = require("remotion/no-react");
7
+ const colors_1 = require("../../helpers/colors");
8
+ const scale_lock_1 = require("../../state/scale-lock");
9
+ const InputDragger_1 = require("../NewComposition/InputDragger");
10
+ const timeline_field_utils_1 = require("./timeline-field-utils");
11
+ const TimelineLayerEye_1 = require("./TimelineLayerEye");
12
+ const leftDraggerStyle = {
13
+ paddingLeft: 0,
14
+ };
15
+ const rightDraggerStyle = {
16
+ paddingRight: 0,
17
+ };
18
+ const containerStyle = {
19
+ alignItems: 'center',
20
+ display: 'flex',
21
+ gap: 4,
22
+ };
23
+ const toggleStyle = {
24
+ ...TimelineLayerEye_1.timelineLayerIconContainer,
25
+ border: 'none',
26
+ color: colors_1.LIGHT_COLOR,
27
+ cursor: 'pointer',
28
+ marginRight: 0,
29
+ padding: 0,
30
+ };
31
+ const linkIconStyle = {
32
+ width: 14,
33
+ height: 14,
34
+ pointerEvents: 'none',
35
+ };
36
+ const gapStyle = {
37
+ marginLeft: -6,
38
+ marginRight: -6,
39
+ };
40
+ const clamp = (value, min, max) => {
41
+ return Math.min(max, Math.max(min, value));
42
+ };
43
+ const getLinkedScale = ({ axis, newValue, baseX, baseY, min, max, }) => {
44
+ const drivingBase = axis === 'x' ? baseX : baseY;
45
+ const linkedBase = axis === 'x' ? baseY : baseX;
46
+ if (drivingBase === 0 || linkedBase === 0) {
47
+ const clamped = (0, timeline_field_utils_1.normalizeTimelineNumber)(clamp(newValue, min, max));
48
+ return [clamped, clamped];
49
+ }
50
+ let factor = newValue / drivingBase;
51
+ let driving = newValue;
52
+ let linked = linkedBase * factor;
53
+ const clampedLinked = clamp(linked, min, max);
54
+ if (clampedLinked !== linked) {
55
+ factor = clampedLinked / linkedBase;
56
+ linked = clampedLinked;
57
+ driving = drivingBase * factor;
58
+ }
59
+ const clampedDriving = clamp(driving, min, max);
60
+ const normalizedDriving = (0, timeline_field_utils_1.normalizeTimelineNumber)(clampedDriving);
61
+ const normalizedLinked = (0, timeline_field_utils_1.normalizeTimelineNumber)(linked);
62
+ return axis === 'x'
63
+ ? [normalizedDriving, normalizedLinked]
64
+ : [normalizedLinked, normalizedDriving];
65
+ };
66
+ exports.getLinkedScale = getLinkedScale;
67
+ const valuesEqual = (left, right) => {
68
+ return JSON.stringify(left) === JSON.stringify(right);
69
+ };
70
+ const LinkToggle = ({ linked, onToggle }) => {
71
+ const onPointerDown = (0, react_1.useCallback)((e) => {
72
+ if (e.button !== 0) {
73
+ return;
74
+ }
75
+ e.stopPropagation();
76
+ onToggle();
77
+ }, [onToggle]);
78
+ return (jsx_runtime_1.jsx("button", { type: "button", style: toggleStyle, onPointerDown: onPointerDown, title: linked ? 'Unlink scale axes' : 'Link scale axes', "aria-label": linked ? 'Unlink scale axes' : 'Link scale axes', children: linked ? (jsx_runtime_1.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 640 640", style: linkIconStyle, children: jsx_runtime_1.jsx("path", { fill: "currentcolor", d: "M32 320C32 214 118 128 224 128L288 128L288 192L224 192C153.3 192 96 249.3 96 320C96 390.7 153.3 448 224 448L288 448L288 512L224 512C118 512 32 426 32 320zM608 320C608 426 522 512 416 512L352 512L352 448L416 448C486.7 448 544 390.7 544 320C544 249.3 486.7 192 416 192L352 192L352 128L416 128C522 128 608 214 608 320zM224 288L448 288L448 352L192 352L192 288L224 288z" }) })) : null }));
79
+ };
80
+ const TimelineScaleField = ({ field, propStatus, effectiveValue, onSave, onDragValueChange, onDragEnd, scaleLockNodePath, }) => {
81
+ var _a, _b, _c;
82
+ const [dragX, setDragX] = (0, react_1.useState)(null);
83
+ const [dragY, setDragY] = (0, react_1.useState)(null);
84
+ const dragStartRef = (0, react_1.useRef)(null);
85
+ const { getScaleLockState, setScaleLockState } = (0, react_1.useContext)(scale_lock_1.ScaleLockContext);
86
+ const [codeX, codeY, codeZ] = (0, react_1.useMemo)(() => no_react_1.NoReactInternals.parseScaleValue(effectiveValue), [effectiveValue]);
87
+ const defaultLinked = codeX === codeY;
88
+ const linked = getScaleLockState({
89
+ nodePath: scaleLockNodePath,
90
+ fieldKey: field.key,
91
+ defaultValue: defaultLinked,
92
+ });
93
+ const step = field.fieldSchema.type === 'scale'
94
+ ? ((_a = field.fieldSchema.step) !== null && _a !== void 0 ? _a : 0.01)
95
+ : 0.01;
96
+ const min = field.fieldSchema.type === 'scale'
97
+ ? ((_b = field.fieldSchema.min) !== null && _b !== void 0 ? _b : -Infinity)
98
+ : -Infinity;
99
+ const max = field.fieldSchema.type === 'scale'
100
+ ? ((_c = field.fieldSchema.max) !== null && _c !== void 0 ? _c : Infinity)
101
+ : Infinity;
102
+ const stepDecimals = (0, react_1.useMemo)(() => (0, timeline_field_utils_1.getDecimalPlaces)(step), [step]);
103
+ const formatter = (0, react_1.useCallback)((v) => {
104
+ const num = Number(v);
105
+ const digits = Math.max(stepDecimals, (0, timeline_field_utils_1.getDecimalPlaces)(num));
106
+ return digits === 0 ? String(num) : num.toFixed(digits);
107
+ }, [stepDecimals]);
108
+ const getDragStart = (0, react_1.useCallback)(() => {
109
+ if (dragStartRef.current === null) {
110
+ dragStartRef.current = [dragX !== null && dragX !== void 0 ? dragX : codeX, dragY !== null && dragY !== void 0 ? dragY : codeY];
111
+ }
112
+ return dragStartRef.current;
113
+ }, [codeX, codeY, dragX, dragY]);
114
+ const serialize = (0, react_1.useCallback)((x, y) => {
115
+ return no_react_1.NoReactInternals.serializeScaleValue([x, y, codeZ]);
116
+ }, [codeZ]);
117
+ const onXChange = (0, react_1.useCallback)((newVal) => {
118
+ if (linked) {
119
+ const [baseX, baseY] = getDragStart();
120
+ const [newX, newY] = (0, exports.getLinkedScale)({
121
+ axis: 'x',
122
+ newValue: newVal,
123
+ baseX,
124
+ baseY,
125
+ min,
126
+ max,
127
+ });
128
+ setDragX(newX);
129
+ setDragY(newY);
130
+ onDragValueChange(serialize(newX, newY));
131
+ return;
132
+ }
133
+ setDragX(newVal);
134
+ const currentY = dragY !== null && dragY !== void 0 ? dragY : codeY;
135
+ onDragValueChange(serialize(newVal, currentY));
136
+ }, [
137
+ codeY,
138
+ dragY,
139
+ getDragStart,
140
+ linked,
141
+ max,
142
+ min,
143
+ onDragValueChange,
144
+ serialize,
145
+ ]);
146
+ const onXChangeEnd = (0, react_1.useCallback)((newVal) => {
147
+ var _a, _b;
148
+ var _c, _d;
149
+ const [newX, newY] = linked
150
+ ? (0, exports.getLinkedScale)({
151
+ axis: 'x',
152
+ newValue: newVal,
153
+ baseX: (_c = (_a = dragStartRef.current) === null || _a === void 0 ? void 0 : _a[0]) !== null && _c !== void 0 ? _c : codeX,
154
+ baseY: (_d = (_b = dragStartRef.current) === null || _b === void 0 ? void 0 : _b[1]) !== null && _d !== void 0 ? _d : codeY,
155
+ min,
156
+ max,
157
+ })
158
+ : [newVal, dragY !== null && dragY !== void 0 ? dragY : codeY];
159
+ const newScale = serialize(newX, newY);
160
+ const clearDragState = () => {
161
+ dragStartRef.current = null;
162
+ setDragX(null);
163
+ setDragY(null);
164
+ onDragEnd();
165
+ };
166
+ if (!valuesEqual(newScale, propStatus.codeValue)) {
167
+ onSave(newScale).finally(clearDragState);
168
+ }
169
+ else {
170
+ clearDragState();
171
+ }
172
+ }, [
173
+ codeX,
174
+ codeY,
175
+ dragY,
176
+ linked,
177
+ max,
178
+ min,
179
+ onDragEnd,
180
+ onSave,
181
+ propStatus,
182
+ serialize,
183
+ ]);
184
+ const onXTextChange = (0, react_1.useCallback)((newVal) => {
185
+ const parsed = Number(newVal);
186
+ if (Number.isNaN(parsed)) {
187
+ return;
188
+ }
189
+ const [newX, newY] = linked
190
+ ? (0, exports.getLinkedScale)({
191
+ axis: 'x',
192
+ newValue: parsed,
193
+ baseX: codeX,
194
+ baseY: codeY,
195
+ min,
196
+ max,
197
+ })
198
+ : [parsed, dragY !== null && dragY !== void 0 ? dragY : codeY];
199
+ const newScale = serialize(newX, newY);
200
+ if (!valuesEqual(newScale, propStatus.codeValue)) {
201
+ setDragX(newX);
202
+ setDragY(newY);
203
+ onSave(newScale).finally(() => {
204
+ dragStartRef.current = null;
205
+ setDragX(null);
206
+ setDragY(null);
207
+ });
208
+ }
209
+ }, [codeX, codeY, dragY, linked, max, min, onSave, propStatus, serialize]);
210
+ const onYChange = (0, react_1.useCallback)((newVal) => {
211
+ if (linked) {
212
+ const [baseX, baseY] = getDragStart();
213
+ const [newX, newY] = (0, exports.getLinkedScale)({
214
+ axis: 'y',
215
+ newValue: newVal,
216
+ baseX,
217
+ baseY,
218
+ min,
219
+ max,
220
+ });
221
+ setDragX(newX);
222
+ setDragY(newY);
223
+ onDragValueChange(serialize(newX, newY));
224
+ return;
225
+ }
226
+ setDragY(newVal);
227
+ const currentX = dragX !== null && dragX !== void 0 ? dragX : codeX;
228
+ onDragValueChange(serialize(currentX, newVal));
229
+ }, [
230
+ codeX,
231
+ dragX,
232
+ getDragStart,
233
+ linked,
234
+ max,
235
+ min,
236
+ onDragValueChange,
237
+ serialize,
238
+ ]);
239
+ const onYChangeEnd = (0, react_1.useCallback)((newVal) => {
240
+ var _a, _b;
241
+ var _c, _d;
242
+ const [newX, newY] = linked
243
+ ? (0, exports.getLinkedScale)({
244
+ axis: 'y',
245
+ newValue: newVal,
246
+ baseX: (_c = (_a = dragStartRef.current) === null || _a === void 0 ? void 0 : _a[0]) !== null && _c !== void 0 ? _c : codeX,
247
+ baseY: (_d = (_b = dragStartRef.current) === null || _b === void 0 ? void 0 : _b[1]) !== null && _d !== void 0 ? _d : codeY,
248
+ min,
249
+ max,
250
+ })
251
+ : [dragX !== null && dragX !== void 0 ? dragX : codeX, newVal];
252
+ const newScale = serialize(newX, newY);
253
+ const clearDragState = () => {
254
+ dragStartRef.current = null;
255
+ setDragX(null);
256
+ setDragY(null);
257
+ onDragEnd();
258
+ };
259
+ if (!valuesEqual(newScale, propStatus.codeValue)) {
260
+ onSave(newScale).finally(clearDragState);
261
+ }
262
+ else {
263
+ clearDragState();
264
+ }
265
+ }, [
266
+ codeX,
267
+ codeY,
268
+ dragX,
269
+ linked,
270
+ max,
271
+ min,
272
+ onDragEnd,
273
+ onSave,
274
+ propStatus,
275
+ serialize,
276
+ ]);
277
+ const onYTextChange = (0, react_1.useCallback)((newVal) => {
278
+ const parsed = Number(newVal);
279
+ if (Number.isNaN(parsed)) {
280
+ return;
281
+ }
282
+ const [newX, newY] = linked
283
+ ? (0, exports.getLinkedScale)({
284
+ axis: 'y',
285
+ newValue: parsed,
286
+ baseX: codeX,
287
+ baseY: codeY,
288
+ min,
289
+ max,
290
+ })
291
+ : [dragX !== null && dragX !== void 0 ? dragX : codeX, parsed];
292
+ const newScale = serialize(newX, newY);
293
+ if (!valuesEqual(newScale, propStatus.codeValue)) {
294
+ setDragX(newX);
295
+ setDragY(newY);
296
+ onSave(newScale).finally(() => {
297
+ dragStartRef.current = null;
298
+ setDragX(null);
299
+ setDragY(null);
300
+ });
301
+ }
302
+ }, [codeX, codeY, dragX, linked, max, min, onSave, propStatus, serialize]);
303
+ const onToggleLink = (0, react_1.useCallback)(() => {
304
+ setScaleLockState({
305
+ nodePath: scaleLockNodePath,
306
+ fieldKey: field.key,
307
+ linked: !linked,
308
+ });
309
+ }, [field.key, linked, scaleLockNodePath, setScaleLockState]);
310
+ return (jsx_runtime_1.jsxs("span", { style: containerStyle, children: [
311
+ jsx_runtime_1.jsx(LinkToggle, { linked: linked, onToggle: onToggleLink }), jsx_runtime_1.jsx(InputDragger_1.InputDragger, { type: "number", value: dragX !== null && dragX !== void 0 ? dragX : codeX, style: leftDraggerStyle, status: "ok", small: true, onValueChange: onXChange, onValueChangeEnd: onXChangeEnd, onTextChange: onXTextChange, min: min, max: max, step: step, formatter: formatter, rightAlign: false }), jsx_runtime_1.jsx("div", { style: gapStyle }), jsx_runtime_1.jsx(InputDragger_1.InputDragger, { type: "number", value: dragY !== null && dragY !== void 0 ? dragY : codeY, style: rightDraggerStyle, status: "ok", small: true, onValueChange: onYChange, onValueChangeEnd: onYChangeEnd, onTextChange: onYTextChange, min: min, max: max, step: step, formatter: formatter, rightAlign: false })
312
+ ] }));
313
+ };
314
+ exports.TimelineScaleField = TimelineScaleField;
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import type { CanUpdateSequencePropStatusFalse, CanUpdateSequencePropStatusTrue } from 'remotion';
2
+ import type { CanUpdateSequencePropStatusFalse, CanUpdateSequencePropStatusStatic, SequencePropsSubscriptionKey } from 'remotion';
3
3
  import type { SchemaFieldInfo, TimelineFieldOnDragValueChange, TimelineFieldOnSave } from '../../helpers/timeline-layout';
4
4
  export declare const UnsupportedStatus: React.FC<{
5
5
  readonly label: string;
@@ -12,6 +12,7 @@ export declare const TimelineFieldValue: React.FC<{
12
12
  readonly onSave: TimelineFieldOnSave;
13
13
  readonly onDragValueChange: TimelineFieldOnDragValueChange;
14
14
  readonly onDragEnd: () => void;
15
- readonly propStatus: CanUpdateSequencePropStatusTrue;
15
+ readonly propStatus: CanUpdateSequencePropStatusStatic;
16
16
  readonly effectiveValue: unknown;
17
+ readonly scaleLockNodePath: SequencePropsSubscriptionKey | null;
17
18
  }>;
@@ -3,13 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TimelineFieldValue = exports.TimelineNonEditableStatus = exports.UnsupportedStatus = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const get_timeline_keyframes_1 = require("./get-timeline-keyframes");
6
- const TimelineBooleanField_1 = require("./TimelineBooleanField");
7
- const TimelineColorField_1 = require("./TimelineColorField");
8
- const TimelineEnumField_1 = require("./TimelineEnumField");
9
- const TimelineNumberField_1 = require("./TimelineNumberField");
10
- const TimelineRotationField_1 = require("./TimelineRotationField");
11
- const TimelineTranslateField_1 = require("./TimelineTranslateField");
12
- const TimelineUvCoordinateField_1 = require("./TimelineUvCoordinateField");
6
+ const TimelineArrayField_1 = require("./TimelineArrayField");
7
+ const TimelinePrimitiveFieldValue_1 = require("./TimelinePrimitiveFieldValue");
13
8
  const unsupportedLabel = {
14
9
  color: 'rgba(255, 255, 255, 0.4)',
15
10
  fontSize: 12,
@@ -17,51 +12,22 @@ const unsupportedLabel = {
17
12
  userSelect: 'none',
18
13
  WebkitUserSelect: 'none',
19
14
  };
20
- const notEditableBackground = {
21
- backgroundColor: 'rgba(255, 0, 0, 0.2)',
22
- borderRadius: 3,
23
- padding: '0 4px',
24
- };
25
- const inlineWrapper = {
26
- fontSize: 12,
27
- };
28
15
  const UnsupportedStatus = ({ label }) => {
29
16
  return jsx_runtime_1.jsx("span", { style: unsupportedLabel, children: label });
30
17
  };
31
18
  exports.UnsupportedStatus = UnsupportedStatus;
32
19
  const TimelineNonEditableStatus = ({ propStatus }) => {
33
- if (propStatus.canUpdate) {
34
- return null;
35
- }
36
- if (propStatus.reason === 'computed' || propStatus.reason === 'keyframed') {
20
+ if (propStatus.status === 'computed') {
37
21
  return (jsx_runtime_1.jsx("span", { style: unsupportedLabel, children: (0, get_timeline_keyframes_1.getComputedStatusLabel)(propStatus) }));
38
22
  }
39
23
  };
40
24
  exports.TimelineNonEditableStatus = TimelineNonEditableStatus;
41
- const TimelineFieldValue = ({ field, onSave, onDragValueChange, onDragEnd, propStatus, effectiveValue, }) => {
42
- const wrapperStyle = !propStatus.canUpdate
43
- ? notEditableBackground
44
- : undefined;
45
- if (field.typeName === 'number') {
46
- return (jsx_runtime_1.jsx("span", { style: wrapperStyle, children: jsx_runtime_1.jsx(TimelineNumberField_1.TimelineNumberField, { field: field, effectiveValue: effectiveValue, onSave: onSave, propStatus: propStatus, onDragValueChange: onDragValueChange, onDragEnd: onDragEnd }) }));
47
- }
48
- if (field.typeName === 'rotation') {
49
- return (jsx_runtime_1.jsx("span", { style: wrapperStyle, children: jsx_runtime_1.jsx(TimelineRotationField_1.TimelineRotationField, { field: field, effectiveValue: effectiveValue, propStatus: propStatus, onSave: onSave, onDragValueChange: onDragValueChange, onDragEnd: onDragEnd }) }));
50
- }
51
- if (field.typeName === 'translate') {
52
- return (jsx_runtime_1.jsx("span", { style: wrapperStyle, children: jsx_runtime_1.jsx(TimelineTranslateField_1.TimelineTranslateField, { field: field, effectiveValue: effectiveValue, propStatus: propStatus, onSave: onSave, onDragValueChange: onDragValueChange, onDragEnd: onDragEnd }) }));
53
- }
54
- if (field.typeName === 'uv-coordinate') {
55
- return (jsx_runtime_1.jsx("span", { style: wrapperStyle, children: jsx_runtime_1.jsx(TimelineUvCoordinateField_1.TimelineUvCoordinateField, { field: field, effectiveValue: effectiveValue, propStatus: propStatus, onSave: onSave, onDragValueChange: onDragValueChange, onDragEnd: onDragEnd }) }));
56
- }
57
- if (field.typeName === 'boolean') {
58
- return (jsx_runtime_1.jsx("span", { style: wrapperStyle, children: jsx_runtime_1.jsx(TimelineBooleanField_1.TimelineBooleanField, { field: field, propStatus: propStatus, onSave: onSave, effectiveValue: effectiveValue }) }));
59
- }
60
- if (field.typeName === 'color') {
61
- return (jsx_runtime_1.jsx("span", { style: wrapperStyle, children: jsx_runtime_1.jsx(TimelineColorField_1.TimelineColorField, { field: field, propStatus: propStatus, onSave: onSave, onDragValueChange: onDragValueChange, onDragEnd: onDragEnd, effectiveValue: effectiveValue }) }));
25
+ const TimelineFieldValue = ({ field, onSave, onDragValueChange, onDragEnd, propStatus, effectiveValue, scaleLockNodePath, }) => {
26
+ if ((0, TimelinePrimitiveFieldValue_1.isTimelinePrimitiveFieldInfo)(field)) {
27
+ return (jsx_runtime_1.jsx(TimelinePrimitiveFieldValue_1.TimelinePrimitiveFieldValue, { effectiveValue: effectiveValue, field: field, onDragEnd: onDragEnd, onDragValueChange: onDragValueChange, onSave: onSave, propStatus: propStatus, scaleLockNodePath: scaleLockNodePath }));
62
28
  }
63
- if (field.typeName === 'enum') {
64
- return (jsx_runtime_1.jsx("span", { style: inlineWrapper, children: jsx_runtime_1.jsx(TimelineEnumField_1.TimelineEnumField, { field: field, propStatus: propStatus, onSave: onSave, effectiveValue: effectiveValue, onDragValueChange: onDragValueChange, onDragEnd: onDragEnd }) }));
29
+ if (field.typeName === 'array') {
30
+ return (jsx_runtime_1.jsx("span", { children: jsx_runtime_1.jsx(TimelineArrayField_1.TimelineArrayField, { effectiveValue: effectiveValue, field: field, onDragEnd: onDragEnd, onDragValueChange: onDragValueChange, onSave: onSave }) }));
65
31
  }
66
32
  throw new Error(`Unsupported field type: ${field.typeName}`);
67
33
  };
@@ -8,6 +8,7 @@ const client_id_1 = require("../../helpers/client-id");
8
8
  const timeline_layout_1 = require("../../helpers/timeline-layout");
9
9
  const timeline_node_path_key_1 = require("../../helpers/timeline-node-path-key");
10
10
  const use_keybinding_1 = require("../../helpers/use-keybinding");
11
+ const TimelineClipboardKeybindings_1 = require("./TimelineClipboardKeybindings");
11
12
  const TimelineDeleteKeybindings_1 = require("./TimelineDeleteKeybindings");
12
13
  exports.TIMELINE_SELECTED_BACKGROUND = '#3B3F42';
13
14
  exports.TIMELINE_SELECTED_LABEL_BACKGROUND = '#B0B0B0';
@@ -255,7 +256,7 @@ const TimelineSelectAllKeybindings = ({ timeline }) => {
255
256
  exports.TimelineSelectAllKeybindings = TimelineSelectAllKeybindings;
256
257
  const TimelineSelectionProvider = ({ children }) => {
257
258
  const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
258
- const canSelect = exports.SELECTION_ENABLED &&
259
+ const canSelect = (exports.SELECTION_ENABLED || exports.ENABLE_OUTLINES) &&
259
260
  previewServerState.type === 'connected' &&
260
261
  !window.remotion_isReadOnlyStudio;
261
262
  const [selectedItems, setSelectedItems] = (0, react_1.useState)([]);
@@ -347,7 +348,7 @@ const TimelineSelectionProvider = ({ children }) => {
347
348
  ]);
348
349
  const currentSelection = (0, react_1.useRef)(value);
349
350
  currentSelection.current = value;
350
- return (jsx_runtime_1.jsx(CurrentTimelineSelectionContext.Provider, { value: currentSelection, children: jsx_runtime_1.jsxs(TimelineSelectionContext.Provider, { value: value, children: [children, jsx_runtime_1.jsx(TimelineDeleteKeybindings_1.TimelineDeleteKeybindings, {})
351
+ return (jsx_runtime_1.jsx(CurrentTimelineSelectionContext.Provider, { value: currentSelection, children: jsx_runtime_1.jsxs(TimelineSelectionContext.Provider, { value: value, children: [children, jsx_runtime_1.jsx(TimelineClipboardKeybindings_1.TimelineClipboardKeybindings, {}), jsx_runtime_1.jsx(TimelineDeleteKeybindings_1.TimelineDeleteKeybindings, {})
351
352
  ] }) }));
352
353
  };
353
354
  exports.TimelineSelectionProvider = TimelineSelectionProvider;
@@ -4,4 +4,5 @@ import type { SequenceNodePathInfo } from '../../helpers/get-timeline-sequence-s
4
4
  export declare const TimelineSequence: React.NamedExoticComponent<{
5
5
  readonly s: TSequence;
6
6
  readonly nodePathInfo: SequenceNodePathInfo | null;
7
+ readonly sequenceFrameOffset: number;
7
8
  }>;
@@ -46,19 +46,21 @@ const LoopedTimelineIndicators_1 = require("./LoopedTimelineIndicators");
46
46
  const TimelineImageInfo_1 = require("./TimelineImageInfo");
47
47
  const TimelineSelection_1 = require("./TimelineSelection");
48
48
  const TimelineSequenceFrame_1 = require("./TimelineSequenceFrame");
49
+ const TimelineSequenceRightEdgeDragHandle_1 = require("./TimelineSequenceRightEdgeDragHandle");
49
50
  const TimelineVideoInfo_1 = require("./TimelineVideoInfo");
50
51
  const TimelineWidthProvider_1 = require("./TimelineWidthProvider");
52
+ const use_resolved_stack_react_to_change_1 = require("./use-resolved-stack-react-to-change");
51
53
  const AUDIO_GRADIENT = 'linear-gradient(rgb(16 171 58), rgb(43 165 63) 60%)';
52
54
  const VIDEO_GRADIENT = 'linear-gradient(to top, #8e44ad, #9b59b6)';
53
55
  const IMAGE_GRADIENT = 'linear-gradient(to top, #2980b9, #3498db)';
54
- const TimelineSequenceFn = ({ s, nodePathInfo }) => {
56
+ const TimelineSequenceFn = ({ s, nodePathInfo, sequenceFrameOffset }) => {
55
57
  const windowWidth = (0, react_1.useContext)(TimelineWidthProvider_1.TimelineWidthContext);
56
58
  if (windowWidth === null) {
57
59
  return null;
58
60
  }
59
- return (jsx_runtime_1.jsx(TimelineSequenceInner, { windowWidth: windowWidth, s: s, nodePathInfo: nodePathInfo }));
61
+ return (jsx_runtime_1.jsx(TimelineSequenceInner, { windowWidth: windowWidth, s: s, nodePathInfo: nodePathInfo, sequenceFrameOffset: sequenceFrameOffset }));
60
62
  };
61
- const TimelineSequenceCurrentFrame = ({ s, displayDurationInFrames, premountWidth, postmountWidth, style, children, nodePathInfo, }) => {
63
+ const TimelineSequenceCurrentFrame = ({ s, displayDurationInFrames, premountWidth, postmountWidth, style, children, nodePathInfo, sequenceFrameOffset, fromCanUpdate, onMoveDragPointerDown, }) => {
62
64
  var _a, _b;
63
65
  const { onSelect, selectable } = (0, TimelineSelection_1.useTimelineRowSelection)(nodePathInfo);
64
66
  const onPointerDown = (0, react_1.useCallback)((e) => {
@@ -68,13 +70,17 @@ const TimelineSequenceCurrentFrame = ({ s, displayDurationInFrames, premountWidt
68
70
  shiftKey: e.shiftKey,
69
71
  toggleKey: e.metaKey || e.ctrlKey,
70
72
  });
73
+ if (TimelineSelection_1.TIMELINE_TOP_DRAG && fromCanUpdate) {
74
+ onMoveDragPointerDown(e);
75
+ }
71
76
  }
72
- }, [onSelect]);
77
+ }, [fromCanUpdate, onMoveDragPointerDown, onSelect]);
73
78
  const frame = (0, remotion_1.useCurrentFrame)();
74
79
  const relativeFrame = frame - s.from;
80
+ const sequenceFrame = relativeFrame + sequenceFrameOffset;
75
81
  const relativeFrameWithPremount = relativeFrame + ((_a = s.premountDisplay) !== null && _a !== void 0 ? _a : 0);
76
82
  const relativeFrameWithPostmount = relativeFrame - displayDurationInFrames;
77
- const roundedFrame = Math.round(relativeFrame * 100) / 100;
83
+ const roundedFrame = Math.round(sequenceFrame * 100) / 100;
78
84
  const isInRange = relativeFrame >= 0 && relativeFrame < displayDurationInFrames;
79
85
  const isPremounting = relativeFrameWithPremount >= 0 &&
80
86
  relativeFrameWithPremount < displayDurationInFrames &&
@@ -86,7 +92,6 @@ const TimelineSequenceCurrentFrame = ({ s, displayDurationInFrames, premountWidt
86
92
  return {
87
93
  ...style,
88
94
  opacity: isInRange ? 1 : 0.5,
89
- ...(TimelineSelection_1.TIMELINE_TOP_DRAG ? { cursor: 'pointer' } : {}),
90
95
  };
91
96
  }, [isInRange, style]);
92
97
  return (jsx_runtime_1.jsxs("div", { style: actualStyle, title: s.displayName, onPointerDown: selectable ? onPointerDown : undefined, children: [premountWidth ? (jsx_runtime_1.jsx("div", { style: {
@@ -123,14 +128,43 @@ const TimelineSequenceCurrentFrame = ({ s, displayDurationInFrames, premountWidt
123
128
  alignItems: 'center',
124
129
  }, children: jsx_runtime_1.jsx(TimelineSequenceFrame_1.TimelineSequenceFrame, { premounted: isPremounting, postmounted: isPostmounting ? s.duration - 1 : null, roundedFrame: roundedFrame }) })) : null] }));
125
130
  };
126
- const TimelineSequenceInner = ({ s, windowWidth, nodePathInfo }) => {
131
+ const TimelineSequenceInner = ({ s, windowWidth, nodePathInfo, sequenceFrameOffset }) => {
127
132
  // If a duration is 1, it is essentially a still and it should have width 0
128
133
  // Some compositions may not be longer than their media duration,
129
134
  // if that is the case, it needs to be asynchronously determined
130
- var _a;
135
+ var _a, _b;
136
+ var _c, _d, _e, _f;
131
137
  const video = remotion_1.Internals.useVideo();
132
- const maxMediaDuration = (0, use_max_media_duration_1.useMaxMediaDuration)(s, (_a = video === null || video === void 0 ? void 0 : video.fps) !== null && _a !== void 0 ? _a : 30);
138
+ const maxMediaDuration = (0, use_max_media_duration_1.useMaxMediaDuration)(s, (_c = video === null || video === void 0 ? void 0 : video.fps) !== null && _c !== void 0 ? _c : 30);
133
139
  const effectiveMaxMediaDuration = s.loopDisplay ? null : maxMediaDuration;
140
+ const originalLocation = (0, use_resolved_stack_react_to_change_1.useResolveStackAndReactToChange)(s.getStack);
141
+ const validatedLocation = (0, react_1.useMemo)(() => {
142
+ var _a;
143
+ if (!originalLocation ||
144
+ !originalLocation.source ||
145
+ !originalLocation.line) {
146
+ return null;
147
+ }
148
+ return {
149
+ source: originalLocation.source,
150
+ line: originalLocation.line,
151
+ column: (_a = originalLocation.column) !== null && _a !== void 0 ? _a : 0,
152
+ };
153
+ }, [originalLocation]);
154
+ const { codeValues } = (0, react_1.useContext)(remotion_1.Internals.VisualModeCodeValuesContext);
155
+ const nodePath = (_d = nodePathInfo === null || nodePathInfo === void 0 ? void 0 : nodePathInfo.sequenceSubscriptionKey) !== null && _d !== void 0 ? _d : null;
156
+ const codeValuesForOverride = (0, react_1.useMemo)(() => {
157
+ return nodePath
158
+ ? remotion_1.Internals.getCodeValuesCtx(codeValues, nodePath)
159
+ : undefined;
160
+ }, [codeValues, nodePath]);
161
+ const durationCanUpdate = Boolean(((_a = codeValuesForOverride === null || codeValuesForOverride === void 0 ? void 0 : codeValuesForOverride.durationInFrames) === null || _a === void 0 ? void 0 : _a.status) === 'static');
162
+ const fromCanUpdate = Boolean(((_b = codeValuesForOverride === null || codeValuesForOverride === void 0 ? void 0 : codeValuesForOverride.from) === null || _b === void 0 ? void 0 : _b.status) === 'static');
163
+ const { onPointerDown: onMoveDragPointerDown } = (0, TimelineSequenceRightEdgeDragHandle_1.useTimelineSequenceFromDrag)({
164
+ nodePathInfo,
165
+ windowWidth,
166
+ timelineDurationInFrames: (_e = video === null || video === void 0 ? void 0 : video.durationInFrames) !== null && _e !== void 0 ? _e : 1,
167
+ });
134
168
  if (!video) {
135
169
  throw new TypeError('Expected video config');
136
170
  }
@@ -174,9 +208,16 @@ const TimelineSequenceInner = ({ s, windowWidth, nodePathInfo }) => {
174
208
  overflow: 'hidden',
175
209
  };
176
210
  }, [marginLeft, s.type, width]);
211
+ const showRightEdgeDragHandle = TimelineSelection_1.TIMELINE_TOP_DRAG &&
212
+ (s.type === 'sequence' || s.type === 'image') &&
213
+ !s.loopDisplay &&
214
+ !s.isInsideSeries &&
215
+ nodePath !== null &&
216
+ validatedLocation !== null &&
217
+ durationCanUpdate;
177
218
  if (maxMediaDuration === null && !s.loopDisplay) {
178
219
  return null;
179
220
  }
180
- return (jsx_runtime_1.jsxs(TimelineSequenceCurrentFrame, { s: s, displayDurationInFrames: displayDurationInFrames, premountWidth: premountWidth, postmountWidth: postmountWidth, style: style, nodePathInfo: nodePathInfo, children: [s.type === 'audio' ? (jsx_runtime_1.jsx(AudioWaveform_1.AudioWaveform, { src: s.src, height: timeline_layout_1.TIMELINE_LAYER_HEIGHT_AUDIO, doesVolumeChange: s.doesVolumeChange, visualizationWidth: width, startFrom: s.startMediaFrom, durationInFrames: s.duration, volume: s.volume, playbackRate: s.playbackRate, loopDisplay: s.loopDisplay })) : null, s.type === 'video' ? (jsx_runtime_1.jsx(TimelineVideoInfo_1.TimelineVideoInfo, { src: s.src, visualizationWidth: width, naturalWidth: naturalWidth, trimBefore: s.startMediaFrom, durationInFrames: s.duration, playbackRate: s.playbackRate, volume: s.volume, doesVolumeChange: s.doesVolumeChange, premountWidth: premountWidth !== null && premountWidth !== void 0 ? premountWidth : 0, postmountWidth: postmountWidth !== null && postmountWidth !== void 0 ? postmountWidth : 0, loopDisplay: s.loopDisplay })) : null, s.type === 'image' ? (jsx_runtime_1.jsx(TimelineImageInfo_1.TimelineImageInfo, { src: s.src, visualizationWidth: width })) : null, s.loopDisplay === undefined ? null : (jsx_runtime_1.jsx(LoopedTimelineIndicators_1.LoopedTimelineIndicator, { loops: s.loopDisplay.numberOfTimes }))] }));
221
+ return (jsx_runtime_1.jsxs(TimelineSequenceCurrentFrame, { s: s, displayDurationInFrames: displayDurationInFrames, premountWidth: premountWidth, postmountWidth: postmountWidth, style: style, nodePathInfo: nodePathInfo, sequenceFrameOffset: sequenceFrameOffset, fromCanUpdate: fromCanUpdate, onMoveDragPointerDown: onMoveDragPointerDown, children: [s.type === 'audio' ? (jsx_runtime_1.jsx(AudioWaveform_1.AudioWaveform, { src: s.src, height: timeline_layout_1.TIMELINE_LAYER_HEIGHT_AUDIO, doesVolumeChange: s.doesVolumeChange, visualizationWidth: width, startFrom: s.startMediaFrom, durationInFrames: s.duration, volume: s.volume, playbackRate: s.playbackRate, loopDisplay: s.loopDisplay })) : null, s.type === 'video' ? (jsx_runtime_1.jsx(TimelineVideoInfo_1.TimelineVideoInfo, { src: s.src, visualizationWidth: width, naturalWidth: naturalWidth, trimBefore: s.startMediaFrom, durationInFrames: s.duration, playbackRate: s.playbackRate, volume: s.volume, doesVolumeChange: s.doesVolumeChange, premountWidth: premountWidth !== null && premountWidth !== void 0 ? premountWidth : 0, postmountWidth: postmountWidth !== null && postmountWidth !== void 0 ? postmountWidth : 0, loopDisplay: s.loopDisplay })) : null, s.type === 'image' ? (jsx_runtime_1.jsx(TimelineImageInfo_1.TimelineImageInfo, { src: s.src, visualizationWidth: width })) : null, s.loopDisplay === undefined ? null : (jsx_runtime_1.jsx(LoopedTimelineIndicators_1.LoopedTimelineIndicator, { loops: s.loopDisplay.numberOfTimes })), showRightEdgeDragHandle && nodePathInfo && validatedLocation ? (jsx_runtime_1.jsx(TimelineSequenceRightEdgeDragHandle_1.TimelineSequenceRightEdgeDragHandle, { nodePathInfo: nodePathInfo, windowWidth: windowWidth, timelineDurationInFrames: (_f = video.durationInFrames) !== null && _f !== void 0 ? _f : 1 })) : null] }));
181
222
  };
182
223
  exports.TimelineSequence = react_1.default.memo(TimelineSequenceFn);
@@ -9,6 +9,7 @@ const relativeFrameStyle = {
9
9
  opacity: 0.5,
10
10
  whiteSpace: 'nowrap',
11
11
  pointerEvents: 'none',
12
+ userSelect: 'none',
12
13
  };
13
14
  const TimelineSequenceFrame = ({ roundedFrame, premounted, postmounted }) => {
14
15
  return (jsx_runtime_1.jsx("div", { style: relativeFrameStyle, children: premounted
@@ -60,9 +60,9 @@ const getEffectDragData = (dataTransfer) => {
60
60
  return null;
61
61
  };
62
62
  const TimelineSequenceItem = ({ nestedDepth, sequence, nodePathInfo, keyframeDisplayOffset }) => {
63
- var _a;
64
- var _b;
65
- const nodePath = (_b = nodePathInfo === null || nodePathInfo === void 0 ? void 0 : nodePathInfo.sequenceSubscriptionKey) !== null && _b !== void 0 ? _b : null;
63
+ var _a, _b;
64
+ var _c;
65
+ const nodePath = (_c = nodePathInfo === null || nodePathInfo === void 0 ? void 0 : nodePathInfo.sequenceSubscriptionKey) !== null && _c !== void 0 ? _c : null;
66
66
  const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
67
67
  const previewConnected = previewServerState.type === 'connected';
68
68
  const { getIsExpanded } = (0, react_1.useContext)(ExpandedTracksProvider_1.ExpandedTracksGetterContext);
@@ -298,7 +298,7 @@ const TimelineSequenceItem = ({ nestedDepth, sequence, nodePathInfo, keyframeDis
298
298
  const codeHiddenStatus = codeValuesForOverride === null || codeValuesForOverride === void 0 ? void 0 : codeValuesForOverride.hidden;
299
299
  const isItemHidden = (0, react_1.useMemo)(() => {
300
300
  var _a;
301
- const codeValue = codeHiddenStatus && codeHiddenStatus.canUpdate
301
+ const codeValue = codeHiddenStatus && codeHiddenStatus.status === 'static'
302
302
  ? codeHiddenStatus.codeValue
303
303
  : undefined;
304
304
  const runtimeValue = (_a = sequence.controls) === null || _a === void 0 ? void 0 : _a.currentRuntimeValueDotNotation.hidden;
@@ -311,7 +311,7 @@ const TimelineSequenceItem = ({ nestedDepth, sequence, nodePathInfo, keyframeDis
311
311
  !validatedLocation ||
312
312
  !codeValuesForOverride ||
313
313
  !codeHiddenStatus ||
314
- !codeHiddenStatus.canUpdate ||
314
+ codeHiddenStatus.status !== 'static' ||
315
315
  previewServerState.type !== 'connected') {
316
316
  return;
317
317
  }
@@ -366,11 +366,11 @@ const TimelineSequenceItem = ({ nestedDepth, sequence, nodePathInfo, keyframeDis
366
366
  validatedLocation !== null &&
367
367
  codeHiddenStatus !== undefined &&
368
368
  codeHiddenStatus !== null &&
369
- codeHiddenStatus.canUpdate;
369
+ codeHiddenStatus.status === 'static';
370
370
  const canDropEffect = previewServerState.type === 'connected' &&
371
371
  nodePath !== null &&
372
372
  validatedLocation !== null &&
373
- sequence.type !== 'audio';
373
+ ((_b = sequence.controls) === null || _b === void 0 ? void 0 : _b.supportsEffects) === true;
374
374
  const onEffectDragOver = (0, react_1.useCallback)((e) => {
375
375
  if (!canDropEffect || !hasEffectDragType(e.dataTransfer)) {
376
376
  return;