@remotion/studio 4.0.474 → 4.0.476

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 (94) hide show
  1. package/dist/components/Canvas.js +6 -0
  2. package/dist/components/ColorPicker/ColorPicker.js +4 -31
  3. package/dist/components/ColorPicker/ColorPickerPopup.d.ts +6 -0
  4. package/dist/components/ColorPicker/ColorPickerPopup.js +11 -6
  5. package/dist/components/CompositionSelectorItem.js +4 -4
  6. package/dist/components/Editor.js +4 -1
  7. package/dist/components/GlobalKeybindings.js +12 -0
  8. package/dist/components/KeyboardShortcutsExplainer.js +24 -0
  9. package/dist/components/Modals.js +2 -1
  10. package/dist/components/NewComposition/ComboBox.js +1 -0
  11. package/dist/components/NewComposition/InputDragger.d.ts +6 -0
  12. package/dist/components/NewComposition/InputDragger.js +40 -14
  13. package/dist/components/NewComposition/RenameComposition.js +8 -1
  14. package/dist/components/NewComposition/RenameFolder.js +8 -1
  15. package/dist/components/NewComposition/RenameStaticFile.js +11 -1
  16. package/dist/components/Notifications/Notification.js +5 -4
  17. package/dist/components/Notifications/NotificationCenter.js +1 -1
  18. package/dist/components/ObserveDefaultPropsContext.js +6 -2
  19. package/dist/components/PlayPause.js +22 -66
  20. package/dist/components/PreviewToolbar.js +17 -3
  21. package/dist/components/RenderModal/RenderModalJSONPropsEditor.js +2 -1
  22. package/dist/components/SelectedOutlineOverlay.d.ts +104 -42
  23. package/dist/components/SelectedOutlineOverlay.js +1278 -336
  24. package/dist/components/SelectedOutlineUvControls.d.ts +17 -0
  25. package/dist/components/SelectedOutlineUvControls.js +167 -0
  26. package/dist/components/StudioCanvasCapture.d.ts +5 -0
  27. package/dist/components/StudioCanvasCapture.js +40 -0
  28. package/dist/components/Timeline/EasingEditorModal.d.ts +11 -0
  29. package/dist/components/Timeline/EasingEditorModal.js +247 -0
  30. package/dist/components/Timeline/KeyframeSettingsModal.js +1 -0
  31. package/dist/components/Timeline/Timeline.js +10 -7
  32. package/dist/components/Timeline/TimelineDeleteKeybindings.js +64 -35
  33. package/dist/components/Timeline/TimelineDragHandler.js +2 -2
  34. package/dist/components/Timeline/TimelineEffectItem.js +1 -2
  35. package/dist/components/Timeline/TimelineEffectPropItem.js +1 -1
  36. package/dist/components/Timeline/TimelineExpandedKeyframeRow.d.ts +1 -0
  37. package/dist/components/Timeline/TimelineExpandedKeyframeRow.js +2 -2
  38. package/dist/components/Timeline/TimelineExpandedTrackKeyframes.js +1 -1
  39. package/dist/components/Timeline/TimelineHeightContainer.js +2 -0
  40. package/dist/components/Timeline/TimelineItemStack.js +3 -56
  41. package/dist/components/Timeline/TimelineKeyframeControls.d.ts +7 -0
  42. package/dist/components/Timeline/TimelineKeyframeControls.js +259 -62
  43. package/dist/components/Timeline/TimelineKeyframeDiamond.js +4 -2
  44. package/dist/components/Timeline/TimelineKeyframeEasingLine.js +128 -3
  45. package/dist/components/Timeline/TimelineKeyframeTracksContext.d.ts +7 -0
  46. package/dist/components/Timeline/TimelineKeyframeTracksContext.js +17 -0
  47. package/dist/components/Timeline/TimelineMediaInfo.js +4 -24
  48. package/dist/components/Timeline/TimelinePrimitiveFieldValue.js +4 -0
  49. package/dist/components/Timeline/TimelineRotationField.js +21 -39
  50. package/dist/components/Timeline/TimelineScaleField.js +1 -1
  51. package/dist/components/Timeline/TimelineScrollable.js +19 -3
  52. package/dist/components/Timeline/TimelineSelection.d.ts +67 -3
  53. package/dist/components/Timeline/TimelineSelection.js +289 -32
  54. package/dist/components/Timeline/TimelineSequence.js +17 -9
  55. package/dist/components/Timeline/TimelineSequenceItem.js +328 -168
  56. package/dist/components/Timeline/TimelineSequenceName.d.ts +4 -2
  57. package/dist/components/Timeline/TimelineSequenceName.js +70 -19
  58. package/dist/components/Timeline/TimelineSequencePropItem.js +1 -1
  59. package/dist/components/Timeline/TimelineTimeIndicators.js +4 -2
  60. package/dist/components/Timeline/TimelineTransformOriginField.d.ts +11 -0
  61. package/dist/components/Timeline/TimelineTransformOriginField.js +138 -0
  62. package/dist/components/Timeline/TimelineTranslateField.js +1 -1
  63. package/dist/components/Timeline/TimelineUvCoordinateField.js +1 -1
  64. package/dist/components/Timeline/call-add-keyframe.d.ts +17 -0
  65. package/dist/components/Timeline/call-add-keyframe.js +65 -1
  66. package/dist/components/Timeline/delete-selected-timeline-item.js +23 -13
  67. package/dist/components/Timeline/disable-sequence-interactivity.d.ts +8 -0
  68. package/dist/components/Timeline/disable-sequence-interactivity.js +24 -0
  69. package/dist/components/Timeline/reset-selected-timeline-props.js +15 -7
  70. package/dist/components/Timeline/timeline-rotation-utils.d.ts +2 -0
  71. package/dist/components/Timeline/timeline-rotation-utils.js +34 -0
  72. package/dist/components/Timeline/transform-origin-utils.d.ts +24 -0
  73. package/dist/components/Timeline/transform-origin-utils.js +170 -0
  74. package/dist/components/Timeline/update-selected-easing.d.ts +35 -0
  75. package/dist/components/Timeline/update-selected-easing.js +133 -0
  76. package/dist/components/Timeline/use-expanded-track-keyframe-rows.d.ts +1 -0
  77. package/dist/components/Timeline/use-expanded-track-keyframe-rows.js +28 -0
  78. package/dist/components/Timeline/use-timeline-keyframe-drag.js +8 -13
  79. package/dist/components/canvas-capture-enabled.d.ts +1 -0
  80. package/dist/components/canvas-capture-enabled.js +4 -0
  81. package/dist/components/effect-drag-and-drop.d.ts +11 -0
  82. package/dist/components/effect-drag-and-drop.js +73 -0
  83. package/dist/components/selected-outline-geometry.d.ts +20 -0
  84. package/dist/components/selected-outline-geometry.js +18 -0
  85. package/dist/components/selected-outline-uv.d.ts +46 -0
  86. package/dist/components/selected-outline-uv.js +240 -0
  87. package/dist/esm/{chunk-xjvc8qen.js → chunk-0atarw3p.js} +8779 -5352
  88. package/dist/esm/internals.mjs +8779 -5352
  89. package/dist/esm/previewEntry.mjs +8789 -5362
  90. package/dist/esm/renderEntry.mjs +1 -1
  91. package/dist/helpers/colors.d.ts +0 -1
  92. package/dist/helpers/colors.js +1 -2
  93. package/dist/state/modals.d.ts +2 -1
  94. package/package.json +11 -10
