@remotion/studio 4.0.475 → 4.0.477

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 (88) hide show
  1. package/dist/components/Canvas.js +40 -1
  2. package/dist/components/ColorPicker/ColorPickerPopup.d.ts +6 -0
  3. package/dist/components/ColorPicker/ColorPickerPopup.js +11 -6
  4. package/dist/components/ControlButton.d.ts +1 -0
  5. package/dist/components/ControlButton.js +7 -2
  6. package/dist/components/EditorGuides/Guide.js +122 -20
  7. package/dist/components/EditorRuler/Ruler.js +21 -15
  8. package/dist/components/EditorRuler/index.js +18 -10
  9. package/dist/components/GlobalKeybindings.js +12 -0
  10. package/dist/components/KeyboardShortcutsExplainer.js +24 -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/OutlineToggle.js +1 -1
  20. package/dist/components/PlayPause.js +22 -66
  21. package/dist/components/PreviewToolbar.js +15 -1
  22. package/dist/components/RenderModal/RenderModalJSONPropsEditor.js +2 -1
  23. package/dist/components/SelectedOutlineElement.d.ts +17 -0
  24. package/dist/components/SelectedOutlineElement.js +938 -0
  25. package/dist/components/SelectedOutlineOverlay.d.ts +4 -174
  26. package/dist/components/SelectedOutlineOverlay.js +310 -1392
  27. package/dist/components/SelectedOutlineUvControls.js +1 -1
  28. package/dist/components/ShowGuidesProvider.js +4 -4
  29. package/dist/components/Timeline/SubscribeToNodePaths.d.ts +2 -1
  30. package/dist/components/Timeline/SubscribeToNodePaths.js +2 -1
  31. package/dist/components/Timeline/Timeline.js +3 -1
  32. package/dist/components/Timeline/TimelineClipboardKeybindings.js +9 -10
  33. package/dist/components/Timeline/TimelineDeleteKeybindings.js +15 -4
  34. package/dist/components/Timeline/TimelineKeyframeEasingLine.js +7 -11
  35. package/dist/components/Timeline/TimelineList.js +1 -1
  36. package/dist/components/Timeline/TimelineRotationField.js +17 -17
  37. package/dist/components/Timeline/TimelineScaleField.js +1 -1
  38. package/dist/components/Timeline/TimelineSelection.d.ts +27 -13
  39. package/dist/components/Timeline/TimelineSelection.js +47 -28
  40. package/dist/components/Timeline/TimelineSequence.js +169 -8
  41. package/dist/components/Timeline/TimelineSequenceFrame.d.ts +1 -0
  42. package/dist/components/Timeline/TimelineSequenceFrame.js +17 -6
  43. package/dist/components/Timeline/TimelineSequenceItem.d.ts +1 -0
  44. package/dist/components/Timeline/TimelineSequenceItem.js +294 -142
  45. package/dist/components/Timeline/TimelineSequenceName.d.ts +4 -2
  46. package/dist/components/Timeline/TimelineSequenceName.js +67 -2
  47. package/dist/components/Timeline/TimelineTransformOriginField.js +1 -1
  48. package/dist/components/Timeline/TimelineTranslateField.js +1 -1
  49. package/dist/components/Timeline/TimelineUvCoordinateField.js +1 -1
  50. package/dist/components/Timeline/delete-selected-timeline-item.js +4 -0
  51. package/dist/components/Timeline/disable-sequence-interactivity.d.ts +8 -0
  52. package/dist/components/Timeline/disable-sequence-interactivity.js +24 -0
  53. package/dist/components/Timeline/duplicate-selected-timeline-item.d.ts +1 -2
  54. package/dist/components/Timeline/get-sequence-context-menu-items.d.ts +20 -0
  55. package/dist/components/Timeline/get-sequence-context-menu-items.js +160 -0
  56. package/dist/components/Timeline/reset-selected-timeline-props.js +2 -2
  57. package/dist/components/Timeline/sequence-props-subscription-store.d.ts +2 -1
  58. package/dist/components/Timeline/sequence-props-subscription-store.js +11 -3
  59. package/dist/components/Timeline/should-clear-selection-on-pointer-down.d.ts +2 -0
  60. package/dist/components/Timeline/should-clear-selection-on-pointer-down.js +16 -2
  61. package/dist/components/Timeline/timeline-rotation-utils.d.ts +1 -1
  62. package/dist/components/Timeline/timeline-rotation-utils.js +4 -2
  63. package/dist/components/Timeline/update-selected-easing.d.ts +4 -6
  64. package/dist/components/Timeline/use-sequence-props-subscription.d.ts +2 -1
  65. package/dist/components/Timeline/use-sequence-props-subscription.js +3 -1
  66. package/dist/components/Timeline/use-timeline-keyframe-drag.d.ts +37 -1
  67. package/dist/components/Timeline/use-timeline-keyframe-drag.js +290 -14
  68. package/dist/components/import-assets.d.ts +36 -8
  69. package/dist/components/import-assets.js +170 -10
  70. package/dist/components/selected-outline-drag.d.ts +117 -0
  71. package/dist/components/selected-outline-drag.js +427 -0
  72. package/dist/components/selected-outline-measurement.d.ts +67 -0
  73. package/dist/components/selected-outline-measurement.js +355 -0
  74. package/dist/components/selected-outline-types.d.ts +121 -0
  75. package/dist/components/selected-outline-types.js +15 -0
  76. package/dist/components/selected-outline-uv.d.ts +1 -0
  77. package/dist/components/selected-outline-uv.js +12 -0
  78. package/dist/esm/{chunk-qaqqvw4q.js → chunk-t8fjnw2d.js} +14059 -12029
  79. package/dist/esm/internals.mjs +14059 -12029
  80. package/dist/esm/previewEntry.mjs +14066 -12036
  81. package/dist/esm/renderEntry.mjs +1 -1
  82. package/dist/helpers/editor-guide-selection.d.ts +31 -0
  83. package/dist/helpers/editor-guide-selection.js +58 -0
  84. package/dist/helpers/editor-ruler.d.ts +3 -3
  85. package/dist/helpers/editor-ruler.js +16 -18
  86. package/dist/state/editor-guides.d.ts +2 -2
  87. package/dist/state/editor-guides.js +2 -2
  88. package/package.json +11 -11
