@remotion/studio 4.0.477 → 4.0.478

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 (31) hide show
  1. package/dist/components/CanvasIfSizeIsAvailable.js +2 -6
  2. package/dist/components/EditorGuides/Guide.js +37 -9
  3. package/dist/components/EditorRuler/Ruler.js +4 -14
  4. package/dist/components/EditorRuler/index.js +9 -4
  5. package/dist/components/InlineAction.js +1 -0
  6. package/dist/components/MenuToolbar.d.ts +1 -0
  7. package/dist/components/MenuToolbar.js +4 -1
  8. package/dist/components/SelectedOutlineElement.js +85 -14
  9. package/dist/components/SelectedOutlineOverlay.d.ts +1 -1
  10. package/dist/components/SelectedOutlineOverlay.js +5 -1
  11. package/dist/components/Timeline/TimelineSequence.js +7 -15
  12. package/dist/components/Timeline/TimelineVideoInfo.d.ts +1 -0
  13. package/dist/components/Timeline/TimelineVideoInfo.js +93 -8
  14. package/dist/components/Timeline/duplicate-selected-timeline-item.d.ts +7 -0
  15. package/dist/components/Timeline/duplicate-selected-timeline-item.js +32 -3
  16. package/dist/components/Timeline/timeline-video-filmstrip-times.d.ts +17 -0
  17. package/dist/components/Timeline/timeline-video-filmstrip-times.js +22 -0
  18. package/dist/components/import-assets.d.ts +11 -2
  19. package/dist/components/import-assets.js +61 -6
  20. package/dist/components/selected-outline-drag.d.ts +23 -0
  21. package/dist/components/selected-outline-drag.js +49 -1
  22. package/dist/error-overlay/remotion-overlay/Overlay.js +3 -0
  23. package/dist/esm/{chunk-t8fjnw2d.js → chunk-hrw9799x.js} +926 -578
  24. package/dist/esm/internals.mjs +926 -578
  25. package/dist/esm/previewEntry.mjs +10204 -9852
  26. package/dist/esm/renderEntry.mjs +1 -1
  27. package/dist/helpers/editor-guide-selection.js +1 -1
  28. package/dist/helpers/get-preview-file-type.js +1 -1
  29. package/dist/helpers/ruler-canvas-size.d.ts +5 -0
  30. package/dist/helpers/ruler-canvas-size.js +17 -0
  31. package/package.json +11 -11
@@ -8,6 +8,7 @@ const remotion_1 = require("remotion");
8
8
  const timeline_layout_1 = require("../../helpers/timeline-layout");
9
9
  const AudioWaveform_1 = require("../AudioWaveform");
10
10
  const get_timeline_video_info_widths_1 = require("./get-timeline-video-info-widths");
11
+ const timeline_video_filmstrip_times_1 = require("./timeline-video-filmstrip-times");
11
12
  const outerStyle = {
12
13
  width: '100%',
13
14
  height: '100%',
@@ -22,7 +23,8 @@ const filmstripContainerStyle = {
22
23
  fontSize: 10,
23
24
  fontFamily: 'Arial, Helvetica',
24
25
  };
25
- const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore, durationInFrames, playbackRate, volume, doesVolumeChange, premountWidth, postmountWidth, loopDisplay, }) => {
26
+ const MAX_FROZEN_FRAME_CACHE_DEVIATION = timeline_utils_1.WEBCODECS_TIMESCALE * 0.05;
27
+ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore, durationInFrames, playbackRate, volume, doesVolumeChange, premountWidth, postmountWidth, loopDisplay, frozenMediaFrame, }) => {
26
28
  const { fps } = (0, remotion_1.useVideoConfig)();
27
29
  const ref = (0, react_1.useRef)(null);
28
30
  const [error, setError] = (0, react_1.useState)(null);
@@ -53,6 +55,94 @@ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore,
53
55
  return;
54
56
  }
55
57
  current.appendChild(canvas);