@@ -5,26 +5,19 @@ const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const TimelineSelection_1 = require("./TimelineSelection");
7
7
  const MAX_DISPLAY_NAME_LENGTH = 1000;
8
+ const MAX_RENAME_INPUT_WIDTH = 240;
9
+ const RENAME_INPUT_CLASS_NAME = 'remotion-timeline-sequence-name-input';
8
10
  const getTruncatedDisplayName = (displayName) => {
9
11
  if (displayName.length > MAX_DISPLAY_NAME_LENGTH) {
10
12
  return displayName.slice(0, MAX_DISPLAY_NAME_LENGTH) + '...';
11
13
  }
12
14
  return displayName;
13
15
  };
14
- const TimelineSequenceName = ({ sequence, selected, containsSelection }) => {
15
- const [hovered, setHovered] = (0, react_1.useState)(false);
16
- const { documentationLink } = sequence;
17
- const hoverable = !TimelineSelection_1.SELECTION_ENABLED && documentationLink !== null;
18
- const onClick = (0, react_1.useCallback)(() => {
19
- if (documentationLink === null) {
20
- return;
21
- }
22
- window.open(documentationLink, '_blank', 'noopener,noreferrer');
23
- }, [documentationLink]);
24
- const onPointerEnter = (0, react_1.useCallback)(() => setHovered(true), []);
25
- const onPointerLeave = (0, react_1.useCallback)(() => setHovered(false), []);
16
+ const TimelineSequenceName = ({ displayName, selected, containsSelection, editing, onCancelEditing, onSaveName, }) => {
17
+ const inputRef = (0, react_1.useRef)(null);
18
+ const [draftName, setDraftName] = (0, react_1.useState)(displayName);
19
+ const cancelNextBlurRef = (0, react_1.useRef)(false);
26
20
  const style = (0, react_1.useMemo)(() => {
27
- const hoverEffect = hovered && hoverable;
28
21
  return {
29
22
  alignItems: 'center',
30
23
  alignSelf: 'stretch',
@@ -33,18 +26,76 @@ const TimelineSequenceName = ({ sequence, selected, containsSelection }) => {
33
26
  fontSize: 12,
34
27
  whiteSpace: 'nowrap',
35
28
  textOverflow: 'ellipsis',
36
- overflow: 'hidden',
37
29
  color: (0, TimelineSelection_1.getTimelineColor)(selected, false),
38
30
  userSelect: 'none',
39
31
  WebkitUserSelect: 'none',
40
- textDecoration: hoverEffect ? 'underline' : 'none',
41
- cursor: hoverEffect ? 'pointer' : undefined,
32
+ textDecoration: 'none',
42
33
  boxShadow: containsSelection && !selected
43
34
  ? `inset 0 0 0 2px ${TimelineSelection_1.TIMELINE_SELECTED_LABEL_BACKGROUND}`
44
35
  : undefined,
45
36
  };
46
- }, [hovered, hoverable, selected, containsSelection]);
47
- const text = getTruncatedDisplayName(sequence.displayName) || '<Sequence>';
48
- return (jsx_runtime_1.jsx("div", { onPointerEnter: hoverable ? onPointerEnter : undefined, onPointerLeave: hoverable ? onPointerLeave : undefined, title: documentationLink ? `Open documentation: ${documentationLink}` : text, style: style, onClick: hoverable ? onClick : undefined, children: text }));
37
+ }, [selected, containsSelection]);
38
+ const inputStyle = (0, react_1.useMemo)(() => {
39
+ return {
40
+ ...style,
41
+ background: 'transparent',
42
+ border: 0,
43
+ color: (0, TimelineSelection_1.getTimelineColor)(false, false),
44
+ fontFamily: 'inherit',
45
+ fontSize: 12,
46
+ outline: 'none',
47
+ paddingBottom: 0,
48
+ paddingTop: 0,
49
+ boxSizing: 'border-box',
50
+ maxWidth: MAX_RENAME_INPUT_WIDTH,
51
+ minWidth: 0,
52
+ userSelect: 'text',
53
+ WebkitUserSelect: 'text',
54
+ };
55
+ }, [style]);
56
+ const text = getTruncatedDisplayName(displayName) || '<Sequence>';
57
+ (0, react_1.useEffect)(() => {
58
+ if (!editing) {
59
+ setDraftName(displayName);
60
+ return;
61
+ }
62
+ const input = inputRef.current;
63
+ if (!input) {
64
+ return;
65
+ }
66
+ input.focus();
67
+ const basenameIndex = displayName.lastIndexOf('.');
68
+ const selectionEnd = basenameIndex > 0 ? basenameIndex : displayName.length;
69
+ input.setSelectionRange(0, selectionEnd);
70
+ }, [displayName, editing]);
71
+ const save = (0, react_1.useCallback)(() => {
72
+ onSaveName(draftName).catch(() => undefined);
73
+ }, [draftName, onSaveName]);
74
+ const onKeyDown = (0, react_1.useCallback)((e) => {
75
+ if (e.key === 'Escape') {
76
+ cancelNextBlurRef.current = true;
77
+ e.preventDefault();
78
+ onCancelEditing();
79
+ return;
80
+ }
81
+ if (e.key === 'Enter') {
82
+ cancelNextBlurRef.current = true;
83
+ e.preventDefault();
84
+ save();
85
+ }
86
+ }, [onCancelEditing, save]);
87
+ const onBlur = (0, react_1.useCallback)(() => {
88
+ if (cancelNextBlurRef.current) {
89
+ cancelNextBlurRef.current = false;
90
+ return;
91
+ }
92
+ save();
93
+ }, [save]);
94
+ if (editing) {
95
+ return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
96
+ jsx_runtime_1.jsx("style", { children: `.${RENAME_INPUT_CLASS_NAME}::selection { background: rgba(255, 255, 255, 0.72); color: black; }` }), jsx_runtime_1.jsx("input", { ref: inputRef, className: RENAME_INPUT_CLASS_NAME, value: draftName, onChange: (e) => setDraftName(e.target.value), onBlur: onBlur, onKeyDown: onKeyDown, size: Math.max(1, draftName.length), style: inputStyle })
97
+ ] }));
98
+ }
99
+ return (jsx_runtime_1.jsx("div", { title: text, style: style, children: text }));
49
100
  };