@@ -113,6 +113,27 @@ const getSfxDragUrl = (event) => {
113
113
  }
114
114
  return null;
115
115
  };
116
+ const getDropPosition = ({ clientX, clientY, contentDimensions, previewSize, size, }) => {
117
+ if (contentDimensions === null || contentDimensions === 'none') {
118
+ return null;
119
+ }
120
+ const scale = remotion_1.Internals.calculateScale({
121
+ canvasSize: size,
122
+ compositionHeight: contentDimensions.height,
123
+ compositionWidth: contentDimensions.width,
124
+ previewSize: previewSize.size,
125
+ });
126
+ const { centerX, centerY } = (0, get_effective_translation_1.getCenterPointWhileScrolling)({
127
+ size,
128
+ clientX,
129
+ clientY,
130
+ compositionWidth: contentDimensions.width,
131
+ compositionHeight: contentDimensions.height,
132
+ scale,
133
+ translation: previewSize.translation,
134
+ });
135
+ return { centerX, centerY };
136
+ };
116
137
  const isDragEventInsideCanvas = (event) => {
117
138
  const { current } = canvas_ref_1.canvasRef;
118
139
  if (!current) {
@@ -572,6 +593,13 @@ const Canvas = ({ canvasContent, size }) => {
572
593
  event.stopPropagation();
573
594
  setIsAddingAsset(true);
574
595
  try {
596
+ const dropPosition = getDropPosition({
597
+ clientX: event.clientX,
598
+ clientY: event.clientY,
599
+ contentDimensions,
600
+ previewSize,
601
+ size,
602
+ });
575
603
  if (isFileDragEvent(event)) {
576
604
  const files = Array.from((_b = (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.files) !== null && _b !== void 0 ? _b : []);
577
605
  if (files.length === 0) {
@@ -581,6 +609,7 @@ const Canvas = ({ canvasContent, size }) => {
581
609
  files,
582
610
  compositionFile,
583
611
  compositionId: currentCompositionId,
612
+ dropPosition,
584
613
  });
585
614
  }
586
615
  else if (isAssetDragEvent(event)) {
@@ -592,6 +621,7 @@ const Canvas = ({ canvasContent, size }) => {
592
621
  assetPaths: [assetPath],
593
622
  compositionFile,
594
623
  compositionId: currentCompositionId,
624
+ dropPosition,
595
625
  });
596
626
  }
597
627
  else if (isSfxDragEvent(event)) {
@@ -612,6 +642,7 @@ const Canvas = ({ canvasContent, size }) => {
612
642
  component: componentDragData.component,
613
643
  compositionFile,
614
644
  compositionId: currentCompositionId,
645
+ dropPosition,
615
646
  });
616
647
  return;
617
648
  }
@@ -623,13 +654,21 @@ const Canvas = ({ canvasContent, size }) => {
623
654
  url,
624
655
  compositionFile,
625
656
  compositionId: currentCompositionId,
657
+ dropPosition,
626
658
  });
627
659
  }
628
660
  }
629
661
  finally {
630
662
  setIsAddingAsset(false);
631
663
  }
