@remotion/studio 4.0.437 → 4.0.439

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 (123) hide show
  1. package/dist/Studio.js +2 -2
  2. package/dist/api/helpers/calc-new-props.js +5 -8
  3. package/dist/api/update-default-props.d.ts +7 -3
  4. package/dist/api/update-default-props.js +7 -21
  5. package/dist/api/visual-control.js +6 -0
  6. package/dist/components/DefaultPropsEditor.d.ts +8 -0
  7. package/dist/components/DefaultPropsEditor.js +15 -0
  8. package/dist/components/Editor.js +0 -14
  9. package/dist/components/EditorContexts.js +6 -3
  10. package/dist/components/KeyboardShortcutsExplainer.js +6 -2
  11. package/dist/components/MenuBuildIndicator.js +1 -0
  12. package/dist/components/MenuToolbar.js +3 -1
  13. package/dist/components/NewComposition/InputDragger.js +2 -2
  14. package/dist/components/NewComposition/RemTextarea.js +4 -0
  15. package/dist/components/ObserveDefaultPropsContext.d.ts +11 -0
  16. package/dist/components/ObserveDefaultPropsContext.js +120 -0
  17. package/dist/components/OptionsPanel.js +90 -28
  18. package/dist/components/RenderModal/DataEditor.d.ts +4 -5
  19. package/dist/components/RenderModal/DataEditor.js +9 -111
  20. package/dist/components/RenderModal/RenderModalJSONPropsEditor.d.ts +1 -1
  21. package/dist/components/RenderModal/RenderModalJSONPropsEditor.js +52 -48
  22. package/dist/components/RenderModal/SchemaEditor/Fieldset.d.ts +0 -1
  23. package/dist/components/RenderModal/SchemaEditor/SchemaEditor.d.ts +4 -9
  24. package/dist/components/RenderModal/SchemaEditor/SchemaEditor.js +2 -61
  25. package/dist/components/RenderModal/SchemaEditor/SchemaErrorMessages.d.ts +0 -1
  26. package/dist/components/RenderModal/SchemaEditor/SchemaErrorMessages.js +2 -3
  27. package/dist/components/RenderModal/SchemaEditor/SchemaLabel.d.ts +0 -6
  28. package/dist/components/RenderModal/SchemaEditor/SchemaLabel.js +2 -5
  29. package/dist/components/RenderModal/SchemaEditor/SchemaSeparationLine.d.ts +3 -1
  30. package/dist/components/RenderModal/SchemaEditor/SchemaSeparationLine.js +1 -1
  31. package/dist/components/RenderModal/SchemaEditor/ZodArrayEditor.d.ts +1 -6
  32. package/dist/components/RenderModal/SchemaEditor/ZodArrayEditor.js +15 -26
  33. package/dist/components/RenderModal/SchemaEditor/ZodArrayItemEditor.d.ts +0 -5
  34. package/dist/components/RenderModal/SchemaEditor/ZodArrayItemEditor.js +7 -12
  35. package/dist/components/RenderModal/SchemaEditor/ZodBooleanEditor.d.ts +0 -7
  36. package/dist/components/RenderModal/SchemaEditor/ZodBooleanEditor.js +5 -15
  37. package/dist/components/RenderModal/SchemaEditor/ZodColorEditor.d.ts +1 -6
  38. package/dist/components/RenderModal/SchemaEditor/ZodColorEditor.js +30 -28
  39. package/dist/components/RenderModal/SchemaEditor/ZodDateEditor.d.ts +1 -6
  40. package/dist/components/RenderModal/SchemaEditor/ZodDateEditor.js +13 -17
  41. package/dist/components/RenderModal/SchemaEditor/ZodDefaultEditor.d.ts +0 -5
  42. package/dist/components/RenderModal/SchemaEditor/ZodDefaultEditor.js +2 -2
  43. package/dist/components/RenderModal/SchemaEditor/ZodDiscriminatedUnionEditor.d.ts +1 -6
  44. package/dist/components/RenderModal/SchemaEditor/ZodDiscriminatedUnionEditor.js +13 -28
  45. package/dist/components/RenderModal/SchemaEditor/ZodEffectEditor.d.ts +1 -5
  46. package/dist/components/RenderModal/SchemaEditor/ZodEffectEditor.js +11 -12
  47. package/dist/components/RenderModal/SchemaEditor/ZodEnumEditor.d.ts +1 -5
  48. package/dist/components/RenderModal/SchemaEditor/ZodEnumEditor.js +12 -16
  49. package/dist/components/RenderModal/SchemaEditor/ZodFieldValidation.d.ts +2 -2
  50. package/dist/components/RenderModal/SchemaEditor/ZodFieldValidation.js +1 -2
  51. package/dist/components/RenderModal/SchemaEditor/ZodMatrixEditor.d.ts +1 -6
  52. package/dist/components/RenderModal/SchemaEditor/ZodMatrixEditor.js +20 -30
  53. package/dist/components/RenderModal/SchemaEditor/ZodNonEditableValue.d.ts +0 -2
  54. package/dist/components/RenderModal/SchemaEditor/ZodNonEditableValue.js +3 -6
  55. package/dist/components/RenderModal/SchemaEditor/ZodNullableEditor.d.ts +0 -5
  56. package/dist/components/RenderModal/SchemaEditor/ZodNullableEditor.js +2 -2
  57. package/dist/components/RenderModal/SchemaEditor/ZodNumberEditor.d.ts +1 -6
  58. package/dist/components/RenderModal/SchemaEditor/ZodNumberEditor.js +16 -21
  59. package/dist/components/RenderModal/SchemaEditor/ZodObjectEditor.d.ts +2 -7
  60. package/dist/components/RenderModal/SchemaEditor/ZodObjectEditor.js +27 -49
  61. package/dist/components/RenderModal/SchemaEditor/ZodOptionalEditor.d.ts +0 -5
  62. package/dist/components/RenderModal/SchemaEditor/ZodOptionalEditor.js +2 -2
  63. package/dist/components/RenderModal/SchemaEditor/ZodOrNullishEditor.d.ts +1 -6
  64. package/dist/components/RenderModal/SchemaEditor/ZodOrNullishEditor.js +8 -15
  65. package/dist/components/RenderModal/SchemaEditor/ZodStaticFileEditor.d.ts +1 -6
  66. package/dist/components/RenderModal/SchemaEditor/ZodStaticFileEditor.js +10 -15
  67. package/dist/components/RenderModal/SchemaEditor/ZodStringEditor.d.ts +1 -6
  68. package/dist/components/RenderModal/SchemaEditor/ZodStringEditor.js +13 -17
  69. package/dist/components/RenderModal/SchemaEditor/ZodSwitch.d.ts +3 -6
  70. package/dist/components/RenderModal/SchemaEditor/ZodSwitch.js +27 -27
  71. package/dist/components/RenderModal/SchemaEditor/ZodTextareaEditor.d.ts +0 -5
  72. package/dist/components/RenderModal/SchemaEditor/ZodTextareaEditor.js +45 -17
  73. package/dist/components/RenderModal/SchemaEditor/ZodTupleEditor.d.ts +1 -6
  74. package/dist/components/RenderModal/SchemaEditor/ZodTupleEditor.js +15 -26
  75. package/dist/components/RenderModal/SchemaEditor/ZodTupleItemEditor.d.ts +0 -5
  76. package/dist/components/RenderModal/SchemaEditor/ZodTupleItemEditor.js +4 -11
  77. package/dist/components/RenderModal/SchemaEditor/ZodUnionEditor.d.ts +0 -5
  78. package/dist/components/RenderModal/SchemaEditor/ZodUnionEditor.js +6 -6
  79. package/dist/components/RenderModal/ServerRenderModal.js +9 -3
  80. package/dist/components/RenderModal/WebRenderModal.js +9 -3
  81. package/dist/components/RenderModal/get-render-modal-warnings.d.ts +1 -2
  82. package/dist/components/RenderModal/get-render-modal-warnings.js +4 -6
  83. package/dist/components/RenderQueue/actions.d.ts +1 -2
  84. package/dist/components/RenderQueue/actions.js +1 -13
  85. package/dist/components/Timeline/Timeline.js +14 -2
  86. package/dist/components/Timeline/TimelineExpandedSection.js +7 -1
  87. package/dist/components/Timeline/TimelineFieldRow.d.ts +1 -0
  88. package/dist/components/Timeline/TimelineFieldRow.js +25 -3
  89. package/dist/components/Timeline/TimelineNumberField.js +4 -6
  90. package/dist/components/Timeline/TimelineRotationField.js +4 -6
  91. package/dist/components/Timeline/TimelineTranslateField.js +28 -13
  92. package/dist/components/TopPanel.js +10 -5
  93. package/dist/components/UndoRedoButtons.d.ts +2 -0
  94. package/dist/components/UndoRedoButtons.js +116 -0
  95. package/dist/components/VisualControls/VisualControlHandle.js +18 -18
  96. package/dist/components/VisualControls/VisualControlsUndoSync.d.ts +2 -0
  97. package/dist/components/VisualControls/VisualControlsUndoSync.js +23 -0
  98. package/dist/error-overlay/react-overlay/listen-to-runtime-errors.js +0 -1
  99. package/dist/esm/{chunk-5dvr7831.js → chunk-nnz9f1vq.js} +4365 -5219
  100. package/dist/esm/index.mjs +25 -28
  101. package/dist/esm/internals.mjs +4365 -5219
  102. package/dist/esm/previewEntry.mjs +4560 -5421
  103. package/dist/esm/renderEntry.mjs +6 -6
  104. package/dist/helpers/client-id.js +9 -0
  105. package/dist/helpers/document-title.d.ts +0 -1
  106. package/dist/helpers/document-title.js +1 -17
  107. package/dist/hot-middleware-client/process-update.js +6 -14
  108. package/dist/icons/redo.d.ts +3 -0
  109. package/dist/icons/redo.js +8 -0
  110. package/dist/icons/undo.d.ts +3 -0
  111. package/dist/icons/undo.js +8 -0
  112. package/dist/renderEntry.js +7 -6
  113. package/dist/visual-controls/VisualControls.js +9 -5
  114. package/dist/visual-controls/get-current-edited-value.js +5 -4
  115. package/dist/visual-controls/visual-control-store.d.ts +7 -0
  116. package/dist/visual-controls/visual-control-store.js +22 -0
  117. package/package.json +9 -9
  118. package/dist/components/GlobalPropsEditorUpdateButton.d.ts +0 -5
  119. package/dist/components/GlobalPropsEditorUpdateButton.js +0 -78
  120. package/dist/components/RenderModal/SchemaEditor/SchemaSaveButton.d.ts +0 -5
  121. package/dist/components/RenderModal/SchemaEditor/SchemaSaveButton.js +0 -18
  122. package/dist/components/RenderModal/SchemaEditor/local-state.d.ts +0 -25
  123. package/dist/components/RenderModal/SchemaEditor/local-state.js +0 -107