50
101
  exports.TimelineSequenceName = TimelineSequenceName;
@@ -142,7 +142,7 @@ const TimelineSequencePropItem = ({ field, validatedLocation, rowDepth, nodePath
142
142
  schema,
143
143
  key: field.key,
144
144
  }),
145
- }) ? (jsx_runtime_1.jsx(TimelineKeyframeControls_1.TimelineKeyframeControls, { fieldKey: field.key, propStatus: propStatus, nodePath: nodePath, fileName: validatedLocation.source, keyframeDisplayOffset: keyframeDisplayOffset, defaultValue: field.fieldSchema.default, dragOverrideValue: dragOverrideValue, schema: schema, effectIndex: null })) : null;
145
+ }) ? (jsx_runtime_1.jsx(TimelineKeyframeControls_1.TimelineKeyframeControls, { fieldKey: field.key, propStatus: propStatus, nodePath: nodePath, fileName: validatedLocation.source, keyframeDisplayOffset: keyframeDisplayOffset, defaultValue: field.fieldSchema.default, dragOverrideValue: dragOverrideValue, schema: schema, effectIndex: null, nodePathInfo: nodePathInfo })) : null;
146
146
  const style = (0, react_1.useMemo)(() => {
147
147
  return {
148
148
  ...fieldRowBase,
@@ -10,13 +10,15 @@ const render_frame_1 = require("../../state/render-frame");
10
10
  const TimeValue_1 = require("../TimeValue");
11
11
  const timeline_refs_1 = require("./timeline-refs");
12
12
  const timeline_scroll_logic_1 = require("./timeline-scroll-logic");
13
+ const TimelineSelection_1 = require("./TimelineSelection");
13
14
  const TimelineWidthProvider_1 = require("./TimelineWidthProvider");
14
15
  exports.TIMELINE_TIME_INDICATOR_HEIGHT = 39;
15
16
  const container = {
16
17
  height: exports.TIMELINE_TIME_INDICATOR_HEIGHT,
17
18
  position: 'absolute',
18
- backgroundColor: colors_1.TIMELINE_BACKGROUND,
19
+ backgroundColor: TimelineSelection_1.TIMELINE_TICKS_BACKGROUND,
19
20
  top: 0,
21
+ borderBottom: `${timeline_layout_1.TIMELINE_ITEM_BORDER_BOTTOM}px solid ${colors_1.TIMELINE_TRACK_SEPARATOR}`,
20
22
  };
21
23
  const tick = {
22
24
  width: 1,
@@ -41,7 +43,7 @@ const timeValue = {
41
43
  height: exports.TIMELINE_TIME_INDICATOR_HEIGHT,
42
44
  position: 'absolute',
43
45
  top: 0,
44
- width: '100%',
46
+ width: 'calc(100% + 1.5px)',
45
47
  paddingLeft: 10,
46
48
  display: 'flex',
47
49
  alignItems: 'center',
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import type { CanUpdateSequencePropStatusStatic } from 'remotion';
3
+ import type { SchemaFieldInfo, TimelineFieldOnDragValueChange, TimelineFieldOnSave } from '../../helpers/timeline-layout';
4
+ export declare const TimelineTransformOriginField: React.FC<{
5
+ readonly field: SchemaFieldInfo;
6
+ readonly propStatus: CanUpdateSequencePropStatusStatic;
7
+ readonly effectiveValue: unknown;
8
+ readonly onSave: TimelineFieldOnSave;
9
+ readonly onDragValueChange: TimelineFieldOnDragValueChange;
10
+ readonly onDragEnd: () => void;
11
+ }>;
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TimelineTransformOriginField = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const InputDragger_1 = require("../NewComposition/InputDragger");
7
+ const timeline_field_utils_1 = require("./timeline-field-utils");
8
+ const TimelineSchemaField_1 = require("./TimelineSchemaField");
9
+ const transform_origin_utils_1 = require("./transform-origin-utils");
10
+ const leftDraggerStyle = {
11
+ paddingLeft: 0,
12
+ };
13
+ const rightDraggerStyle = {
14
+ paddingRight: 0,
15
+ };
16
+ const containerStyle = {
17
+ display: 'flex',
18
+ gap: 4,
19
+ };
20
+ const TimelineTransformOriginField = ({ field, propStatus, effectiveValue, onSave, onDragValueChange, onDragEnd, }) => {
21
+ const [dragX, setDragX] = (0, react_1.useState)(null);
22
+ const [dragY, setDragY] = (0, react_1.useState)(null);
23
+ const parsed = (0, react_1.useMemo)(() => (0, transform_origin_utils_1.parseTransformOrigin)(effectiveValue), [effectiveValue]);
24
+ const percent = (0, react_1.useMemo)(() => (parsed === null ? null : (0, transform_origin_utils_1.parsedTransformOriginToPercent)(parsed)), [parsed]);
25
+ const configuredStep = field.fieldSchema.type === 'transform-origin'
26
+ ? field.fieldSchema.step
27
+ : undefined;
28
+ const step = configuredStep !== null && configuredStep !== void 0 ? configuredStep : 1;
29
+ const decimalPlaces = (0, react_1.useMemo)(() => (0, timeline_field_utils_1.getTimelineDisplayDecimalPlaces)({
30
+ defaultDecimalPlaces: 2,
31
+ step: configuredStep,
32
+ }), [configuredStep]);
33
+ const formatter = (0, react_1.useCallback)((v) => {
34
+ const formatted = (0, timeline_field_utils_1.formatTimelineNumber)({
35
+ decimalPlaces,
36
+ fixed: false,
37
+ value: v,
38
+ });
39
+ return `${formatted}%`;
40
+ }, [decimalPlaces]);
41
+ const serialize = (0, react_1.useCallback)((x, y) => {
42
+ var _a;
43
+ return (0, transform_origin_utils_1.serializeTransformOrigin)({
44
+ uv: (0, transform_origin_utils_1.transformOriginPercentToUv)([x, y]),
45
+ z: (_a = parsed === null || parsed === void 0 ? void 0 : parsed.z) !== null && _a !== void 0 ? _a : null,
46
+ decimalPlaces,
47
+ });
48
+ }, [decimalPlaces, parsed === null || parsed === void 0 ? void 0 : parsed.z]);
49
+ const onXChange = (0, react_1.useCallback)((newVal) => {
50
+ if (percent === null) {
51
+ return;
52
+ }
53
+ setDragX(newVal);
54
+ const currentY = dragY !== null && dragY !== void 0 ? dragY : percent[1];
55
+ onDragValueChange(serialize(newVal, currentY));
56
+ }, [dragY, onDragValueChange, percent, serialize]);
57
+ const onXChangeEnd = (0, react_1.useCallback)((newVal) => {
58
+ if (percent === null) {
59
+ return;
60
+ }
61
+ const currentY = dragY !== null && dragY !== void 0 ? dragY : percent[1];
62
+ const value = serialize(newVal, currentY);
63
+ if (value !== propStatus.codeValue) {
64
+ onSave(value).finally(() => {
65
+ setDragX(null);
66
+ onDragEnd();
67
+ });
68
+ }
69
+ else {
70
+ setDragX(null);
71
+ onDragEnd();
72
+ }
73
+ }, [dragY, onDragEnd, onSave, percent, propStatus.codeValue, serialize]);
74
+ const onXTextChange = (0, react_1.useCallback)((newVal) => {
75
+ if (percent === null) {
76
+ return;
77
+ }
78
+ const parsedValue = Number(newVal);
79
+ if (!Number.isNaN(parsedValue)) {
80
+ const currentY = dragY !== null && dragY !== void 0 ? dragY : percent[1];
81
+ const value = serialize(parsedValue, currentY);
82
+ if (value !== propStatus.codeValue) {
83
+ setDragX(parsedValue);
84
+ onSave(value).finally(() => {
85
+ setDragX(null);
86
+ });
87
+ }
88
+ }
89
+ }, [dragY, onSave, percent, propStatus.codeValue, serialize]);
90
+ const onYChange = (0, react_1.useCallback)((newVal) => {
91
+ if (percent === null) {
92
+ return;
93
+ }
94
+ setDragY(newVal);
95
+ const currentX = dragX !== null && dragX !== void 0 ? dragX : percent[0];
96
+ onDragValueChange(serialize(currentX, newVal));
97
+ }, [dragX, onDragValueChange, percent, serialize]);
98
+ const onYChangeEnd = (0, react_1.useCallback)((newVal) => {
99
+ if (percent === null) {
100
+ return;
101
+ }
102
+ const currentX = dragX !== null && dragX !== void 0 ? dragX : percent[0];
103
+ const value = serialize(currentX, newVal);
104
+ if (value !== propStatus.codeValue) {
105
+ onSave(value).finally(() => {
106
+ setDragY(null);
107
+ onDragEnd();
108
+ });
109
+ }
110
+ else {
111
+ setDragY(null);
112
+ onDragEnd();
113
+ }
114
+ }, [dragX, onDragEnd, onSave, percent, propStatus.codeValue, serialize]);
115
+ const onYTextChange = (0, react_1.useCallback)((newVal) => {
116
+ if (percent === null) {
117
+ return;
118
+ }
119
+ const parsedValue = Number(newVal);
120
+ if (!Number.isNaN(parsedValue)) {
121
+ const currentX = dragX !== null && dragX !== void 0 ? dragX : percent[0];
122
+ const value = serialize(currentX, parsedValue);
123
+ if (value !== propStatus.codeValue) {
124
+ setDragY(parsedValue);
125
+ onSave(value).finally(() => {
126
+ setDragY(null);
127
+ });
128
+ }
129
+ }
130
+ }, [dragX, onSave, percent, propStatus.codeValue, serialize]);
131
+ if (percent === null) {
132
+ return jsx_runtime_1.jsx(TimelineSchemaField_1.UnsupportedStatus, { label: "unsupported origin" });
133
+ }
134
+ return (jsx_runtime_1.jsxs("span", { style: containerStyle, children: [
135
+ jsx_runtime_1.jsx(InputDragger_1.InputDragger, { type: "number", value: dragX !== null && dragX !== void 0 ? dragX : percent[0], style: leftDraggerStyle, status: "ok", small: true, onValueChange: onXChange, onValueChangeEnd: onXChangeEnd, onTextChange: onXTextChange, min: -Infinity, max: Infinity, step: step, formatter: formatter, rightAlign: false, snapToStep: false, dragDecimalPlaces: decimalPlaces }), jsx_runtime_1.jsx("div", { style: { marginLeft: -6, marginRight: -6 } }), jsx_runtime_1.jsx(InputDragger_1.InputDragger, { type: "number", value: dragY !== null && dragY !== void 0 ? dragY : percent[1], style: rightDraggerStyle, status: "ok", small: true, onValueChange: onYChange, onValueChangeEnd: onYChangeEnd, onTextChange: onYTextChange, min: -Infinity, max: Infinity, step: step, formatter: formatter, rightAlign: false, snapToStep: false, dragDecimalPlaces: decimalPlaces })
136
+ ] }));
137
+ };
138
+ exports.TimelineTransformOriginField = TimelineTransformOriginField;
@@ -101,7 +101,7 @@ const TimelineTranslateField = ({ field, propStatus, effectiveValue, onSave, onD
101
101
  }
102
102
  }, [propStatus, onSave, dragX, codeX, decimalPlaces]);
103
103
  return (jsx_runtime_1.jsxs("span", { style: containerStyle, children: [
104
- 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: -Infinity, max: Infinity, step: step, formatter: formatter, rightAlign: false }), jsx_runtime_1.jsx("div", { style: { marginLeft: -6, marginRight: -6 } }), 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: -Infinity, max: Infinity, step: step, formatter: formatter, rightAlign: false })
104
+ 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: -Infinity, max: Infinity, step: step, formatter: formatter, rightAlign: false, snapToStep: false, dragDecimalPlaces: decimalPlaces }), jsx_runtime_1.jsx("div", { style: { marginLeft: -6, marginRight: -6 } }), 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: -Infinity, max: Infinity, step: step, formatter: formatter, rightAlign: false, snapToStep: false, dragDecimalPlaces: decimalPlaces })
105
105
  ] }));