58
+ const drawRepeatedFrame = (frame) => {
59
+ const thumbnailWidth = Math.max(1, frame.displayWidth / window.devicePixelRatio);
60
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
61
+ for (let x = 0; x < canvas.width; x += thumbnailWidth) {
62
+ ctx.drawImage(frame, x, 0, thumbnailWidth, timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT);
63
+ }
64
+ };
65
+ const getCachedFrozenFrame = (timestamp) => {
66
+ const prefix = (0, timeline_utils_1.getFrameDatabaseKeyPrefix)(src);
67
+ const keys = Array.from(timeline_utils_1.frameDatabase.keys()).filter((k) => k.startsWith(prefix));
68
+ let bestDistance = Infinity;
69
+ let bestFrame = null;
70
+ for (const key of keys) {
71
+ const frame = timeline_utils_1.frameDatabase.get(key);
72
+ if (!frame) {
73
+ continue;
74
+ }
75
+ const distance = Math.abs((0, timeline_utils_1.getTimestampFromFrameDatabaseKey)(key) - timestamp);
76
+ if (distance < bestDistance) {
77
+ bestDistance = distance;
78
+ bestFrame = frame.frame;
79
+ }
80
+ }
81
+ return bestDistance <= MAX_FROZEN_FRAME_CACHE_DEVIATION
82
+ ? bestFrame
83
+ : null;
84
+ };
85
+ const times = (0, timeline_video_filmstrip_times_1.getTimelineVideoFilmstripTimes)({
86
+ trimBefore,
87
+ durationInFrames,
88
+ playbackRate,
89
+ fps,
90
+ loopDisplay,
91
+ frozenMediaFrame,
92
+ });
93
+ if (times.type === 'frozen') {
94
+ const timestamp = times.timestampInSeconds * timeline_utils_1.WEBCODECS_TIMESCALE;
95
+ const cachedFrame = getCachedFrozenFrame(timestamp);
96
+ if (cachedFrame) {
97
+ drawRepeatedFrame(cachedFrame);
98
+ return () => {
99
+ current.removeChild(canvas);
100
+ };
101
+ }
102
+ (0, timeline_utils_1.extractFrames)({
103
+ timestampsInSeconds: ({ track, }) => {
104
+ aspectRatio.current = track.width / track.height;
105
+ timeline_utils_1.aspectRatioCache.set(src, aspectRatio.current);
106
+ return [times.timestampInSeconds];
107
+ },
108
+ src,
109
+ onVideoSample: (sample) => {
110
+ let frame;
111
+ try {
112
+ frame = sample.toVideoFrame();
113
+ const scale = (timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT / frame.displayHeight) *
114
+ window.devicePixelRatio;
115
+ const transformed = (0, timeline_utils_1.resizeVideoFrame)({
116
+ frame,
117
+ scale,
118
+ });
119
+ if (transformed !== frame) {
120
+ frame.close();
121
+ }
122
+ frame = undefined;
123
+ const databaseKey = (0, timeline_utils_1.makeFrameDatabaseKey)(src, transformed.timestamp);
124
+ (0, timeline_utils_1.addFrameToCache)(databaseKey, transformed);
125
+ drawRepeatedFrame(transformed);
126
+ }
127
+ catch (e) {
128
+ if (frame) {
129
+ frame.close();
130
+ }
131
+ throw e;
132
+ }
133
+ finally {
134
+ sample.close();
135
+ }
136
+ },
137
+ signal: controller.signal,
138
+ }).catch((e) => {
139
+ setError(e);
140
+ });
141
+ return () => {
142
+ controller.abort();
143
+ current.removeChild(canvas);
144
+ };
145
+ }
56
146
  const loopWidth = (0, timeline_utils_1.getLoopDisplayWidth)({
57
147
  visualizationWidth: mediaNaturalWidth,
58
148
  loopDisplay,
@@ -85,13 +175,7 @@ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore,
85
175
  };
86
176
  // desired-timestamp -> filled-timestamp
87
177
  const filledSlots = new Map();
88
- const fromSeconds = trimBefore / fps;
89
- const visibleDurationInFrames = shouldRepeatVideo && loopDisplay
90
- ? loopDisplay.durationInFrames
91
- : durationInFrames;
92
- // Trim is applied first, then playbackRate. Each composition frame
93
- // advances the source video by `playbackRate` source frames.
94
- const toSeconds = fromSeconds + (visibleDurationInFrames * playbackRate) / fps;
178
+ const { fromSeconds, toSeconds } = times;
95
179
  const targetWidth = shouldRepeatVideo
96
180
  ? targetCanvas.width
97
181
  : mediaNaturalWidth;
@@ -216,6 +300,7 @@ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore,
216
300
  durationInFrames,
217
301
  error,
218
302
  fps,
303
+ frozenMediaFrame,
219
304
  loopDisplay,
220
305
  mediaNaturalWidth,
221
306
  mediaVisualizationWidth,
@@ -8,6 +8,13 @@ export declare const isDuplicatableSequenceRowSelection: (selection: TimelineSel
8
8
  } & {
9
9
  type: "sequence";
10
10
  };
11
+ export declare const isDuplicatableEffectSelection: (selection: TimelineSelection) => selection is {
12
+ readonly type: "sequence-effect";
13
+ readonly nodePathInfo: SequenceNodePathInfo;
14
+ readonly i: number;
15
+ } & {
16
+ type: "sequence-effect";
17
+ };
11
18
  export declare const duplicateSelectedTimelineItems: ({ selections, confirm, }: {
12
19
  selections: readonly TimelineSelection[];
13
20
  confirm: ConfirmationDialogFunction;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.duplicateSelectedTimelineItems = exports.isDuplicatableSequenceRowSelection = exports.duplicateSequencesFromSource = void 0;
3
+ exports.duplicateSelectedTimelineItems = exports.isDuplicatableEffectSelection = exports.isDuplicatableSequenceRowSelection = exports.duplicateSequencesFromSource = void 0;
4
4
  const call_api_1 = require("../call-api");
5
5
  const NotificationCenter_1 = require("../Notifications/NotificationCenter");
6
6
  const confirmDuplicatingProgrammaticallyDuplicatedSequences = (nodePathInfos, confirm) => {
@@ -61,11 +61,40 @@ const duplicateSequencesFromSource = (nodePathInfos, confirm) => {
61
61
  exports.duplicateSequencesFromSource = duplicateSequencesFromSource;
62
62
  const isDuplicatableSequenceRowSelection = (selection) => selection.type === 'sequence';
63
63
  exports.isDuplicatableSequenceRowSelection = isDuplicatableSequenceRowSelection;
64
+ const isDuplicatableEffectSelection = (selection) => selection.type === 'sequence-effect';
65
+ exports.isDuplicatableEffectSelection = isDuplicatableEffectSelection;
66
+ const duplicateEffectsFromSource = (effects) => {
67
+ return (0, call_api_1.callApi)('/api/duplicate-effect', effects.map((effect) => {
68
+ const nodePath = effect.nodePathInfo.sequenceSubscriptionKey;
69
+ return {
70
+ fileName: nodePath.absolutePath,
71
+ sequenceNodePath: nodePath,
72
+ effectIndex: effect.i,
73
+ };
74
+ }))
75
+ .then((result) => {
76
+ if (result.success) {
77
+ (0, NotificationCenter_1.showNotification)(effects.length === 1
78
+ ? 'Duplicated effect in source file'
79
+ : 'Duplicated effects in source files', 2000);
80
+ }
81
+ else {
82
+ (0, NotificationCenter_1.showNotification)(result.reason, 4000);
83
+ }
84
+ })
85
+ .catch((err) => {
86
+ (0, NotificationCenter_1.showNotification)(err.message, 4000);
87
+ });
88
+ };
64
89
  const duplicateSelectedTimelineItems = ({ selections, confirm, }) => {
65
90
  const sequenceSelections = selections.filter(exports.isDuplicatableSequenceRowSelection);
66
- if (sequenceSelections.length === 0) {
91
+ if (sequenceSelections.length > 0) {
92
+ return (0, exports.duplicateSequencesFromSource)(sequenceSelections.map((selection) => selection.nodePathInfo), confirm);
93
+ }
94
+ const effectSelections = selections.filter(exports.isDuplicatableEffectSelection);
95
+ if (effectSelections.length === 0) {
67
96
  return null;
68
97
  }
69
- return (0, exports.duplicateSequencesFromSource)(sequenceSelections.map((selection) => selection.nodePathInfo), confirm);
98
+ return duplicateEffectsFromSource(effectSelections);
70
99
  };
71
100
  exports.duplicateSelectedTimelineItems = duplicateSelectedTimelineItems;
@@ -0,0 +1,17 @@
1
+ import type { LoopDisplay } from 'remotion';
2
+ export type TimelineVideoFilmstripTimes = {
3
+ type: 'frozen';
4
+ timestampInSeconds: number;
5
+ } | {
6
+ type: 'range';
7
+ fromSeconds: number;
8
+ toSeconds: number;
9
+ };
10
+ export declare const getTimelineVideoFilmstripTimes: ({ trimBefore, durationInFrames, playbackRate, fps, loopDisplay, frozenMediaFrame, }: {
11
+ trimBefore: number;
12
+ durationInFrames: number;
13
+ playbackRate: number;
14
+ fps: number;
15
+ loopDisplay: LoopDisplay | undefined;
16
+ frozenMediaFrame: number | null;
17
+ }) => TimelineVideoFilmstripTimes;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTimelineVideoFilmstripTimes = void 0;
4
+ const timeline_utils_1 = require("@remotion/timeline-utils");
5
+ const getTimelineVideoFilmstripTimes = ({ trimBefore, durationInFrames, playbackRate, fps, loopDisplay, frozenMediaFrame, }) => {
6
+ if (frozenMediaFrame !== null) {
7
+ return {
8
+ type: 'frozen',
9
+ timestampInSeconds: Math.max(0, frozenMediaFrame / fps),
10
+ };
11
+ }
12
+ const fromSeconds = trimBefore / fps;
13
+ const visibleDurationInFrames = (0, timeline_utils_1.shouldTileLoopDisplay)(loopDisplay) && loopDisplay
14
+ ? loopDisplay.durationInFrames
15
+ : durationInFrames;
16
+ return {
17
+ type: 'range',
18
+ fromSeconds,
19
+ toSeconds: fromSeconds + (visibleDurationInFrames * playbackRate) / fps,
20
+ };
21
+ };
22
+ exports.getTimelineVideoFilmstripTimes = getTimelineVideoFilmstripTimes;
@@ -1,4 +1,5 @@
1
1
  import { type ComponentProp, type FileType, type InsertableCompositionElementPosition } from '@remotion/studio-shared';
2
+ import type { Dimensions } from '../helpers/is-current-selected-still';
2
3
  export type InsertElementDropPosition = {
3
4
  readonly centerX: number;
4
5
  readonly centerY: number;
@@ -8,7 +9,7 @@ export declare const getAssetElement: ({ fileType, src, }: {
8
9
  src: string;
9
10
  }) => {
10
11
  type: "asset";
11
- assetType: "audio" | "gif" | "image" | "video";
12
+ assetType: "animated-image" | "audio" | "gif" | "image" | "video";
12
13
  src: string;
13
14
  srcType: "remote" | "static";
14
15
  dimensions: {
@@ -19,7 +20,7 @@ export declare const getAssetElement: ({ fileType, src, }: {
19
20
  } | null;
20
21
  export declare const getAssetElementFromPath: (assetPath: string) => {
21
22
  type: "asset";
22
- assetType: "audio" | "gif" | "image" | "video";
23
+ assetType: "animated-image" | "audio" | "gif" | "image" | "video";
23
24
  src: string;
24
25
  srcType: "remote" | "static";
25
26
  dimensions: {
@@ -28,6 +29,13 @@ export declare const getAssetElementFromPath: (assetPath: string) => {
28
29
  } | null;
29
30
  position: InsertableCompositionElementPosition | null;
30
31
  } | null;
32
+ export declare const getComponentDimensions: (component: {
33
+ componentName: string;
34
+ dimensions?: import("@remotion/studio-shared").ComponentDimensions | undefined;
35
+ importName: string;
36
+ importPath: string;
37
+ props: ComponentProp[];
38
+ }) => Dimensions | null;
31
39
  export declare const pickFilesToImport: () => Promise<File[]>;
32
40
  export declare const importAssets: ({ compositionFile, compositionId, dropPosition, files, }: {
33
41
  compositionFile: string;
@@ -55,6 +63,7 @@ export declare const insertExistingAssets: ({ assetPaths, compositionFile, compo
55
63
  export declare const insertComponent: ({ component, compositionFile, compositionId, dropPosition, }: {
56
64
  component: {
57
65
  componentName: string;
66
+ dimensions?: import("@remotion/studio-shared").ComponentDimensions | undefined;
58
67
  importName: string;
59
68
  importPath: string;
60
69
  props: ComponentProp[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.insertComponent = exports.insertExistingAssets = exports.insertRemoteAudio = exports.importRemoteAsset = exports.importAssets = exports.pickFilesToImport = exports.getAssetElementFromPath = exports.getAssetElement = void 0;
3
+ exports.insertComponent = exports.insertExistingAssets = exports.insertRemoteAudio = exports.importRemoteAsset = exports.importAssets = exports.pickFilesToImport = exports.getComponentDimensions = exports.getAssetElementFromPath = exports.getAssetElement = void 0;
4
4
  const media_utils_1 = require("@remotion/media-utils");
5
5
  const studio_shared_1 = require("@remotion/studio-shared");
6
6
  const remotion_1 = require("remotion");
@@ -23,6 +23,16 @@ const getAssetElement = ({ fileType, src, }) => {
23
23
  position: null,
24
24
  };
25
25
  }
26
+ if (fileType.type === 'apng') {
27
+ return {
28
+ type: 'asset',
29
+ assetType: 'animated-image',
30
+ src,
31
+ srcType: 'static',
32
+ dimensions: fileType.dimensions,
33
+ position: null,
34
+ };
35
+ }
26
36
  if (fileType.type === 'gif') {
27
37
  return {
28
38
  type: 'asset',
@@ -81,6 +91,16 @@ const getAssetElementFromPath = (assetPath) => {
81
91
  position: null,
82
92
  };
83
93
  }
94
+ if (extension === 'apng') {
95
+ return {
96
+ type: 'asset',
97
+ assetType: 'animated-image',
98
+ src: assetPath,
99
+ srcType: 'static',
100
+ dimensions: null,
101
+ position: null,
102
+ };
103
+ }
84
104
  if (extension === 'gif') {
85
105
  return {
86
106
  type: 'asset',
@@ -127,6 +147,9 @@ const getAssetLabel = (element) => {
127
147
  if (element.assetType === 'gif') {
128
148
  return '<Gif>';
129
149
  }
150
+ if (element.assetType === 'animated-image') {
151
+ return '<AnimatedImage>';
152
+ }
130
153
  if (element.assetType === 'audio') {
131
154
  return '<Audio>';
132
155
  }
@@ -155,6 +178,9 @@ const getComponentPropNumber = (props, name) => {
155
178
  return typeof (prop === null || prop === void 0 ? void 0 : prop.value) === 'number' ? prop.value : null;
156
179
  };
157
180
  const getComponentDimensions = (component) => {
181
+ if (component.dimensions) {
182
+ return component.dimensions;
183
+ }
158
184
  const width = getComponentPropNumber(component.props, 'width');
159
185
  const height = getComponentPropNumber(component.props, 'height');
160
186
  if (width !== null && height !== null) {
@@ -166,6 +192,7 @@ const getComponentDimensions = (component) => {
166
192
  }
167
193
  return null;
168
194
  };
195
+ exports.getComponentDimensions = getComponentDimensions;
169
196
  const getImageDimensions = ({ revokeObjectUrl, src, }) => {
170
197
  return new Promise((resolve, reject) => {
171
198
  const image = new Image();
@@ -199,6 +226,7 @@ const getFileDimensions = async ({ file, fileType, }) => {
199
226
  fileType.type === 'jpeg' ||
200
227
  fileType.type === 'webp' ||
201
228
  fileType.type === 'bmp' ||
229
+ fileType.type === 'apng' ||
202
230
  fileType.type === 'gif') {
203
231
  if (fileType.dimensions) {
204
232
  return fileType.dimensions;
@@ -225,7 +253,7 @@ const getStaticAssetDimensions = (assetPath) => {
225
253
  const extension = (_a = assetPath.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase();
226
254
  const src = (0, remotion_1.staticFile)(assetPath);
227
255
  if (extension &&
228
- ['png', 'jpg', 'jpeg', 'webp', 'bmp', 'gif'].includes(extension)) {
256
+ ['png', 'jpg', 'jpeg', 'webp', 'bmp', 'gif', 'apng'].includes(extension)) {
229
257
  return getImageDimensions({ revokeObjectUrl: false, src });
230
258
  }
231
259
  if (extension &&
@@ -250,6 +278,33 @@ const getStaticAssetDimensionsOrNull = async (assetPath) => {
250
278
  return null;
251
279
  }
252
280
  };
281
+ const getStaticAssetFileType = async (assetPath) => {
282
+ var _a;
283
+ const extension = (_a = assetPath.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase();
284
+ if (extension !== 'png' && extension !== 'apng') {
285
+ return null;
286
+ }
287
+ try {
288
+ const response = await fetch((0, remotion_1.staticFile)(assetPath));
289
+ if (!response.ok) {
290
+ return null;
291
+ }
292
+ return (0, studio_shared_1.detectFileType)(new Uint8Array(await response.arrayBuffer()));
293
+ }
294
+ catch (_b) {
295
+ return null;
296
+ }
297
+ };
298
+ const getAssetElementFromStaticAsset = async (assetPath) => {
299
+ const fileType = await getStaticAssetFileType(assetPath);
300
+ if (fileType) {
301
+ const element = (0, exports.getAssetElement)({ fileType, src: assetPath });
302
+ if (element) {
303
+ return element;
304
+ }
305
+ }
306
+ return (0, exports.getAssetElementFromPath)(assetPath);
307
+ };
253
308
  const pickFilesToImport = () => {
254
309
  return new Promise((resolve) => {
255
310
  const input = document.createElement('input');
@@ -456,18 +511,18 @@ const insertExistingAssets = async ({ assetPaths, compositionFile, compositionId
456
511
  const unsupportedFiles = [];
457
512
  try {
458
513
  for (const assetPath of assetPaths) {
459
- const element = (0, exports.getAssetElementFromPath)(assetPath);
514
+ const element = await getAssetElementFromStaticAsset(assetPath);
460
515
  if (element === null) {
461
516
  unsupportedFiles.push(assetPath);
462
517
  continue;
463
518
  }
464
- const dimensions = await getStaticAssetDimensionsOrNull(assetPath);
519
+ const dimensions = (_a = element.dimensions) !== null && _a !== void 0 ? _a : (await getStaticAssetDimensionsOrNull(assetPath));
465
520
  const inserted = await insertAssetElement({
466
521
  compositionFile,
467
522
  compositionId,
468
523
  element: {
469
524
  ...element,
470
- dimensions: (_a = element.dimensions) !== null && _a !== void 0 ? _a : dimensions,
525
+ dimensions,
471
526
  position: getCenteredPosition({
472
527
  dimensions,
473
528
  dropPosition,
@@ -499,7 +554,7 @@ const insertComponent = async ({ component, compositionFile, compositionId, drop
499
554
  importPath: component.importPath,
500
555
  props: component.props,
501
556
  position: getCenteredPosition({
502
- dimensions: getComponentDimensions(component),
557
+ dimensions: (0, exports.getComponentDimensions)(component),
503
558
  dropPosition,
504
559
  }),
505
560
  },
@@ -1,6 +1,7 @@
1
1
  import type { GetDragOverrides, SequencePropsSubscriptionKey, SequenceSchema } from 'remotion';
2
2
  import { type OutlinePoint } from './selected-outline-geometry';
3
3
  import type { SelectedOutlineDragState, SelectedOutlineRotationDragState, SelectedOutlineRotationDragTarget, SelectedOutlineScaleDragState, SelectedOutlineScaleDragTarget, SelectedOutlineDragTarget } from './selected-outline-types';
4
+ import { type UvCoordinate } from './selected-outline-uv';
4
5
  import type { SaveSequencePropChange } from './Timeline/save-sequence-prop';
5
6
  export declare const getSelectedOutlineDragStates: ({ dragTargets, getDragOverrides, timelinePosition, }: {
6
7
  readonly dragTargets: readonly SelectedOutlineDragTarget[];
@@ -114,4 +115,26 @@ export declare const compensateTranslateForTransformOrigin: ({ startTranslate, d
114
115
  readonly scale: readonly [number, number];
115
116
  }) => readonly [number, number];
116
117
  export declare const uvsEqual: (left: readonly [number, number], right: readonly [number, number]) => boolean;
118
+ export type SelectedOutlineTransformOriginLockedAxis = 'x' | 'y' | null;
119
+ export declare const getSelectedOutlineTransformOriginLockedAxis: ({ axisLocked, dimensions, startUv, uv, }: {
120
+ readonly axisLocked: boolean;
121
+ readonly dimensions: {
122
+ readonly width: number;
123
+ readonly height: number;
124
+ };
125
+ readonly startUv: UvCoordinate;
126
+ readonly uv: UvCoordinate;
127
+ }) => SelectedOutlineTransformOriginLockedAxis;
128
+ export declare const applySelectedOutlineTransformOriginAxisLock: ({ lockedAxis, startUv, uv, }: {
129
+ readonly lockedAxis: SelectedOutlineTransformOriginLockedAxis;
130
+ readonly startUv: UvCoordinate;
131
+ readonly uv: UvCoordinate;
132
+ }) => UvCoordinate;
133
+ export declare const selectedOutlineTransformOriginSnapThresholdPx = 10;
134
+ export declare const snapSelectedOutlineTransformOriginUv: ({ point, points, thresholdPx, uv, }: {
135
+ readonly point: OutlinePoint;
136
+ readonly points: readonly [OutlinePoint, OutlinePoint, OutlinePoint, OutlinePoint];
137
+ readonly thresholdPx?: number | undefined;
138
+ readonly uv: UvCoordinate;
139
+ }) => UvCoordinate;
117
140
  export {};
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.uvsEqual = exports.compensateTranslateForTransformOrigin = exports.parseCssRotationToRadians = exports.clearSelectedOutlineRotationDragOverrides = exports.clearSelectedOutlineScaleDragOverrides = exports.getSelectedOutlineKeyboardNudgeDirection = exports.clearSelectedOutlineDragOverrides = exports.getSelectedOutlineRotationDragChanges = exports.getSelectedOutlineRotationDragValues = exports.getSelectedOutlineRotationDragStates = exports.getSelectedOutlineScaleDragChanges = exports.getSelectedOutlineScaleDragValues = exports.getSelectedOutlineScaleDragStates = exports.getSelectedOutlineScaleEdgeInfo = exports.getSelectedOutlineKeyboardNudgeDeltas = exports.getSelectedOutlineKeyboardNudgeDelta = exports.getSelectedOutlineDragChanges = exports.isSelectedOutlineDragPastThreshold = exports.applySelectedOutlineDragAxisLock = exports.getSelectedOutlineDragValues = exports.getSelectedOutlineDragStates = void 0;
3
+ exports.snapSelectedOutlineTransformOriginUv = exports.selectedOutlineTransformOriginSnapThresholdPx = exports.applySelectedOutlineTransformOriginAxisLock = exports.getSelectedOutlineTransformOriginLockedAxis = exports.uvsEqual = exports.compensateTranslateForTransformOrigin = exports.parseCssRotationToRadians = exports.clearSelectedOutlineRotationDragOverrides = exports.clearSelectedOutlineScaleDragOverrides = exports.getSelectedOutlineKeyboardNudgeDirection = exports.clearSelectedOutlineDragOverrides = exports.getSelectedOutlineRotationDragChanges = exports.getSelectedOutlineRotationDragValues = exports.getSelectedOutlineRotationDragStates = exports.getSelectedOutlineScaleDragChanges = exports.getSelectedOutlineScaleDragValues = exports.getSelectedOutlineScaleDragStates = exports.getSelectedOutlineScaleEdgeInfo = exports.getSelectedOutlineKeyboardNudgeDeltas = exports.getSelectedOutlineKeyboardNudgeDelta = exports.getSelectedOutlineDragChanges = exports.isSelectedOutlineDragPastThreshold = exports.applySelectedOutlineDragAxisLock = exports.getSelectedOutlineDragValues = exports.getSelectedOutlineDragStates = void 0;
4
4
  const remotion_1 = require("remotion");
5
5
  const no_react_1 = require("remotion/no-react");
6
6
  const selected_outline_geometry_1 = require("./selected-outline-geometry");
7
7
  const selected_outline_measurement_1 = require("./selected-outline-measurement");
8
8
  const selected_outline_types_1 = require("./selected-outline-types");
9
+ const selected_outline_uv_1 = require("./selected-outline-uv");
9
10
  const timeline_field_utils_1 = require("./Timeline/timeline-field-utils");
10
11
  const timeline_rotation_utils_1 = require("./Timeline/timeline-rotation-utils");
11
12
  const timeline_translate_utils_1 = require("./Timeline/timeline-translate-utils");
@@ -425,3 +426,50 @@ exports.compensateTranslateForTransformOrigin = compensateTranslateForTransformO
425
426
  const uvsEqual = (left, right) => Math.abs(left[0] - right[0]) < 0.000001 &&
426
427
  Math.abs(left[1] - right[1]) < 0.000001;
427
428
  exports.uvsEqual = uvsEqual;
429
+ const getSelectedOutlineTransformOriginLockedAxis = ({ axisLocked, dimensions, startUv, uv, }) => {
430
+ if (!axisLocked) {
431
+ return null;
432
+ }
433
+ const deltaX = (uv[0] - startUv[0]) * dimensions.width;
434
+ const deltaY = (uv[1] - startUv[1]) * dimensions.height;
435
+ return Math.abs(deltaX) >= Math.abs(deltaY) ? 'x' : 'y';
436
+ };
437
+ exports.getSelectedOutlineTransformOriginLockedAxis = getSelectedOutlineTransformOriginLockedAxis;
438
+ const applySelectedOutlineTransformOriginAxisLock = ({ lockedAxis, startUv, uv, }) => {
439
+ if (lockedAxis === 'x') {
440
+ return [uv[0], startUv[1]];
441
+ }
442
+ if (lockedAxis === 'y') {
443
+ return [startUv[0], uv[1]];
444
+ }
445
+ return uv;
446
+ };
447
+ exports.applySelectedOutlineTransformOriginAxisLock = applySelectedOutlineTransformOriginAxisLock;
448
+ const transformOriginSnapTargets = [
449
+ [0, 0],
450
+ [0.5, 0],
451
+ [1, 0],
452
+ [1, 0.5],
453
+ [1, 1],
454
+ [0.5, 1],
455
+ [0, 1],
456
+ [0, 0.5],
457
+ [0.5, 0.5],
458
+ ];
459
+ exports.selectedOutlineTransformOriginSnapThresholdPx = 10;
460
+ const snapSelectedOutlineTransformOriginUv = ({ point, points, thresholdPx = exports.selectedOutlineTransformOriginSnapThresholdPx, uv, }) => {
461
+ var _a;
462
+ let best = null;
463
+ for (const snapUv of transformOriginSnapTargets) {
464
+ const snapPoint = (0, selected_outline_uv_1.getUvHandlePosition)(points, snapUv);
465
+ const distance = Math.hypot(point.x - snapPoint.x, point.y - snapPoint.y);
466
+ if (distance > thresholdPx) {
467
+ continue;
468
+ }
469
+ if (best === null || distance < best.distance) {
470
+ best = { distance, uv: snapUv };
471
+ }
472
+ }
473
+ return (_a = best === null || best === void 0 ? void 0 : best.uv) !== null && _a !== void 0 ? _a : uv;
474
+ };
475
+ exports.snapSelectedOutlineTransformOriginUv = snapSelectedOutlineTransformOriginUv;
@@ -4,6 +4,7 @@ exports.Overlay = exports.setErrorsRef = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const remotion_1 = require("remotion");
7
+ const MenuToolbar_1 = require("../../components/MenuToolbar");
7
8
  const keybindings_1 = require("../../state/keybindings");
8
9
  const ErrorLoader_1 = require("./ErrorLoader");
9
10
  exports.setErrorsRef = (0, react_1.createRef)();
@@ -43,6 +44,8 @@ const Overlay = () => {
43
44
  backgroundColor: BACKGROUND_COLOR,
44
45
  overflow: 'auto',
45
46
  color: 'white',
47
+ top: MenuToolbar_1.MENU_TOOLBAR_HEIGHT,
48
+ height: `calc(100% - ${MenuToolbar_1.MENU_TOOLBAR_HEIGHT}px)`,
46
49
  }, children: errors.errors.map((err, i) => {
47
50
  var _a;
48
51
  return (jsx_runtime_1.jsx(ErrorLoader_1.ErrorLoader