@remotion/studio 4.0.443 → 4.0.444
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.
- package/dist/components/CanvasOrLoading.js +5 -0
- package/dist/components/Editor.js +10 -2
- package/dist/components/RenderErrorContext.d.ts +4 -0
- package/dist/components/RenderErrorContext.js +7 -0
- package/dist/components/RenderModal/RenderModalJSONPropsEditor.js +7 -5
- package/dist/components/RenderModal/SchemaEditor/SchemaEditor.js +5 -1
- package/dist/components/RenderModal/SchemaEditor/SchemaErrorMessages.js +5 -3
- package/dist/components/Timeline/TimelineListItem.js +71 -8
- package/dist/components/Timeline/TimelineVideoInfo.js +1 -18
- package/dist/components/Timeline/use-sequence-props-subscription.d.ts +1 -1
- package/dist/components/Timeline/use-sequence-props-subscription.js +25 -6
- package/dist/error-overlay/react-overlay/listen-to-runtime-errors.js +1 -5
- package/dist/error-overlay/remotion-overlay/CopyStackTrace.d.ts +5 -0
- package/dist/error-overlay/remotion-overlay/CopyStackTrace.js +50 -0
- package/dist/error-overlay/remotion-overlay/ErrorDisplay.js +16 -13
- package/dist/error-overlay/remotion-overlay/MediaPlaybackErrorExplainer.d.ts +4 -0
- package/dist/error-overlay/remotion-overlay/MediaPlaybackErrorExplainer.js +87 -0
- package/dist/esm/{chunk-cpv44d93.js → chunk-1zvzzrkf.js} +2481 -2300
- package/dist/esm/internals.mjs +2481 -2300
- package/dist/esm/previewEntry.mjs +2813 -2636
- package/dist/esm/renderEntry.mjs +1 -1
- package/dist/helpers/frame-database.d.ts +2 -2
- package/dist/helpers/frame-database.js +36 -25
- package/dist/helpers/use-max-media-duration.js +1 -1
- package/package.json +9 -9
- package/dist/error-overlay/remotion-overlay/CompositionIdsDropdown.d.ts +0 -5
- package/dist/error-overlay/remotion-overlay/CompositionIdsDropdown.js +0 -108
|
@@ -11,6 +11,7 @@ const Canvas_1 = require("./Canvas");
|
|
|
11
11
|
const FramePersistor_1 = require("./FramePersistor");
|
|
12
12
|
const is_menu_item_1 = require("./Menu/is-menu-item");
|
|
13
13
|
const RefreshCompositionOverlay_1 = require("./RefreshCompositionOverlay");
|
|
14
|
+
const RenderErrorContext_1 = require("./RenderErrorContext");
|
|
14
15
|
const RunningCalculateMetadata_1 = require("./RunningCalculateMetadata");
|
|
15
16
|
const imperative_state_1 = require("./Timeline/imperative-state");
|
|
16
17
|
const timeline_scroll_logic_1 = require("./Timeline/timeline-scroll-logic");
|
|
@@ -28,6 +29,7 @@ const CanvasOrLoading = ({ size }) => {
|
|
|
28
29
|
const resolved = remotion_1.Internals.useResolvedVideoConfig(null);
|
|
29
30
|
const { setZoom } = (0, react_1.useContext)(timeline_zoom_1.TimelineZoomCtx);
|
|
30
31
|
const { canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
|
|
32
|
+
const { error: renderError } = (0, react_1.useContext)(RenderErrorContext_1.RenderErrorContext);
|
|
31
33
|
(0, react_1.useEffect)(() => {
|
|
32
34
|
if ((resolved === null || resolved === void 0 ? void 0 : resolved.type) !== 'success' &&
|
|
33
35
|
(resolved === null || resolved === void 0 ? void 0 : resolved.type) !== 'success-and-refreshing') {
|
|
@@ -42,6 +44,9 @@ const CanvasOrLoading = ({ size }) => {
|
|
|
42
44
|
});
|
|
43
45
|
});
|
|
44
46
|
}, [resolved, setZoom]);
|
|
47
|
+
if (renderError) {
|
|
48
|
+
return jsx_runtime_1.jsx(ErrorLoading, { error: renderError });
|
|
49
|
+
}
|
|
45
50
|
if (!canvasContent) {
|
|
46
51
|
const compname = window.location.pathname.replace('/', '');
|
|
47
52
|
return (jsx_runtime_1.jsx("div", { style: container, className: "css-reset", children: jsx_runtime_1.jsxs("div", { style: RunningCalculateMetadata_1.loaderLabel, children: ["Composition with ID ", compname, " not found."] }) }));
|
|
@@ -48,6 +48,7 @@ const ForceSpecificCursor_1 = require("./ForceSpecificCursor");
|
|
|
48
48
|
const GlobalKeybindings_1 = require("./GlobalKeybindings");
|
|
49
49
|
const Modals_1 = require("./Modals");
|
|
50
50
|
const NotificationCenter_1 = require("./Notifications/NotificationCenter");
|
|
51
|
+
const RenderErrorContext_1 = require("./RenderErrorContext");
|
|
51
52
|
const TopPanel_1 = require("./TopPanel");
|
|
52
53
|
const background = {
|
|
53
54
|
backgroundColor: colors_1.BACKGROUND,
|
|
@@ -83,10 +84,17 @@ const Editor = ({ Root, readOnlyStudio }) => {
|
|
|
83
84
|
const MemoRoot = (0, react_1.useMemo)(() => {
|
|
84
85
|
return react_1.default.memo(Root);
|
|
85
86
|
}, [Root]);
|
|
87
|
+
const [renderError, setRenderError] = (0, react_1.useState)(null);
|
|
88
|
+
const clearError = (0, react_1.useCallback)(() => {
|
|
89
|
+
setRenderError(null);
|
|
90
|
+
}, []);
|
|
91
|
+
const compositionRenderErrorContextValue = (0, react_1.useMemo)(() => ({ setError: setRenderError, clearError }), [clearError]);
|
|
92
|
+
const renderErrorContextValue = (0, react_1.useMemo)(() => ({ error: renderError }), [renderError]);
|
|
86
93
|
return (jsx_runtime_1.jsx(z_index_1.HigherZIndex, { onEscape: noop_1.noop, onOutsideClick: noop_1.noop, children: jsx_runtime_1.jsxs(timeline_zoom_1.TimelineZoomContext, { children: [
|
|
87
94
|
jsx_runtime_1.jsxs(remotion_1.Internals.CurrentScaleContext.Provider, { value: value, children: [
|
|
88
|
-
jsx_runtime_1.jsx(ForceSpecificCursor_1.ForceSpecificCursor, {}), jsx_runtime_1.jsxs("div", { style: background, children: [
|
|
89
|
-
|
|
95
|
+
jsx_runtime_1.jsx(ForceSpecificCursor_1.ForceSpecificCursor, {}), jsx_runtime_1.jsxs("div", { style: background, children: [
|
|
96
|
+
jsx_runtime_1.jsx(remotion_1.Internals.CompositionRenderErrorContext.Provider, { value: compositionRenderErrorContextValue, children: canvasMounted ? jsx_runtime_1.jsx(MemoRoot, {}) : null }), jsx_runtime_1.jsxs(remotion_1.Internals.CanUseRemotionHooksProvider, { children: [
|
|
97
|
+
jsx_runtime_1.jsx(RenderErrorContext_1.RenderErrorContext.Provider, { value: renderErrorContextValue, children: jsx_runtime_1.jsx(EditorContent_1.EditorContent, { readOnlyStudio: readOnlyStudio, children: jsx_runtime_1.jsx(TopPanel_1.TopPanel, { drawRef: canvas_ref_1.drawRef, bufferStateDelayInMilliseconds: exports.BUFFER_STATE_DELAY_IN_MILLISECONDS, onMounted: onMounted, readOnlyStudio: readOnlyStudio }) }) }), jsx_runtime_1.jsx(GlobalKeybindings_1.GlobalKeybindings, {})
|
|
90
98
|
] })
|
|
91
99
|
] })
|
|
92
100
|
] }), jsx_runtime_1.jsx(Modals_1.Modals, { readOnlyStudio: readOnlyStudio }), jsx_runtime_1.jsx(NotificationCenter_1.NotificationCenter, {})
|
|
@@ -112,11 +112,13 @@ const RenderModalJSONPropsEditor = ({ setValue, value, defaultProps, onSave, ser
|
|
|
112
112
|
};
|
|
113
113
|
}, [subscribeToEvent, compositionId, schema]);
|
|
114
114
|
(0, react_1.useEffect)(() => {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
setLocalValue((prev) => {
|
|
116
|
+
if (prev.validJSON && (0, deep_equal_1.deepEqual)(value, prev.value)) {
|
|
117
|
+
return prev;
|
|
118
|
+
}
|
|
119
|
+
return parseJS(value, schema);
|
|
120
|
+
});
|
|
121
|
+
}, [value, schema]);
|
|
120
122
|
const onPretty = (0, react_1.useCallback)(() => {
|
|
121
123
|
if (!localValue.validJSON) {
|
|
122
124
|
return;
|
|
@@ -7,6 +7,7 @@ const is_menu_item_1 = require("../../Menu/is-menu-item");
|
|
|
7
7
|
const SchemaErrorMessages_1 = require("./SchemaErrorMessages");
|
|
8
8
|
const scroll_to_default_props_path_1 = require("./scroll-to-default-props-path");
|
|
9
9
|
const zod_schema_type_1 = require("./zod-schema-type");
|
|
10
|
+
const ZodDiscriminatedUnionEditor_1 = require("./ZodDiscriminatedUnionEditor");
|
|
10
11
|
const ZodObjectEditor_1 = require("./ZodObjectEditor");
|
|
11
12
|
const scrollable = {
|
|
12
13
|
display: 'flex',
|
|
@@ -19,9 +20,12 @@ const SchemaEditor = ({ schema, value, setValue }) => {
|
|
|
19
20
|
throw new Error('expected zod');
|
|
20
21
|
}
|
|
21
22
|
const typeName = (0, zod_schema_type_1.getZodSchemaType)(schema);
|
|
22
|
-
if (typeName !== 'object') {
|
|
23
|
+
if (typeName !== 'object' && typeName !== 'discriminatedUnion') {
|
|
23
24
|
return jsx_runtime_1.jsx(SchemaErrorMessages_1.TopLevelZodValue, { typeReceived: typeName });
|
|
24
25
|
}
|
|
26
|
+
if (typeName === 'discriminatedUnion') {
|
|
27
|
+
return (jsx_runtime_1.jsx("div", { ref: scroll_to_default_props_path_1.defaultPropsEditorScrollableAreaRef, style: scrollable, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(ZodDiscriminatedUnionEditor_1.ZodDiscriminatedUnionEditor, { schema: schema, setValue: setValue, value: value, mayPad: true, jsonPath: [], onRemove: null }) }));
|
|
28
|
+
}
|
|
25
29
|
return (jsx_runtime_1.jsx("div", { ref: scroll_to_default_props_path_1.defaultPropsEditorScrollableAreaRef, style: scrollable, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(ZodObjectEditor_1.ZodObjectEditor, { discriminatedUnionReplacement: null, value: value, setValue: setValue, jsonPath: [], schema: schema, onRemove: null, mayPad: true }) }));
|
|
26
30
|
};
|
|
27
31
|
exports.SchemaEditor = SchemaEditor;
|
|
@@ -78,9 +78,11 @@ const InvalidSchema = ({ zodValidationResult }) => {
|
|
|
78
78
|
exports.InvalidSchema = InvalidSchema;
|
|
79
79
|
const TopLevelZodValue = ({ typeReceived }) => {
|
|
80
80
|
return (jsx_runtime_1.jsxs("div", { style: explainer, children: [
|
|
81
|
-
jsx_runtime_1.jsxs("div", { style: errorExplanation, children: ["The top-level type of the schema must be
|
|
82
|
-
"
|
|
83
|
-
|
|
81
|
+
jsx_runtime_1.jsxs("div", { style: errorExplanation, children: ["The top-level type of the schema must be", ' ', jsx_runtime_1.jsx("code", { style: codeSnippet, children: "z.object()" }),
|
|
82
|
+
" or", ' ', jsx_runtime_1.jsx("code", { style: codeSnippet, children: "z.discriminatedUnion()" }),
|
|
83
|
+
". Instead got a schema of type ",
|
|
84
|
+
jsx_runtime_1.jsx("code", { style: codeSnippet, children: typeReceived })
|
|
85
|
+
] }), jsx_runtime_1.jsx(layout_1.Spacing, { y: 1 }), jsx_runtime_1.jsx("div", { style: errorExplanation, children: "Fix the schema by changing the top-level Zod type to an object or discriminated union." }), jsx_runtime_1.jsx(layout_1.Spacing, { y: 2, block: true }), jsx_runtime_1.jsx(Button_1.Button, { onClick: openDocs, children: "Learn more" })
|
|
84
86
|
] }));
|
|
85
87
|
};
|
|
86
88
|
exports.TopLevelZodValue = TopLevelZodValue;
|
|
@@ -7,7 +7,10 @@ const remotion_1 = require("remotion");
|
|
|
7
7
|
const client_id_1 = require("../../helpers/client-id");
|
|
8
8
|
const colors_1 = require("../../helpers/colors");
|
|
9
9
|
const timeline_layout_1 = require("../../helpers/timeline-layout");
|
|
10
|
+
const call_api_1 = require("../call-api");
|
|
11
|
+
const ContextMenu_1 = require("../ContextMenu");
|
|
10
12
|
const ExpandedTracksProvider_1 = require("../ExpandedTracksProvider");
|
|
13
|
+
const NotificationCenter_1 = require("../Notifications/NotificationCenter");
|
|
11
14
|
const TimelineExpandedSection_1 = require("./TimelineExpandedSection");
|
|
12
15
|
const TimelineLayerEye_1 = require("./TimelineLayerEye");
|
|
13
16
|
const TimelineStack_1 = require("./TimelineStack");
|
|
@@ -39,13 +42,73 @@ const arrowButton = {
|
|
|
39
42
|
const TimelineListItem = ({ nestedDepth, sequence, isCompact }) => {
|
|
40
43
|
var _a, _b;
|
|
41
44
|
const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
|
|
42
|
-
const
|
|
43
|
-
|
|
45
|
+
const visualModeEnvEnabled = Boolean(process.env.EXPERIMENTAL_VISUAL_MODE_ENABLED);
|
|
46
|
+
const previewConnected = previewServerState.type === 'connected';
|
|
47
|
+
const visualModeActive = visualModeEnvEnabled && previewConnected;
|
|
44
48
|
const { hidden, setHidden } = (0, react_1.useContext)(remotion_1.Internals.SequenceVisibilityToggleContext);
|
|
45
49
|
const { expandedTracks, toggleTrack } = (0, react_1.useContext)(ExpandedTracksProvider_1.ExpandedTracksContext);
|
|
46
50
|
const originalLocation = (0, use_resolved_stack_1.useResolvedStack)((_a = sequence.stack) !== null && _a !== void 0 ? _a : null);
|
|
47
|
-
const nodePath = (0, use_sequence_props_subscription_1.useSequencePropsSubscription)(sequence, originalLocation);
|
|
48
|
-
const
|
|
51
|
+
const nodePath = (0, use_sequence_props_subscription_1.useSequencePropsSubscription)(sequence, originalLocation, visualModeActive);
|
|
52
|
+
const validatedLocation = (0, react_1.useMemo)(() => {
|
|
53
|
+
var _a;
|
|
54
|
+
if (!originalLocation ||
|
|
55
|
+
!originalLocation.source ||
|
|
56
|
+
!originalLocation.line) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
source: originalLocation.source,
|
|
61
|
+
line: originalLocation.line,
|
|
62
|
+
column: (_a = originalLocation.column) !== null && _a !== void 0 ? _a : 0,
|
|
63
|
+
};
|
|
64
|
+
}, [originalLocation]);
|
|
65
|
+
const canDeleteFromSource = Boolean(nodePath && (validatedLocation === null || validatedLocation === void 0 ? void 0 : validatedLocation.source));
|
|
66
|
+
const deleteDisabled = (0, react_1.useMemo)(() => !previewConnected || !sequence.controls || !canDeleteFromSource, [previewConnected, sequence.controls, canDeleteFromSource]);
|
|
67
|
+
const onDeleteSequenceFromSource = (0, react_1.useCallback)(async () => {
|
|
68
|
+
if (!(validatedLocation === null || validatedLocation === void 0 ? void 0 : validatedLocation.source) || !nodePath) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const result = await (0, call_api_1.callApi)('/api/delete-jsx-node', {
|
|
73
|
+
fileName: validatedLocation.source,
|
|
74
|
+
nodePath,
|
|
75
|
+
});
|
|
76
|
+
if (result.success) {
|
|
77
|
+
(0, NotificationCenter_1.showNotification)('Removed sequence from source file', 2000);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
(0, NotificationCenter_1.showNotification)(result.reason, 4000);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
(0, NotificationCenter_1.showNotification)(err.message, 4000);
|
|
85
|
+
}
|
|
86
|
+
}, [nodePath, validatedLocation === null || validatedLocation === void 0 ? void 0 : validatedLocation.source]);
|
|
87
|
+
const contextMenuValues = (0, react_1.useMemo)(() => {
|
|
88
|
+
if (!visualModeEnvEnabled) {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
type: 'item',
|
|
94
|
+
id: 'delete-sequence',
|
|
95
|
+
keyHint: null,
|
|
96
|
+
label: 'Delete',
|
|
97
|
+
leftItem: null,
|
|
98
|
+
disabled: deleteDisabled,
|
|
99
|
+
onClick: () => {
|
|
100
|
+
if (deleteDisabled) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
onDeleteSequenceFromSource();
|
|
104
|
+
},
|
|
105
|
+
quickSwitcherLabel: null,
|
|
106
|
+
subMenu: null,
|
|
107
|
+
value: 'delete-sequence',
|
|
108
|
+
},
|
|
109
|
+
];
|
|
110
|
+
}, [deleteDisabled, onDeleteSequenceFromSource, visualModeEnvEnabled]);
|
|
111
|
+
const isExpanded = visualModeActive && ((_b = expandedTracks[sequence.id]) !== null && _b !== void 0 ? _b : false);
|
|
49
112
|
const onToggleExpand = (0, react_1.useCallback)(() => {
|
|
50
113
|
toggleTrack(sequence.id);
|
|
51
114
|
}, [sequence.id, toggleTrack]);
|
|
@@ -87,9 +150,9 @@ const TimelineListItem = ({ nestedDepth, sequence, isCompact }) => {
|
|
|
87
150
|
transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
|
|
88
151
|
};
|
|
89
152
|
}, [isExpanded]);
|
|
90
|
-
|
|
91
|
-
jsx_runtime_1.
|
|
92
|
-
|
|
93
|
-
|
|
153
|
+
const trackRow = (jsx_runtime_1.jsxs("div", { style: outer, children: [
|
|
154
|
+
jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEye, { type: sequence.type === 'audio' ? 'speaker' : 'eye', hidden: isItemHidden, onInvoked: onToggleVisibility }), jsx_runtime_1.jsx("div", { style: padder }), sequence.parent && nestedDepth > 0 ? jsx_runtime_1.jsx("div", { style: space }) : null, visualModeActive ? (sequence.controls ? (jsx_runtime_1.jsx("button", { type: "button", style: arrowStyle, onClick: onToggleExpand, "aria-expanded": isExpanded, "aria-label": `${isExpanded ? 'Collapse' : 'Expand'} track`, children: jsx_runtime_1.jsx("svg", { width: "12", height: "12", viewBox: "0 0 8 8", style: { display: 'block' }, children: jsx_runtime_1.jsx("path", { d: "M2 1L6 4L2 7Z", fill: "white" }) }) })) : (jsx_runtime_1.jsx("div", { style: arrowButton }))) : null, jsx_runtime_1.jsx(TimelineStack_1.TimelineStack, { sequence: sequence, isCompact: isCompact, originalLocation: originalLocation })
|
|
155
|
+
] }));
|
|
156
|
+
return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [visualModeEnvEnabled ? (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { values: contextMenuValues, children: trackRow })) : (trackRow), visualModeActive && isExpanded && sequence.controls ? (jsx_runtime_1.jsx(TimelineExpandedSection_1.TimelineExpandedSection, { sequence: sequence, originalLocation: originalLocation, nestedDepth: nestedDepth, nodePath: nodePath })) : null] }));
|
|
94
157
|
};
|
|
95
158
|
exports.TimelineListItem = TimelineListItem;
|
|
@@ -151,11 +151,6 @@ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore,
|
|
|
151
151
|
const ref = (0, react_1.useRef)(null);
|
|
152
152
|
const [error, setError] = (0, react_1.useState)(null);
|
|
153
153
|
const aspectRatio = (0, react_1.useRef)((0, frame_database_1.getAspectRatioFromCache)(src));
|
|
154
|
-
(0, react_1.useEffect)(() => {
|
|
155
|
-
return () => {
|
|
156
|
-
(0, frame_database_1.clearFramesForSrc)(src);
|
|
157
|
-
};
|
|
158
|
-
}, [src]);
|
|
159
154
|
// for rendering frames
|
|
160
155
|
(0, react_1.useEffect)(() => {
|
|
161
156
|
if (error) {
|
|
@@ -201,11 +196,9 @@ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore,
|
|
|
201
196
|
if (unfilled.length === 0) {
|
|
202
197
|
return () => {
|
|
203
198
|
current.removeChild(canvas);
|
|
204
|
-
(0, frame_database_1.clearOldFrames)();
|
|
205
199
|
};
|
|
206
200
|
}
|
|
207
201
|
}
|
|
208
|
-
(0, frame_database_1.clearOldFrames)();
|
|
209
202
|
(0, extract_frames_1.extractFrames)({
|
|
210
203
|
timestampsInSeconds: ({ track, }) => {
|
|
211
204
|
aspectRatio.current = track.width / track.height;
|
|
@@ -234,14 +227,7 @@ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore,
|
|
|
234
227
|
}
|
|
235
228
|
frame = undefined;
|
|
236
229
|
const databaseKey = (0, frame_database_1.makeFrameDatabaseKey)(src, transformed.timestamp);
|
|
237
|
-
|
|
238
|
-
if (existingFrame) {
|
|
239
|
-
existingFrame.frame.close();
|
|
240
|
-
}
|
|
241
|
-
frame_database_1.frameDatabase.set(databaseKey, {
|
|
242
|
-
frame: transformed,
|
|
243
|
-
lastUsed: Date.now(),
|
|
244
|
-
});
|
|
230
|
+
(0, frame_database_1.addFrameToCache)(databaseKey, transformed);
|
|
245
231
|
if (aspectRatio.current === null) {
|
|
246
232
|
throw new Error('Aspect ratio is not set');
|
|
247
233
|
}
|
|
@@ -288,9 +274,6 @@ const TimelineVideoInfo = ({ src, visualizationWidth, naturalWidth, trimBefore,
|
|
|
288
274
|
})
|
|
289
275
|
.catch((e) => {
|
|
290
276
|
setError(e);
|
|
291
|
-
})
|
|
292
|
-
.finally(() => {
|
|
293
|
-
(0, frame_database_1.clearOldFrames)();
|
|
294
277
|
});
|
|
295
278
|
return () => {
|
|
296
279
|
controller.abort();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { SequenceNodePath } from '@remotion/studio-shared';
|
|
2
2
|
import type { TSequence } from 'remotion';
|
|
3
3
|
import type { OriginalPosition } from '../../error-overlay/react-overlay/utils/get-source-map';
|
|
4
|
-
export declare const useSequencePropsSubscription: (sequence: TSequence, originalLocation: OriginalPosition | null) => SequenceNodePath | null;
|
|
4
|
+
export declare const useSequencePropsSubscription: (sequence: TSequence, originalLocation: OriginalPosition | null, visualModeEnabled: boolean) => SequenceNodePath | null;
|
|
@@ -6,7 +6,7 @@ const remotion_1 = require("remotion");
|
|
|
6
6
|
const client_id_1 = require("../../helpers/client-id");
|
|
7
7
|
const timeline_layout_1 = require("../../helpers/timeline-layout");
|
|
8
8
|
const call_api_1 = require("../call-api");
|
|
9
|
-
const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
9
|
+
const useSequencePropsSubscription = (sequence, originalLocation, visualModeEnabled) => {
|
|
10
10
|
var _a;
|
|
11
11
|
var _b, _c, _d, _e;
|
|
12
12
|
const { setCodeValues } = (0, react_1.useContext)(remotion_1.Internals.VisualModeOverridesContext);
|
|
@@ -44,7 +44,12 @@ const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
|
44
44
|
const currentLocationColumn = (0, react_1.useRef)(locationColumn);
|
|
45
45
|
currentLocationColumn.current = locationColumn;
|
|
46
46
|
const nodePathRef = (0, react_1.useRef)(null);
|
|
47
|
+
const [nodePath, setNodePath] = (0, react_1.useState)(null);
|
|
47
48
|
const isMountedRef = (0, react_1.useRef)(true);
|
|
49
|
+
const setNodePathBoth = (0, react_1.useCallback)((next) => {
|
|
50
|
+
nodePathRef.current = next;
|
|
51
|
+
setNodePath(next);
|
|
52
|
+
}, []);
|
|
48
53
|
(0, react_1.useEffect)(() => {
|
|
49
54
|
isMountedRef.current = true;
|
|
50
55
|
return () => {
|
|
@@ -52,12 +57,18 @@ const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
|
52
57
|
};
|
|
53
58
|
}, []);
|
|
54
59
|
(0, react_1.useEffect)(() => {
|
|
60
|
+
if (!visualModeEnabled) {
|
|
61
|
+
setPropStatusesForSequence(null);
|
|
62
|
+
setNodePathBoth(null);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
55
65
|
if (!clientId ||
|
|
56
66
|
!locationSource ||
|
|
57
67
|
!locationLine ||
|
|
58
68
|
locationColumn === null ||
|
|
59
69
|
!schemaKeysString) {
|
|
60
70
|
setPropStatusesForSequence(null);
|
|
71
|
+
setNodePathBoth(null);
|
|
61
72
|
return;
|
|
62
73
|
}
|
|
63
74
|
const keys = schemaKeysString.split(',');
|
|
@@ -75,16 +86,16 @@ const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
|
75
86
|
return;
|
|
76
87
|
}
|
|
77
88
|
if (result.canUpdate) {
|
|
78
|
-
|
|
89
|
+
setNodePathBoth(result.nodePath);
|
|
79
90
|
setPropStatusesForSequence(result.props);
|
|
80
91
|
}
|
|
81
92
|
else {
|
|
82
|
-
|
|
93
|
+
setNodePathBoth(null);
|
|
83
94
|
setPropStatusesForSequence(null);
|
|
84
95
|
}
|
|
85
96
|
})
|
|
86
97
|
.catch((err) => {
|
|
87
|
-
|
|
98
|
+
setNodePathBoth(null);
|
|
88
99
|
remotion_1.Internals.Log.error(err);
|
|
89
100
|
setPropStatusesForSequence(null);
|
|
90
101
|
});
|
|
@@ -95,7 +106,7 @@ const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
|
95
106
|
if (!isMountedRef.current) {
|
|
96
107
|
setPropStatusesForSequence(null);
|
|
97
108
|
}
|
|
98
|
-
|
|
109
|
+
setNodePathBoth(null);
|
|
99
110
|
if (currentNodePath) {
|
|
100
111
|
(0, call_api_1.callApi)('/api/unsubscribe-from-sequence-props', {
|
|
101
112
|
fileName: locationSource,
|
|
@@ -107,14 +118,19 @@ const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
|
107
118
|
}
|
|
108
119
|
};
|
|
109
120
|
}, [
|
|
121
|
+
visualModeEnabled,
|
|
110
122
|
clientId,
|
|
111
123
|
locationSource,
|
|
112
124
|
locationLine,
|
|
113
125
|
locationColumn,
|
|
114
126
|
schemaKeysString,
|
|
115
127
|
setPropStatusesForSequence,
|
|
128
|
+
setNodePathBoth,
|
|
116
129
|
]);
|
|
117
130
|
(0, react_1.useEffect)(() => {
|
|
131
|
+
if (!visualModeEnabled) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
118
134
|
if (!locationSource || !locationLine || locationColumn === null) {
|
|
119
135
|
return;
|
|
120
136
|
}
|
|
@@ -132,6 +148,7 @@ const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
|
132
148
|
}
|
|
133
149
|
else {
|
|
134
150
|
setPropStatusesForSequence(null);
|
|
151
|
+
setNodePathBoth(null);
|
|
135
152
|
}
|
|
136
153
|
};
|
|
137
154
|
const unsub = subscribeToEvent('sequence-props-updated', listener);
|
|
@@ -139,12 +156,14 @@ const useSequencePropsSubscription = (sequence, originalLocation) => {
|
|
|
139
156
|
unsub();
|
|
140
157
|
};
|
|
141
158
|
}, [
|
|
159
|
+
visualModeEnabled,
|
|
142
160
|
locationSource,
|
|
143
161
|
locationLine,
|
|
144
162
|
locationColumn,
|
|
145
163
|
subscribeToEvent,
|
|
146
164
|
setPropStatusesForSequence,
|
|
165
|
+
setNodePathBoth,
|
|
147
166
|
]);
|
|
148
|
-
return
|
|
167
|
+
return nodePath;
|
|
149
168
|
};
|
|
150
169
|
exports.useSequencePropsSubscription = useSequencePropsSubscription;
|
|
@@ -48,11 +48,7 @@ const crashWithFrames = (crash) => (error) => {
|
|
|
48
48
|
function listenToRuntimeErrors(crash) {
|
|
49
49
|
const crashWithFramesRunTime = crashWithFrames(crash);
|
|
50
50
|
(0, unhandled_error_1.register)(window, (error) => {
|
|
51
|
-
return crashWithFramesRunTime(
|
|
52
|
-
message: error.message,
|
|
53
|
-
stack: error.stack,
|
|
54
|
-
name: error.name,
|
|
55
|
-
});
|
|
51
|
+
return crashWithFramesRunTime(error);
|
|
56
52
|
});
|
|
57
53
|
(0, unhandled_rejection_1.register)(window, (error) => {
|
|
58
54
|
return crashWithFramesRunTime(error);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CopyStackTrace = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const Button_1 = require("../../components/Button");
|
|
7
|
+
const use_keybinding_1 = require("../../helpers/use-keybinding");
|
|
8
|
+
const ShortcutHint_1 = require("./ShortcutHint");
|
|
9
|
+
const CopyStackTrace = ({ canHaveKeyboardShortcuts, errorText }) => {
|
|
10
|
+
const [copyState, setCopyState] = (0, react_1.useState)('idle');
|
|
11
|
+
const handleCopyToClipboard = (0, react_1.useCallback)(() => {
|
|
12
|
+
navigator.clipboard
|
|
13
|
+
.writeText(errorText)
|
|
14
|
+
.then(() => {
|
|
15
|
+
setCopyState('copied');
|
|
16
|
+
setTimeout(() => setCopyState('idle'), 2000);
|
|
17
|
+
})
|
|
18
|
+
.catch(() => {
|
|
19
|
+
setCopyState('failed');
|
|
20
|
+
setTimeout(() => setCopyState('idle'), 2000);
|
|
21
|
+
});
|
|
22
|
+
}, [errorText]);
|
|
23
|
+
const { registerKeybinding } = (0, use_keybinding_1.useKeybinding)();
|
|
24
|
+
(0, react_1.useEffect)(() => {
|
|
25
|
+
if (!canHaveKeyboardShortcuts) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const { unregister } = registerKeybinding({
|
|
29
|
+
event: 'keydown',
|
|
30
|
+
key: 't',
|
|
31
|
+
callback: handleCopyToClipboard,
|
|
32
|
+
commandCtrlKey: true,
|
|
33
|
+
preventDefault: true,
|
|
34
|
+
triggerIfInputFieldFocused: false,
|
|
35
|
+
keepRegisteredWhenNotHighestContext: false,
|
|
36
|
+
});
|
|
37
|
+
return () => unregister();
|
|
38
|
+
}, [canHaveKeyboardShortcuts, handleCopyToClipboard, registerKeybinding]);
|
|
39
|
+
const label = (0, react_1.useMemo)(() => {
|
|
40
|
+
if (copyState === 'copied') {
|
|
41
|
+
return 'Copied!';
|
|
42
|
+
}
|
|
43
|
+
if (copyState === 'failed') {
|
|
44
|
+
return 'Failed!';
|
|
45
|
+
}
|
|
46
|
+
return 'Copy Stacktrace';
|
|
47
|
+
}, [copyState]);
|
|
48
|
+
return (jsx_runtime_1.jsxs(Button_1.Button, { onClick: handleCopyToClipboard, children: [label, ' ', copyState === 'idle' && canHaveKeyboardShortcuts ? (jsx_runtime_1.jsx(ShortcutHint_1.ShortcutHint, { cmdOrCtrl: true, keyToPress: "t" })) : null] }));
|
|
49
|
+
};
|
|
50
|
+
exports.CopyStackTrace = CopyStackTrace;
|
|
@@ -4,15 +4,16 @@ exports.ErrorDisplay = 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");
|
|
7
|
+
const remotion_1 = require("remotion");
|
|
7
8
|
const layout_1 = require("../../components/layout");
|
|
8
9
|
const is_menu_item_1 = require("../../components/Menu/is-menu-item");
|
|
9
|
-
const url_state_1 = require("../../helpers/url-state");
|
|
10
10
|
const AskOnDiscord_1 = require("./AskOnDiscord");
|
|
11
11
|
const CalculateMetadataErrorExplainer_1 = require("./CalculateMetadataErrorExplainer");
|
|
12
|
-
const
|
|
12
|
+
const CopyStackTrace_1 = require("./CopyStackTrace");
|
|
13
13
|
const ErrorTitle_1 = require("./ErrorTitle");
|
|
14
14
|
const get_help_link_1 = require("./get-help-link");
|
|
15
15
|
const HelpLink_1 = require("./HelpLink");
|
|
16
|
+
const MediaPlaybackErrorExplainer_1 = require("./MediaPlaybackErrorExplainer");
|
|
16
17
|
const OpenInEditor_1 = require("./OpenInEditor");
|
|
17
18
|
const Retry_1 = require("./Retry");
|
|
18
19
|
const SearchGitHubIssues_1 = require("./SearchGitHubIssues");
|
|
@@ -27,8 +28,6 @@ const spacer = {
|
|
|
27
28
|
display: 'inline-block',
|
|
28
29
|
};
|
|
29
30
|
const ErrorDisplay = ({ display, keyboardShortcuts, onRetry, canHaveDismissButton, calculateMetadata, }) => {
|
|
30
|
-
var _a;
|
|
31
|
-
const compositionIds = (_a = window === null || window === void 0 ? void 0 : window.remotion_seenCompositionIds) !== null && _a !== void 0 ? _a : [];
|
|
32
31
|
const highestLineNumber = Math.max(...display.stackFrames
|
|
33
32
|
.map((s) => s.originalScriptCode)
|
|
34
33
|
.flat(1)
|
|
@@ -50,23 +49,27 @@ const ErrorDisplay = ({ display, keyboardShortcuts, onRetry, canHaveDismissButto
|
|
|
50
49
|
}, [display.error]);
|
|
51
50
|
const lineNumberWidth = String(highestLineNumber).length;
|
|
52
51
|
const helpLink = (0, get_help_link_1.getHelpLink)(message);
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
const errorTextForCopy = (0, react_1.useMemo)(() => {
|
|
53
|
+
const header = `${display.error.name}: ${message}`;
|
|
54
|
+
if (display.stackFrames.length > 0) {
|
|
55
|
+
const stackLines = display.stackFrames
|
|
56
|
+
.map((frame) => `at ${frame.originalFunctionName || '<anonymous>'} (${frame.originalFileName || 'unknown'}:${frame.originalLineNumber || '?'}:${frame.originalColumnNumber || '?'})`)
|
|
57
|
+
.join('\n');
|
|
58
|
+
return `${header}\n${stackLines}`;
|
|
59
|
+
}
|
|
60
|
+
return display.error.stack || header;
|
|
61
|
+
}, [display.stackFrames, display.error, message]);
|
|
59
62
|
return (jsx_runtime_1.jsxs("div", { children: [
|
|
60
63
|
jsx_runtime_1.jsx(ErrorTitle_1.ErrorTitle, { symbolicating: false, name: display.error.name, message: message, canHaveDismissButton: canHaveDismissButton }), helpLink ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
|
|
61
64
|
jsx_runtime_1.jsx(HelpLink_1.HelpLink, { link: helpLink, canHaveKeyboardShortcuts: keyboardShortcuts }), jsx_runtime_1.jsx("div", { style: spacer })
|
|
62
65
|
] })) : null, display.stackFrames.length > 0 && window.remotion_editorName ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
|
|
63
66
|
jsx_runtime_1.jsx(OpenInEditor_1.OpenInEditor, { canHaveKeyboardShortcuts: keyboardShortcuts, stack: display.stackFrames[0] }), jsx_runtime_1.jsx("div", { style: spacer })
|
|
64
|
-
] })) : null,
|
|
65
|
-
jsx_runtime_1.jsx(CompositionIdsDropdown_1.CompositionIdsDropdown, { compositionIds: compositionIds, currentId: getCurrentCompositionId() }), jsx_runtime_1.jsx("div", { style: spacer })
|
|
66
|
-
] })) : null, jsx_runtime_1.jsx(SearchGitHubIssues_1.SearchGithubIssues, { canHaveKeyboardShortcuts: keyboardShortcuts, message: display.error.message }), jsx_runtime_1.jsx("div", { style: spacer }), jsx_runtime_1.jsx(AskOnDiscord_1.AskOnDiscord, { canHaveKeyboardShortcuts: keyboardShortcuts }), onRetry ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
|
|
67
|
+
] })) : null, jsx_runtime_1.jsx(CopyStackTrace_1.CopyStackTrace, { canHaveKeyboardShortcuts: keyboardShortcuts, errorText: errorTextForCopy }), jsx_runtime_1.jsx("div", { style: spacer }), jsx_runtime_1.jsx(SearchGitHubIssues_1.SearchGithubIssues, { canHaveKeyboardShortcuts: keyboardShortcuts, message: display.error.message }), jsx_runtime_1.jsx("div", { style: spacer }), jsx_runtime_1.jsx(AskOnDiscord_1.AskOnDiscord, { canHaveKeyboardShortcuts: keyboardShortcuts }), onRetry ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
|
|
67
68
|
jsx_runtime_1.jsx("div", { style: spacer }), jsx_runtime_1.jsx(Retry_1.RetryButton, { onClick: onRetry })
|
|
68
69
|
] })) : null, calculateMetadata ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
|
|
69
70
|
jsx_runtime_1.jsx("br", {}), jsx_runtime_1.jsx(layout_1.Spacing, { y: 0.5 }), jsx_runtime_1.jsx(CalculateMetadataErrorExplainer_1.CalculateMetadataErrorExplainer, {})
|
|
71
|
+
] })) : null, display.error instanceof remotion_1.MediaPlaybackError ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
|
|
72
|
+
jsx_runtime_1.jsx("br", {}), jsx_runtime_1.jsx(layout_1.Spacing, { y: 0.5 }), jsx_runtime_1.jsx(MediaPlaybackErrorExplainer_1.MediaPlaybackErrorExplainer, { src: display.error.src })
|
|
70
73
|
] })) : null, jsx_runtime_1.jsx("div", { style: stack, className: is_menu_item_1.HORIZONTAL_SCROLLBAR_CLASSNAME, children: display.stackFrames.map((s, i) => {
|
|
71
74
|
return (jsx_runtime_1.jsx(StackFrame_1.StackElement
|
|
72
75
|
// eslint-disable-next-line react/no-array-index-key
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MediaPlaybackErrorExplainer = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const colors_1 = require("../../helpers/colors");
|
|
7
|
+
const container = {
|
|
8
|
+
borderRadius: 3,
|
|
9
|
+
color: 'white',
|
|
10
|
+
padding: 12,
|
|
11
|
+
backgroundColor: colors_1.BORDER_COLOR,
|
|
12
|
+
fontSize: 14,
|
|
13
|
+
fontFamily: 'sans-serif',
|
|
14
|
+
lineHeight: 1.5,
|
|
15
|
+
};
|
|
16
|
+
const codeStyle = {
|
|
17
|
+
backgroundColor: 'rgba(255,255,255,0.1)',
|
|
18
|
+
padding: '2px 5px',
|
|
19
|
+
borderRadius: 3,
|
|
20
|
+
fontFamily: 'monospace',
|
|
21
|
+
fontSize: 13,
|
|
22
|
+
};
|
|
23
|
+
const linkStyle = {
|
|
24
|
+
color: '#58a6ff',
|
|
25
|
+
};
|
|
26
|
+
const MediaPlaybackErrorExplainer = ({ src }) => {
|
|
27
|
+
const [result, setResult] = (0, react_1.useState)({ type: 'loading' });
|
|
28
|
+
(0, react_1.useEffect)(() => {
|
|
29
|
+
fetch(src, { method: 'HEAD' })
|
|
30
|
+
.then((res) => {
|
|
31
|
+
var _a;
|
|
32
|
+
if (res.status === 404) {
|
|
33
|
+
setResult({ type: 'not-found' });
|
|
34
|
+
}
|
|
35
|
+
else if (!res.ok) {
|
|
36
|
+
setResult({ type: 'other-error', statusCode: res.status });
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const contentType = (_a = res.headers.get('content-type')) !== null && _a !== void 0 ? _a : '';
|
|
40
|
+
if (contentType.includes('text/html') ||
|
|
41
|
+
contentType.includes('application/json')) {
|
|
42
|
+
setResult({ type: 'wrong-content-type', contentType });
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
setResult({ type: 'ok' });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
.catch(() => {
|
|
50
|
+
setResult({ type: 'fetch-error' });
|
|
51
|
+
});
|
|
52
|
+
}, [src]);
|
|
53
|
+
if (result.type === 'loading') {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (result.type === 'not-found') {
|
|
57
|
+
return (jsx_runtime_1.jsxs("div", { style: container, children: ["The file ",
|
|
58
|
+
jsx_runtime_1.jsx("code", { style: codeStyle, children: src }),
|
|
59
|
+
" was not found (404).",
|
|
60
|
+
jsx_runtime_1.jsx("br", {}),
|
|
61
|
+
"If the file is in your ",
|
|
62
|
+
jsx_runtime_1.jsx("code", { style: codeStyle, children: "public/" }),
|
|
63
|
+
" folder, make sure to use", ' ', jsx_runtime_1.jsx("a", { style: linkStyle, href: "https://remotion.dev/docs/staticfile", target: "_blank", children: jsx_runtime_1.jsx("code", { style: codeStyle, children: "staticFile()" }) }), ' ', "to reference it.",
|
|
64
|
+
jsx_runtime_1.jsx("br", {}), jsx_runtime_1.jsx("a", { style: linkStyle, href: "https://remotion.dev/docs/media-playback-error", target: "_blank", children: "Learn more" })
|
|
65
|
+
] }));
|
|
66
|
+
}
|
|
67
|
+
if (result.type === 'other-error') {
|
|
68
|
+
return (jsx_runtime_1.jsxs("div", { style: container, children: ["\u26A0\uFE0F Fetching ",
|
|
69
|
+
jsx_runtime_1.jsx("code", { style: codeStyle, children: src }),
|
|
70
|
+
" returned status code", ' ', result.statusCode, ".",
|
|
71
|
+
jsx_runtime_1.jsx("br", {}), jsx_runtime_1.jsx("a", { style: linkStyle, href: "https://remotion.dev/docs/media-playback-error", target: "_blank", children: "Learn more" })
|
|
72
|
+
] }));
|
|
73
|
+
}
|
|
74
|
+
if (result.type === 'wrong-content-type') {
|
|
75
|
+
return (jsx_runtime_1.jsxs("div", { style: container, children: ["\u26A0\uFE0F Fetching ",
|
|
76
|
+
jsx_runtime_1.jsx("code", { style: codeStyle, children: src }),
|
|
77
|
+
" returned a", ' ', jsx_runtime_1.jsx("code", { style: codeStyle, children: result.contentType }),
|
|
78
|
+
" content type.",
|
|
79
|
+
jsx_runtime_1.jsx("br", {}),
|
|
80
|
+
"\uD83D\uDC49 If the file is in your ",
|
|
81
|
+
jsx_runtime_1.jsx("code", { style: codeStyle, children: "public/" }), ' ', "folder, make sure to use", ' ', jsx_runtime_1.jsx("a", { style: linkStyle, href: "https://remotion.dev/docs/staticfile", target: "_blank", children: jsx_runtime_1.jsx("code", { style: codeStyle, children: "staticFile()" }) }), ' ', "to reference it.",
|
|
82
|
+
jsx_runtime_1.jsx("br", {}), jsx_runtime_1.jsx("a", { style: linkStyle, href: "https://remotion.dev/docs/media-playback-error", target: "_blank", children: "Learn more" })
|
|
83
|
+
] }));
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
};
|
|
87
|
+
exports.MediaPlaybackErrorExplainer = MediaPlaybackErrorExplainer;
|