@remotion/studio 4.0.509 → 4.0.510

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 (48) hide show
  1. package/dist/components/Canvas.js +6 -3
  2. package/dist/components/CompositionSelectorItem.js +1 -1
  3. package/dist/components/InspectorOpenInEditor.js +11 -0
  4. package/dist/components/InspectorPanel/CompositionMetadata.js +23 -1
  5. package/dist/components/QuickSwitcher/asset-search.js +1 -0
  6. package/dist/components/SelectedOutlineCropControls.js +1 -1
  7. package/dist/components/SelectedOutlinePolygon.js +2 -1
  8. package/dist/components/Timeline/EasingEditorModal.d.ts +8 -0
  9. package/dist/components/Timeline/EasingEditorModal.js +8 -9
  10. package/dist/components/Timeline/TimelineColorField.js +1 -6
  11. package/dist/components/Timeline/TimelineEffectItem.js +1 -1
  12. package/dist/components/Timeline/TimelineEffectPropItem.js +1 -1
  13. package/dist/components/Timeline/TimelineEnumField.js +1 -4
  14. package/dist/components/Timeline/TimelineExpandedRow.js +2 -2
  15. package/dist/components/Timeline/TimelineFieldRowContent.d.ts +2 -1
  16. package/dist/components/Timeline/TimelineFieldRowContent.js +18 -3
  17. package/dist/components/Timeline/TimelineFontFamilyField.js +1 -2
  18. package/dist/components/Timeline/TimelineKeyframeEasingLine.js +3 -2
  19. package/dist/components/Timeline/TimelineNumberField.js +4 -2
  20. package/dist/components/Timeline/TimelineRotationField.js +3 -3
  21. package/dist/components/Timeline/TimelineRowChrome.d.ts +5 -0
  22. package/dist/components/Timeline/TimelineRowChrome.js +14 -9
  23. package/dist/components/Timeline/TimelineRowLayoutContext.d.ts +1 -0
  24. package/dist/components/Timeline/TimelineRowLayoutContext.js +4 -1
  25. package/dist/components/Timeline/TimelineScaleField.js +4 -0
  26. package/dist/components/Timeline/TimelineSequenceItem.js +1 -1
  27. package/dist/components/Timeline/TimelineSequencePropItem.js +1 -1
  28. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.d.ts +2 -0
  29. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.js +63 -14
  30. package/dist/components/Timeline/TimelineTextContentField.js +1 -1
  31. package/dist/components/Timeline/get-sequence-context-menu-items.js +1 -0
  32. package/dist/components/Timeline/split-selected-timeline-item.d.ts +0 -1
  33. package/dist/components/Timeline/split-selected-timeline-item.js +5 -13
  34. package/dist/components/Timeline/timeline-field-row-layout.js +2 -2
  35. package/dist/components/Timeline/timeline-field-utils.d.ts +1 -0
  36. package/dist/components/Timeline/timeline-field-utils.js +6 -1
  37. package/dist/components/UndoRedoButtons.js +5 -3
  38. package/dist/components/get-open-in-menu-items.d.ts +2 -1
  39. package/dist/components/get-open-in-menu-items.js +28 -3
  40. package/dist/esm/{chunk-jdn6nkbk.js → chunk-axbagh08.js} +3922 -3730
  41. package/dist/esm/internals.mjs +3922 -3730
  42. package/dist/esm/previewEntry.mjs +3808 -3616
  43. package/dist/esm/renderEntry.mjs +1 -1
  44. package/dist/helpers/open-in-editor.d.ts +1 -0
  45. package/dist/helpers/open-in-editor.js +5 -1
  46. package/dist/icons/git-client.d.ts +6 -0
  47. package/dist/icons/git-client.js +8 -0
  48. package/package.json +12 -12
@@ -723,7 +723,7 @@ const Canvas = ({ canvasContent, size }) => {
723
723
  return;
724
724
  }