632
- }, [canDropAssets, compositionFile, currentCompositionId]);
664
+ }, [
665
+ canDropAssets,
666
+ compositionFile,
667
+ contentDimensions,
668
+ currentCompositionId,
669
+ previewSize,
670
+ size,
671
+ ]);
633
672
  (0, react_1.useEffect)(() => {
634
673
  if (!canDropAssets) {
635
674
  return;
@@ -1,5 +1,11 @@
1
1
  import React from 'react';
2
2
  export declare const POPUP_WIDTH = 240;
3
+ export declare const parseEyeDropperColor: (pickedColor: string) => {
4
+ r: number;
5
+ g: number;
6
+ b: number;
7
+ a: number;
8
+ };
3
9
  export type ChannelKey = 'r' | 'g' | 'b' | 'a-percent';
4
10
  export declare const ColorPickerPopup: React.FC<{
5
11
  readonly value: string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ColorPickerPopup = exports.POPUP_WIDTH = void 0;
3
+ exports.ColorPickerPopup = exports.parseEyeDropperColor = exports.POPUP_WIDTH = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const color_conversion_1 = require("../../helpers/color-conversion");
@@ -100,6 +100,14 @@ const eyedropperButtonStyle = {
100
100
  flex: '0 0 auto',
101
101
  };
102
102
  const hasEyeDropper = () => typeof window !== 'undefined' && 'EyeDropper' in window;
103
+ const parseEyeDropperColor = (pickedColor) => {
104
+ const parsed = (0, color_conversion_1.parseAnyColor)(pickedColor);
105
+ return {
106
+ ...parsed,
107
+ a: 255,
108
+ };
109
+ };
110
+ exports.parseEyeDropperColor = parseEyeDropperColor;
103
111
  const ChannelInput = ({ label, channel, value, min, max, onCommit, }) => {
104
112
  const [draft, setDraft] = (0, react_1.useState)(String(Math.round(value)));
105
113
  const { tabIndex } = (0, z_index_1.useZIndex)();
@@ -243,10 +251,7 @@ const ColorPickerPopup = ({ value, onChange, onChangeComplete }) => {
243
251
  dropper
244
252
  .open()
245
253
  .then((result) => {
246
- const parsed = (0, color_conversion_1.parseAnyColor)(result.sRGBHex);
247
- // `EyeDropper` always returns full opacity; preserve the user's
248
- // previously chosen alpha so opening it doesn't drop transparency.
249
- parsed.a = rgba.a;
254
+ const parsed = (0, exports.parseEyeDropperColor)(result.sRGBHex);
250
255
  const newHsva = (0, color_conversion_1.rgbaToHsva)(parsed);
251
256
  if (newHsva.s === 0) {
252
257
  newHsva.h = hsva.h;
@@ -256,7 +261,7 @@ const ColorPickerPopup = ({ value, onChange, onChangeComplete }) => {
256
261
  .catch(() => {
257
262
  // Aborted; ignore.
258
263
  });
259
- }, [emit, hsva.h, rgba.a]);
264
+ }, [emit, hsva.h]);
260
265
  const previewFill = (0, react_1.useMemo)(() => {
261
266
  return {
262
267
  ...previewSwatchFillBase,
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  export declare const CONTROL_BUTTON_PADDING = 6;
3
+ export declare const CONTROL_BUTTON_SIZE = 30;
3
4
  export declare const ControlButton: (props: React.ClassAttributes<HTMLButtonElement> & React.ButtonHTMLAttributes<HTMLButtonElement> & {
4
5
  readonly title: string;
5
6
  }) => import("react/jsx-runtime").JSX.Element;
@@ -1,18 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ControlButton = exports.CONTROL_BUTTON_PADDING = void 0;
3
+ exports.ControlButton = exports.CONTROL_BUTTON_SIZE = exports.CONTROL_BUTTON_PADDING = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const z_index_1 = require("../state/z-index");
7
7
  exports.CONTROL_BUTTON_PADDING = 6;
8
+ exports.CONTROL_BUTTON_SIZE = 30;
8
9
  const ControlButton = (props) => {
9
10
  const style = (0, react_1.useMemo)(() => {
10
11
  return {
11
12
  opacity: props.disabled ? 0.5 : 1,
12
13
  display: 'inline-flex',
14
+ alignItems: 'center',
15
+ justifyContent: 'center',
16
+ width: exports.CONTROL_BUTTON_SIZE,
17
+ height: exports.CONTROL_BUTTON_SIZE,
13
18
  background: 'none',
14
19
  border: 'none',
15
- padding: exports.CONTROL_BUTTON_PADDING,
20
+ padding: 0,
16
21
  };
17
22
  }, [props.disabled]);
18
23
  const { tabIndex } = (0, z_index_1.useZIndex)();
@@ -3,14 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const jsx_runtime_1 = require("react/jsx-runtime");
4
4
  const react_1 = require("react");
5
5
  const no_react_1 = require("remotion/no-react");
6
- const colors_1 = require("../../helpers/colors");
6
+ const editor_guide_selection_1 = require("../../helpers/editor-guide-selection");
7
7
  const editor_guides_1 = require("../../state/editor-guides");
8
8
  const editor_rulers_1 = require("../../state/editor-rulers");
9
9
  const ContextMenu_1 = require("../ContextMenu");
10
10
  const ForceSpecificCursor_1 = require("../ForceSpecificCursor");
11
+ const should_clear_selection_on_pointer_down_1 = require("../Timeline/should-clear-selection-on-pointer-down");
12
+ const TimelineSelection_1 = require("../Timeline/TimelineSelection");
11
13
  const PADDING_FOR_EASY_DRAG = 4;
12
14
  const GuideComp = ({ guide, canvasDimensions, scale }) => {
13
- const { shouldCreateGuideRef, setGuidesList, setSelectedGuideId, selectedGuideId, setHoveredGuideId, hoveredGuideId, } = (0, react_1.useContext)(editor_guides_1.EditorShowGuidesContext);
15
+ const { shouldCreateGuideRef, shouldDeleteGuideRef, setGuidesList, setDraggingGuideId, setHoveredGuideId, hoveredGuideId, draggingGuideId, } = (0, react_1.useContext)(editor_guides_1.EditorShowGuidesContext);
16
+ const { clearSelection, onSelect, selected } = (0, TimelineSelection_1.useTimelineGuideSelection)(guide.id);
17
+ const pointerDownPositionRef = (0, react_1.useRef)(null);
18
+ const hasMovedGuideRef = (0, react_1.useRef)(false);
14
19
  const onPointerEnter = (0, react_1.useCallback)(() => {
15
20
  setHoveredGuideId(() => guide.id);
16
21
  }, [guide.id, setHoveredGuideId]);
@@ -43,20 +48,121 @@ const GuideComp = ({ guide, canvasDimensions, scale }) => {
43
48
  top: `${isVerticalGuide ? `-${editor_rulers_1.RULER_WIDTH}px` : '0px'}`,
44
49
  left: `${isVerticalGuide ? '0px' : `-${editor_rulers_1.RULER_WIDTH}px`}`,
45
50
  display: guide.show ? 'block' : 'none',
46
- backgroundColor: selectedGuideId === guide.id || hoveredGuideId === guide.id
47
- ? colors_1.SELECTED_GUIDE
48
- : colors_1.UNSELECTED_GUIDE,
51
+ backgroundColor: (0, editor_guide_selection_1.getEditorGuideColor)({
52
+ selected,
53
+ active: hoveredGuideId === guide.id || draggingGuideId === guide.id,
54
+ }),
49
55
  };
50
- }, [isVerticalGuide, guide.show, guide.id, selectedGuideId, hoveredGuideId]);
56
+ }, [
57
+ isVerticalGuide,
58
+ guide.show,
59
+ hoveredGuideId,
60
+ guide.id,
61
+ selected,
62
+ draggingGuideId,
63
+ ]);
64
+ const onPointerDown = (0, react_1.useCallback)((e) => {
65
+ if (e.button !== 0) {
66
+ return;
67
+ }
68
+ e.stopPropagation();
69
+ e.currentTarget.setPointerCapture(e.pointerId);
70
+ hasMovedGuideRef.current = false;
71
+ pointerDownPositionRef.current = {
72
+ guideId: guide.id,
73
+ clientX: e.clientX,
74
+ clientY: e.clientY,
75
+ };
76
+ shouldCreateGuideRef.current = true;
77
+ (0, ForceSpecificCursor_1.forceSpecificCursor)('no-drop');
78
+ setDraggingGuideId(() => guide.id);
79
+ }, [guide.id, setDraggingGuideId, shouldCreateGuideRef]);
51
80
  const onMouseDown = (0, react_1.useCallback)((e) => {
52
- e.preventDefault();
53
81
  if (e.button !== 0) {
54
82
  return;
55
83
  }
84
+ e.preventDefault();
85
+ e.stopPropagation();
86
+ hasMovedGuideRef.current = false;
87
+ pointerDownPositionRef.current = {
88
+ guideId: guide.id,
89
+ clientX: e.clientX,
90
+ clientY: e.clientY,
91
+ };
56
92
  shouldCreateGuideRef.current = true;
57
93
  (0, ForceSpecificCursor_1.forceSpecificCursor)('no-drop');
58
- setSelectedGuideId(() => guide.id);
59
- }, [shouldCreateGuideRef, setSelectedGuideId, guide.id]);
94
+ setDraggingGuideId(() => guide.id);
95
+ }, [guide.id, setDraggingGuideId, shouldCreateGuideRef]);
96
+ const updateHasMovedGuide = (0, react_1.useCallback)((clientX, clientY) => {
97
+ const pointerDownPosition = pointerDownPositionRef.current;
98
+ if (pointerDownPosition === null) {
99
+ return;
100
+ }
101
+ if (!(0, editor_guide_selection_1.isGuidePointerUpAClick)({
102
+ pointerDownPosition,
103
+ guideId: guide.id,
104
+ clientX,
105
+ clientY,
106
+ })) {
107
+ hasMovedGuideRef.current = true;
108
+ }
109
+ }, [guide.id]);
110
+ const onPointerMove = (0, react_1.useCallback)((e) => {
111
+ updateHasMovedGuide(e.clientX, e.clientY);
112
+ }, [updateHasMovedGuide]);
113
+ const onMouseMove = (0, react_1.useCallback)((e) => {
114
+ updateHasMovedGuide(e.clientX, e.clientY);
115
+ }, [updateHasMovedGuide]);
116
+ const onPointerUp = (0, react_1.useCallback)((e) => {
117
+ if (e.currentTarget.hasPointerCapture(e.pointerId)) {
118
+ e.currentTarget.releasePointerCapture(e.pointerId);
119
+ }
120
+ const pointerDownPosition = pointerDownPositionRef.current;
121
+ pointerDownPositionRef.current = null;
122
+ if (shouldDeleteGuideRef.current) {
123
+ return;
124
+ }
125
+ if ((0, editor_guide_selection_1.isGuidePointerUpAClick)({
126
+ pointerDownPosition,
127
+ guideId: guide.id,
128
+ clientX: e.clientX,
129
+ clientY: e.clientY,
130
+ })) {
131
+ onSelect();
132
+ }
133
+ }, [guide.id, onSelect, shouldDeleteGuideRef]);
134
+ const onMouseUp = (0, react_1.useCallback)((e) => {
135
+ const pointerDownPosition = pointerDownPositionRef.current;
136
+ pointerDownPositionRef.current = null;
137
+ if (shouldDeleteGuideRef.current) {
138
+ return;
139
+ }
140
+ if ((0, editor_guide_selection_1.isGuidePointerUpAClick)({
141
+ pointerDownPosition,
142
+ guideId: guide.id,
143
+ clientX: e.clientX,
144
+ clientY: e.clientY,
145
+ })) {
146
+ onSelect();
147
+ }
148
+ }, [guide.id, onSelect, shouldDeleteGuideRef]);
149
+ const onClick = (0, react_1.useCallback)((e) => {
150
+ if (e.button !== 0) {
151
+ return;
152
+ }
153
+ e.preventDefault();
154
+ e.stopPropagation();
155
+ }, []);
156
+ const onPointerCancel = (0, react_1.useCallback)(() => {
157
+ pointerDownPositionRef.current = null;
158
+ }, []);
159
+ const isActive = selected || hoveredGuideId === guide.id;
160
+ const activeClassName = isActive ? '__remotion_editor_guide_selected' : null;
161
+ const guideClassName = (0, react_1.useMemo)(() => {
162
+ return ['__remotion_editor_guide_content', activeClassName]
163
+ .filter(no_react_1.NoReactInternals.truthy)
164
+ .join(' ');
165
+ }, [activeClassName]);
60
166
  const values = (0, react_1.useMemo)(() => {
61
167
  return [
62
168
  {
@@ -66,12 +172,15 @@ const GuideComp = ({ guide, canvasDimensions, scale }) => {
66
172
  leftItem: null,
67
173
  onClick: () => {
68
174
  setGuidesList((prevState) => {
69
- const newGuides = prevState.filter((selected) => {
70
- return selected.id !== guide.id;
175
+ const newGuides = prevState.filter((candidate) => {
176
+ return candidate.id !== guide.id;
71
177
  });
72
178
  (0, editor_guides_1.persistGuidesList)(newGuides);
73
179
  return newGuides;
74
180
  });
181
+ if (selected) {
182
+ clearSelection();
183
+ }
75
184
  },
76
185
  quickSwitcherLabel: null,
77
186
  subMenu: null,
@@ -79,14 +188,7 @@ const GuideComp = ({ guide, canvasDimensions, scale }) => {
79
188
  value: 'remove',
80
189
  },
81
190
  ];
82
- }, [guide.id, setGuidesList]);
83
- return (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { values: values, onOpen: null, children: jsx_runtime_1.jsx("div", { style: guideStyle, onMouseDown: onMouseDown, className: "__remotion_editor_guide", onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, children: jsx_runtime_1.jsx("div", { style: guideContentStyle, className: [
84
- '__remotion_editor_guide_content',
85
- selectedGuideId === guide.id || hoveredGuideId === guide.id
86
- ? '__remotion_editor_guide_selected'
87
- : null,
88
- ]
89
- .filter(no_react_1.NoReactInternals.truthy)
90
- .join(' ') }) }) }));
191
+ }, [clearSelection, guide.id, selected, setGuidesList]);
192
+ return (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { values: values, onOpen: null, children: jsx_runtime_1.jsx("div", { style: guideStyle, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onPointerCancel: onPointerCancel, onMouseDown: onMouseDown, onMouseMove: onMouseMove, onMouseUp: onMouseUp, onClick: onClick, className: "__remotion_editor_guide", [should_clear_selection_on_pointer_down_1.PREVENT_CLEAR_SELECTION_ON_POINTER_DOWN_ATTR]: 'true', onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, children: jsx_runtime_1.jsx("div", { style: guideContentStyle, className: guideClassName }) }) }));
91
193
  };
92
194
  exports.default = (0, react_1.memo)(GuideComp);
@@ -4,26 +4,32 @@ const jsx_runtime_1 = require("react/jsx-runtime");
4
4
  const react_1 = require("react");
5
5
  const remotion_1 = require("remotion");
6
6
  const colors_1 = require("../../helpers/colors");
7
+ const editor_guide_selection_1 = require("../../helpers/editor-guide-selection");
7
8
  const editor_ruler_1 = require("../../helpers/editor-ruler");
8
9
  const editor_guides_1 = require("../../state/editor-guides");
9
10
  const editor_rulers_1 = require("../../state/editor-rulers");
10
11
  const ForceSpecificCursor_1 = require("../ForceSpecificCursor");
12
+ const should_clear_selection_on_pointer_down_1 = require("../Timeline/should-clear-selection-on-pointer-down");
13
+ const TimelineSelection_1 = require("../Timeline/TimelineSelection");
11
14
  const makeGuideId = () => {
12
15
  return Math.random().toString(36).substring(7);
13
16
  };
14
17
  const Ruler = ({ scale, points, originOffset, startMarking, size, markingGaps, orientation, }) => {
15
18
  const rulerCanvasRef = (0, react_1.useRef)(null);
16
19
  const isVerticalRuler = orientation === 'vertical';
17
- const { shouldCreateGuideRef, setGuidesList, selectedGuideId, hoveredGuideId, setSelectedGuideId, guidesList, setEditorShowGuides, } = (0, react_1.useContext)(editor_guides_1.EditorShowGuidesContext);
20
+ const { shouldCreateGuideRef, setGuidesList, draggingGuideId, hoveredGuideId, setDraggingGuideId, guidesList, setEditorShowGuides, } = (0, react_1.useContext)(editor_guides_1.EditorShowGuidesContext);
21
+ const { selectedItems } = (0, TimelineSelection_1.useTimelineSelection)();
18
22
  const unsafeVideoConfig = remotion_1.Internals.useUnsafeVideoConfig();
19
23
  if (!unsafeVideoConfig) {
20
24
  throw new Error('Video config not set');
21
25
  }
22
26
  const [cursor, setCursor] = (0, react_1.useState)(isVerticalRuler ? 'ew-resize' : 'ns-resize');
23
- const selectedOrHoveredGuide = (0, react_1.useMemo)(() => {
24
- var _a, _b;
25
- return ((_b = (_a = guidesList.find((guide) => guide.id === selectedGuideId)) !== null && _a !== void 0 ? _a : guidesList.find((guide) => guide.id === hoveredGuideId)) !== null && _b !== void 0 ? _b : null);
26
- }, [guidesList, hoveredGuideId, selectedGuideId]);
27
+ const guideHighlight = (0, react_1.useMemo)(() => (0, editor_guide_selection_1.getRulerGuideHighlight)({
28
+ guidesList,
29
+ selectedItems,
30
+ hoveredGuideId,
31
+ draggingGuideId,
32
+ }), [draggingGuideId, guidesList, hoveredGuideId, selectedItems]);
27
33
  const rulerWidth = isVerticalRuler ? editor_rulers_1.RULER_WIDTH : size.width - editor_rulers_1.RULER_WIDTH;
28
34
  const rulerHeight = isVerticalRuler ? size.height - editor_rulers_1.RULER_WIDTH : editor_rulers_1.RULER_WIDTH;
29
35
  (0, react_1.useEffect)(() => {
@@ -35,7 +41,7 @@ const Ruler = ({ scale, points, originOffset, startMarking, size, markingGaps, o
35
41
  markingGaps,
36
42
  orientation,
37
43
  rulerCanvasRef,
38
- selectedGuide: selectedOrHoveredGuide,
44
+ guideHighlight,
39
45
  canvasHeight: rulerHeight * window.devicePixelRatio,
40
46
  canvasWidth: rulerWidth * window.devicePixelRatio,
41
47
  });
@@ -46,7 +52,7 @@ const Ruler = ({ scale, points, originOffset, startMarking, size, markingGaps, o
46
52
  originOffset,
47
53
  markingGaps,
48
54
  orientation,
49
- selectedOrHoveredGuide,
55
+ guideHighlight,
50
56
  size,
51
57
  rulerHeight,
52
58
  rulerWidth,
@@ -62,7 +68,7 @@ const Ruler = ({ scale, points, originOffset, startMarking, size, markingGaps, o
62
68
  borderRight: isVerticalRuler ? '1px solid ' + colors_1.RULER_COLOR : undefined,
63
69
  cursor,
64
70
  }), [rulerWidth, rulerHeight, cursor, isVerticalRuler]);
65
- const onMouseDown = (0, react_1.useCallback)((e) => {
71
+ const onPointerDown = (0, react_1.useCallback)((e) => {
66
72
  if (e.button !== 0) {
67
73
  return;
68
74
  }
@@ -73,7 +79,7 @@ const Ruler = ({ scale, points, originOffset, startMarking, size, markingGaps, o
73
79
  (0, ForceSpecificCursor_1.forceSpecificCursor)('no-drop');
74
80
  const guideId = makeGuideId();
75
81
  setEditorShowGuides(() => true);
76
- setSelectedGuideId(() => guideId);
82
+ setDraggingGuideId(() => guideId);
77
83
  setGuidesList((prevState) => {
78
84
  return [
79
85
  ...prevState,
@@ -89,7 +95,7 @@ const Ruler = ({ scale, points, originOffset, startMarking, size, markingGaps, o
89
95
  }, [
90
96
  shouldCreateGuideRef,
91
97
  setEditorShowGuides,
92
- setSelectedGuideId,
98
+ setDraggingGuideId,
93
99
  setGuidesList,
94
100
  orientation,
95
101
  originOffset,
@@ -97,15 +103,15 @@ const Ruler = ({ scale, points, originOffset, startMarking, size, markingGaps, o
97
103
  ]);
98
104
  const changeCursor = (0, react_1.useCallback)((e) => {
99
105
  e.preventDefault();
100
- if (selectedGuideId !== null) {
106
+ if (draggingGuideId !== null) {
101
107
  setCursor('no-drop');
102
108
  }
103
- }, [setCursor, selectedGuideId]);
109
+ }, [setCursor, draggingGuideId]);
104
110
  (0, react_1.useEffect)(() => {
105
- if (selectedGuideId === null) {
111
+ if (draggingGuideId === null) {
106
112
  setCursor(isVerticalRuler ? 'ew-resize' : 'ns-resize');
107
113
  }
108
- }, [selectedGuideId, isVerticalRuler]);
109
- return (jsx_runtime_1.jsx("canvas", { ref: rulerCanvasRef, width: rulerWidth * window.devicePixelRatio, height: rulerHeight * window.devicePixelRatio, style: rulerStyle, onPointerDown: onMouseDown, onPointerEnter: changeCursor, onPointerLeave: changeCursor }));
114
+ }, [draggingGuideId, isVerticalRuler]);
115
+ return (jsx_runtime_1.jsx("canvas", { ref: rulerCanvasRef, width: rulerWidth * window.devicePixelRatio, height: rulerHeight * window.devicePixelRatio, style: rulerStyle, [should_clear_selection_on_pointer_down_1.PREVENT_CLEAR_SELECTION_ON_POINTER_DOWN_ATTR]: 'true', onPointerDown: onPointerDown, onPointerEnter: changeCursor, onPointerLeave: changeCursor }));
110
116
  };
111
117
  exports.default = Ruler;
@@ -12,6 +12,7 @@ const use_studio_canvas_dimensions_1 = require("../../helpers/use-studio-canvas-
12
12
  const editor_guides_1 = require("../../state/editor-guides");
13
13
  const editor_rulers_1 = require("../../state/editor-rulers");
14
14
  const ForceSpecificCursor_1 = require("../ForceSpecificCursor");
15
+ const TimelineSelection_1 = require("../Timeline/TimelineSelection");
15
16
  const Ruler_1 = __importDefault(require("./Ruler"));
16
17
  const originBlockStyles = {
17
18
  position: 'absolute',
@@ -29,7 +30,8 @@ const EditorRulers = ({ contentDimensions, canvasSize, assetMetadata, containerR
29
30
  contentDimensions,
30
31
  assetMetadata,
31
32
  });
32
- const { shouldCreateGuideRef, shouldDeleteGuideRef, setGuidesList, selectedGuideId, setSelectedGuideId, } = (0, react_1.useContext)(editor_guides_1.EditorShowGuidesContext);
33
+ const { shouldCreateGuideRef, shouldDeleteGuideRef, setGuidesList, draggingGuideId, setDraggingGuideId, } = (0, react_1.useContext)(editor_guides_1.EditorShowGuidesContext);
34
+ const { clearSelection, selectedItems } = (0, TimelineSelection_1.useTimelineSelection)();
33
35
  const rulerMarkingGaps = (0, react_1.useMemo)(() => {
34
36
  const minimumGap = editor_rulers_1.MINIMUM_RULER_MARKING_GAP_PX;
35
37
  const predefinedGap = editor_rulers_1.PREDEFINED_RULER_SCALE_GAPS.find((gap) => gap * scale > minimumGap);
@@ -74,7 +76,7 @@ const EditorRulers = ({ contentDimensions, canvasSize, assetMetadata, containerR
74
76
  (0, ForceSpecificCursor_1.forceSpecificCursor)('no-drop');
75
77
  setGuidesList((prevState) => {
76
78
  const newGuides = prevState.map((guide) => {
77
- if (guide.id !== selectedGuideId) {
79
+ if (guide.id !== draggingGuideId) {
78
80
  return guide;
79
81
  }
80
82
  return {
@@ -93,7 +95,7 @@ const EditorRulers = ({ contentDimensions, canvasSize, assetMetadata, containerR
93
95
  setGuidesList((prevState) => {
94
96
  // Intentionally no persist, only persist on mouse up
95
97
  return prevState.map((guide) => {
96
- if (guide.id !== selectedGuideId) {
98
+ if (guide.id !== draggingGuideId) {
97
99
  return guide;
98
100
  }
99
101
  const position = guide.orientation === 'vertical'
@@ -116,7 +118,7 @@ const EditorRulers = ({ contentDimensions, canvasSize, assetMetadata, containerR
116
118
  containerRef,
117
119
  shouldDeleteGuideRef,
118
120
  setGuidesList,
119
- selectedGuideId,
121
+ draggingGuideId,
120
122
  scale,
121
123
  canvasPosition.left,
122
124
  canvasPosition.top,
@@ -127,27 +129,33 @@ const EditorRulers = ({ contentDimensions, canvasSize, assetMetadata, containerR
127
129
  if (!shouldDeleteGuideRef.current) {
128
130
  return true;
129
131
  }
130
- return selected.id !== selectedGuideId;
132
+ return selected.id !== draggingGuideId;
131
133
  });
132
134
  (0, editor_guides_1.persistGuidesList)(newGuides);
133
135
  return newGuides;
134
136
  });
137
+ const deletedGuideWasSelected = selectedItems.some((item) => item.type === 'guide' && item.guideId === draggingGuideId);
138
+ if (shouldDeleteGuideRef.current && deletedGuideWasSelected) {
139
+ clearSelection();
140
+ }
135
141
  shouldDeleteGuideRef.current = false;
136
142
  (0, ForceSpecificCursor_1.stopForcingSpecificCursor)();
137
143
  shouldCreateGuideRef.current = false;
138
- setSelectedGuideId(() => null);
144
+ setDraggingGuideId(() => null);
139
145
  document.removeEventListener('pointerup', onMouseUp);
140
146
  document.removeEventListener('pointermove', onMouseMove);
141
147
  }, [
142
- selectedGuideId,
148
+ clearSelection,
149
+ draggingGuideId,
143
150
  shouldCreateGuideRef,
144
151
  shouldDeleteGuideRef,
145
- setSelectedGuideId,
152
+ setDraggingGuideId,
146
153
  setGuidesList,
147
154
  onMouseMove,
155
+ selectedItems,
148
156
  ]);
149
157
  (0, react_1.useEffect)(() => {
150
- if (selectedGuideId !== null) {
158
+ if (draggingGuideId !== null) {
151
159
  document.addEventListener('pointermove', onMouseMove);
152
160
  document.addEventListener('pointerup', onMouseUp);
153
161
  }
@@ -158,7 +166,7 @@ const EditorRulers = ({ contentDimensions, canvasSize, assetMetadata, containerR
158
166
  cancelAnimationFrame(requestAnimationFrameRef.current);
159
167
  }
160
168
  };
161
- }, [selectedGuideId, onMouseMove, onMouseUp]);
169
+ }, [draggingGuideId, onMouseMove, onMouseUp]);
162
170
  return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
163
171
  jsx_runtime_1.jsx("div", { style: originBlockStyles }), jsx_runtime_1.jsx(Ruler_1.default, { orientation: "horizontal", scale: scale, points: horizontalRulerPoints, startMarking: horizontalRulerStartMarking, markingGaps: rulerMarkingGaps, originOffset: canvasPosition.left, size: canvasSize }), jsx_runtime_1.jsx(Ruler_1.default, { orientation: "vertical", scale: scale, points: verticalRulerPoints, startMarking: verticalRulerStartMarking, markingGaps: rulerMarkingGaps, originOffset: canvasPosition.top, size: canvasSize })
164
172
  ] }));
@@ -40,6 +40,17 @@ const GlobalKeybindings = () => {
40
40
  commandCtrlKey: true,
41
41
  preventDefault: true,
42
42
  });
43
+ const cmdSKey = keybindings.registerKeybinding({
44
+ event: 'keydown',
45
+ key: 's',
46
+ callback: () => {
47
+ (0, NotificationCenter_1.showNotification)('Remotion saves automatically', 2000);
48
+ },
49
+ triggerIfInputFieldFocused: true,
50
+ keepRegisteredWhenNotHighestContext: false,
51
+ commandCtrlKey: true,
52
+ preventDefault: true,
53
+ });
43
54
  const cmdIKey = process.env.ASK_AI_ENABLED
44
55
  ? keybindings.registerKeybinding({
45
56
  event: 'keydown',
@@ -103,6 +114,7 @@ const GlobalKeybindings = () => {
103
114
  cKey.unregister();
104
115
  questionMark.unregister();
105
116
  cmdKKey.unregister();
117
+ cmdSKey.unregister();
106
118
  cmdIKey === null || cmdIKey === void 0 ? void 0 : cmdIKey.unregister();
107
119
  pageDown.unregister();
108
120
  pageUp.unregister();
@@ -138,6 +138,30 @@ const KeyboardShortcutsExplainer = () => {
138
138
  jsx_runtime_1.jsxs("div", { style: left, children: [
139
139
  jsx_runtime_1.jsx("kbd", { style: key, children: ShortcutHint_1.cmdOrCtrlCharacter }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.3 }), jsx_runtime_1.jsx("kbd", { style: key, children: "Y" })
140
140
  ] }), jsx_runtime_1.jsx("div", { style: right, children: "Redo" })
141
+ ] }), jsx_runtime_1.jsx("br", {}), jsx_runtime_1.jsx("div", { style: title, children: "Interactivity" }), jsx_runtime_1.jsxs(layout_1.Row, { align: "center", children: [
142
+ jsx_runtime_1.jsx("div", { style: left, children: jsx_runtime_1.jsx("kbd", { style: key, children: "Shift" }) }), jsx_runtime_1.jsx("div", { style: right, children: "Select range / axis lock drag" })
143
+ ] }), jsx_runtime_1.jsxs(layout_1.Row, { align: "center", children: [
144
+ jsx_runtime_1.jsx("div", { style: left, children: jsx_runtime_1.jsx("kbd", { style: key, children: ShortcutHint_1.cmdOrCtrlCharacter }) }), jsx_runtime_1.jsx("div", { style: right, children: "Toggle selection" })
145
+ ] }), jsx_runtime_1.jsxs(layout_1.Row, { align: "center", children: [
146
+ jsx_runtime_1.jsxs("div", { style: left, children: [
147
+ jsx_runtime_1.jsx("kbd", { style: key, children: ShortcutHint_1.cmdOrCtrlCharacter }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.3 }), jsx_runtime_1.jsx("kbd", { style: key, children: "A" })
148
+ ] }), jsx_runtime_1.jsx("div", { style: right, children: "Select sequence rows" })
149
+ ] }), jsx_runtime_1.jsxs(layout_1.Row, { align: "center", children: [
150
+ jsx_runtime_1.jsxs("div", { style: left, children: [
151
+ jsx_runtime_1.jsx("kbd", { style: key, children: ShortcutHint_1.cmdOrCtrlCharacter }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.3 }), jsx_runtime_1.jsx("kbd", { style: key, children: "D" })
152
+ ] }), jsx_runtime_1.jsx("div", { style: right, children: "Duplicate sequences" })
153
+ ] }), jsx_runtime_1.jsxs(layout_1.Row, { align: "center", children: [
154
+ jsx_runtime_1.jsxs("div", { style: left, children: [
155
+ jsx_runtime_1.jsx("kbd", { style: key, children: ShortcutHint_1.cmdOrCtrlCharacter }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.3 }), jsx_runtime_1.jsx("kbd", { style: key, children: "C" })
156
+ ] }), jsx_runtime_1.jsx("div", { style: right, children: "Copy effects / values" })
157
+ ] }), jsx_runtime_1.jsxs(layout_1.Row, { align: "center", children: [
158
+ jsx_runtime_1.jsxs("div", { style: left, children: [
159
+ jsx_runtime_1.jsx("kbd", { style: key, children: ShortcutHint_1.cmdOrCtrlCharacter }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.3 }), jsx_runtime_1.jsx("kbd", { style: key, children: "V" })
160
+ ] }), jsx_runtime_1.jsx("div", { style: right, children: "Paste effects / values" })
161
+ ] }), jsx_runtime_1.jsxs(layout_1.Row, { align: "center", children: [
162
+ jsx_runtime_1.jsxs("div", { style: left, children: [
163
+ jsx_runtime_1.jsx("kbd", { style: key, children: "Del" }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.3 }), jsx_runtime_1.jsx("kbd", { style: key, children: "\u232B" })
164
+ ] }), jsx_runtime_1.jsx("div", { style: right, children: "Delete / reset selection" })
141
165
  ] }), process.env.ASK_AI_ENABLED && (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
142
166
  jsx_runtime_1.jsx("br", {}), jsx_runtime_1.jsx("div", { style: title, children: "AI" }), jsx_runtime_1.jsxs(layout_1.Row, { align: "center", children: [
143
167
  jsx_runtime_1.jsxs("div", { style: left, children: [
@@ -1,6 +1,11 @@
1
1
  import type { InputHTMLAttributes } from 'react';
2
2
  import React from 'react';
3
3
  import type { RemInputStatus } from './RemInput';
4
+ export declare const deriveInputDraggerStep: ({ min, snapToStep, step, }: {
5
+ readonly min: string | number | undefined;
6
+ readonly snapToStep: boolean;
7
+ readonly step: string | number | undefined;
8
+ }) => string | number;
4
9
  export declare const InputDragger: React.ForwardRefExoticComponent<InputHTMLAttributes<HTMLInputElement> & {
5
10
  readonly onValueChange: (newVal: number) => void;
6
11
  readonly onValueChangeEnd?: ((newVal: number) => void) | undefined;
@@ -10,4 +15,5 @@ export declare const InputDragger: React.ForwardRefExoticComponent<InputHTMLAttr
10
15
  readonly rightAlign: boolean;
11
16
  readonly small?: boolean | undefined;
12
17
  readonly snapToStep?: boolean | undefined;
18
+ readonly dragDecimalPlaces?: number | undefined;
13
19
  } & React.RefAttributes<HTMLButtonElement>>;