@@ -38,8 +38,10 @@ const jsx_runtime_1 = require("react/jsx-runtime");
38
38
  const react_1 = __importStar(require("react"));
39
39
  const remotion_1 = require("remotion");
40
40
  const calculate_timeline_1 = require("../../helpers/calculate-timeline");
41
+ const client_id_1 = require("../../helpers/client-id");
41
42
  const colors_1 = require("../../helpers/colors");
42
43
  const timeline_layout_1 = require("../../helpers/timeline-layout");
44
+ const ExpandedTracksProvider_1 = require("../ExpandedTracksProvider");
43
45
  const is_menu_item_1 = require("../Menu/is-menu-item");
44
46
  const SplitterContainer_1 = require("../Splitter/SplitterContainer");
45
47
  const SplitterElement_1 = require("../Splitter/SplitterElement");
@@ -68,6 +70,10 @@ const noop = () => undefined;
68
70
  const TimelineInner = () => {
69
71
  var _a;
70
72
  const { sequences } = (0, react_1.useContext)(remotion_1.Internals.SequenceManager);
73
+ const { expandedTracks } = (0, react_1.useContext)(ExpandedTracksProvider_1.ExpandedTracksContext);
74
+ const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
75
+ const visualModeEnabled = Boolean(process.env.EXPERIMENTAL_VISUAL_MODE_ENABLED) &&
76
+ previewServerState.type === 'connected';
71
77
  const videoConfig = remotion_1.Internals.useUnsafeVideoConfig();
72
78
  const timeline = (0, react_1.useMemo)(() => {
73
79
  if (!videoConfig) {
@@ -91,9 +97,15 @@ const TimelineInner = () => {
91
97
  const inner = (0, react_1.useMemo)(() => {
92
98
  return {
93
99
  height: shown.reduce((acc, track) => {
100
+ var _a;
101
+ const isExpanded = visualModeEnabled && ((_a = expandedTracks[track.sequence.id]) !== null && _a !== void 0 ? _a : false);
94
102
  return (acc +
95
103
  (0, timeline_layout_1.getTimelineLayerHeight)(track.sequence.type === 'video' ? 'video' : 'other') +
96
- Number(timeline_layout_1.TIMELINE_ITEM_BORDER_BOTTOM));
104
+ Number(timeline_layout_1.TIMELINE_ITEM_BORDER_BOTTOM) +
105
+ (isExpanded
106
+ ? (0, timeline_layout_1.getExpandedTrackHeight)(track.sequence.controls) +
107
+ timeline_layout_1.TIMELINE_ITEM_BORDER_BOTTOM
108
+ : 0));
97
109
  }, 0) +
98
110
  timeline_layout_1.TIMELINE_ITEM_BORDER_BOTTOM +
99
111
  (hasBeenCut ? MaxTimelineTracks_1.MAX_TIMELINE_TRACKS_NOTICE_HEIGHT : 0) +
@@ -103,7 +115,7 @@ const TimelineInner = () => {
103
115
  minHeight: '100%',
104
116
  overflowX: 'hidden',
105
117
  };
106
- }, [hasBeenCut, shown]);
118
+ }, [hasBeenCut, shown, expandedTracks, visualModeEnabled]);
107
119
  return (jsx_runtime_1.jsx("div", { ref: timeline_refs_1.timelineVerticalScroll, style: container, className: 'css-reset ' + is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(TimelineWidthProvider_1.TimelineWidthProvider, { children: jsx_runtime_1.jsx("div", { style: inner, children: jsx_runtime_1.jsxs(SplitterContainer_1.SplitterContainer, { orientation: "vertical", defaultFlex: 0.2, id: "names-to-timeline", maxFlex: 0.5, minFlex: 0.15, children: [
108
120
  jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { type: "flexer", sticky: jsx_runtime_1.jsx(TimelineTimeIndicators_1.TimelineTimePlaceholders, {}), children: jsx_runtime_1.jsx(TimelineList_1.TimelineList, { timeline: shown }) }), jsx_runtime_1.jsx(SplitterHandle_1.SplitterHandle, { onCollapse: noop, allowToCollapse: "none" }), jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { type: "anti-flexer", sticky: null, children: jsx_runtime_1.jsxs(TimelineScrollable_1.TimelineScrollable, { children: [
109
121
  jsx_runtime_1.jsx(TimelineTracks_1.TimelineTracks, { timeline: shown, hasBeenCut: hasBeenCut }), jsx_runtime_1.jsx(TimelineInOutPointer_1.TimelineInOutPointer, {}), jsx_runtime_1.jsx(TimelinePlayCursorSyncer_1.TimelinePlayCursorSyncer, {}), jsx_runtime_1.jsx(TimelineDragHandler_1.TimelineDragHandler, {}), jsx_runtime_1.jsx(TimelineTimeIndicators_1.TimelineTimeIndicators, {}), jsx_runtime_1.jsx(TimelineSlider_1.TimelineSlider, {})
@@ -78,9 +78,15 @@ const TimelineExpandedSection = ({ sequence, originalLocation, nestedDepth, node
78
78
  height: expandedHeight,
79
79
  };
80
80
  }, [expandedHeight]);
81
+ const keysToObserve = (0, react_1.useMemo)(() => {
82
+ if (!schemaFields) {
83
+ return [];
84
+ }
85
+ return schemaFields.map((f) => f.key);
86
+ }, [schemaFields]);
81
87
  return (jsx_runtime_1.jsx("div", { style: style, children: schemaFields
82
88
  ? schemaFields.map((field, i) => {
83
- return (jsx_runtime_1.jsxs(react_1.default.Fragment, { children: [i > 0 ? jsx_runtime_1.jsx("div", { style: separator }) : null, jsx_runtime_1.jsx(TimelineFieldRow_1.TimelineFieldRow, { field: field, overrideId: overrideId, validatedLocation: validatedLocation, nestedDepth: nestedDepth, nodePath: nodePath })
89
+ return (jsx_runtime_1.jsxs(react_1.default.Fragment, { children: [i > 0 ? jsx_runtime_1.jsx("div", { style: separator }) : null, jsx_runtime_1.jsx(TimelineFieldRow_1.TimelineFieldRow, { field: field, overrideId: overrideId, validatedLocation: validatedLocation, nestedDepth: nestedDepth, nodePath: nodePath, keysToObserve: keysToObserve })
84
90
  ] }, field.key));
85
91
  })
86
92
  : 'No schema' }));
@@ -8,4 +8,5 @@ export declare const TimelineFieldRow: React.FC<{
8
8
  readonly validatedLocation: CodePosition | null;
9
9
  readonly nestedDepth: number;
10
10
  readonly nodePath: SequenceNodePath | null;
11
+ readonly keysToObserve: string[];
11
12
  }>;
@@ -18,6 +18,7 @@ const fieldRowBase = {
18
18
  const fieldName = {
19
19
  fontSize: 12,
20
20
  color: 'rgba(255, 255, 255, 0.8)',
21
+ userSelect: 'none',
21
22
  };
22
23
  const fieldLabelRow = {
23
24
  flex: '0 0 50%',
@@ -26,7 +27,7 @@ const fieldLabelRow = {
26
27
  alignItems: 'center',
27
28
  gap: 6,
28
29
  };
29
- const TimelineFieldRow = ({ field, overrideId, validatedLocation, nestedDepth, nodePath }) => {
30
+ const TimelineFieldRow = ({ field, overrideId, validatedLocation, nestedDepth, nodePath, keysToObserve, }) => {
30
31
  var _a, _b, _c, _d;
31
32
  const { setDragOverrides, clearDragOverrides, dragOverrides, codeValues: allPropStatuses, } = (0, react_1.useContext)(remotion_1.Internals.VisualModeOverridesContext);
32
33
  const propStatuses = ((_a = allPropStatuses[overrideId]) !== null && _a !== void 0 ? _a : null);
@@ -42,6 +43,7 @@ const TimelineFieldRow = ({ field, overrideId, validatedLocation, nestedDepth, n
42
43
  defaultValue: field.fieldSchema.default,
43
44
  shouldResortToDefaultValueIfUndefined: true,
44
45
  });
46
+ const { setCodeValues } = (0, react_1.useContext)(remotion_1.Internals.VisualModeOverridesContext);
45
47
  const onSave = (0, react_1.useCallback)((key, value) => {
46
48
  if (!propStatuses || !validatedLocation || !nodePath) {
47
49
  return Promise.reject(new Error('Cannot save'));
@@ -59,8 +61,28 @@ const TimelineFieldRow = ({ field, overrideId, validatedLocation, nestedDepth, n
59
61
  key,
60
62
  value: JSON.stringify(value),
61
63
  defaultValue,
62
- }).then(() => undefined);
63
- }, [propStatuses, validatedLocation, nodePath, field.fieldSchema.default]);
64
+ observedKeys: keysToObserve,
65
+ }).then((data) => {
66
+ if (data.success) {
67
+ if (data.newStatus.canUpdate) {
68
+ setCodeValues(overrideId, data.newStatus.props);
69
+ }
70
+ else {
71
+ setCodeValues(overrideId, null);
72
+ }
73
+ return;
74
+ }
75
+ return Promise.reject(new Error(data.reason));
76
+ });
77
+ }, [
78
+ field.fieldSchema.default,
79
+ keysToObserve,
80
+ nodePath,
81
+ overrideId,
82
+ propStatuses,
83
+ setCodeValues,
84
+ validatedLocation,
85
+ ]);
64
86
  const onDragValueChange = (0, react_1.useCallback)((key, value) => {
65
87
  setDragOverrides(overrideId, key, value);
66
88
  }, [setDragOverrides, overrideId]);
@@ -12,20 +12,18 @@ const TimelineNumberField = ({ field, effectiveValue, canUpdate, onSave, onDragV
12
12
  setDragValue(newVal);
13
13
  onDragValueChange(field.key, newVal);
14
14
  }, [onDragValueChange, field.key]);
15
- (0, react_1.useEffect)(() => {
16
- setDragValue(null);
17
- onDragEnd();
18
- }, [field.currentValue, onDragEnd]);
19
15
  const onValueChangeEnd = (0, react_1.useCallback)((newVal) => {
20
16
  if (canUpdate && newVal !== codeValue) {
21
- onSave(field.key, newVal).catch(() => {
17
+ onSave(field.key, newVal).finally(() => {
22
18
  setDragValue(null);
19
+ onDragEnd();
23
20
  });
24
21
  }
25
22
  else {
26
23
  setDragValue(null);
24
+ onDragEnd();
27
25
  }
28
- }, [canUpdate, onSave, field.key, codeValue]);
26
+ }, [canUpdate, onSave, field.key, codeValue, onDragEnd]);
29
27
  const onTextChange = (0, react_1.useCallback)((newVal) => {
30
28
  if (canUpdate) {
31
29
  const parsed = Number(newVal);
@@ -22,21 +22,19 @@ const TimelineRotationField = ({ field, effectiveValue, codeValue, canUpdate, on
22
22
  setDragValue(newVal);
23
23
  onDragValueChange(field.key, `${newVal}deg`);
24
24
  }, [onDragValueChange, field.key]);
25
- (0, react_1.useEffect)(() => {
26
- setDragValue(null);
27
- onDragEnd();
28
- }, [field.currentValue, onDragEnd]);
29
25
  const onValueChangeEnd = (0, react_1.useCallback)((newVal) => {
30
26
  const newStr = `${newVal}deg`;
31
27
  if (canUpdate && newStr !== codeValue) {
32
- onSave(field.key, newStr).catch(() => {
28
+ onSave(field.key, newStr).finally(() => {
33
29
  setDragValue(null);
30
+ onDragEnd();
34
31
  });
35
32
  }
36
33
  else {
37
34
  setDragValue(null);
35
+ onDragEnd();
38
36
  }
39
- }, [canUpdate, onSave, field.key, codeValue]);
37
+ }, [canUpdate, onSave, field.key, codeValue, onDragEnd]);
40
38
  const onTextChange = (0, react_1.useCallback)((newVal) => {
41
39
  if (canUpdate) {
42
40
  const parsed = Number(newVal);
@@ -29,11 +29,6 @@ const TimelineTranslateField = ({ field, codeValue, effectiveValue, canUpdate, o
29
29
  const [dragY, setDragY] = (0, react_1.useState)(null);
30
30
  const [codeX, codeY] = (0, react_1.useMemo)(() => parseTranslate(String(effectiveValue !== null && effectiveValue !== void 0 ? effectiveValue : '0px 0px')), [effectiveValue]);
31
31
  const makeString = (0, react_1.useCallback)((x, y) => `${x}px ${y}px`, []);
32
- (0, react_1.useEffect)(() => {
33
- setDragX(null);
34
- setDragY(null);
35
- onDragEnd();
36
- }, [field.currentValue, onDragEnd]);
37
32
  const step = field.fieldSchema.type === 'translate' ? ((_a = field.fieldSchema.step) !== null && _a !== void 0 ? _a : 1) : 1;
38
33
  const stepDecimals = (0, react_1.useMemo)(() => (0, timeline_field_utils_1.getDecimalPlaces)(step), [step]);
39
34
  const formatter = (0, react_1.useCallback)((v) => {
@@ -52,14 +47,25 @@ const TimelineTranslateField = ({ field, codeValue, effectiveValue, canUpdate, o
52
47
  const currentY = dragY !== null && dragY !== void 0 ? dragY : codeY;
53
48
  const newStr = makeString(newVal, currentY);
54
49
  if (canUpdate && newStr !== codeValue) {
55
- onSave(field.key, newStr).catch(() => {
50
+ onSave(field.key, newStr).finally(() => {
56
51
  setDragX(null);
52
+ onDragEnd();
57
53
  });
58
54
  }
59
55
  else {
60
56
  setDragX(null);
57
+ onDragEnd();
61
58
  }
62
- }, [canUpdate, onSave, field.key, codeValue, dragY, codeY, makeString]);
59
+ }, [
60
+ dragY,
61
+ codeY,
62
+ makeString,
63
+ canUpdate,
64
+ codeValue,
65
+ onSave,
66
+ field.key,
67
+ onDragEnd,
68
+ ]);
63
69
  const onXTextChange = (0, react_1.useCallback)((newVal) => {
64
70
  if (canUpdate) {
65
71
  const parsed = Number(newVal);
@@ -68,13 +74,11 @@ const TimelineTranslateField = ({ field, codeValue, effectiveValue, canUpdate, o
68
74
  const newStr = makeString(parsed, currentY);
69
75
  if (newStr !== codeValue) {
70
76
  setDragX(parsed);
71
- onSave(field.key, newStr).catch(() => {
72
- setDragX(null);
73
- });
77
+ onSave(field.key, newStr);
74
78
  }
75
79
  }
76
80
  }
77
- }, [canUpdate, onSave, field.key, codeValue, dragY, codeY, makeString]);
81
+ }, [canUpdate, dragY, codeY, makeString, codeValue, onSave, field.key]);
78
82
  // --- Y callbacks ---
79
83
  const onYChange = (0, react_1.useCallback)((newVal) => {
80
84
  setDragY(newVal);
@@ -85,14 +89,25 @@ const TimelineTranslateField = ({ field, codeValue, effectiveValue, canUpdate, o
85
89
  const currentX = dragX !== null && dragX !== void 0 ? dragX : codeX;
86
90
  const newStr = makeString(currentX, newVal);
87
91
  if (canUpdate && newStr !== codeValue) {
88
- onSave(field.key, newStr).catch(() => {
92
+ onSave(field.key, newStr).finally(() => {
89
93
  setDragY(null);
94
+ onDragEnd();
90
95
  });
91
96
  }
92
97
  else {
93
98
  setDragY(null);
99
+ onDragEnd();
94
100
  }
95
- }, [canUpdate, onSave, field.key, codeValue, dragX, codeX, makeString]);
101
+ }, [
102
+ dragX,
103
+ codeX,
104
+ makeString,
105
+ canUpdate,
106
+ codeValue,
107
+ onSave,
108
+ field.key,
109
+ onDragEnd,
110
+ ]);
96
111
  const onYTextChange = (0, react_1.useCallback)((newVal) => {
97
112
  if (canUpdate) {
98
113
  const parsed = Number(newVal);
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.TopPanel = exports.useResponsiveSidebarStatus = void 0;
40
40
  const jsx_runtime_1 = require("react/jsx-runtime");
41
41
  const react_1 = __importStar(require("react"));
42
+ const remotion_1 = require("remotion");
42
43
  const mobile_layout_1 = require("../helpers/mobile-layout");
43
44
  const use_breakpoint_1 = require("../helpers/use-breakpoint");
44
45
  const editor_rulers_1 = require("../state/editor-rulers");
@@ -48,6 +49,7 @@ const CurrentCompositionSideEffects_1 = require("./CurrentCompositionSideEffects
48
49
  const use_is_ruler_visible_1 = require("./EditorRuler/use-is-ruler-visible");
49
50
  const ExplorerPanel_1 = require("./ExplorerPanel");
50
51
  const MobilePanel_1 = __importDefault(require("./MobilePanel"));
52
+ const ObserveDefaultPropsContext_1 = require("./ObserveDefaultPropsContext");
51
53
  const OptionsPanel_1 = require("./OptionsPanel");
52
54
  const PreviewToolbar_1 = require("./PreviewToolbar");
53
55
  const SplitterContainer_1 = require("./Splitter/SplitterContainer");
@@ -84,6 +86,7 @@ exports.useResponsiveSidebarStatus = useResponsiveSidebarStatus;
84
86
  const TopPanelInner = ({ readOnlyStudio, onMounted, drawRef, bufferStateDelayInMilliseconds }) => {
85
87
  const { setSidebarCollapsedState, sidebarCollapsedStateRight } = (0, react_1.useContext)(sidebar_1.SidebarContext);
86
88
  const rulersAreVisible = (0, use_is_ruler_visible_1.useIsRulerVisible)();
89
+ const { canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
87
90
  const actualStateLeft = (0, exports.useResponsiveSidebarStatus)();
88
91
  const actualStateRight = (0, react_1.useMemo)(() => {
89
92
  if (sidebarCollapsedStateRight === 'collapsed') {
@@ -107,10 +110,12 @@ const TopPanelInner = ({ readOnlyStudio, onMounted, drawRef, bufferStateDelayInM
107
110
  setSidebarCollapsedState({ left: null, right: 'collapsed' });
108
111
  }, [setSidebarCollapsedState]);
109
112
  const isMobileLayout = (0, mobile_layout_1.useMobileLayout)();
110
- return (jsx_runtime_1.jsxs("div", { style: container, children: [
111
- jsx_runtime_1.jsx("div", { style: row, children: jsx_runtime_1.jsxs(SplitterContainer_1.SplitterContainer, { minFlex: 0.15, maxFlex: 0.4, defaultFlex: 0.2, id: "sidebar-to-preview", orientation: "vertical", children: [actualStateLeft === 'expanded' ? (isMobileLayout ? (jsx_runtime_1.jsx(MobilePanel_1.default, { onClose: onCollapseLeft, children: jsx_runtime_1.jsx(ExplorerPanel_1.ExplorerPanel, { readOnlyStudio: readOnlyStudio }) })) : (jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { sticky: null, type: "flexer", children: jsx_runtime_1.jsx(ExplorerPanel_1.ExplorerPanel, { readOnlyStudio: readOnlyStudio }) }))) : null, actualStateLeft === 'expanded' ? (jsx_runtime_1.jsx(SplitterHandle_1.SplitterHandle, { allowToCollapse: "left", onCollapse: onCollapseLeft })) : null, jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { sticky: null, type: "anti-flexer", children: jsx_runtime_1.jsxs(SplitterContainer_1.SplitterContainer, { minFlex: 0.5, maxFlex: 0.8, defaultFlex: 0.7, id: "canvas-to-right-sidebar", orientation: "vertical", children: [
112
- jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { sticky: null, type: "flexer", children: jsx_runtime_1.jsx("div", { ref: drawRef, style: canvasContainerStyle, children: jsx_runtime_1.jsx(CanvasIfSizeIsAvailable_1.CanvasIfSizeIsAvailable, {}) }) }), actualStateRight === 'expanded' ? (jsx_runtime_1.jsx(SplitterHandle_1.SplitterHandle, { allowToCollapse: "right", onCollapse: onCollapseRight })) : null, actualStateRight === 'expanded' ? (isMobileLayout ? (jsx_runtime_1.jsx(MobilePanel_1.default, { onClose: onCollapseRight, children: jsx_runtime_1.jsx(OptionsPanel_1.OptionsPanel, { readOnlyStudio: readOnlyStudio }) })) : (jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { sticky: null, type: "anti-flexer", children: jsx_runtime_1.jsx(OptionsPanel_1.OptionsPanel, { readOnlyStudio: readOnlyStudio }) }))) : null] }) })
113
- ] }) }), jsx_runtime_1.jsx(PreviewToolbar_1.PreviewToolbar, { bufferStateDelayInMilliseconds: bufferStateDelayInMilliseconds, readOnlyStudio: readOnlyStudio }), jsx_runtime_1.jsx(CurrentCompositionSideEffects_1.CurrentCompositionKeybindings, { readOnlyStudio: readOnlyStudio }), jsx_runtime_1.jsx(CurrentCompositionSideEffects_1.TitleUpdater, {})
114
- ] }));
113
+ return (jsx_runtime_1.jsx(ObserveDefaultPropsContext_1.ObserveDefaultProps, { compositionId: (canvasContent === null || canvasContent === void 0 ? void 0 : canvasContent.type) === 'composition'
114
+ ? canvasContent.compositionId
115
+ : null, readOnlyStudio: readOnlyStudio, children: jsx_runtime_1.jsxs("div", { style: container, children: [
116
+ jsx_runtime_1.jsx("div", { style: row, children: jsx_runtime_1.jsxs(SplitterContainer_1.SplitterContainer, { minFlex: 0.15, maxFlex: 0.4, defaultFlex: 0.2, id: "sidebar-to-preview", orientation: "vertical", children: [actualStateLeft === 'expanded' ? (isMobileLayout ? (jsx_runtime_1.jsx(MobilePanel_1.default, { onClose: onCollapseLeft, children: jsx_runtime_1.jsx(ExplorerPanel_1.ExplorerPanel, { readOnlyStudio: readOnlyStudio }) })) : (jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { sticky: null, type: "flexer", children: jsx_runtime_1.jsx(ExplorerPanel_1.ExplorerPanel, { readOnlyStudio: readOnlyStudio }) }))) : null, actualStateLeft === 'expanded' ? (jsx_runtime_1.jsx(SplitterHandle_1.SplitterHandle, { allowToCollapse: "left", onCollapse: onCollapseLeft })) : null, jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { sticky: null, type: "anti-flexer", children: jsx_runtime_1.jsxs(SplitterContainer_1.SplitterContainer, { minFlex: 0.5, maxFlex: 0.8, defaultFlex: 0.7, id: "canvas-to-right-sidebar", orientation: "vertical", children: [
117
+ jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { sticky: null, type: "flexer", children: jsx_runtime_1.jsx("div", { ref: drawRef, style: canvasContainerStyle, children: jsx_runtime_1.jsx(CanvasIfSizeIsAvailable_1.CanvasIfSizeIsAvailable, {}) }) }), actualStateRight === 'expanded' ? (jsx_runtime_1.jsx(SplitterHandle_1.SplitterHandle, { allowToCollapse: "right", onCollapse: onCollapseRight })) : null, actualStateRight === 'expanded' ? (isMobileLayout ? (jsx_runtime_1.jsx(MobilePanel_1.default, { onClose: onCollapseRight, children: jsx_runtime_1.jsx(OptionsPanel_1.OptionsPanel, { readOnlyStudio: readOnlyStudio }) })) : (jsx_runtime_1.jsx(SplitterElement_1.SplitterElement, { sticky: null, type: "anti-flexer", children: jsx_runtime_1.jsx(OptionsPanel_1.OptionsPanel, { readOnlyStudio: readOnlyStudio }) }))) : null] }) })
118
+ ] }) }), jsx_runtime_1.jsx(PreviewToolbar_1.PreviewToolbar, { bufferStateDelayInMilliseconds: bufferStateDelayInMilliseconds, readOnlyStudio: readOnlyStudio }), jsx_runtime_1.jsx(CurrentCompositionSideEffects_1.CurrentCompositionKeybindings, { readOnlyStudio: readOnlyStudio }), jsx_runtime_1.jsx(CurrentCompositionSideEffects_1.TitleUpdater, {})
119
+ ] }) }));
115
120
  };
116
121
  exports.TopPanel = react_1.default.memo(TopPanelInner);
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const UndoRedoButtons: React.FC;
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UndoRedoButtons = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const ShortcutHint_1 = require("../error-overlay/remotion-overlay/ShortcutHint");
7
+ const client_id_1 = require("../helpers/client-id");
8
+ const use_keybinding_1 = require("../helpers/use-keybinding");
9
+ const redo_1 = require("../icons/redo");
10
+ const undo_1 = require("../icons/undo");
11
+ const call_api_1 = require("./call-api");
12
+ const InlineAction_1 = require("./InlineAction");
13
+ const iconStyle = {
14
+ width: 16,
15
+ height: 16,
16
+ };
17
+ const UndoRedoButtons = () => {
18
+ const [undoFile, setUndoFile] = (0, react_1.useState)(null);
19
+ const [redoFile, setRedoFile] = (0, react_1.useState)(null);
20
+ const { subscribeToEvent } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
21
+ const keybindings = (0, use_keybinding_1.useKeybinding)();
22
+ const undoInFlight = (0, react_1.useRef)(false);
23
+ const redoInFlight = (0, react_1.useRef)(false);
24
+ (0, react_1.useEffect)(() => {
25
+ const unsub = subscribeToEvent('undo-redo-stack-changed', (event) => {
26
+ if (event.type !== 'undo-redo-stack-changed') {
27
+ return;
28
+ }
29
+ setUndoFile(event.undoFile);
30
+ setRedoFile(event.redoFile);
31
+ });
32
+ return () => unsub();
33
+ }, [subscribeToEvent]);
34
+ const onUndo = (0, react_1.useCallback)(() => {
35
+ if (undoInFlight.current) {
36
+ return;
37
+ }
38
+ undoInFlight.current = true;
39
+ (0, call_api_1.callApi)('/api/undo', {})
40
+ .catch(() => {
41
+ // Ignore errors
42
+ })
43
+ .finally(() => {
44
+ undoInFlight.current = false;
45
+ });
46
+ }, []);
47
+ const onRedo = (0, react_1.useCallback)(() => {
48
+ if (redoInFlight.current) {
49
+ return;
50
+ }
51
+ redoInFlight.current = true;
52
+ (0, call_api_1.callApi)('/api/redo', {})
53
+ .catch(() => {
54
+ // Ignore errors
55
+ })
56
+ .finally(() => {
57
+ redoInFlight.current = false;
58
+ });
59
+ }, []);
60
+ (0, react_1.useEffect)(() => {
61
+ const undo = keybindings.registerKeybinding({
62
+ event: 'keydown',
63
+ key: 'z',
64
+ commandCtrlKey: true,
65
+ callback: (e) => {
66
+ if (e.shiftKey) {
67
+ return;
68
+ }
69
+ if (undoFile) {
70
+ onUndo();
71
+ }
72
+ },
73
+ preventDefault: true,
74
+ triggerIfInputFieldFocused: false,
75
+ keepRegisteredWhenNotHighestContext: false,
76
+ });
77
+ const redo = keybindings.registerKeybinding({
78
+ event: 'keydown',
79
+ key: 'y',
80
+ commandCtrlKey: true,
81
+ callback: () => {
82
+ if (redoFile) {
83
+ onRedo();
84
+ }
85
+ },
86
+ preventDefault: true,
87
+ triggerIfInputFieldFocused: false,
88
+ keepRegisteredWhenNotHighestContext: false,
89
+ });
90
+ return () => {
91
+ undo.unregister();
92
+ redo.unregister();
93
+ };
94
+ }, [keybindings, onRedo, onUndo, redoFile, undoFile]);
95
+ const undoTooltip = (0, use_keybinding_1.areKeyboardShortcutsDisabled)()
96
+ ? 'Undo'
97
+ : `Undo (${ShortcutHint_1.cmdOrCtrlCharacter}+Z)`;
98
+ const redoTooltip = (0, use_keybinding_1.areKeyboardShortcutsDisabled)()
99
+ ? 'Redo'
100
+ : `Redo (${ShortcutHint_1.cmdOrCtrlCharacter}+Y)`;
101
+ const renderUndo = (0, react_1.useCallback)((color) => {
102
+ return (jsx_runtime_1.jsx(undo_1.UndoIcon, { style: { ...iconStyle, color, opacity: undoFile ? 1 : 0.5 } }));
103
+ }, [undoFile]);
104
+ const renderRedo = (0, react_1.useCallback)((color) => {
105
+ return (jsx_runtime_1.jsx(redo_1.RedoIcon, { style: { ...iconStyle, color, opacity: redoFile ? 1 : 0.5 } }));
106
+ }, [redoFile]);
107
+ const canUndo = undoFile !== null;
108
+ const canRedo = redoFile !== null;
109
+ if (!canUndo && !canRedo) {
110
+ return null;
111
+ }
112
+ return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
113
+ jsx_runtime_1.jsx(InlineAction_1.InlineAction, { onClick: onUndo, renderAction: renderUndo, title: undoTooltip, disabled: !canUndo }), jsx_runtime_1.jsx(InlineAction_1.InlineAction, { onClick: onRedo, renderAction: renderRedo, title: redoTooltip, disabled: !canRedo })
114
+ ] }));
115
+ };
116
+ exports.UndoRedoButtons = UndoRedoButtons;
@@ -11,7 +11,6 @@ const get_zod_if_possible_1 = require("../get-zod-if-possible");
11
11
  const layout_1 = require("../layout");
12
12
  const NotificationCenter_1 = require("../Notifications/NotificationCenter");
13
13
  const extract_enum_json_paths_1 = require("../RenderModal/SchemaEditor/extract-enum-json-paths");
14
- const local_state_1 = require("../RenderModal/SchemaEditor/local-state");
15
14
  const ZodSwitch_1 = require("../RenderModal/SchemaEditor/ZodSwitch");
16
15
  const actions_1 = require("../RenderQueue/actions");
17
16
  const get_original_stack_trace_1 = require("./get-original-stack-trace");
@@ -25,23 +24,14 @@ const VisualControlHandle = ({ value, keyName }) => {
25
24
  const state = (0, react_1.useContext)(VisualControls_1.VisualControlsContext);
26
25
  const { updateValue } = (0, react_1.useContext)(VisualControls_1.SetVisualControlsContext);
27
26
  const { fastRefreshes, increaseManualRefreshes } = (0, react_1.useContext)(fast_refresh_context_1.FastRefreshContext);
28
- const [saving, setSaving] = (0, react_1.useState)(false);
27
+ const [_saving, setSaving] = (0, react_1.useState)(false);
29
28
  const currentValue = (0, get_current_edited_value_1.getVisualControlEditedValue)({
30
29
  handles: state.handles,
31
30
  key: keyName,
32
31
  });
33
32
  const originalFileName = (0, get_original_stack_trace_1.useOriginalFileName)(value.stack);
34
- const { localValue, RevisionContextProvider, onChange } = (0, local_state_1.useLocalState)({
35
- schema: value.schema,
36
- setValue: (updater) => {
37
- updateValue(keyName, updater(currentValue));
38
- increaseManualRefreshes();
39
- },
40
- unsavedValue: currentValue,
41
- savedValue: value.valueInCode,
42
- });
43
33
  const disableSave = window.remotion_isReadOnlyStudio || originalFileName.type !== 'loaded';
44
- const onSave = (0, react_1.useCallback)((updater) => {
34
+ const saveToFile = (0, react_1.useCallback)((updater) => {
45
35
  if (disableSave) {
46
36
  return;
47
37
  }
@@ -64,11 +54,14 @@ const VisualControlHandle = ({ value, keyName }) => {
64
54
  changes: [
65
55
  {
66
56
  id: keyName,
67
- newValueSerialized: no_react_1.NoReactInternals.serializeJSONWithSpecialTypes({
68
- data: val,
69
- indent: 2,
70
- staticBase: window.remotion_staticBase,
71
- }).serializedString,
57
+ newValueSerialized: val === undefined
58
+ ? ''
59
+ : no_react_1.NoReactInternals.serializeJSONWithSpecialTypes({
60
+ data: val,
61
+ indent: 2,
62
+ staticBase: window.remotion_staticBase,
63
+ }).serializedString,
64
+ newValueIsUndefined: val === undefined,
72
65
  enumPaths,
73
66
  },
74
67
  ],
@@ -90,8 +83,15 @@ const VisualControlHandle = ({ value, keyName }) => {
90
83
  (0, react_1.useEffect)(() => {
91
84
  setSaving(false);
92
85
  }, [fastRefreshes]);
86
+ const setValue = (0, react_1.useCallback)((updater, { shouldSave }) => {
87
+ updateValue(keyName, updater(currentValue));
88
+ increaseManualRefreshes();
89
+ if (shouldSave) {
90
+ saveToFile(updater);
91
+ }
92
+ }, [currentValue, increaseManualRefreshes, keyName, saveToFile, updateValue]);
93
93
  return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
94
- jsx_runtime_1.jsx(VisualControlHandleHeader_1.VisualControlHandleHeader, { originalFileName: originalFileName }), jsx_runtime_1.jsx(layout_1.Spacing, { block: true, y: 0.5 }), jsx_runtime_1.jsx(RevisionContextProvider, { children: jsx_runtime_1.jsx(ZodSwitch_1.ZodSwitch, { mayPad: true, schema: value.schema, showSaveButton: !disableSave, saving: saving, saveDisabledByParent: false, onSave: onSave, jsonPath: [keyName], value: localValue.value, defaultValue: value.valueInCode, setValue: onChange, onRemove: null }) })
94
+ jsx_runtime_1.jsx(VisualControlHandleHeader_1.VisualControlHandleHeader, { originalFileName: originalFileName }), jsx_runtime_1.jsx(layout_1.Spacing, { block: true, y: 0.5 }), jsx_runtime_1.jsx(ZodSwitch_1.ZodSwitch, { mayPad: true, schema: value.schema, jsonPath: [keyName], value: currentValue, setValue: setValue, onRemove: null })
95
95
  ] }));
96
96
  };
97
97
  exports.VisualControlHandle = VisualControlHandle;
@@ -0,0 +1,2 @@
1
+ import type React from 'react';
2
+ export declare const VisualControlsUndoSync: React.FC;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VisualControlsUndoSync = void 0;
4
+ const react_1 = require("react");
5
+ const client_id_1 = require("../../helpers/client-id");
6
+ const VisualControls_1 = require("../../visual-controls/VisualControls");
7
+ const VisualControlsUndoSync = () => {
8
+ const { subscribeToEvent } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
9
+ const { updateValue } = (0, react_1.useContext)(VisualControls_1.SetVisualControlsContext);
10
+ (0, react_1.useEffect)(() => {
11
+ const unsub = subscribeToEvent('visual-control-values-changed', (event) => {
12
+ if (event.type !== 'visual-control-values-changed') {
13
+ return;
14
+ }
15
+ for (const entry of event.values) {
16
+ updateValue(entry.id, entry.isUndefined ? undefined : entry.value);
17
+ }
18
+ });
19
+ return () => unsub();
20
+ }, [subscribeToEvent, updateValue]);
21
+ return null;
22
+ };
23
+ exports.VisualControlsUndoSync = VisualControlsUndoSync;
@@ -38,7 +38,6 @@ const crashWithFrames = (crash) => (error) => {
38
38
  if (didHookOrderChange && !justRefreshedBecauseOfHooks) {
39
39
  // eslint-disable-next-line no-console
40
40
  console.log('Hook order changed. Reloading app...');
41
- window.remotion_unsavedProps = false;
42
41
  (0, url_state_1.reloadUrl)();
43
42
  }
44
43
  else {