106
106
  };
107
107
  exports.TimelineTranslateField = TimelineTranslateField;
@@ -120,7 +120,7 @@ const TimelineUvCoordinateField = ({ field, propStatus, effectiveValue, onSave,
120
120
  }
121
121
  }, [propStatus, onSave, dragX, codeX]);
122
122
  return (jsx_runtime_1.jsxs("span", { style: containerStyle, children: [
123
- 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, snapToStep: false }), jsx_runtime_1.jsx("div", { style: { marginLeft: -6, marginRight: -6 } }), 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, snapToStep: false })
123
+ 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, snapToStep: false, dragDecimalPlaces: decimalPlaces }), jsx_runtime_1.jsx("div", { style: { marginLeft: -6, marginRight: -6 } }), 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, snapToStep: false, dragDecimalPlaces: decimalPlaces })
124
124
  ] }));
125
125
  };
126
126
  exports.TimelineUvCoordinateField = TimelineUvCoordinateField;
@@ -1,5 +1,16 @@
1
1
  import type { SequencePropsSubscriptionKey, SequenceSchema } from 'remotion';
2
2
  import type { SetPropStatuses } from './save-sequence-prop';
3
+ export type AddSequenceKeyframeChange = {
4
+ fileName: string;
5
+ nodePath: SequencePropsSubscriptionKey;
6
+ fieldKey: string;
7
+ sourceFrame: number;
8
+ value: unknown;
9
+ schema: SequenceSchema;
10
+ };
11
+ export type AddEffectKeyframeChange = AddSequenceKeyframeChange & {
12
+ effectIndex: number;
13
+ };
3
14
  export declare const callAddSequenceKeyframe: ({ fileName, nodePath, fieldKey, sourceFrame, value, schema, setPropStatuses, clientId, }: {
4
15
  fileName: string;
5
16
  nodePath: SequencePropsSubscriptionKey;
@@ -10,6 +21,12 @@ export declare const callAddSequenceKeyframe: ({ fileName, nodePath, fieldKey, s
10
21
  setPropStatuses: SetPropStatuses;
11
22
  clientId: string;
12
23
  }) => Promise<void>;
24
+ export declare const callAddKeyframes: ({ sequenceKeyframes, effectKeyframes, setPropStatuses, clientId, }: {
25
+ sequenceKeyframes: AddSequenceKeyframeChange[];
26
+ effectKeyframes: AddEffectKeyframeChange[];
27
+ setPropStatuses: SetPropStatuses;
28
+ clientId: string;
29
+ }) => Promise<void>;
13
30
  export declare const callAddEffectKeyframe: ({ fileName, nodePath, effectIndex, fieldKey, sourceFrame, value, schema, setPropStatuses, clientId, }: {
14
31
  fileName: string;
15
32
  nodePath: SequencePropsSubscriptionKey;
@@ -1,10 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.callAddEffectKeyframe = exports.callAddSequenceKeyframe = void 0;
3
+ exports.callAddEffectKeyframe = exports.callAddKeyframes = exports.callAddSequenceKeyframe = void 0;
4
4
  const studio_shared_1 = require("@remotion/studio-shared");
5
5
  const call_api_1 = require("../call-api");
6
6
  const apply_effect_response_to_prop_statuses_1 = require("./apply-effect-response-to-prop-statuses");
7
7
  const save_prop_queue_1 = require("./save-prop-queue");
8
+ const groupByNodePath = (keyframes) => {
9
+ var _a;
10
+ const groups = new Map();
11
+ for (const keyframe of keyframes) {
12
+ const key = JSON.stringify(keyframe.nodePath);
13
+ const group = (_a = groups.get(key)) !== null && _a !== void 0 ? _a : [];
14
+ group.push(keyframe);
15
+ groups.set(key, group);
16
+ }
17
+ return [...groups.values()];
18
+ };
8
19
  const callAddSequenceKeyframe = ({ fileName, nodePath, fieldKey, sourceFrame, value, schema, setPropStatuses, clientId, }) => {
9
20
  return (0, save_prop_queue_1.enqueueSavePropChange)({
10
21
  nodePath,
@@ -29,6 +40,59 @@ const callAddSequenceKeyframe = ({ fileName, nodePath, fieldKey, sourceFrame, va
29
40
  });
30
41
  };
31
42
  exports.callAddSequenceKeyframe = callAddSequenceKeyframe;
43
+ const callAddKeyframes = ({ sequenceKeyframes, effectKeyframes, setPropStatuses, clientId, }) => {
44
+ if (sequenceKeyframes.length === 0 && effectKeyframes.length === 0) {
45
+ return Promise.resolve();
46
+ }
47
+ for (const keyframes of groupByNodePath(sequenceKeyframes)) {
48
+ const [firstKeyframe] = keyframes;
49
+ if (!firstKeyframe) {
50
+ continue;
51
+ }
52
+ setPropStatuses(firstKeyframe.nodePath, (prev) => keyframes.reduce((current, keyframe) => (0, studio_shared_1.optimisticAddSequenceKeyframe)({
53
+ previous: current,
54
+ fieldKey: keyframe.fieldKey,
55
+ frame: keyframe.sourceFrame,
56
+ value: keyframe.value,
57
+ schema: keyframe.schema,
58
+ }), prev));
59
+ }
60
+ for (const keyframes of groupByNodePath(effectKeyframes)) {
61
+ const [firstKeyframe] = keyframes;
62
+ if (!firstKeyframe) {
63
+ continue;
64
+ }
65
+ setPropStatuses(firstKeyframe.nodePath, (prev) => keyframes.reduce((current, keyframe) => (0, studio_shared_1.optimisticAddEffectKeyframe)({
66
+ previous: current,
67
+ effectIndex: keyframe.effectIndex,
68
+ fieldKey: keyframe.fieldKey,
69
+ frame: keyframe.sourceFrame,
70
+ value: keyframe.value,
71
+ schema: keyframe.schema,
72
+ }), prev));
73
+ }
74
+ return (0, call_api_1.callApi)('/api/add-keyframes', {
75
+ sequenceKeyframes: sequenceKeyframes.map((keyframe) => ({
76
+ fileName: keyframe.fileName,
77
+ nodePath: keyframe.nodePath,
78
+ key: keyframe.fieldKey,
79
+ frame: keyframe.sourceFrame,
80
+ value: JSON.stringify(keyframe.value),
81
+ schema: keyframe.schema,
82
+ })),
83
+ effectKeyframes: effectKeyframes.map((keyframe) => ({
84
+ fileName: keyframe.fileName,
85
+ sequenceNodePath: keyframe.nodePath,
86
+ effectIndex: keyframe.effectIndex,
87
+ key: keyframe.fieldKey,
88
+ frame: keyframe.sourceFrame,
89
+ value: JSON.stringify(keyframe.value),
90
+ schema: keyframe.schema,
91
+ })),
92
+ clientId,
93
+ }).then(() => undefined);
94
+ };
95
+ exports.callAddKeyframes = callAddKeyframes;
32
96
  const callAddEffectKeyframe = ({ fileName, nodePath, effectIndex, fieldKey, sourceFrame, value, schema, setPropStatuses, clientId, }) => {
33
97
  return (0, save_prop_queue_1.enqueueSavePropChange)({
34
98
  nodePath,
@@ -134,6 +134,7 @@ const isSequenceRowSelection = (selection) => selection.type === 'sequence';
134
134
  const isSequenceEffectSelection = (selection) => selection.type === 'sequence-effect';
135
135
  const isSequenceAllEffectsSelection = (selection) => selection.type === 'sequence-all-effects';
136
136
  const isKeyframeSelection = (selection) => selection.type === 'keyframe';
137
+ const areSelectionsOnlyOfType = (selections, type) => selections.every((selection) => selection.type === type);
137
138
  const assertTimelineSelectionsHaveSameType = (selections) => {
138
139
  const firstSelection = selections[0];
139
140
  if (!firstSelection) {
@@ -145,12 +146,33 @@ const assertTimelineSelectionsHaveSameType = (selections) => {
145
146
  }
146
147
  }
147
148
  };
149
+ const containsOnlyKeyframesAndEasings = (selections) => selections.every((selection) => selection.type === 'keyframe' || selection.type === 'easing');
148
150
  const deleteSelectedTimelineItems = ({ selections, sequences, overrideIdsToNodePaths, setPropStatuses, clientId, confirm, }) => {
149
151
  var _a;
150
152
  const firstSelection = selections[0];
151
153
  if (!firstSelection) {
152
154
  return null;
153
155
  }
156
+ if (containsOnlyKeyframesAndEasings(selections)) {
157
+ const keyframes = selections.filter(isKeyframeSelection);
158
+ if (keyframes.length === 0) {
159
+ return null;
160
+ }
161
+ const promise = (0, delete_selected_keyframe_1.deleteSelectedKeyframes)({
162
+ keyframes: keyframes.map((selection) => ({
163
+ nodePathInfo: selection.nodePathInfo,
164
+ frame: selection.frame,
165
+ })),
166
+ sequences,
167
+ overrideIdsToNodePaths,
168
+ setPropStatuses,
169
+ clientId,
170
+ });
171
+ return (_a = promise === null || promise === void 0 ? void 0 : promise.then(() => true)) !== null && _a !== void 0 ? _a : null;
172
+ }
173
+ if (!areSelectionsOnlyOfType(selections, firstSelection.type)) {
174
+ return null;
175
+ }
154
176
  assertTimelineSelectionsHaveSameType(selections);
155
177
  switch (firstSelection.type) {
156
178
  case 'sequence':
@@ -163,19 +185,7 @@ const deleteSelectedTimelineItems = ({ selections, sequences, overrideIdsToNodeP
163
185
  nodePathInfo: selection.nodePathInfo,
164
186
  effectIndex: selection.i,
165
187
  })));
166
- case 'keyframe': {
167
- const promise = (0, delete_selected_keyframe_1.deleteSelectedKeyframes)({
168
- keyframes: selections.filter(isKeyframeSelection).map((selection) => ({
169
- nodePathInfo: selection.nodePathInfo,
170
- frame: selection.frame,
171
- })),
172
- sequences,
173
- overrideIdsToNodePaths,
174
- setPropStatuses,
175
- clientId,
176
- });
177
- return (_a = promise === null || promise === void 0 ? void 0 : promise.then(() => true)) !== null && _a !== void 0 ? _a : null;
178
- }
188
+ case 'keyframe':
179
189
  case 'sequence-prop':
180
190
  case 'sequence-effect-prop':
181
191
  case 'easing':
@@ -0,0 +1,8 @@
1
+ import type { SequencePropsSubscriptionKey } from 'remotion';
2
+ import { type SetPropStatuses } from './save-sequence-prop';
3
+ export declare const disableSequenceInteractivity: ({ clientId, fileName, nodePath, setPropStatuses, }: {
4
+ readonly clientId: string;
5
+ readonly fileName: string;
6
+ readonly nodePath: SequencePropsSubscriptionKey;
7
+ readonly setPropStatuses: SetPropStatuses;
8
+ }) => Promise<void>;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.disableSequenceInteractivity = void 0;
4
+ const no_react_1 = require("remotion/no-react");
5
+ const save_sequence_prop_1 = require("./save-sequence-prop");
6
+ const disableSequenceInteractivity = ({ clientId, fileName, nodePath, setPropStatuses, }) => {
7
+ return (0, save_sequence_prop_1.saveSequenceProps)({
8
+ changes: [
9
+ {
10
+ fileName,
11
+ nodePath,
12
+ fieldKey: 'showInTimeline',
13
+ value: false,
14
+ defaultValue: 'true',
15
+ schema: no_react_1.NoReactInternals.sequenceSchema,
16
+ },
17
+ ],
18
+ setPropStatuses,
19
+ clientId,
20
+ undoLabel: 'Disable interactivity',
21
+ redoLabel: 'Disable interactivity again',
22
+ });
23
+ };
24
+ exports.disableSequenceInteractivity = disableSequenceInteractivity;
@@ -7,16 +7,23 @@ const save_effect_prop_1 = require("./save-effect-prop");
7
7
  const save_sequence_prop_1 = require("./save-sequence-prop");
8
8
  const isPropResetSelection = (selection) => selection.type === 'sequence-prop' ||
9
9
  selection.type === 'sequence-effect-prop';
10
+ function assertPropResetSelections(selections) {
11
+ for (const selection of selections) {
12
+ if (!isPropResetSelection(selection)) {
13
+ throw new Error(`Assertion failed: Cannot reset timeline selection of type ${selection.type}`);
14
+ }
15
+ }
16
+ }
10
17
  const isVisibleFieldSchema = (fieldSchema) => fieldSchema !== undefined && fieldSchema.type !== 'hidden';
11
18
  const isNonDefaultCodeValue = ({ propStatus, defaultValue, }) => JSON.stringify(propStatus !== null && propStatus !== void 0 ? propStatus : defaultValue) !== JSON.stringify(defaultValue);
12
19
  const isResettablePropStatus = ({ propStatus, defaultValue, }) => {
13
- if (!propStatus || propStatus.status === 'computed') {
20
+ if (!propStatus) {
14
21
  return false;
15
22
  }
16
23
  if (defaultValue === undefined) {
17
24
  return false;
18
25
  }
19
- if (propStatus.status === 'keyframed') {
26
+ if (propStatus.status === 'keyframed' || propStatus.status === 'computed') {
20
27
  return true;
21
28
  }
22
29
  return isNonDefaultCodeValue({
@@ -30,15 +37,16 @@ const getDefaultValue = (fieldSchema) => fieldSchema.default !== undefined
30
37
  const getTimelinePropResetTargets = ({ selections, sequences, overrideIdsToNodePaths, propStatuses, }) => {
31
38
  var _a;
32
39
  var _b;
33
- const firstSelection = selections[0];
34
- if (!firstSelection || !isPropResetSelection(firstSelection)) {
40
+ const propSelections = selections.filter(isPropResetSelection);
41
+ if (propSelections.length === 0) {
35
42
  return null;
36
43
  }
44
+ if (propSelections.length !== selections.length) {
45
+ return null;
46
+ }
47
+ assertPropResetSelections(selections);
37
48
  const resetTargets = [];
38
49
  for (const selection of selections) {
39
- if (!isPropResetSelection(selection)) {
40
- throw new Error(`Assertion failed: Cannot reset timeline selections of different types (${firstSelection.type}, ${selection.type})`);
41
- }
42
50
  const track = (0, find_track_for_node_path_info_1.findTrackForNodePathInfo)({
43
51
  sequences,
44
52
  overrideIdsToNodePaths,
@@ -0,0 +1,2 @@
1
+ export declare const parseCssRotationToDegrees: (value: string) => number;
2
+ export declare const serializeCssRotation: (value: number, decimalPlaces?: number) => string;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serializeCssRotation = exports.parseCssRotationToDegrees = void 0;
4
+ const timeline_field_utils_1 = require("./timeline-field-utils");
5
+ const unitPattern = /^([+-]?(?:\d+\.?\d*|\.\d+))(deg|rad|turn|grad)$/;
6
+ const unitToDegrees = {
7
+ deg: 1,
8
+ rad: 180 / Math.PI,
9
+ turn: 360,
10
+ grad: 360 / 400,
11
+ };
12
+ const parseCssRotationToDegrees = (value) => {
13
+ const match = value.trim().match(unitPattern);
14
+ if (match) {
15
+ return (0, timeline_field_utils_1.normalizeTimelineNumber)(Number(match[1]) * unitToDegrees[match[2]]);
16
+ }
17
+ if (typeof DOMMatrix === 'undefined') {
18
+ return 0;
19
+ }
20
+ try {
21
+ const m = new DOMMatrix(`rotate(${value})`);
22
+ return (0, timeline_field_utils_1.normalizeTimelineNumber)(Math.atan2(m.b, m.a) * (180 / Math.PI));
23
+ }
24
+ catch (_a) {
25
+ return 0;
26
+ }
27
+ };
28
+ exports.parseCssRotationToDegrees = parseCssRotationToDegrees;
29
+ const serializeCssRotation = (value, decimalPlaces = 6) => {
30
+ const factor = 10 ** decimalPlaces;
31
+ const rounded = Math.round((0, timeline_field_utils_1.normalizeTimelineNumber)(value) * factor) / factor;
32
+ return `${Object.is(rounded, -0) ? 0 : rounded}deg`;
33
+ };
34
+ exports.serializeCssRotation = serializeCssRotation;
@@ -0,0 +1,24 @@
1
+ export type TransformOriginUnit = '%' | 'px';
2
+ export type TransformOriginAxisValue = {
3
+ readonly value: number;
4
+ readonly unit: TransformOriginUnit;
5
+ };
6
+ export type ParsedTransformOrigin = {
7
+ readonly x: TransformOriginAxisValue;
8
+ readonly y: TransformOriginAxisValue;
9
+ readonly z: string | null;
10
+ };
11
+ export declare const parseTransformOrigin: (value: unknown) => ParsedTransformOrigin | null;
12
+ export declare const axisValueToUv: (axis: TransformOriginAxisValue, size: number) => number | null;
13
+ export declare const parsedTransformOriginToUv: ({ parsed, width, height, }: {
14
+ readonly parsed: ParsedTransformOrigin;
15
+ readonly width: number;
16
+ readonly height: number;
17
+ }) => readonly [number, number] | null;
18
+ export declare const serializeTransformOrigin: ({ uv, z, decimalPlaces, }: {
19
+ readonly uv: readonly [number, number];
20
+ readonly z: string | null;
21
+ readonly decimalPlaces?: number | undefined;
22
+ }) => string;
23
+ export declare const parsedTransformOriginToPercent: (parsed: ParsedTransformOrigin) => readonly [number, number] | null;
24
+ export declare const transformOriginPercentToUv: (percent: readonly [number, number]) => readonly [number, number];