725
725
  const metadata = studio_protocol_1.StudioProtocolInternals.getDragPreviewMetadata((_b = (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.types) !== null && _b !== void 0 ? _b : []);
726
- if ((metadata === null || metadata === void 0 ? void 0 : metadata.type) !== 'composition' ||
726
+ if (((metadata === null || metadata === void 0 ? void 0 : metadata.type) !== 'composition' && (metadata === null || metadata === void 0 ? void 0 : metadata.type) !== 'element') ||
727
727
  metadata.width === undefined ||
728
728
  metadata.height === undefined) {
729
729
  setCompositionDropPreview(null);
@@ -734,7 +734,7 @@ const Canvas = ({ canvasContent, size }) => {
734
734
  clientX: event.clientX,
735
735
  clientY: event.clientY,
736
736
  contentDimensions,
737
- unbounded: true,
737
+ unbounded: metadata.type === 'composition',
738
738
  previewSize,
739
739
  size,
740
740
  });
@@ -746,7 +746,10 @@ const Canvas = ({ canvasContent, size }) => {
746
746
  width: metadata.width,
747
747
  height: metadata.height,
748
748
  };
749
- if (editorSnapping && !event.metaKey && !event.ctrlKey) {
749
+ if (metadata.type === 'composition' &&
750
+ editorSnapping &&
751
+ !event.metaKey &&
752
+ !event.ctrlKey) {
750
753
  dropPosition = (0, composition_drop_preview_1.snapCompositionDropPosition)({
751
754
  compositionDimensions,
752
755
  destinationDimensions: contentDimensions,
@@ -112,7 +112,7 @@ const CompositionSelectorItem = ({ item, level, currentComposition, tabIndex, se
112
112
  }
113
113
  }, [item, selectComposition, toggleFolder]);
114
114
  const onKeyDown = (0, react_1.useCallback)((evt) => {
115
- if (evt.key === 'Enter' || evt.key === ' ') {
115
+ if (evt.key === 'Enter') {
116
116
  onClick(evt);
117
117
  }
118
118
  }, [onClick]);
@@ -150,6 +150,17 @@ const InspectorOpenInEditor = ({ contextForAgents = null, label, location, locat
150
150
  (0, NotificationCenter_1.showNotification)(`Could not open file: ${err.message}`, 2000);
151
151
  });
152
152
  },
153
+ onOpenInGitClient: (gitClientId) => {
154
+ (0, open_in_editor_1.openInGitClient)(gitClientId)
155
+ .then((response) => {
156
+ if (!response.success) {
157
+ (0, NotificationCenter_1.showNotification)('Could not open Git client', 2000);
158
+ }
159
+ })
160
+ .catch((err) => {
161
+ (0, NotificationCenter_1.showNotification)(`Could not open Git client: ${err.message}`, 2000);
162
+ });
163
+ },
153
164
  onOpenInTerminal: (terminalId) => {
154
165
  if (!(location === null || location === void 0 ? void 0 : location.source) || locationType !== 'folder') {
155
166
  return;
@@ -39,6 +39,28 @@ const computedValueStyle = {
39
39
  fontStyle: 'italic',
40
40
  lineHeight: '20px',
41
41
  };
42
+ const metadataDraggerStyles = {
43
+ durationInFrames: {
44
+ padding: '2px 0 2px 6px',
45
+ textAlign: 'right',
46
+ width: 92,
47
+ },
48
+ fps: {
49
+ padding: '2px 0 2px 6px',
50
+ textAlign: 'right',
51
+ width: 72,
52
+ },
53
+ height: {
54
+ padding: '2px 0 2px 6px',
55
+ textAlign: 'right',
56
+ width: 52,
57
+ },
58
+ width: {
59
+ padding: '2px 0 2px 6px',
60
+ textAlign: 'right',
61
+ width: 52,
62
+ },
63
+ };
42
64
  const dimensionsControls = {
43
65
  alignItems: 'center',
44
66
  color: 'inherit',
@@ -124,7 +146,7 @@ const CompositionMetadataValue = ({ computed, disabled, field, onSave, pendingVa
124
146
  }
125
147
  return String(roundedValue);
126
148
  }, [field]);
127
- return computed ? (jsx_runtime_1.jsx("span", { style: computedContainerStyle, children: jsx_runtime_1.jsx("span", { style: computedValueStyle, children: formatValue(value) }) })) : disabled ? (formatValue(value)) : (jsx_runtime_1.jsx(InputDragger_1.InputDragger, { "aria-label": fieldLabels[field], type: "number", value: (_a = dragValue !== null && dragValue !== void 0 ? dragValue : pendingValue === null || pendingValue === void 0 ? void 0 : pendingValue.value) !== null && _a !== void 0 ? _a : value, disabled: pendingValue !== null, status: "ok", onValueChange: setDragValue, onValueChangeEnd: save, onTextChange: () => undefined, min: isFps ? 0.01 : 1, step: isFps ? 0.01 : 1, dragDecimalPlaces: isFps ? 2 : 0, formatter: formatValue, rightAlign: true, small: true }));
149
+ return computed ? (jsx_runtime_1.jsx("span", { style: computedContainerStyle, children: jsx_runtime_1.jsx("span", { style: computedValueStyle, children: formatValue(value) }) })) : disabled ? (formatValue(value)) : (jsx_runtime_1.jsx(InputDragger_1.InputDragger, { "aria-label": fieldLabels[field], buttonStyle: metadataDraggerStyles[field], type: "number", value: (_a = dragValue !== null && dragValue !== void 0 ? dragValue : pendingValue === null || pendingValue === void 0 ? void 0 : pendingValue.value) !== null && _a !== void 0 ? _a : value, disabled: pendingValue !== null, status: "ok", onValueChange: setDragValue, onValueChangeEnd: save, onTextChange: () => undefined, min: isFps ? 0.01 : 1, step: isFps ? 0.01 : 1, dragDecimalPlaces: isFps ? 2 : 0, formatter: formatValue, rightAlign: true, style: metadataDraggerStyles[field] }));
128
150
  };
129
151
  const CompositionMetadata = ({ compositionId, disabled, stack }) => {
130
152
  var _a, _b, _c, _d;
@@ -30,6 +30,7 @@ const componentAssetTypes = {
30
30
  'dev.remotion.media.Audio': 'audio',
31
31
  'dev.remotion.media.Video': 'video',
32
32
  'dev.remotion.remotion.AnimatedImage': 'image',
33
+ 'dev.remotion.remotion.CanvasImage': 'image',
33
34
  'dev.remotion.remotion.Img': 'image',
34
35
  };
35
36
  const getAssetSearchQueryForComponent = (componentIdentity) => {
@@ -287,7 +287,7 @@ const CropHandle = ({ handle, outline, onDraggingChange, target }) => {
287
287
  setPropStatuses,
288
288
  ]);
289
289
  return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
290
- jsx_runtime_1.jsx("path", { d: path, fill: "none", stroke: colors_1.TRANSPARENT, strokeWidth: CROP_HANDLE_HIT_WIDTH, strokeLinejoin: "miter", vectorEffect: "non-scaling-stroke", pointerEvents: "stroke", cursor: cursor, onPointerDown: onPointerDown }), jsx_runtime_1.jsx("path", { d: path, fill: "none", stroke: colors_1.BLUE, strokeWidth: CROP_HANDLE_THICKNESS, strokeLinejoin: "miter", strokeLinecap: "butt", vectorEffect: "non-scaling-stroke", pointerEvents: "none", style: { filter: colors_1.SELECTED_OUTLINE_DROP_SHADOW } })
290
+ jsx_runtime_1.jsx("path", { d: path, fill: "none", stroke: colors_1.TRANSPARENT, strokeWidth: CROP_HANDLE_HIT_WIDTH, strokeLinejoin: "miter", vectorEffect: "non-scaling-stroke", pointerEvents: "stroke", cursor: cursor, onPointerDown: onPointerDown }), jsx_runtime_1.jsx("path", { d: path, fill: "none", stroke: colors_1.BLUE, strokeWidth: CROP_HANDLE_THICKNESS, strokeLinejoin: "miter", strokeLinecap: "butt", vectorEffect: "non-scaling-stroke", pointerEvents: "none" })
291
291
  ] }));
292
292
  };
293
293
  const SelectedOutlineCropControls = ({ outline, onDraggingChange, target }) => {
@@ -55,6 +55,7 @@ const call_add_keyframe_1 = require("./Timeline/call-add-keyframe");
55
55
  const focus_inspector_field_1 = require("./Timeline/focus-inspector-field");
56
56
  const imperative_state_1 = require("./Timeline/imperative-state");
57
57
  const save_sequence_prop_1 = require("./Timeline/save-sequence-prop");
58
+ const should_clear_selection_on_pointer_down_1 = require("./Timeline/should-clear-selection-on-pointer-down");
58
59
  const SelectedOutlinePolygonUnmemoized = ({ compositionHeight, compositionWidth, dragging, getAllDragOutlines, getAllDragTargets, getLayoutTarget, getTarget, hasTarget, hovered, onContextMenuOpen, onContextMenuOpenChange, outline, onDraggingChange, onHoverChange, onSnapPointsChange, onSelect, onDoubleClickTarget, scale, showSelectedOutline, }) => {
59
60
  const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
60
61
  const { canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
@@ -346,7 +347,7 @@ const SelectedOutlinePolygonUnmemoized = ({ compositionHeight, compositionWidth,
346
347
  });
347
348
  }, [getEffectDropTarget]);
348
349
  return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
349
- jsx_runtime_1.jsx("polygon", { ref: polygonRef, points: points, fill: effectDropHovered ? colors_1.TIMELINE_DROP_BLUE_ALPHA_12 : colors_1.TRANSPARENT, stroke: colors_1.BLUE, strokeOpacity: visible || effectDropHovered ? 1 : 0, strokeWidth: 2, vectorEffect: "non-scaling-stroke", pointerEvents: hasTarget ? 'all' : undefined, onPointerEnter: () => {
350
+ jsx_runtime_1.jsx("polygon", { ref: polygonRef, [should_clear_selection_on_pointer_down_1.PREVENT_CLEAR_SELECTION_ON_POINTER_DOWN_ATTR]: 'true', points: points, fill: effectDropHovered ? colors_1.TIMELINE_DROP_BLUE_ALPHA_12 : colors_1.TRANSPARENT, stroke: colors_1.BLUE, strokeOpacity: visible || effectDropHovered ? 1 : 0, strokeWidth: 2, vectorEffect: "non-scaling-stroke", pointerEvents: hasTarget ? 'all' : undefined, onPointerEnter: () => {
350
351
  if (!dragging) {
351
352
  onHoverChange(outline.key);
352
353
  }
@@ -2,6 +2,14 @@ import React from 'react';
2
2
  import type { SegmentedControlItem } from '../SegmentedControl';
3
3
  import type { TimelineSelection } from './TimelineSelection';
4
4
  import type { TimelineEasingValue } from './update-selected-easing';
5
+ export declare const EasingPresetPreview: React.FC<{
6
+ readonly color: string;
7
+ readonly easing: TimelineEasingValue;
8
+ readonly height?: number;
9
+ readonly nonScalingStroke?: boolean;
10
+ readonly strokeWidth?: number;
11
+ readonly width?: number;
12
+ }>;
5
13
  export type EasingEditorState = {
6
14
  initialEasing: TimelineEasingValue;
7
15
  selections: TimelineSelection[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EasingEditor = void 0;
3
+ exports.EasingEditor = exports.EasingPresetPreview = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const studio_shared_1 = require("@remotion/studio-shared");
6
6
  const react_1 = require("react");
@@ -110,8 +110,6 @@ const presetButtonBase = {
110
110
  };
111
111
  const presetPreviewSvgStyle = {
112
112
  display: 'block',
113
- height: PRESET_PREVIEW_HEIGHT,
114
- width: PRESET_PREVIEW_WIDTH,
115
113
  };
116
114
  const coordinatesGridBase = {
117
115
  display: 'grid',
@@ -363,6 +361,11 @@ const getPresetPreviewPath = (easing) => {
363
361
  }
364
362
  return points.join(' ');
365
363
  };
364
+ const EasingPresetPreview = ({ color, easing, height = PRESET_PREVIEW_HEIGHT, nonScalingStroke = false, strokeWidth = 2, width = PRESET_PREVIEW_WIDTH, }) => {
365
+ const path = (0, react_1.useMemo)(() => getPresetPreviewPath(easing), [easing]);
366
+ return (jsx_runtime_1.jsx("svg", { width: width, height: height, viewBox: `0 0 ${PRESET_PREVIEW_WIDTH} ${PRESET_PREVIEW_HEIGHT}`, style: presetPreviewSvgStyle, "aria-hidden": "true", focusable: false, children: jsx_runtime_1.jsx("path", { d: path, fill: "none", stroke: color, strokeWidth: strokeWidth, strokeLinecap: "round", strokeLinejoin: "round", vectorEffect: nonScalingStroke ? 'non-scaling-stroke' : undefined }) }));
367
+ };
368
+ exports.EasingPresetPreview = EasingPresetPreview;
366
369
  const pointFromBezier = (bezier, handle) => {
367
370
  const x = handle === 0 ? bezier[0] : bezier[2];
368
371
  const y = handle === 0 ? bezier[1] : bezier[3];
@@ -401,7 +404,6 @@ const EasingGraphScaffold = ({ labels }) => {
401
404
  const EasingPresetButton = ({ currentEasing, disabled, onClick, preset }) => {
402
405
  const selected = areEasingsEqual(currentEasing, preset.easing);
403
406
  const [hovered, setHovered] = (0, react_1.useState)(false);
404
- const path = (0, react_1.useMemo)(() => getPresetPreviewPath(preset.easing), [preset.easing]);
405
407
  const onPointerEnter = (0, react_1.useCallback)(() => {
406
408
  setHovered(true);
407
409
  }, []);
@@ -417,7 +419,7 @@ const EasingPresetButton = ({ currentEasing, disabled, onClick, preset }) => {
417
419
  const handleClick = (0, react_1.useCallback)(() => {
418
420
  onClick(preset.easing);
419
421
  }, [onClick, preset.easing]);
420
- return (jsx_runtime_1.jsx("button", { type: "button", style: style, title: preset.label, "aria-label": `Apply ${preset.label} easing`, disabled: disabled, onClick: handleClick, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, children: jsx_runtime_1.jsx("svg", { width: PRESET_PREVIEW_WIDTH, height: PRESET_PREVIEW_HEIGHT, viewBox: `0 0 ${PRESET_PREVIEW_WIDTH} ${PRESET_PREVIEW_HEIGHT}`, style: presetPreviewSvgStyle, "aria-hidden": "true", focusable: false, children: jsx_runtime_1.jsx("path", { d: path, fill: "none", stroke: selected || hovered ? colors_1.WHITE : colors_1.LIGHT_TEXT, strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" }) }) }));
422
+ return (jsx_runtime_1.jsx("button", { type: "button", style: style, title: preset.label, "aria-label": `Apply ${preset.label} easing`, disabled: disabled, onClick: handleClick, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, children: jsx_runtime_1.jsx(exports.EasingPresetPreview, { color: selected || hovered ? colors_1.WHITE : colors_1.LIGHT_TEXT, easing: preset.easing }) }));
421
423
  };
422
424
  const EasingEditor = ({ state, renderHeader }) => {
423
425
  var _a, _b;
@@ -434,7 +436,6 @@ const EasingEditor = ({ state, renderHeader }) => {
434
436
  const springRef = (0, react_1.useRef)(spring);
435
437
  const liveOverrideVersionRef = (0, react_1.useRef)(0);
436
438
  const pendingOverrideTargetsRef = (0, react_1.useRef)([]);
437
- const [activeHandle, setActiveHandle] = (0, react_1.useState)(null);
438
439
  (0, react_1.useEffect)(() => {
439
440
  const nextBezier = easingToBezier(state.initialEasing);
440
441
  const nextSpring = easingToSpring(state.initialEasing);
@@ -679,7 +680,6 @@ const EasingEditor = ({ state, renderHeader }) => {
679
680
  event.preventDefault();
680
681
  event.stopPropagation();
681
682
  const bezierBeforeDrag = [...bezierRef.current];
682
- setActiveHandle(handle);
683
683
  updateHandleFromPointer(handle, event);
684
684
  (0, pointer_session_1.startCapturedPointerSession)({
685
685
  event,
@@ -697,7 +697,6 @@ const EasingEditor = ({ state, renderHeader }) => {
697
697
  bezierRef.current = bezierBeforeDrag;
698
698
  setBezier(bezierBeforeDrag);
699
699
  }
700
- setActiveHandle(null);
701
700
  },
702
701
  });
703
702
  }, [
@@ -777,7 +776,7 @@ const EasingEditor = ({ state, renderHeader }) => {
777
776
  const modeSwitcher = (jsx_runtime_1.jsx("div", { style: segmentedControlWrapper, children: jsx_runtime_1.jsx(SegmentedControl_1.SegmentedControl, { items: modeItems, needsWrapping: false }) }));
778
777
  return (jsx_runtime_1.jsxs("div", { style: inlineContainer, children: [renderHeader ? renderHeader(modeItems) : null, jsx_runtime_1.jsx("div", { style: renderHeader ? inspectorPresetButtonsWrapper : presetButtonsWrapper, children: EDITOR_EASING_PRESETS.map((preset) => (jsx_runtime_1.jsx(EasingPresetButton, { currentEasing: currentEasing, disabled: disabled, onClick: applyPreset, preset: preset }, preset.id))) }), mode === 'bezier' ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
779
778
  jsx_runtime_1.jsxs("svg", { ref: svgRef, width: SVG_WIDTH, height: SVG_HEIGHT, viewBox: `0 0 ${SVG_WIDTH} ${SVG_HEIGHT}`, style: svgStyle, "aria-label": "Bezier curve editor", children: [
780
- jsx_runtime_1.jsx(EasingGraphScaffold, { labels: graphLabels }), jsx_runtime_1.jsx("line", { x1: startPoint.x, y1: startPoint.y, x2: firstHandle.x, y2: firstHandle.y, stroke: colors_1.WHITE_ALPHA_35, strokeWidth: 1 }), jsx_runtime_1.jsx("line", { x1: endPoint.x, y1: endPoint.y, x2: secondHandle.x, y2: secondHandle.y, stroke: colors_1.WHITE_ALPHA_35, strokeWidth: 1 }), jsx_runtime_1.jsx("path", { d: bezierPath, fill: "none", stroke: colors_1.BLUE, strokeWidth: 3 }), jsx_runtime_1.jsx("circle", { cx: startPoint.x, cy: startPoint.y, r: 4, fill: colors_1.WHITE }), jsx_runtime_1.jsx("circle", { cx: endPoint.x, cy: endPoint.y, r: 4, fill: colors_1.WHITE }), jsx_runtime_1.jsx("circle", { cx: firstHandle.x, cy: firstHandle.y, r: 6, fill: colors_1.WHITE, stroke: colors_1.BLUE, strokeWidth: 2, vectorEffect: "non-scaling-stroke", pointerEvents: disabled ? 'none' : 'all', cursor: activeHandle === 0 ? 'grabbing' : 'default', onPointerDown: (event) => onHandlePointerDown(0, event) }), jsx_runtime_1.jsx("circle", { cx: secondHandle.x, cy: secondHandle.y, r: 6, fill: colors_1.WHITE, stroke: colors_1.BLUE, strokeWidth: 2, vectorEffect: "non-scaling-stroke", pointerEvents: disabled ? 'none' : 'all', cursor: activeHandle === 1 ? 'grabbing' : 'default', onPointerDown: (event) => onHandlePointerDown(1, event) })
779
+ jsx_runtime_1.jsx(EasingGraphScaffold, { labels: graphLabels }), jsx_runtime_1.jsx("line", { x1: startPoint.x, y1: startPoint.y, x2: firstHandle.x, y2: firstHandle.y, stroke: colors_1.WHITE_ALPHA_35, strokeWidth: 1 }), jsx_runtime_1.jsx("line", { x1: endPoint.x, y1: endPoint.y, x2: secondHandle.x, y2: secondHandle.y, stroke: colors_1.WHITE_ALPHA_35, strokeWidth: 1 }), jsx_runtime_1.jsx("path", { d: bezierPath, fill: "none", stroke: colors_1.BLUE, strokeWidth: 3 }), jsx_runtime_1.jsx("circle", { cx: startPoint.x, cy: startPoint.y, r: 4, fill: colors_1.WHITE }), jsx_runtime_1.jsx("circle", { cx: endPoint.x, cy: endPoint.y, r: 4, fill: colors_1.WHITE }), jsx_runtime_1.jsx("circle", { cx: firstHandle.x, cy: firstHandle.y, r: 6, fill: colors_1.WHITE, stroke: colors_1.BLUE, strokeWidth: 2, vectorEffect: "non-scaling-stroke", pointerEvents: disabled ? 'none' : 'all', cursor: "default", onPointerDown: (event) => onHandlePointerDown(0, event) }), jsx_runtime_1.jsx("circle", { cx: secondHandle.x, cy: secondHandle.y, r: 6, fill: colors_1.WHITE, stroke: colors_1.BLUE, strokeWidth: 2, vectorEffect: "non-scaling-stroke", pointerEvents: disabled ? 'none' : 'all', cursor: "default", onPointerDown: (event) => onHandlePointerDown(1, event) })
781
780
  ] }), modeSwitcher, jsx_runtime_1.jsxs("div", { style: coordinatesGrid, children: [
782
781
  jsx_runtime_1.jsxs("div", { style: coordinateRow, children: [
783
782
  jsx_runtime_1.jsx("div", { style: coordinateLabel, children: "X1" }), jsx_runtime_1.jsx("div", { style: coordinateInputWrapper, children: jsx_runtime_1.jsx(InputDragger_1.InputDragger, { type: "number", value: bezier[0], status: "ok", onValueChange: (value) => setCoordinate(0, 'x', value, false), onValueChangeEnd: (value) => setCoordinate(0, 'x', value, true), onTextChange: () => undefined, min: 0, max: 1, step: 0.01, formatter: formatNumber, rightAlign: false, style: numberInputStyle, snapToStep: false, disabled: disabled }) })
@@ -42,16 +42,11 @@ const TimelineColorField = ({ field, effectiveValue, propStatus, onSave, onDragV
42
42
  }
43
43
  onDragEnd();
44
44
  }, [onSave, onDragEnd, propStatus]);
45
- const swatchStyle = (0, react_1.useMemo)(() => {
46
- return {
47
- marginLeft: 5,
48
- };
49
- }, []);
50
45
  if (currentValue === 'none') {
51
46
  return (jsx_runtime_1.jsxs("span", { style: containerStyle, children: [
52
47
  jsx_runtime_1.jsx("span", { style: noneLabelStyle, children: "None" }), jsx_runtime_1.jsx("button", { type: "button", style: setButtonStyle, title: `Set ${(_b = field.description) !== null && _b !== void 0 ? _b : field.key} to gray`, onClick: () => onChangeComplete(DEFAULT_SET_COLOR), children: "Set" })
53
48
  ] }));
54
49
  }
55
- return (jsx_runtime_1.jsx("span", { style: containerStyle, children: jsx_runtime_1.jsx(ColorPicker_1.ColorPicker, { value: currentValue, status: "ok", onChange: onChange, onChangeComplete: onChangeComplete, width: SWATCH_WIDTH, height: SWATCH_HEIGHT, disabled: false, name: field.key, title: currentValue, style: swatchStyle }) }));
50
+ return (jsx_runtime_1.jsx("span", { style: containerStyle, children: jsx_runtime_1.jsx(ColorPicker_1.ColorPicker, { value: currentValue, status: "ok", onChange: onChange, onChangeComplete: onChangeComplete, width: SWATCH_WIDTH, height: SWATCH_HEIGHT, disabled: false, name: field.key, title: currentValue }) }));
56
51
  };
57
52
  exports.TimelineColorField = TimelineColorField;
@@ -321,7 +321,7 @@ const TimelineEffectItem = ({ label, nodePathInfo, effectIndex, effectSchema, do
321
321
  ...(dropIndicator === 'before' ? { top: -1 } : { bottom: -1 }),
322
322
  };
323
323
  }, [dropIndicator]);
324
- const row = (jsx_runtime_1.jsx(TimelineRowChrome_1.TimelineRowChrome, { depth: rowDepth, eye: canToggle ? (jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEye, { type: "effect", hidden: isDisabled, onInvoked: onToggle })) : (jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEyeSpacer, {})), arrow: jsx_runtime_1.jsx(TimelineExpandArrowButton_1.TimelineExpandArrowButton, { isExpanded: isExpanded, onClick: () => toggleTrack(nodePathInfo), label: `${label} section`, disabled: false }), style: rowStyle, selected: selection.selected, selectable: selection.selectable, selectionItem: selection.selectionItem, onSelect: selection.onSelect, showSelectedBackground: true, containsSelection: containsSelection, outerHeight: null, children: jsx_runtime_1.jsx("span", { title: label, style: labelStyle, children: label }) }));
324
+ const row = (jsx_runtime_1.jsx(TimelineRowChrome_1.TimelineRowChrome, { depth: rowDepth, eye: canToggle ? (jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEye, { type: "effect", hidden: isDisabled, onInvoked: onToggle })) : (jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEyeSpacer, {})), arrow: jsx_runtime_1.jsx(TimelineExpandArrowButton_1.TimelineExpandArrowButton, { isExpanded: isExpanded, onClick: () => toggleTrack(nodePathInfo), label: `${label} section`, disabled: false }), style: rowStyle, selected: selection.selected, selectable: selection.selectable, selectionItem: selection.selectionItem, onSelect: selection.onSelect, showSelectedBackground: true, containsSelection: containsSelection, isFieldRow: false, outerHeight: null, children: jsx_runtime_1.jsx("span", { title: label, style: labelStyle, children: label }) }));
325
325
  const draggableRow = canReorder ? (jsx_runtime_1.jsxs("div", { draggable: true, onDragStart: onDragStart, onDragEnd: onDragEnd, onDragOver: onDragOver, onDragLeave: onDragLeave, onDrop: onDrop, style: reorderWrapper, children: [reorderLineStyle ? jsx_runtime_1.jsx("div", { style: reorderLineStyle }) : null, row] })) : (row);
326
326
  return previewConnected ? (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { getItems: getContextMenuItems, children: draggableRow })) : (draggableRow);
327
327
  };
@@ -333,7 +333,7 @@ const TimelineEffectPropItem = ({ field, validatedLocation, rowDepth, nodePath,
333
333
  property: field.key,
334
334
  }).catch(() => undefined);
335
335
  }, [field.key, previewServerState.type, validatedLocation]);
336
- const row = (jsx_runtime_1.jsx(TimelineRowChrome_1.TimelineRowChrome, { depth: rowDepth, eye: jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEyeSpacer, {}), keyframeControls: keyframeControls, arrow: jsx_runtime_1.jsx(TimelineExpandArrowButton_1.TimelineExpandArrowSpacer, {}), style: style, selected: selection.selected, selectable: selection.selectable, selectionItem: selection.selectionItem, onSelect: selection.onSelect, onDoubleClick: onPropertyDoubleClick, showSelectedBackground: true, containsSelection: false, outerHeight: null, children: jsx_runtime_1.jsx(TimelineFieldRowContent_1.TimelineFieldRowContent, { field: field, rowDepth: rowDepth, selected: selection.selected, children: jsx_runtime_1.jsx(TimelineEffectPropValueAtCurrentFrame, { field: field, nodePath: nodePath, validatedLocation: validatedLocation, keyframeDisplayOffset: keyframeDisplayOffset }) }) }));
336
+ const row = (jsx_runtime_1.jsx(TimelineRowChrome_1.TimelineRowChrome, { depth: rowDepth, eye: jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEyeSpacer, {}), keyframeControls: keyframeControls, arrow: jsx_runtime_1.jsx(TimelineExpandArrowButton_1.TimelineExpandArrowSpacer, {}), style: style, selected: selection.selected, selectable: selection.selectable, selectionItem: selection.selectionItem, onSelect: selection.onSelect, onDoubleClick: onPropertyDoubleClick, showSelectedBackground: true, containsSelection: false, isFieldRow: true, outerHeight: null, children: jsx_runtime_1.jsx(TimelineFieldRowContent_1.TimelineFieldRowContent, { field: field, rowDepth: rowDepth, selected: selection.selected, keyframeControls: keyframeControls, children: jsx_runtime_1.jsx(TimelineEffectPropValueAtCurrentFrame, { field: field, nodePath: nodePath, validatedLocation: validatedLocation, keyframeDisplayOffset: keyframeDisplayOffset }) }) }));
337
337
  return jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { getItems: getContextMenuItems, children: row });
338
338
  };
339
339
  exports.TimelineEffectPropItem = TimelineEffectPropItem;
@@ -4,9 +4,6 @@ exports.TimelineEnumField = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const ComboBox_1 = require("../NewComposition/ComboBox");
7
- const comboboxStyle = {
8
- marginLeft: 8,
9
- };
10
7
  const TimelineEnumField = ({ field, propStatus, effectiveValue, onSave, onDragValueChange, onDragEnd, }) => {
11
8
  const { fieldSchema } = field;
12
9
  if (fieldSchema.type !== 'enum') {
@@ -37,6 +34,6 @@ const TimelineEnumField = ({ field, propStatus, effectiveValue, onSave, onDragVa
37
34
  disabled: false,
38
35
  }));
39
36
  }, [variantKeys, onSelect]);
40
- return (jsx_runtime_1.jsx(ComboBox_1.Combobox, { size: "small", title: field.key, selectedId: current, values: items, style: comboboxStyle }));
37
+ return (jsx_runtime_1.jsx(ComboBox_1.Combobox, { size: "small", title: field.key, selectedId: current, values: items }));
41
38
  };
42
39
  exports.TimelineEnumField = TimelineEnumField;
@@ -45,7 +45,7 @@ const TimelineExpandedRowInner = ({ node, depth, nestedDepth, rowDepthBase, getI
45
45
  const isExpanded = getIsExpanded(node.nodePathInfo);
46
46
  return (jsx_runtime_1.jsx(TimelineRowChrome_1.TimelineRowChrome, { depth: rowDepth, eye: jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEyeSpacer, {}), arrow: jsx_runtime_1.jsx(TimelineExpandArrowButton_1.TimelineExpandArrowButton, { isExpanded: isExpanded, onClick: () => toggleTrack(node.nodePathInfo), label: `${node.label} section`, disabled: false }), style: {
47
47
  height: timeline_layout_1.TREE_GROUP_ROW_HEIGHT,
48
- }, selected: selection.selected, selectable: selection.selectable, selectionItem: selection.selectionItem, onSelect: selection.onSelect, showSelectedBackground: true, containsSelection: false, outerHeight: null, children: jsx_runtime_1.jsx("span", { style: labelStyle, children: node.label }) }));
48
+ }, selected: selection.selected, selectable: selection.selectable, selectionItem: selection.selectionItem, onSelect: selection.onSelect, showSelectedBackground: true, containsSelection: false, isFieldRow: false, outerHeight: null, children: jsx_runtime_1.jsx("span", { style: labelStyle, children: node.label }) }));
49
49
  }
50
50
  if (node.field) {
51
51
  if (node.field.kind === 'effect-field') {
@@ -58,6 +58,6 @@ const TimelineExpandedRowInner = ({ node, depth, nestedDepth, rowDepthBase, getI
58
58
  }
59
59
  return (jsx_runtime_1.jsx(TimelineRowChrome_1.TimelineRowChrome, { depth: rowDepth, eye: jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEyeSpacer, {}), arrow: jsx_runtime_1.jsx(TimelineExpandArrowButton_1.TimelineExpandArrowSpacer, {}), style: {
60
60
  height: (0, timeline_layout_1.getTreeRowHeight)(node),
61
- }, selected: selection.selected, selectable: selection.selectable, selectionItem: selection.selectionItem, onSelect: selection.onSelect, showSelectedBackground: true, containsSelection: false, outerHeight: null, children: jsx_runtime_1.jsx("span", { style: labelStyle, children: node.label }) }));
61
+ }, selected: selection.selected, selectable: selection.selectable, selectionItem: selection.selectionItem, onSelect: selection.onSelect, showSelectedBackground: true, containsSelection: false, isFieldRow: false, outerHeight: null, children: jsx_runtime_1.jsx("span", { style: labelStyle, children: node.label }) }));
62
62
  };
63
63
  exports.TimelineExpandedRow = react_1.default.memo(TimelineExpandedRowInner);
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
- import type { SchemaFieldInfo } from '../../helpers/timeline-layout';
2
+ import { type SchemaFieldInfo } from '../../helpers/timeline-layout';
3
3
  export declare const TimelineFieldRowContent: React.FC<{
4
4
  readonly field: SchemaFieldInfo;
5
5
  readonly rowDepth: number;
6
6
  readonly selected: boolean;
7
+ readonly keyframeControls: React.ReactNode;
7
8
  readonly children: React.ReactNode;
8
9
  }>;
@@ -3,21 +3,36 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TimelineFieldRowContent = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
+ const timeline_layout_1 = require("../../helpers/timeline-layout");
6
7
  const timeline_field_row_layout_1 = require("./timeline-field-row-layout");
7
8
  const TimelineFieldLabel_1 = require("./TimelineFieldLabel");
9
+ const TimelineRowChrome_1 = require("./TimelineRowChrome");
10
+ const TimelineRowLayoutContext_1 = require("./TimelineRowLayoutContext");
8
11
  const Transform3DModeContext_1 = require("./Transform3DModeContext");
9
- const TimelineFieldRowContent = ({ field, rowDepth, selected, children }) => {
12
+ const stackedHeaderStyle = {
13
+ display: 'flex',
14
+ flex: `0 0 ${timeline_layout_1.SCHEMA_FIELD_ROW_HEIGHT}px`,
15
+ minWidth: 0,
16
+ };
17
+ const stackedLabelStyle = {
18
+ flex: 1,
19
+ minWidth: 0,
20
+ };
21
+ const TimelineFieldRowContent = ({ field, rowDepth, selected, keyframeControls, children }) => {
10
22
  var _a;
11
23
  const transform3DMode = (0, react_1.useContext)(Transform3DModeContext_1.Transform3DModeContext);
24
+ const { keyframeControlsPlacement } = (0, react_1.useContext)(TimelineRowLayoutContext_1.TimelineRowLayoutContext);
12
25
  const stacked = (0, timeline_field_row_layout_1.isTimelineFieldStacked)({ field, transform3DMode });
13
26
  const compactStacked = stacked && field.typeName !== 'text-content';
14
27
  const label = (jsx_runtime_1.jsx(TimelineFieldLabel_1.TimelineFieldLabel, { rowDepth: rowDepth, selected: selected, label: (_a = field.description) !== null && _a !== void 0 ? _a : field.key, stacked: stacked }));
15
28
  const value = (jsx_runtime_1.jsx("div", { style: compactStacked
16
29
  ? timeline_field_row_layout_1.timelineCompactStackedFieldValueColumnStyle
17
30
  : timeline_field_row_layout_1.timelineFieldValueColumnStyle, children: children }));
31
+ const controls = keyframeControlsPlacement === 'after-label' ? (jsx_runtime_1.jsx(TimelineRowChrome_1.TimelineRowKeyframeControlsColumn, { depth: rowDepth, children: keyframeControls })) : null;
18
32
  if (stacked) {
19
- return (jsx_runtime_1.jsxs("div", { style: timeline_field_row_layout_1.timelineStackedFieldContentStyle, children: [label, value] }));
33
+ return (jsx_runtime_1.jsxs("div", { style: timeline_field_row_layout_1.timelineStackedFieldContentStyle, children: [controls ? (jsx_runtime_1.jsxs("div", { style: stackedHeaderStyle, children: [
34
+ jsx_runtime_1.jsx("div", { style: stackedLabelStyle, children: label }), controls] })) : (label), value] }));
20
35
  }
21
- return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [label, value] }));
36
+ return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [label, controls, value] }));
22
37
  };
23
38
  exports.TimelineFieldRowContent = TimelineFieldRowContent;
@@ -60,7 +60,6 @@ const systemFonts = [
60
60
  value: fontFamily,
61
61
  }));
62
62
  const triggerStyle = {
63
- marginLeft: 8,
64
63
  padding: '3px 4px',
65
64
  display: 'inline-flex',
66
65
  alignItems: 'center',
@@ -508,7 +507,7 @@ const TimelineFontFamilyField = ({ field, propStatus, effectiveValue, onSave, on
508
507
  jsx_runtime_1.jsx("div", { title: selected.label, style: { ...triggerLabel, ...selectedPreviewStyle }, children: selected.label }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(caret_1.CaretDown, { small: true })
509
508
  ] }), saveError === null ? null : jsx_runtime_1.jsx("div", { style: errorText, children: saveError }), opened && size
510
509
  ? react_dom_1.default.createPortal(jsx_runtime_1.jsx("div", { style: styles_1.fullScreenOverlay, onPointerDown: (event) => event.stopPropagation(), children: jsx_runtime_1.jsx("div", { style: styles_1.outerPortal, className: "css-reset", children: jsx_runtime_1.jsx(z_index_1.HigherZIndex, { onOutsideClick: hide, onEscape: hide, children: jsx_runtime_1.jsxs("div", { style: portalStyle({ size }), children: [
511
- jsx_runtime_1.jsx("div", { style: { padding: 6 }, children: jsx_runtime_1.jsx("input", { ref: inputRef, value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: onKeyDown, placeholder: "Search fonts", style: searchInput }) }), filteredOptions.length === 0 ? (jsx_runtime_1.jsx("div", { style: noResults, children: "No fonts found" })) : (jsx_runtime_1.jsxs("div", { ref: listRef, style: listStyle, onScroll: (event) => {
510
+ jsx_runtime_1.jsx("div", { style: { padding: 6 }, children: jsx_runtime_1.jsx("input", { ref: inputRef, value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: onKeyDown, placeholder: "Search fonts", style: searchInput }) }), filteredOptions.length === 0 ? (jsx_runtime_1.jsx("div", { style: noResults, children: "No fonts found" })) : (jsx_runtime_1.jsxs("div", { ref: listRef, style: listStyle, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, onScroll: (event) => {
512
511
  setScrollTop(event.currentTarget.scrollTop);
513
512
  }, children: [
514
513
  jsx_runtime_1.jsx("div", { style: { height: visibleRange.start * ROW_HEIGHT } }), renderedOptions.map((option, index) => {
@@ -43,6 +43,7 @@ const colors_1 = require("../../helpers/colors");
43
43
  const get_left_of_timeline_slider_1 = require("../../helpers/get-left-of-timeline-slider");
44
44
  const timeline_layout_1 = require("../../helpers/timeline-layout");
45
45
  const ContextMenu_1 = require("../ContextMenu");
46
+ const EasingEditorModal_1 = require("./EasingEditorModal");
46
47
  const TimelineSelection_1 = require("./TimelineSelection");
47
48
  const TimelineWidthProvider_1 = require("./TimelineWidthProvider");
48
49
  const update_selected_easing_1 = require("./update-selected-easing");
@@ -138,7 +139,7 @@ const TimelineKeyframeEasingLineInteraction = ({ fromFrame, toFrame, nodePathInf
138
139
  id: 'linear',
139
140
  keyHint: null,
140
141
  label: 'Linear',
141
- leftItem: null,
142
+ leftItem: (jsx_runtime_1.jsx(EasingEditorModal_1.EasingPresetPreview, { color: colors_1.LIGHT_TEXT, easing: studio_shared_1.LINEAR_KEYFRAME_EASING, height: 18, nonScalingStroke: true, strokeWidth: 5, width: 18 })),
142
143
  disabled: previewServerState.type !== 'connected',
143
144
  onClick: () => updateEasing(studio_shared_1.LINEAR_KEYFRAME_EASING),
144
145
  quickSwitcherLabel: null,
@@ -150,7 +151,7 @@ const TimelineKeyframeEasingLineInteraction = ({ fromFrame, toFrame, nodePathInf
150
151
  id: preset.id,
151
152
  keyHint: null,
152
153
  label: preset.label,
153
- leftItem: null,
154
+ leftItem: (jsx_runtime_1.jsx(EasingEditorModal_1.EasingPresetPreview, { color: colors_1.LIGHT_TEXT, easing: preset.easing, height: 18, nonScalingStroke: true, strokeWidth: 5, width: 18 })),
154
155
  disabled: previewServerState.type !== 'connected',
155
156
  onClick: () => updateEasing(preset.easing),
156
157
  quickSwitcherLabel: null,
@@ -36,16 +36,18 @@ const TimelineNumberField = ({ field, effectiveValue, onSave, onDragValueChange,
36
36
  }, [onSave, propStatus]);
37
37
  const configuredStep = field.fieldSchema.type === 'number' ? field.fieldSchema.step : undefined;
38
38
  const step = configuredStep !== null && configuredStep !== void 0 ? configuredStep : 1;
39
+ const allowStepMismatch = field.group === 'crop' ||
40
+ ('kind' in field && field.kind === 'effect-field');
39
41
  const formatter = (0, react_1.useCallback)((v) => {
40
42
  return (0, timeline_field_display_utils_1.formatTimelineFieldValueForDisplay)({
41
43
  fieldSchema: field.fieldSchema,
42
44
  value: v,
43
45
  });
44
46
  }, [field.fieldSchema]);
45
- return (jsx_runtime_1.jsx(InputDragger_1.InputDragger, { type: "number", value: dragValue !== null && dragValue !== void 0 ? dragValue : effectiveValue, style: timeline_field_utils_1.draggerStyle, status: "ok", small: true, onValueChange: onValueChange, onValueChangeEnd: onValueChangeEnd, onTextChange: onTextChange, min: field.fieldSchema.type === 'number'
47
+ return (jsx_runtime_1.jsx(InputDragger_1.InputDragger, { type: "number", value: dragValue !== null && dragValue !== void 0 ? dragValue : effectiveValue, buttonStyle: timeline_field_utils_1.leftAlignedDraggerStyle, style: timeline_field_utils_1.draggerStyle, status: "ok", small: true, onValueChange: onValueChange, onValueChangeEnd: onValueChangeEnd, onTextChange: onTextChange, min: field.fieldSchema.type === 'number'
46
48
  ? ((_a = field.fieldSchema.min) !== null && _a !== void 0 ? _a : -Infinity)
47
49
  : -Infinity, max: field.fieldSchema.type === 'number'
48
50
  ? ((_b = field.fieldSchema.max) !== null && _b !== void 0 ? _b : Infinity)
49
- : Infinity, step: step, formatter: formatter, rightAlign: false }));
51
+ : Infinity, step: step, formatter: formatter, rightAlign: false, allowStepMismatch: allowStepMismatch }));
50
52
  };
51
53
  exports.TimelineNumberField = TimelineNumberField;
@@ -52,7 +52,7 @@ const TimelineRotationField = ({ field, effectiveValue, propStatus, onSave, onDr
52
52
  step: configuredStep,
53
53
  }), [configuredStep]);
54
54
  const serializeValue = (0, react_1.useCallback)((value) => isCssRotation
55
- ? (0, timeline_rotation_utils_1.serializeCssRotation)(value, decimalPlaces)
55
+ ? (0, timeline_rotation_utils_1.serializeCssRotation)(value, Math.max(6, decimalPlaces))
56
56
  : (0, timeline_field_utils_1.normalizeTimelineNumber)(value), [decimalPlaces, isCssRotation]);
57
57
  const serializeRotation = (0, react_1.useCallback)((rotation) => (0, timeline_rotation_utils_1.serializeCssRotationFromEuler)({
58
58
  rotation,
@@ -141,9 +141,9 @@ const TimelineRotationField = ({ field, effectiveValue, propStatus, onSave, onDr
141
141
  propStatus.codeValue,
142
142
  serializeRotation,
143
143
  ]);
144
- const angleDragger = (jsx_runtime_1.jsx(InputDragger_1.InputDragger, { type: "number", value: dragValue !== null && dragValue !== void 0 ? dragValue : degrees, style: timeline_field_utils_1.draggerStyle, status: "ok", small: true, onValueChange: onValueChange, onValueChangeEnd: onValueChangeEnd, onTextChange: onTextChange, min: min, max: max, step: step, formatter: formatter, rightAlign: false, "aria-label": isCssRotation ? 'Rotation Z' : 'Rotation angle' }));
144
+ const angleDragger = (jsx_runtime_1.jsx(InputDragger_1.InputDragger, { type: "number", value: dragValue !== null && dragValue !== void 0 ? dragValue : degrees, buttonStyle: timeline_field_utils_1.leftAlignedDraggerStyle, style: timeline_field_utils_1.draggerStyle, status: "ok", small: true, onValueChange: onValueChange, onValueChangeEnd: onValueChangeEnd, onTextChange: onTextChange, min: min, max: max, step: step, formatter: formatter, rightAlign: false, allowStepMismatch: true, "aria-label": isCssRotation ? 'Rotation Z' : 'Rotation angle' }));
145
145
  if (show3D) {
146
- return (jsx_runtime_1.jsx("span", { style: containerStyle, children: ROTATION_LABELS.map((rotationLabel, rotationIndex) => (jsx_runtime_1.jsx(InputDragger_1.InputDragger, { type: "number", value: (dragRotation !== null && dragRotation !== void 0 ? dragRotation : cssRotation)[rotationIndex], buttonStyle: compactDraggerStyle, status: "ok", small: true, onValueChange: (newValue) => onRotationValueChange(rotationIndex, newValue), onValueChangeEnd: (newValue) => onRotationValueChangeEnd(rotationIndex, newValue), onTextChange: (newValue) => onRotationTextChange(rotationIndex, newValue), min: -Infinity, max: Infinity, step: step, formatter: formatter, rightAlign: false, "aria-label": `Rotation ${rotationLabel}` }, rotationLabel))) }));
146
+ return (jsx_runtime_1.jsx("span", { style: containerStyle, children: ROTATION_LABELS.map((rotationLabel, rotationIndex) => (jsx_runtime_1.jsx(InputDragger_1.InputDragger, { type: "number", value: (dragRotation !== null && dragRotation !== void 0 ? dragRotation : cssRotation)[rotationIndex], buttonStyle: compactDraggerStyle, status: "ok", small: true, onValueChange: (newValue) => onRotationValueChange(rotationIndex, newValue), onValueChangeEnd: (newValue) => onRotationValueChangeEnd(rotationIndex, newValue), onTextChange: (newValue) => onRotationTextChange(rotationIndex, newValue), min: -Infinity, max: Infinity, step: step, formatter: formatter, rightAlign: false, allowStepMismatch: true, "aria-label": `Rotation ${rotationLabel}` }, rotationLabel))) }));
147
147
  }
148
148
  return angleDragger;
149
149
  };
@@ -1,6 +1,10 @@
1
1
  import React from 'react';
2
2
  import type { TimelineSelectionInteraction } from './TimelineSelection';
3
3
  import { type TimelineSelection } from './TimelineSelection';
4
+ export declare const TimelineRowKeyframeControlsColumn: React.FC<{
5
+ readonly children: React.ReactNode;
6
+ readonly depth: number;
7
+ }>;
4
8
  export declare const TimelineRowChrome: React.FC<{
5
9
  readonly depth: number;
6
10
  readonly eye: React.ReactNode;
@@ -15,6 +19,7 @@ export declare const TimelineRowChrome: React.FC<{
15
19
  readonly showSelectedBackground: boolean;
16
20
  readonly containsSelection: boolean;
17
21
  readonly hovered?: boolean;
22
+ readonly isFieldRow: boolean;
18
23
  readonly outerHeight: number | null;
19
24
  readonly onDragLeave?: (e: React.DragEvent<HTMLDivElement>) => void;
20
25
  readonly onDragOver?: (e: React.DragEvent<HTMLDivElement>) => void;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TimelineRowChrome = void 0;
3
+ exports.TimelineRowChrome = exports.TimelineRowKeyframeControlsColumn = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const colors_1 = require("../../helpers/colors");
@@ -24,17 +24,22 @@ const keyframeControlsColumnBaseStyle = {
24
24
  flexShrink: 0,
25
25
  justifyContent: 'flex-start',
26
26
  };
27
- const TimelineRowChrome = ({ depth, eye, keyframeControls, arrow, children, style, selected, selectable, selectionItem, onSelect, showSelectedBackground, containsSelection, hovered = false, outerHeight, onDragLeave, onDragOver, onDrop, onDoubleClick, onPointerEnter, onPointerLeave, }) => {
28
- const ref = (0, react_1.useRef)(null);
29
- const { basePadding, keyframeControlsPadding, rowBorderRadius, rowHorizontalMargin, } = (0, react_1.useContext)(TimelineRowLayoutContext_1.TimelineRowLayoutContext);
30
- const indentWidth = (0, timeline_row_layout_1.getTimelineRowIndentWidth)(depth);
31
- (0, TimelineSelection_1.useTimelineFocusableItem)(selectionItem, ref);
32
- const keyframeControlsColumnStyle = (0, react_1.useMemo)(() => ({
27
+ const TimelineRowKeyframeControlsColumn = ({ children, depth }) => {
28
+ const { basePadding, keyframeControlsPadding } = (0, react_1.useContext)(TimelineRowLayoutContext_1.TimelineRowLayoutContext);
29
+ const style = (0, react_1.useMemo)(() => ({
33
30
  ...keyframeControlsColumnBaseStyle,
34
31
  boxSizing: keyframeControlsPadding === 0 ? undefined : 'border-box',
35
32
  paddingLeft: keyframeControlsPadding,
36
33
  width: (0, timeline_row_layout_1.getTimelineRowLeftChromeWidth)(depth, basePadding),
37
34
  }), [basePadding, depth, keyframeControlsPadding]);
35
+ return (jsx_runtime_1.jsx("div", { style: leftChromeStyle, children: jsx_runtime_1.jsx("div", { style: style, children: children }) }));
36
+ };
37
+ exports.TimelineRowKeyframeControlsColumn = TimelineRowKeyframeControlsColumn;
38
+ const TimelineRowChrome = ({ depth, eye, keyframeControls, arrow, children, style, selected, selectable, selectionItem, onSelect, showSelectedBackground, containsSelection, hovered = false, isFieldRow, outerHeight, onDragLeave, onDragOver, onDrop, onDoubleClick, onPointerEnter, onPointerLeave, }) => {
39
+ const ref = (0, react_1.useRef)(null);
40
+ const { basePadding, keyframeControlsPlacement, rowBorderRadius, rowHorizontalMargin, } = (0, react_1.useContext)(TimelineRowLayoutContext_1.TimelineRowLayoutContext);
41
+ const indentWidth = (0, timeline_row_layout_1.getTimelineRowIndentWidth)(depth);
42
+ (0, TimelineSelection_1.useTimelineFocusableItem)(selectionItem, ref);
38
43
  const chromeColumnStyle = (0, react_1.useMemo)(() => ({
39
44
  alignItems: 'center',
40
45
  alignSelf: 'stretch',
@@ -87,8 +92,8 @@ const TimelineRowChrome = ({ depth, eye, keyframeControls, arrow, children, styl
87
92
  backgroundColor: highlightBackground,
88
93
  };
89
94
  }, [outerHeight, highlightBackground]);
90
- const chrome = (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
91
- jsx_runtime_1.jsx("div", { style: leftChromeStyle, children: keyframeControls ? (jsx_runtime_1.jsx("div", { style: keyframeControlsColumnStyle, children: keyframeControls })) : (jsx_runtime_1.jsxs("div", { style: chromeColumnStyle, children: [eye, indentWidth > 0 ? jsx_runtime_1.jsx(Padder_1.Padder, { depth: depth }) : null, arrow] })) }), children] }));
95
+ const shouldRenderLeftChrome = !isFieldRow || keyframeControlsPlacement === 'before-label';
96
+ const chrome = (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [shouldRenderLeftChrome ? (keyframeControls ? (jsx_runtime_1.jsx(exports.TimelineRowKeyframeControlsColumn, { depth: depth, children: keyframeControls })) : (jsx_runtime_1.jsx("div", { style: leftChromeStyle, children: jsx_runtime_1.jsxs("div", { style: chromeColumnStyle, children: [eye, indentWidth > 0 ? jsx_runtime_1.jsx(Padder_1.Padder, { depth: depth }) : null, arrow] }) }))) : null, children] }));
92
97
  if (outerStyle) {
93
98
  return (jsx_runtime_1.jsx("div", { ref: ref, style: outerStyle, onDragLeave: onDragLeave, onDragOver: onDragOver, onDrop: onDrop, onPointerDown: selectable ? onPointerDown : undefined, onContextMenu: selectable ? onContextMenu : undefined, onDoubleClick: onDoubleClick, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, children: jsx_runtime_1.jsx("div", { style: innerRowStyle, children: chrome }) }));
94
99
  }
@@ -2,6 +2,7 @@ type TimelineRowLayout = {
2
2
  readonly basePadding: number;
3
3
  readonly highlightSelectedLabel: boolean;
4
4
  readonly keyframeControlsPadding: number;
5
+ readonly keyframeControlsPlacement: 'before-label' | 'after-label';
5
6
  readonly rowBorderRadius: number;
6
7
  readonly rowHorizontalMargin: number;
7
8
  };
@@ -2,18 +2,21 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TimelineRowLayoutContext = exports.INSPECTOR_TIMELINE_ROW_LAYOUT = void 0;
4
4
  const react_1 = require("react");
5
+ const InspectorPanelLayout_1 = require("../InspectorPanelLayout");
5
6
  const timeline_row_layout_1 = require("./timeline-row-layout");
6
7
  exports.INSPECTOR_TIMELINE_ROW_LAYOUT = {
7
8
  basePadding: timeline_row_layout_1.INSPECTOR_ROW_BASE_PADDING,
8
9
  highlightSelectedLabel: false,
9
10
  keyframeControlsPadding: 0,
11
+ keyframeControlsPlacement: 'after-label',
10
12
  rowBorderRadius: 4,
11
- rowHorizontalMargin: 4,
13
+ rowHorizontalMargin: InspectorPanelLayout_1.INSPECTOR_PANEL_HORIZONTAL_PADDING,
12
14
  };
13
15
  exports.TimelineRowLayoutContext = (0, react_1.createContext)({
14
16
  basePadding: timeline_row_layout_1.TIMELINE_ROW_BASE_PADDING,
15
17
  highlightSelectedLabel: true,
16
18
  keyframeControlsPadding: timeline_row_layout_1.TIMELINE_ROW_BASE_PADDING,
19
+ keyframeControlsPlacement: 'before-label',
17
20
  rowBorderRadius: 0,
18
21
  rowHorizontalMargin: 0,
19
22
  });
@@ -15,11 +15,15 @@ const leftDraggerStyle = {
15
15
  paddingBottom: 2,
16
16
  paddingLeft: 0,
17
17
  paddingTop: 2,
18
+ textAlign: 'left',
19
+ width: 52,
18
20
  };
19
21
  const rightDraggerStyle = {
20
22
  paddingBottom: 2,
21
23
  paddingRight: 0,
22
24
  paddingTop: 2,
25
+ textAlign: 'left',
26
+ width: 52,
23
27
  };
24
28
  const containerStyle = {
25
29
  alignItems: 'center',
@@ -905,7 +905,7 @@ const TimelineSequenceItemInner = ({ children, connectedCompositions, nestedDept
905
905
  clientId: previewServerState.clientId,
906
906
  });
907
907
  }, [canDropEffect, nodePath, previewServerState, validatedLocation]);
908
- const trackRow = (jsx_runtime_1.jsx(TimelineRowChrome_1.TimelineRowChrome, { depth: nestedDepth, eye: canToggleVisibility ? (jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEye, { type: sequence.type === 'audio' ? 'speaker' : 'eye', hidden: isItemHidden, onInvoked: onToggleVisibility })) : (jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEyeSpacer, {})), arrow: hasExpandableContent && nodePathInfo !== null ? (jsx_runtime_1.jsx(TimelineSequenceExpandArrow, { disabled: !previewInteractive, isExpanded: isExpanded, nodePathInfo: nodePathInfo, sequence: sequence })) : (jsx_runtime_1.jsx(TimelineExpandArrowButton_1.TimelineExpandArrowSpacer, {})), style: rowStyle, selected: selected, selectable: selectable, selectionItem: selectionItem, onSelect: onSelect, showSelectedBackground: true, containsSelection: containsSelection, hovered: hovered, outerHeight: outerHeight, onDragLeave: canDropEffect ? onEffectDragLeave : undefined, onDragOver: canDropEffect ? onEffectDragOver : undefined, onDrop: canDropEffect ? onEffectDrop : undefined, onDoubleClick: canHandleSequenceDoubleClick ? onSequenceDoubleClick : undefined, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, children: jsx_runtime_1.jsxs("div", { style: labelContainerStyle, children: [connectedCompositions.length > 0 ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
908
+ const trackRow = (jsx_runtime_1.jsx(TimelineRowChrome_1.TimelineRowChrome, { depth: nestedDepth, eye: canToggleVisibility ? (jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEye, { type: sequence.type === 'audio' ? 'speaker' : 'eye', hidden: isItemHidden, onInvoked: onToggleVisibility })) : (jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEyeSpacer, {})), arrow: hasExpandableContent && nodePathInfo !== null ? (jsx_runtime_1.jsx(TimelineSequenceExpandArrow, { disabled: !previewInteractive, isExpanded: isExpanded, nodePathInfo: nodePathInfo, sequence: sequence })) : (jsx_runtime_1.jsx(TimelineExpandArrowButton_1.TimelineExpandArrowSpacer, {})), style: rowStyle, selected: selected, selectable: selectable, selectionItem: selectionItem, onSelect: onSelect, showSelectedBackground: true, containsSelection: containsSelection, hovered: hovered, isFieldRow: false, outerHeight: outerHeight, onDragLeave: canDropEffect ? onEffectDragLeave : undefined, onDragOver: canDropEffect ? onEffectDragOver : undefined, onDrop: canDropEffect ? onEffectDrop : undefined, onDoubleClick: canHandleSequenceDoubleClick ? onSequenceDoubleClick : undefined, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, children: jsx_runtime_1.jsxs("div", { style: labelContainerStyle, children: [connectedCompositions.length > 0 ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
909
909
  jsx_runtime_1.jsx(CompositionOrStillIcon_1.CompositionOrStillIcon, { color: (0, TimelineSelection_1.getTimelineColor)(false, false), composition: connectedCompositions[0], style: connectedCompositionIconStyle }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 })
910
910
  ] })) : null, jsx_runtime_1.jsx(TimelineSequenceName_1.TimelineSequenceName, { displayName: timelineDisplayName, fallbackDisplayName: fallbackDisplayName, selected: selected, containsSelection: containsSelection, editing: isRenaming, onCancelEditing: onCancelRenaming, onSaveName: onSaveName }), numberOfHiddenDuplicates > 0 ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
911
911
  jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(TimelineDuplicateCount_1.TimelineDuplicateCount, { count: numberOfHiddenDuplicates })