@remotion/studio 4.0.446 → 4.0.447

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.
@@ -10,6 +10,7 @@ const is_menu_item_1 = require("../Menu/is-menu-item");
10
10
  const MenuDivider_1 = require("../Menu/MenuDivider");
11
11
  const MenuSubItem_1 = require("../Menu/MenuSubItem");
12
12
  const styles_1 = require("../Menu/styles");
13
+ const menu_typeahead_1 = require("./menu-typeahead");
13
14
  const BORDER_SIZE = 1;
14
15
  const container = {
15
16
  paddingTop: styles_1.MENU_VERTICAL_PADDING,
@@ -26,6 +27,8 @@ const MenuContent = ({ onHide, values, preselectIndex, onNextMenu, onPreviousMen
26
27
  const containerRef = (0, react_1.useRef)(null);
27
28
  const isMobileLayout = (0, mobile_layout_1.useMobileLayout)();
28
29
  const [subMenuActivated, setSubMenuActivated] = (0, react_1.useState)(false);
30
+ const typeaheadQueryRef = (0, react_1.useRef)('');
31
+ const typeaheadTimeoutRef = (0, react_1.useRef)(null);
29
32
  if (values[0].type === 'divider') {
30
33
  throw new Error('first value cant be divide');
31
34
  }
@@ -38,6 +41,13 @@ const MenuContent = ({ onHide, values, preselectIndex, onNextMenu, onPreviousMen
38
41
  const onItemSelected = (0, react_1.useCallback)((id) => {
39
42
  setSelectedItem(id);
40
43
  }, []);
44
+ const clearTypeahead = (0, react_1.useCallback)(() => {
45
+ typeaheadQueryRef.current = '';
46
+ if (typeaheadTimeoutRef.current !== null) {
47
+ window.clearTimeout(typeaheadTimeoutRef.current);
48
+ typeaheadTimeoutRef.current = null;
49
+ }
50
+ }, []);
41
51
  const isItemSelectable = (0, react_1.useCallback)((v) => {
42
52
  return v.type !== 'divider' && !v.disabled;
43
53
  }, []);
@@ -114,6 +124,35 @@ const MenuContent = ({ onHide, values, preselectIndex, onNextMenu, onPreviousMen
114
124
  }
115
125
  setSubMenuActivated('without-mouse');
116
126
  }, [onNextMenu, selectedItem, values]);
127
+ const onTypeahead = (0, react_1.useCallback)((event) => {
128
+ if (event.ctrlKey ||
129
+ event.metaKey ||
130
+ event.altKey ||
131
+ event.key.length !== 1 ||
132
+ event.key.trim().length === 0) {
133
+ return;
134
+ }
135
+ const { activeElement } = document;
136
+ if (activeElement instanceof HTMLInputElement ||
137
+ activeElement instanceof HTMLTextAreaElement) {
138
+ return;
139
+ }
140
+ typeaheadQueryRef.current = `${typeaheadQueryRef.current}${event.key}`;
141
+ const matchedId = (0, menu_typeahead_1.findTypeaheadMenuItem)({
142
+ query: typeaheadQueryRef.current,
143
+ values,
144
+ });
145
+ if (matchedId !== null) {
146
+ setSelectedItem(matchedId);
147
+ }
148
+ if (typeaheadTimeoutRef.current !== null) {
149
+ window.clearTimeout(typeaheadTimeoutRef.current);
150
+ }
151
+ typeaheadTimeoutRef.current = window.setTimeout(() => {
152
+ typeaheadQueryRef.current = '';
153
+ typeaheadTimeoutRef.current = null;
154
+ }, 700);
155
+ }, [values]);
117
156
  const containerWithHeight = (0, react_1.useMemo)(() => {
118
157
  const containerStyles = { ...container };
119
158
  if (fixedHeight === null) {
@@ -127,6 +166,25 @@ const MenuContent = ({ onHide, values, preselectIndex, onNextMenu, onPreviousMen
127
166
  }
128
167
  return containerStyles;
129
168
  }, [fixedHeight, isMobileLayout]);
169
+ (0, react_1.useEffect)(() => {
170
+ if (!keybindings.isHighestContext ||
171
+ !process.env.KEYBOARD_SHORTCUTS_ENABLED) {
172
+ return;
173
+ }
174
+ const onKeyDown = (event) => {
175
+ onTypeahead(event);
176
+ };
177
+ window.addEventListener('keydown', onKeyDown);
178
+ return () => {
179
+ window.removeEventListener('keydown', onKeyDown);
180
+ clearTypeahead();
181
+ };
182
+ }, [clearTypeahead, keybindings.isHighestContext, onTypeahead]);
183
+ (0, react_1.useEffect)(() => {
184
+ return () => {
185
+ clearTypeahead();
186
+ };
187
+ }, [clearTypeahead]);
130
188
  (0, react_1.useEffect)(() => {
131
189
  const escapeBinding = keybindings.registerKeybinding({
132
190
  event: 'keydown',
@@ -0,0 +1,5 @@
1
+ import type { ComboboxValue } from './ComboBox';
2
+ export declare const findTypeaheadMenuItem: ({ query, values, }: {
3
+ query: string;
4
+ values: ComboboxValue[];
5
+ }) => string | null;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findTypeaheadMenuItem = void 0;
4
+ const getLabelToMatch = (value) => {
5
+ if (value.type === 'divider' || value.disabled) {
6
+ return null;
7
+ }
8
+ if (typeof value.label === 'string') {
9
+ return value.label;
10
+ }
11
+ return null;
12
+ };
13
+ const findTypeaheadMenuItem = ({ query, values, }) => {
14
+ const normalizedQuery = query.trim().toLowerCase();
15
+ if (normalizedQuery.length === 0) {
16
+ return null;
17
+ }
18
+ const matched = values.find((value) => {
19
+ const label = getLabelToMatch(value);
20
+ return label ? label.toLowerCase().startsWith(normalizedQuery) : false;
21
+ });
22
+ if (!matched || matched.type === 'divider') {
23
+ return null;
24
+ }
25
+ return matched.id;
26
+ };
27
+ exports.findTypeaheadMenuItem = findTypeaheadMenuItem;
@@ -262,6 +262,7 @@ const RenderButton = ({ readOnlyStudio, }) => {
262
262
  initialTransparent: null,
263
263
  initialMuted: null,
264
264
  initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes,
265
+ initialAllowHtmlInCanvas: defaults.allowHtmlInCanvas,
265
266
  });
266
267
  }, [video, setSelectedModal, getCurrentFrame, props, inFrame, outFrame]);
267
268
  const onClick = (0, react_1.useCallback)(() => {
@@ -19,8 +19,9 @@ const padding = {
19
19
  const title = {
20
20
  fontSize: 14,
21
21
  };
22
+ const DESCRIPTION_FONT_SIZE_PX = 14;
22
23
  const description = {
23
- fontSize: 14,
24
+ fontSize: DESCRIPTION_FONT_SIZE_PX,
24
25
  maxWidth: 400,
25
26
  };
26
27
  const link = {
@@ -56,7 +57,14 @@ const OptionExplainer = ({ option }) => {
56
57
  jsx_runtime_1.jsxs("div", { children: [
57
58
  jsx_runtime_1.jsx("strong", { style: title, children: option.name }), option.docLink ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
58
59
  jsx_runtime_1.jsx(layout_1.Spacing, { x: 1 }), jsx_runtime_1.jsx("a", { style: link, href: option.docLink, target: "_blank", children: "Docs" })
59
- ] })) : null] }), jsx_runtime_1.jsx("div", { style: description, children: option.description('ssr') })
60
+ ] })) : null] }), jsx_runtime_1.jsxs("div", { style: description, children: [
61
+ jsx_runtime_1.jsx("style", { children: `
62
+ .__remotion-option-explainer-description a,
63
+ .__remotion-option-explainer-description code {
64
+ font-size: ${DESCRIPTION_FONT_SIZE_PX}px;
65
+ }
66
+ ` }), jsx_runtime_1.jsx("div", { className: "__remotion-option-explainer-description", children: option.description('ssr') })
67
+ ] })
60
68
  ] }), jsx_runtime_1.jsx(layout_1.Spacing, { y: 0.5, block: true }), jsx_runtime_1.jsx(MenuDivider_1.MenuDivider, {}), jsx_runtime_1.jsx(layout_1.Spacing, { y: 0.5, block: true }), jsx_runtime_1.jsxs("div", { children: [
61
69
  jsx_runtime_1.jsxs("div", { style: infoRow, children: [
62
70
  jsx_runtime_1.jsx("div", { style: infoRowLabel, children: "CLI flag" }), jsx_runtime_1.jsx("div", { style: flexSpacer }), jsx_runtime_1.jsxs("code", { children: ["--", option.cliFlag] }), jsx_runtime_1.jsx("div", { style: copyWrapper, children: jsx_runtime_1.jsx(CliCopyButton_1.CliCopyButton, { valueToCopy: option.cliFlag }) })
@@ -80,7 +80,7 @@ const validateOutnameForStill = ({ outName, stillImageFormat, }) => {
80
80
  // TODO: Shortcut: Shift + R
81
81
  // TODO: Apply defaultCodec
82
82
  // TODO: Apply defaultOutName
83
- const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark, initialLogLevel, initialLicenseKey, initialStillImageFormat, initialDefaultOutName, initialScale, initialDelayRenderTimeout, initialMediaCacheSizeInBytes, initialContainer, initialVideoCodec, initialAudioCodec, initialAudioBitrate, initialVideoBitrate, initialHardwareAcceleration, initialKeyframeIntervalInSeconds, initialTransparent, initialMuted, }) => {
83
+ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark, initialLogLevel, initialLicenseKey, initialStillImageFormat, initialDefaultOutName, initialScale, initialDelayRenderTimeout, initialMediaCacheSizeInBytes, initialContainer, initialVideoCodec, initialAudioCodec, initialAudioBitrate, initialVideoBitrate, initialHardwareAcceleration, initialKeyframeIntervalInSeconds, initialTransparent, initialMuted, initialAllowHtmlInCanvas, }) => {
84
84
  var _a;
85
85
  const context = (0, react_1.useContext)(ResolveCompositionBeforeModal_1.ResolvedCompositionContext);
86
86
  const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
@@ -124,6 +124,7 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
124
124
  const [muted, setMuted] = (0, react_1.useState)(initialMuted !== null && initialMuted !== void 0 ? initialMuted : false);
125
125
  const [scale, setScale] = (0, react_1.useState)(initialScale !== null && initialScale !== void 0 ? initialScale : 1);
126
126
  const [licenseKey, setLicenseKey] = (0, react_1.useState)(initialLicenseKey);
127
+ const [allowHtmlInCanvas, setAllowHtmlInCanvas] = (0, react_1.useState)(initialAllowHtmlInCanvas !== null && initialAllowHtmlInCanvas !== void 0 ? initialAllowHtmlInCanvas : false);
127
128
  const encodableAudioCodecs = (0, use_encodable_audio_codecs_1.useEncodableAudioCodecs)(container);
128
129
  const encodableVideoCodecs = (0, use_encodable_video_codecs_1.useEncodableVideoCodecs)(container);
129
130
  const effectiveAudioCodec = (0, react_1.useMemo)(() => {
@@ -336,6 +337,7 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
336
337
  logLevel,
337
338
  licenseKey,
338
339
  scale,
340
+ allowHtmlInCanvas,
339
341
  }, compositionRef);
340
342
  }
341
343
  else {
@@ -362,6 +364,7 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
362
364
  logLevel,
363
365
  licenseKey,
364
366
  scale,
367
+ allowHtmlInCanvas,
365
368
  }, compositionRef);
366
369
  }
367
370
  setSidebarCollapsedState({ left: null, right: 'expanded' });
@@ -402,6 +405,7 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
402
405
  addClientStillJob,
403
406
  addClientVideoJob,
404
407
  scale,
408
+ allowHtmlInCanvas,
405
409
  ]);
406
410
  return (jsx_runtime_1.jsxs("div", { style: render_modals_1.outerModalStyle, children: [
407
411
  jsx_runtime_1.jsx(ModalHeader_1.ModalHeader, { title: `Render ${resolvedComposition.id}` }), jsx_runtime_1.jsxs("div", { style: render_modals_1.container, children: [
@@ -426,7 +430,7 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
426
430
  canUpdate: false,
427
431
  reason: 'render dialogue',
428
432
  determined: false,
429
- } })) : tab === 'picture' ? (jsx_runtime_1.jsx(WebRenderModalPicture_1.WebRenderModalPicture, { renderMode: renderMode, videoBitrate: videoBitrate, setVideoBitrate: setVideoBitrate, keyframeIntervalInSeconds: keyframeIntervalInSeconds, setKeyframeIntervalInSeconds: setKeyframeIntervalInSeconds, transparent: transparent, setTransparent: setTransparent, scale: scale, setScale: setScale, compositionWidth: resolvedComposition.width, compositionHeight: resolvedComposition.height })) : tab === 'audio' ? (jsx_runtime_1.jsx(WebRenderModalAudio_1.WebRenderModalAudio, { renderMode: renderMode, muted: muted, setMuted: setMuted, audioCodec: audioCodec, setAudioCodec: setAudioCodec, audioBitrate: audioBitrate, setAudioBitrate: setAudioBitrate, container: container, encodableCodecs: encodableAudioCodecs, effectiveAudioCodec: effectiveAudioCodec })) : tab === 'advanced' ? (jsx_runtime_1.jsx(WebRenderModalAdvanced_1.WebRenderModalAdvanced, { renderMode: renderMode, delayRenderTimeout: delayRenderTimeout, setDelayRenderTimeout: setDelayRenderTimeout, mediaCacheSizeInBytes: mediaCacheSizeInBytes, setMediaCacheSizeInBytes: setMediaCacheSizeInBytes, hardwareAcceleration: hardwareAcceleration, setHardwareAcceleration: setHardwareAcceleration })) : (jsx_runtime_1.jsx(WebRenderModalLicense_1.WebRenderModalLicense, { licenseKey: licenseKey, setLicenseKey: setLicenseKey, initialPublicLicenseKey: initialLicenseKey })) })
433
+ } })) : tab === 'picture' ? (jsx_runtime_1.jsx(WebRenderModalPicture_1.WebRenderModalPicture, { renderMode: renderMode, videoBitrate: videoBitrate, setVideoBitrate: setVideoBitrate, keyframeIntervalInSeconds: keyframeIntervalInSeconds, setKeyframeIntervalInSeconds: setKeyframeIntervalInSeconds, transparent: transparent, setTransparent: setTransparent, scale: scale, setScale: setScale, compositionWidth: resolvedComposition.width, compositionHeight: resolvedComposition.height })) : tab === 'audio' ? (jsx_runtime_1.jsx(WebRenderModalAudio_1.WebRenderModalAudio, { renderMode: renderMode, muted: muted, setMuted: setMuted, audioCodec: audioCodec, setAudioCodec: setAudioCodec, audioBitrate: audioBitrate, setAudioBitrate: setAudioBitrate, container: container, encodableCodecs: encodableAudioCodecs, effectiveAudioCodec: effectiveAudioCodec })) : tab === 'advanced' ? (jsx_runtime_1.jsx(WebRenderModalAdvanced_1.WebRenderModalAdvanced, { renderMode: renderMode, delayRenderTimeout: delayRenderTimeout, setDelayRenderTimeout: setDelayRenderTimeout, mediaCacheSizeInBytes: mediaCacheSizeInBytes, setMediaCacheSizeInBytes: setMediaCacheSizeInBytes, hardwareAcceleration: hardwareAcceleration, setHardwareAcceleration: setHardwareAcceleration, allowHtmlInCanvas: allowHtmlInCanvas, setAllowHtmlInCanvas: setAllowHtmlInCanvas })) : (jsx_runtime_1.jsx(WebRenderModalLicense_1.WebRenderModalLicense, { licenseKey: licenseKey, setLicenseKey: setLicenseKey, initialPublicLicenseKey: initialLicenseKey })) })
430
434
  ] })
431
435
  ] }));
432
436
  };
@@ -8,6 +8,8 @@ type WebRenderModalAdvancedProps = {
8
8
  readonly setMediaCacheSizeInBytes: React.Dispatch<React.SetStateAction<number | null>>;
9
9
  readonly hardwareAcceleration: 'no-preference' | 'prefer-hardware' | 'prefer-software';
10
10
  readonly setHardwareAcceleration: (value: 'no-preference' | 'prefer-hardware' | 'prefer-software') => void;
11
+ readonly allowHtmlInCanvas: boolean;
12
+ readonly setAllowHtmlInCanvas: React.Dispatch<React.SetStateAction<boolean>>;
11
13
  };
12
14
  export declare const WebRenderModalAdvanced: React.FC<WebRenderModalAdvancedProps>;
13
15
  export {};
@@ -13,7 +13,7 @@ const OptionExplainerBubble_1 = require("./OptionExplainerBubble");
13
13
  const tabContainer = {
14
14
  flex: 1,
15
15
  };
16
- const WebRenderModalAdvanced = ({ renderMode, delayRenderTimeout, setDelayRenderTimeout, mediaCacheSizeInBytes, setMediaCacheSizeInBytes, hardwareAcceleration, setHardwareAcceleration, }) => {
16
+ const WebRenderModalAdvanced = ({ renderMode, delayRenderTimeout, setDelayRenderTimeout, mediaCacheSizeInBytes, setMediaCacheSizeInBytes, hardwareAcceleration, setHardwareAcceleration, allowHtmlInCanvas, setAllowHtmlInCanvas, }) => {
17
17
  const toggleCustomMediaCacheSizeInBytes = (0, react_1.useCallback)(() => {
18
18
  setMediaCacheSizeInBytes((previous) => {
19
19
  if (previous === null) {
@@ -22,6 +22,9 @@ const WebRenderModalAdvanced = ({ renderMode, delayRenderTimeout, setDelayRender
22
22
  return null;
23
23
  });
24
24
  }, [setMediaCacheSizeInBytes]);
25
+ const toggleAllowHtmlInCanvas = (0, react_1.useCallback)(() => {
26
+ setAllowHtmlInCanvas((prev) => !prev);
27
+ }, [setAllowHtmlInCanvas]);
25
28
  const changeMediaCacheSizeInBytes = (0, react_1.useCallback)((cb) => {
26
29
  setMediaCacheSizeInBytes((prev) => {
27
30
  if (prev === null) {
@@ -77,6 +80,11 @@ const WebRenderModalAdvanced = ({ renderMode, delayRenderTimeout, setDelayRender
77
80
  ] }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(Checkbox_1.Checkbox, { checked: mediaCacheSizeInBytes !== null, onChange: toggleCustomMediaCacheSizeInBytes, name: "media-cache-size" }) })
78
81
  ] }), mediaCacheSizeInBytes === null ? null : (jsx_runtime_1.jsx(NumberSetting_1.NumberSetting, { name: "@remotion/media cache size", formatter: (w) => `${w} bytes`, min: 0, max: 10000000000, step: 10 * 1024 * 1024, value: mediaCacheSizeInBytes, onValueChanged: changeMediaCacheSizeInBytes })), renderMode === 'video' ? (jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
79
82
  jsx_runtime_1.jsx("div", { style: layout_2.label, children: "Hardware Acceleration" }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(ComboBox_1.Combobox, { values: hardwareAccelerationOptions, selectedId: hardwareAcceleration, title: "Hardware Acceleration" }) })
80
- ] })) : null] }));
83
+ ] })) : null, jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
84
+ jsx_runtime_1.jsxs("div", { style: layout_2.label, children: ["Allow HTML-in-canvas ",
85
+ jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(OptionExplainerBubble_1.OptionExplainerBubble, { id: "allowHtmlInCanvasOption" })
86
+ ] }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(Checkbox_1.Checkbox, { checked: allowHtmlInCanvas, onChange: toggleAllowHtmlInCanvas, name: "allow-html-in-canvas" }) })
87
+ ] })
88
+ ] }));
81
89
  };
82
90
  exports.WebRenderModalAdvanced = WebRenderModalAdvanced;
@@ -25,7 +25,7 @@ const ClientRenderQueueProcessor = () => {
25
25
  if (!compositionRef) {
26
26
  throw new Error(`Composition not found for job ${job.id}`);
27
27
  }
28
- const { blob } = await (0, web_renderer_1.renderStillOnWeb)({
28
+ const blob = await (await (0, web_renderer_1.renderStillOnWeb)({
29
29
  composition: {
30
30
  component: compositionRef.component,
31
31
  width: compositionRef.width,
@@ -37,7 +37,6 @@ const ClientRenderQueueProcessor = () => {
37
37
  id: job.compositionId,
38
38
  },
39
39
  frame: job.frame,
40
- imageFormat: job.imageFormat,
41
40
  inputProps: job.inputProps,
42
41
  delayRenderTimeoutInMilliseconds: job.delayRenderTimeout,
43
42
  mediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
@@ -45,7 +44,8 @@ const ClientRenderQueueProcessor = () => {
45
44
  licenseKey: (_b = job.licenseKey) !== null && _b !== void 0 ? _b : undefined,
46
45
  scale: job.scale,
47
46
  signal,
48
- });
47
+ allowHtmlInCanvas: job.allowHtmlInCanvas,
48
+ })).blob({ format: job.imageFormat });
49
49
  return {
50
50
  getBlob: () => Promise.resolve(blob),
51
51
  width: compositionRef.width,
@@ -97,6 +97,7 @@ const ClientRenderQueueProcessor = () => {
97
97
  },
98
98
  outputTarget: 'web-fs',
99
99
  licenseKey: (_c = job.licenseKey) !== null && _c !== void 0 ? _c : undefined,
100
+ allowHtmlInCanvas: job.allowHtmlInCanvas,
100
101
  });
101
102
  return {
102
103
  getBlob,
@@ -40,6 +40,7 @@ type ClientRenderJobBase = {
40
40
  logLevel: LogLevel;
41
41
  licenseKey: string | null;
42
42
  scale: number;
43
+ allowHtmlInCanvas: boolean;
43
44
  };
44
45
  export type ClientStillRenderJob = ClientRenderJobBase & {
45
46
  type: 'client-still';
@@ -2440,6 +2440,34 @@ var MenuSubItem = ({
2440
2440
  });
2441
2441
  };
2442
2442
 
2443
+ // src/components/NewComposition/menu-typeahead.ts
2444
+ var getLabelToMatch = (value) => {
2445
+ if (value.type === "divider" || value.disabled) {
2446
+ return null;
2447
+ }
2448
+ if (typeof value.label === "string") {
2449
+ return value.label;
2450
+ }
2451
+ return null;
2452
+ };
2453
+ var findTypeaheadMenuItem = ({
2454
+ query,
2455
+ values
2456
+ }) => {
2457
+ const normalizedQuery = query.trim().toLowerCase();
2458
+ if (normalizedQuery.length === 0) {
2459
+ return null;
2460
+ }
2461
+ const matched = values.find((value) => {
2462
+ const label = getLabelToMatch(value);
2463
+ return label ? label.toLowerCase().startsWith(normalizedQuery) : false;
2464
+ });
2465
+ if (!matched || matched.type === "divider") {
2466
+ return null;
2467
+ }
2468
+ return matched.id;
2469
+ };
2470
+
2443
2471
  // src/components/NewComposition/MenuContent.tsx
2444
2472
  import { jsx as jsx25 } from "react/jsx-runtime";
2445
2473
  var BORDER_SIZE = 1;
@@ -2467,6 +2495,8 @@ var MenuContent = ({
2467
2495
  const containerRef = useRef8(null);
2468
2496
  const isMobileLayout = useMobileLayout();
2469
2497
  const [subMenuActivated, setSubMenuActivated] = useState12(false);
2498
+ const typeaheadQueryRef = useRef8("");
2499
+ const typeaheadTimeoutRef = useRef8(null);
2470
2500
  if (values[0].type === "divider") {
2471
2501
  throw new Error("first value cant be divide");
2472
2502
  }
@@ -2477,6 +2507,13 @@ var MenuContent = ({
2477
2507
  const onItemSelected = useCallback11((id) => {
2478
2508
  setSelectedItem(id);
2479
2509
  }, []);
2510
+ const clearTypeahead = useCallback11(() => {
2511
+ typeaheadQueryRef.current = "";
2512
+ if (typeaheadTimeoutRef.current !== null) {
2513
+ window.clearTimeout(typeaheadTimeoutRef.current);
2514
+ typeaheadTimeoutRef.current = null;
2515
+ }
2516
+ }, []);
2480
2517
  const isItemSelectable = useCallback11((v) => {
2481
2518
  return v.type !== "divider" && !v.disabled;
2482
2519
  }, []);
@@ -2550,6 +2587,30 @@ var MenuContent = ({
2550
2587
  }
2551
2588
  setSubMenuActivated("without-mouse");
2552
2589
  }, [onNextMenu, selectedItem, values]);
2590
+ const onTypeahead = useCallback11((event) => {
2591
+ if (event.ctrlKey || event.metaKey || event.altKey || event.key.length !== 1 || event.key.trim().length === 0) {
2592
+ return;
2593
+ }
2594
+ const { activeElement } = document;
2595
+ if (activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement) {
2596
+ return;
2597
+ }
2598
+ typeaheadQueryRef.current = `${typeaheadQueryRef.current}${event.key}`;
2599
+ const matchedId = findTypeaheadMenuItem({
2600
+ query: typeaheadQueryRef.current,
2601
+ values
2602
+ });
2603
+ if (matchedId !== null) {
2604
+ setSelectedItem(matchedId);
2605
+ }
2606
+ if (typeaheadTimeoutRef.current !== null) {
2607
+ window.clearTimeout(typeaheadTimeoutRef.current);
2608
+ }
2609
+ typeaheadTimeoutRef.current = window.setTimeout(() => {
2610
+ typeaheadQueryRef.current = "";
2611
+ typeaheadTimeoutRef.current = null;
2612
+ }, 700);
2613
+ }, [values]);
2553
2614
  const containerWithHeight = useMemo15(() => {
2554
2615
  const containerStyles = { ...container6 };
2555
2616
  if (fixedHeight === null) {
@@ -2562,6 +2623,24 @@ var MenuContent = ({
2562
2623
  }
2563
2624
  return containerStyles;
2564
2625
  }, [fixedHeight, isMobileLayout]);
2626
+ useEffect10(() => {
2627
+ if (!keybindings.isHighestContext || !process.env.KEYBOARD_SHORTCUTS_ENABLED) {
2628
+ return;
2629
+ }
2630
+ const onKeyDown = (event) => {
2631
+ onTypeahead(event);
2632
+ };
2633
+ window.addEventListener("keydown", onKeyDown);
2634
+ return () => {
2635
+ window.removeEventListener("keydown", onKeyDown);
2636
+ clearTypeahead();
2637
+ };
2638
+ }, [clearTypeahead, keybindings.isHighestContext, onTypeahead]);
2639
+ useEffect10(() => {
2640
+ return () => {
2641
+ clearTypeahead();
2642
+ };
2643
+ }, [clearTypeahead]);
2565
2644
  useEffect10(() => {
2566
2645
  const escapeBinding = keybindings.registerKeybinding({
2567
2646
  event: "keydown",
@@ -16214,7 +16293,7 @@ var ClientRenderQueueProcessor = () => {
16214
16293
  if (!compositionRef) {
16215
16294
  throw new Error(`Composition not found for job ${job.id}`);
16216
16295
  }
16217
- const { blob } = await renderStillOnWeb({
16296
+ const blob = await (await renderStillOnWeb({
16218
16297
  composition: {
16219
16298
  component: compositionRef.component,
16220
16299
  width: compositionRef.width,
@@ -16226,15 +16305,15 @@ var ClientRenderQueueProcessor = () => {
16226
16305
  id: job.compositionId
16227
16306
  },
16228
16307
  frame: job.frame,
16229
- imageFormat: job.imageFormat,
16230
16308
  inputProps: job.inputProps,
16231
16309
  delayRenderTimeoutInMilliseconds: job.delayRenderTimeout,
16232
16310
  mediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
16233
16311
  logLevel: job.logLevel,
16234
16312
  licenseKey: job.licenseKey ?? undefined,
16235
16313
  scale: job.scale,
16236
- signal
16237
- });
16314
+ signal,
16315
+ allowHtmlInCanvas: job.allowHtmlInCanvas
16316
+ })).blob({ format: job.imageFormat });
16238
16317
  return {
16239
16318
  getBlob: () => Promise.resolve(blob),
16240
16319
  width: compositionRef.width,
@@ -16284,7 +16363,8 @@ var ClientRenderQueueProcessor = () => {
16284
16363
  });
16285
16364
  },
16286
16365
  outputTarget: "web-fs",
16287
- licenseKey: job.licenseKey ?? undefined
16366
+ licenseKey: job.licenseKey ?? undefined,
16367
+ allowHtmlInCanvas: job.allowHtmlInCanvas
16288
16368
  });
16289
16369
  return {
16290
16370
  getBlob,
@@ -16962,7 +17042,8 @@ var makeClientRetryPayload = (job) => {
16962
17042
  initialStillImageFormat: job.type === "client-still" ? job.imageFormat : "png",
16963
17043
  initialKeyframeIntervalInSeconds: job.type === "client-video" ? job.keyframeIntervalInSeconds : null,
16964
17044
  initialMuted: job.type === "client-video" ? job.muted : null,
16965
- initialTransparent: job.type === "client-video" ? job.transparent : null
17045
+ initialTransparent: job.type === "client-video" ? job.transparent : null,
17046
+ initialAllowHtmlInCanvas: job.allowHtmlInCanvas
16966
17047
  };
16967
17048
  };
16968
17049
 
@@ -18820,7 +18901,8 @@ var RenderButton = ({
18820
18901
  initialKeyframeIntervalInSeconds: null,
18821
18902
  initialTransparent: null,
18822
18903
  initialMuted: null,
18823
- initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes
18904
+ initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes,
18905
+ initialAllowHtmlInCanvas: defaults.allowHtmlInCanvas
18824
18906
  });
18825
18907
  }, [video, setSelectedModal, getCurrentFrame2, props, inFrame, outFrame]);
18826
18908
  const onClick = useCallback93(() => {
@@ -28253,8 +28335,9 @@ var padding3 = {
28253
28335
  var title6 = {
28254
28336
  fontSize: 14
28255
28337
  };
28338
+ var DESCRIPTION_FONT_SIZE_PX = 14;
28256
28339
  var description = {
28257
- fontSize: 14,
28340
+ fontSize: DESCRIPTION_FONT_SIZE_PX,
28258
28341
  maxWidth: 400
28259
28342
  };
28260
28343
  var link2 = {
@@ -28313,9 +28396,22 @@ var OptionExplainer = ({ option }) => {
28313
28396
  }) : null
28314
28397
  ]
28315
28398
  }),
28316
- /* @__PURE__ */ jsx250("div", {
28399
+ /* @__PURE__ */ jsxs126("div", {
28317
28400
  style: description,
28318
- children: option.description("ssr")
28401
+ children: [
28402
+ /* @__PURE__ */ jsx250("style", {
28403
+ children: `
28404
+ .__remotion-option-explainer-description a,
28405
+ .__remotion-option-explainer-description code {
28406
+ font-size: ${DESCRIPTION_FONT_SIZE_PX}px;
28407
+ }
28408
+ `
28409
+ }),
28410
+ /* @__PURE__ */ jsx250("div", {
28411
+ className: "__remotion-option-explainer-description",
28412
+ children: option.description("ssr")
28413
+ })
28414
+ ]
28319
28415
  })
28320
28416
  ]
28321
28417
  }),
@@ -32433,7 +32529,9 @@ var WebRenderModalAdvanced = ({
32433
32529
  mediaCacheSizeInBytes,
32434
32530
  setMediaCacheSizeInBytes,
32435
32531
  hardwareAcceleration,
32436
- setHardwareAcceleration
32532
+ setHardwareAcceleration,
32533
+ allowHtmlInCanvas,
32534
+ setAllowHtmlInCanvas
32437
32535
  }) => {
32438
32536
  const toggleCustomMediaCacheSizeInBytes = useCallback142(() => {
32439
32537
  setMediaCacheSizeInBytes((previous) => {
@@ -32443,6 +32541,9 @@ var WebRenderModalAdvanced = ({
32443
32541
  return null;
32444
32542
  });
32445
32543
  }, [setMediaCacheSizeInBytes]);
32544
+ const toggleAllowHtmlInCanvas = useCallback142(() => {
32545
+ setAllowHtmlInCanvas((prev) => !prev);
32546
+ }, [setAllowHtmlInCanvas]);
32446
32547
  const changeMediaCacheSizeInBytes = useCallback142((cb) => {
32447
32548
  setMediaCacheSizeInBytes((prev) => {
32448
32549
  if (prev === null) {
@@ -32554,7 +32655,32 @@ var WebRenderModalAdvanced = ({
32554
32655
  })
32555
32656
  })
32556
32657
  ]
32557
- }) : null
32658
+ }) : null,
32659
+ /* @__PURE__ */ jsxs145("div", {
32660
+ style: optionRow,
32661
+ children: [
32662
+ /* @__PURE__ */ jsxs145("div", {
32663
+ style: label5,
32664
+ children: [
32665
+ "Allow HTML-in-canvas ",
32666
+ /* @__PURE__ */ jsx275(Spacing, {
32667
+ x: 0.5
32668
+ }),
32669
+ /* @__PURE__ */ jsx275(OptionExplainerBubble, {
32670
+ id: "allowHtmlInCanvasOption"
32671
+ })
32672
+ ]
32673
+ }),
32674
+ /* @__PURE__ */ jsx275("div", {
32675
+ style: rightRow,
32676
+ children: /* @__PURE__ */ jsx275(Checkbox, {
32677
+ checked: allowHtmlInCanvas,
32678
+ onChange: toggleAllowHtmlInCanvas,
32679
+ name: "allow-html-in-canvas"
32680
+ })
32681
+ })
32682
+ ]
32683
+ })
32558
32684
  ]
32559
32685
  });
32560
32686
  };
@@ -33571,7 +33697,8 @@ var WebRenderModal = ({
33571
33697
  initialHardwareAcceleration,
33572
33698
  initialKeyframeIntervalInSeconds,
33573
33699
  initialTransparent,
33574
- initialMuted
33700
+ initialMuted,
33701
+ initialAllowHtmlInCanvas
33575
33702
  }) => {
33576
33703
  const context = useContext95(ResolvedCompositionContext);
33577
33704
  const { setSelectedModal } = useContext95(ModalsContext);
@@ -33611,6 +33738,7 @@ var WebRenderModal = ({
33611
33738
  const [muted, setMuted] = useState95(initialMuted ?? false);
33612
33739
  const [scale, setScale] = useState95(initialScale ?? 1);
33613
33740
  const [licenseKey, setLicenseKey] = useState95(initialLicenseKey);
33741
+ const [allowHtmlInCanvas, setAllowHtmlInCanvas] = useState95(initialAllowHtmlInCanvas ?? false);
33614
33742
  const encodableAudioCodecs = useEncodableAudioCodecs(container61);
33615
33743
  const encodableVideoCodecs = useEncodableVideoCodecs(container61);
33616
33744
  const effectiveAudioCodec = useMemo147(() => {
@@ -33803,7 +33931,8 @@ var WebRenderModal = ({
33803
33931
  mediaCacheSizeInBytes,
33804
33932
  logLevel,
33805
33933
  licenseKey,
33806
- scale
33934
+ scale,
33935
+ allowHtmlInCanvas
33807
33936
  }, compositionRef);
33808
33937
  } else {
33809
33938
  addClientVideoJob({
@@ -33826,7 +33955,8 @@ var WebRenderModal = ({
33826
33955
  mediaCacheSizeInBytes,
33827
33956
  logLevel,
33828
33957
  licenseKey,
33829
- scale
33958
+ scale,
33959
+ allowHtmlInCanvas
33830
33960
  }, compositionRef);
33831
33961
  }
33832
33962
  setSidebarCollapsedState({ left: null, right: "expanded" });
@@ -33866,7 +33996,8 @@ var WebRenderModal = ({
33866
33996
  setSelectedModal,
33867
33997
  addClientStillJob,
33868
33998
  addClientVideoJob,
33869
- scale
33999
+ scale,
34000
+ allowHtmlInCanvas
33870
34001
  ]);
33871
34002
  return /* @__PURE__ */ jsxs151("div", {
33872
34003
  style: outerModalStyle,
@@ -34061,7 +34192,9 @@ var WebRenderModal = ({
34061
34192
  mediaCacheSizeInBytes,
34062
34193
  setMediaCacheSizeInBytes,
34063
34194
  hardwareAcceleration,
34064
- setHardwareAcceleration
34195
+ setHardwareAcceleration,
34196
+ allowHtmlInCanvas,
34197
+ setAllowHtmlInCanvas
34065
34198
  }) : /* @__PURE__ */ jsx283(WebRenderModalLicense, {
34066
34199
  licenseKey,
34067
34200
  setLicenseKey,
@@ -2459,6 +2459,34 @@ var MenuSubItem = ({
2459
2459
  });
2460
2460
  };
2461
2461
 
2462
+ // src/components/NewComposition/menu-typeahead.ts
2463
+ var getLabelToMatch = (value) => {
2464
+ if (value.type === "divider" || value.disabled) {
2465
+ return null;
2466
+ }
2467
+ if (typeof value.label === "string") {
2468
+ return value.label;
2469
+ }
2470
+ return null;
2471
+ };
2472
+ var findTypeaheadMenuItem = ({
2473
+ query,
2474
+ values
2475
+ }) => {
2476
+ const normalizedQuery = query.trim().toLowerCase();
2477
+ if (normalizedQuery.length === 0) {
2478
+ return null;
2479
+ }
2480
+ const matched = values.find((value) => {
2481
+ const label = getLabelToMatch(value);
2482
+ return label ? label.toLowerCase().startsWith(normalizedQuery) : false;
2483
+ });
2484
+ if (!matched || matched.type === "divider") {
2485
+ return null;
2486
+ }
2487
+ return matched.id;
2488
+ };
2489
+
2462
2490
  // src/components/NewComposition/MenuContent.tsx
2463
2491
  import { jsx as jsx25 } from "react/jsx-runtime";
2464
2492
  var BORDER_SIZE = 1;
@@ -2486,6 +2514,8 @@ var MenuContent = ({
2486
2514
  const containerRef = useRef8(null);
2487
2515
  const isMobileLayout = useMobileLayout();
2488
2516
  const [subMenuActivated, setSubMenuActivated] = useState12(false);
2517
+ const typeaheadQueryRef = useRef8("");
2518
+ const typeaheadTimeoutRef = useRef8(null);
2489
2519
  if (values[0].type === "divider") {
2490
2520
  throw new Error("first value cant be divide");
2491
2521
  }
@@ -2496,6 +2526,13 @@ var MenuContent = ({
2496
2526
  const onItemSelected = useCallback11((id) => {
2497
2527
  setSelectedItem(id);
2498
2528
  }, []);
2529
+ const clearTypeahead = useCallback11(() => {
2530
+ typeaheadQueryRef.current = "";
2531
+ if (typeaheadTimeoutRef.current !== null) {
2532
+ window.clearTimeout(typeaheadTimeoutRef.current);
2533
+ typeaheadTimeoutRef.current = null;
2534
+ }
2535
+ }, []);
2499
2536
  const isItemSelectable = useCallback11((v) => {
2500
2537
  return v.type !== "divider" && !v.disabled;
2501
2538
  }, []);
@@ -2569,6 +2606,30 @@ var MenuContent = ({
2569
2606
  }
2570
2607
  setSubMenuActivated("without-mouse");
2571
2608
  }, [onNextMenu, selectedItem, values]);
2609
+ const onTypeahead = useCallback11((event) => {
2610
+ if (event.ctrlKey || event.metaKey || event.altKey || event.key.length !== 1 || event.key.trim().length === 0) {
2611
+ return;
2612
+ }
2613
+ const { activeElement } = document;
2614
+ if (activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement) {
2615
+ return;
2616
+ }
2617
+ typeaheadQueryRef.current = `${typeaheadQueryRef.current}${event.key}`;
2618
+ const matchedId = findTypeaheadMenuItem({
2619
+ query: typeaheadQueryRef.current,
2620
+ values
2621
+ });
2622
+ if (matchedId !== null) {
2623
+ setSelectedItem(matchedId);
2624
+ }
2625
+ if (typeaheadTimeoutRef.current !== null) {
2626
+ window.clearTimeout(typeaheadTimeoutRef.current);
2627
+ }
2628
+ typeaheadTimeoutRef.current = window.setTimeout(() => {
2629
+ typeaheadQueryRef.current = "";
2630
+ typeaheadTimeoutRef.current = null;
2631
+ }, 700);
2632
+ }, [values]);
2572
2633
  const containerWithHeight = useMemo15(() => {
2573
2634
  const containerStyles = { ...container6 };
2574
2635
  if (fixedHeight === null) {
@@ -2581,6 +2642,24 @@ var MenuContent = ({
2581
2642
  }
2582
2643
  return containerStyles;
2583
2644
  }, [fixedHeight, isMobileLayout]);
2645
+ useEffect10(() => {
2646
+ if (!keybindings.isHighestContext || !process.env.KEYBOARD_SHORTCUTS_ENABLED) {
2647
+ return;
2648
+ }
2649
+ const onKeyDown = (event) => {
2650
+ onTypeahead(event);
2651
+ };
2652
+ window.addEventListener("keydown", onKeyDown);
2653
+ return () => {
2654
+ window.removeEventListener("keydown", onKeyDown);
2655
+ clearTypeahead();
2656
+ };
2657
+ }, [clearTypeahead, keybindings.isHighestContext, onTypeahead]);
2658
+ useEffect10(() => {
2659
+ return () => {
2660
+ clearTypeahead();
2661
+ };
2662
+ }, [clearTypeahead]);
2584
2663
  useEffect10(() => {
2585
2664
  const escapeBinding = keybindings.registerKeybinding({
2586
2665
  event: "keydown",
@@ -16233,7 +16312,7 @@ var ClientRenderQueueProcessor = () => {
16233
16312
  if (!compositionRef) {
16234
16313
  throw new Error(`Composition not found for job ${job.id}`);
16235
16314
  }
16236
- const { blob } = await renderStillOnWeb({
16315
+ const blob = await (await renderStillOnWeb({
16237
16316
  composition: {
16238
16317
  component: compositionRef.component,
16239
16318
  width: compositionRef.width,
@@ -16245,15 +16324,15 @@ var ClientRenderQueueProcessor = () => {
16245
16324
  id: job.compositionId
16246
16325
  },
16247
16326
  frame: job.frame,
16248
- imageFormat: job.imageFormat,
16249
16327
  inputProps: job.inputProps,
16250
16328
  delayRenderTimeoutInMilliseconds: job.delayRenderTimeout,
16251
16329
  mediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
16252
16330
  logLevel: job.logLevel,
16253
16331
  licenseKey: job.licenseKey ?? undefined,
16254
16332
  scale: job.scale,
16255
- signal
16256
- });
16333
+ signal,
16334
+ allowHtmlInCanvas: job.allowHtmlInCanvas
16335
+ })).blob({ format: job.imageFormat });
16257
16336
  return {
16258
16337
  getBlob: () => Promise.resolve(blob),
16259
16338
  width: compositionRef.width,
@@ -16303,7 +16382,8 @@ var ClientRenderQueueProcessor = () => {
16303
16382
  });
16304
16383
  },
16305
16384
  outputTarget: "web-fs",
16306
- licenseKey: job.licenseKey ?? undefined
16385
+ licenseKey: job.licenseKey ?? undefined,
16386
+ allowHtmlInCanvas: job.allowHtmlInCanvas
16307
16387
  });
16308
16388
  return {
16309
16389
  getBlob,
@@ -16981,7 +17061,8 @@ var makeClientRetryPayload = (job) => {
16981
17061
  initialStillImageFormat: job.type === "client-still" ? job.imageFormat : "png",
16982
17062
  initialKeyframeIntervalInSeconds: job.type === "client-video" ? job.keyframeIntervalInSeconds : null,
16983
17063
  initialMuted: job.type === "client-video" ? job.muted : null,
16984
- initialTransparent: job.type === "client-video" ? job.transparent : null
17064
+ initialTransparent: job.type === "client-video" ? job.transparent : null,
17065
+ initialAllowHtmlInCanvas: job.allowHtmlInCanvas
16985
17066
  };
16986
17067
  };
16987
17068
 
@@ -18839,7 +18920,8 @@ var RenderButton = ({
18839
18920
  initialKeyframeIntervalInSeconds: null,
18840
18921
  initialTransparent: null,
18841
18922
  initialMuted: null,
18842
- initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes
18923
+ initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes,
18924
+ initialAllowHtmlInCanvas: defaults.allowHtmlInCanvas
18843
18925
  });
18844
18926
  }, [video, setSelectedModal, getCurrentFrame2, props, inFrame, outFrame]);
18845
18927
  const onClick = useCallback93(() => {
@@ -28272,8 +28354,9 @@ var padding3 = {
28272
28354
  var title6 = {
28273
28355
  fontSize: 14
28274
28356
  };
28357
+ var DESCRIPTION_FONT_SIZE_PX = 14;
28275
28358
  var description = {
28276
- fontSize: 14,
28359
+ fontSize: DESCRIPTION_FONT_SIZE_PX,
28277
28360
  maxWidth: 400
28278
28361
  };
28279
28362
  var link2 = {
@@ -28332,9 +28415,22 @@ var OptionExplainer = ({ option }) => {
28332
28415
  }) : null
28333
28416
  ]
28334
28417
  }),
28335
- /* @__PURE__ */ jsx250("div", {
28418
+ /* @__PURE__ */ jsxs126("div", {
28336
28419
  style: description,
28337
- children: option.description("ssr")
28420
+ children: [
28421
+ /* @__PURE__ */ jsx250("style", {
28422
+ children: `
28423
+ .__remotion-option-explainer-description a,
28424
+ .__remotion-option-explainer-description code {
28425
+ font-size: ${DESCRIPTION_FONT_SIZE_PX}px;
28426
+ }
28427
+ `
28428
+ }),
28429
+ /* @__PURE__ */ jsx250("div", {
28430
+ className: "__remotion-option-explainer-description",
28431
+ children: option.description("ssr")
28432
+ })
28433
+ ]
28338
28434
  })
28339
28435
  ]
28340
28436
  }),
@@ -32452,7 +32548,9 @@ var WebRenderModalAdvanced = ({
32452
32548
  mediaCacheSizeInBytes,
32453
32549
  setMediaCacheSizeInBytes,
32454
32550
  hardwareAcceleration,
32455
- setHardwareAcceleration
32551
+ setHardwareAcceleration,
32552
+ allowHtmlInCanvas,
32553
+ setAllowHtmlInCanvas
32456
32554
  }) => {
32457
32555
  const toggleCustomMediaCacheSizeInBytes = useCallback142(() => {
32458
32556
  setMediaCacheSizeInBytes((previous) => {
@@ -32462,6 +32560,9 @@ var WebRenderModalAdvanced = ({
32462
32560
  return null;
32463
32561
  });
32464
32562
  }, [setMediaCacheSizeInBytes]);
32563
+ const toggleAllowHtmlInCanvas = useCallback142(() => {
32564
+ setAllowHtmlInCanvas((prev) => !prev);
32565
+ }, [setAllowHtmlInCanvas]);
32465
32566
  const changeMediaCacheSizeInBytes = useCallback142((cb) => {
32466
32567
  setMediaCacheSizeInBytes((prev) => {
32467
32568
  if (prev === null) {
@@ -32573,7 +32674,32 @@ var WebRenderModalAdvanced = ({
32573
32674
  })
32574
32675
  })
32575
32676
  ]
32576
- }) : null
32677
+ }) : null,
32678
+ /* @__PURE__ */ jsxs145("div", {
32679
+ style: optionRow,
32680
+ children: [
32681
+ /* @__PURE__ */ jsxs145("div", {
32682
+ style: label5,
32683
+ children: [
32684
+ "Allow HTML-in-canvas ",
32685
+ /* @__PURE__ */ jsx275(Spacing, {
32686
+ x: 0.5
32687
+ }),
32688
+ /* @__PURE__ */ jsx275(OptionExplainerBubble, {
32689
+ id: "allowHtmlInCanvasOption"
32690
+ })
32691
+ ]
32692
+ }),
32693
+ /* @__PURE__ */ jsx275("div", {
32694
+ style: rightRow,
32695
+ children: /* @__PURE__ */ jsx275(Checkbox, {
32696
+ checked: allowHtmlInCanvas,
32697
+ onChange: toggleAllowHtmlInCanvas,
32698
+ name: "allow-html-in-canvas"
32699
+ })
32700
+ })
32701
+ ]
32702
+ })
32577
32703
  ]
32578
32704
  });
32579
32705
  };
@@ -33590,7 +33716,8 @@ var WebRenderModal = ({
33590
33716
  initialHardwareAcceleration,
33591
33717
  initialKeyframeIntervalInSeconds,
33592
33718
  initialTransparent,
33593
- initialMuted
33719
+ initialMuted,
33720
+ initialAllowHtmlInCanvas
33594
33721
  }) => {
33595
33722
  const context = useContext95(ResolvedCompositionContext);
33596
33723
  const { setSelectedModal } = useContext95(ModalsContext);
@@ -33630,6 +33757,7 @@ var WebRenderModal = ({
33630
33757
  const [muted, setMuted] = useState95(initialMuted ?? false);
33631
33758
  const [scale, setScale] = useState95(initialScale ?? 1);
33632
33759
  const [licenseKey, setLicenseKey] = useState95(initialLicenseKey);
33760
+ const [allowHtmlInCanvas, setAllowHtmlInCanvas] = useState95(initialAllowHtmlInCanvas ?? false);
33633
33761
  const encodableAudioCodecs = useEncodableAudioCodecs(container61);
33634
33762
  const encodableVideoCodecs = useEncodableVideoCodecs(container61);
33635
33763
  const effectiveAudioCodec = useMemo147(() => {
@@ -33822,7 +33950,8 @@ var WebRenderModal = ({
33822
33950
  mediaCacheSizeInBytes,
33823
33951
  logLevel,
33824
33952
  licenseKey,
33825
- scale
33953
+ scale,
33954
+ allowHtmlInCanvas
33826
33955
  }, compositionRef);
33827
33956
  } else {
33828
33957
  addClientVideoJob({
@@ -33845,7 +33974,8 @@ var WebRenderModal = ({
33845
33974
  mediaCacheSizeInBytes,
33846
33975
  logLevel,
33847
33976
  licenseKey,
33848
- scale
33977
+ scale,
33978
+ allowHtmlInCanvas
33849
33979
  }, compositionRef);
33850
33980
  }
33851
33981
  setSidebarCollapsedState({ left: null, right: "expanded" });
@@ -33885,7 +34015,8 @@ var WebRenderModal = ({
33885
34015
  setSelectedModal,
33886
34016
  addClientStillJob,
33887
34017
  addClientVideoJob,
33888
- scale
34018
+ scale,
34019
+ allowHtmlInCanvas
33889
34020
  ]);
33890
34021
  return /* @__PURE__ */ jsxs151("div", {
33891
34022
  style: outerModalStyle,
@@ -34080,7 +34211,9 @@ var WebRenderModal = ({
34080
34211
  mediaCacheSizeInBytes,
34081
34212
  setMediaCacheSizeInBytes,
34082
34213
  hardwareAcceleration,
34083
- setHardwareAcceleration
34214
+ setHardwareAcceleration,
34215
+ allowHtmlInCanvas,
34216
+ setAllowHtmlInCanvas
34084
34217
  }) : /* @__PURE__ */ jsx283(WebRenderModalLicense, {
34085
34218
  licenseKey,
34086
34219
  setLicenseKey,
@@ -4689,6 +4689,34 @@ var MenuSubItem = ({
4689
4689
  });
4690
4690
  };
4691
4691
 
4692
+ // src/components/NewComposition/menu-typeahead.ts
4693
+ var getLabelToMatch = (value) => {
4694
+ if (value.type === "divider" || value.disabled) {
4695
+ return null;
4696
+ }
4697
+ if (typeof value.label === "string") {
4698
+ return value.label;
4699
+ }
4700
+ return null;
4701
+ };
4702
+ var findTypeaheadMenuItem = ({
4703
+ query,
4704
+ values
4705
+ }) => {
4706
+ const normalizedQuery = query.trim().toLowerCase();
4707
+ if (normalizedQuery.length === 0) {
4708
+ return null;
4709
+ }
4710
+ const matched = values.find((value) => {
4711
+ const label2 = getLabelToMatch(value);
4712
+ return label2 ? label2.toLowerCase().startsWith(normalizedQuery) : false;
4713
+ });
4714
+ if (!matched || matched.type === "divider") {
4715
+ return null;
4716
+ }
4717
+ return matched.id;
4718
+ };
4719
+
4692
4720
  // src/components/NewComposition/MenuContent.tsx
4693
4721
  import { jsx as jsx46 } from "react/jsx-runtime";
4694
4722
  var BORDER_SIZE = 1;
@@ -4716,6 +4744,8 @@ var MenuContent = ({
4716
4744
  const containerRef = useRef10(null);
4717
4745
  const isMobileLayout = useMobileLayout();
4718
4746
  const [subMenuActivated, setSubMenuActivated] = useState19(false);
4747
+ const typeaheadQueryRef = useRef10("");
4748
+ const typeaheadTimeoutRef = useRef10(null);
4719
4749
  if (values[0].type === "divider") {
4720
4750
  throw new Error("first value cant be divide");
4721
4751
  }
@@ -4726,6 +4756,13 @@ var MenuContent = ({
4726
4756
  const onItemSelected = useCallback20((id) => {
4727
4757
  setSelectedItem(id);
4728
4758
  }, []);
4759
+ const clearTypeahead = useCallback20(() => {
4760
+ typeaheadQueryRef.current = "";
4761
+ if (typeaheadTimeoutRef.current !== null) {
4762
+ window.clearTimeout(typeaheadTimeoutRef.current);
4763
+ typeaheadTimeoutRef.current = null;
4764
+ }
4765
+ }, []);
4729
4766
  const isItemSelectable = useCallback20((v) => {
4730
4767
  return v.type !== "divider" && !v.disabled;
4731
4768
  }, []);
@@ -4799,6 +4836,30 @@ var MenuContent = ({
4799
4836
  }
4800
4837
  setSubMenuActivated("without-mouse");
4801
4838
  }, [onNextMenu, selectedItem, values]);
4839
+ const onTypeahead = useCallback20((event) => {
4840
+ if (event.ctrlKey || event.metaKey || event.altKey || event.key.length !== 1 || event.key.trim().length === 0) {
4841
+ return;
4842
+ }
4843
+ const { activeElement } = document;
4844
+ if (activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement) {
4845
+ return;
4846
+ }
4847
+ typeaheadQueryRef.current = `${typeaheadQueryRef.current}${event.key}`;
4848
+ const matchedId = findTypeaheadMenuItem({
4849
+ query: typeaheadQueryRef.current,
4850
+ values
4851
+ });
4852
+ if (matchedId !== null) {
4853
+ setSelectedItem(matchedId);
4854
+ }
4855
+ if (typeaheadTimeoutRef.current !== null) {
4856
+ window.clearTimeout(typeaheadTimeoutRef.current);
4857
+ }
4858
+ typeaheadTimeoutRef.current = window.setTimeout(() => {
4859
+ typeaheadQueryRef.current = "";
4860
+ typeaheadTimeoutRef.current = null;
4861
+ }, 700);
4862
+ }, [values]);
4802
4863
  const containerWithHeight = useMemo20(() => {
4803
4864
  const containerStyles = { ...container11 };
4804
4865
  if (fixedHeight === null) {
@@ -4811,6 +4872,24 @@ var MenuContent = ({
4811
4872
  }
4812
4873
  return containerStyles;
4813
4874
  }, [fixedHeight, isMobileLayout]);
4875
+ useEffect18(() => {
4876
+ if (!keybindings.isHighestContext || !process.env.KEYBOARD_SHORTCUTS_ENABLED) {
4877
+ return;
4878
+ }
4879
+ const onKeyDown = (event) => {
4880
+ onTypeahead(event);
4881
+ };
4882
+ window.addEventListener("keydown", onKeyDown);
4883
+ return () => {
4884
+ window.removeEventListener("keydown", onKeyDown);
4885
+ clearTypeahead();
4886
+ };
4887
+ }, [clearTypeahead, keybindings.isHighestContext, onTypeahead]);
4888
+ useEffect18(() => {
4889
+ return () => {
4890
+ clearTypeahead();
4891
+ };
4892
+ }, [clearTypeahead]);
4814
4893
  useEffect18(() => {
4815
4894
  const escapeBinding = keybindings.registerKeybinding({
4816
4895
  event: "keydown",
@@ -16825,7 +16904,7 @@ var ClientRenderQueueProcessor = () => {
16825
16904
  if (!compositionRef) {
16826
16905
  throw new Error(`Composition not found for job ${job.id}`);
16827
16906
  }
16828
- const { blob } = await renderStillOnWeb({
16907
+ const blob = await (await renderStillOnWeb({
16829
16908
  composition: {
16830
16909
  component: compositionRef.component,
16831
16910
  width: compositionRef.width,
@@ -16837,15 +16916,15 @@ var ClientRenderQueueProcessor = () => {
16837
16916
  id: job.compositionId
16838
16917
  },
16839
16918
  frame: job.frame,
16840
- imageFormat: job.imageFormat,
16841
16919
  inputProps: job.inputProps,
16842
16920
  delayRenderTimeoutInMilliseconds: job.delayRenderTimeout,
16843
16921
  mediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
16844
16922
  logLevel: job.logLevel,
16845
16923
  licenseKey: job.licenseKey ?? undefined,
16846
16924
  scale: job.scale,
16847
- signal
16848
- });
16925
+ signal,
16926
+ allowHtmlInCanvas: job.allowHtmlInCanvas
16927
+ })).blob({ format: job.imageFormat });
16849
16928
  return {
16850
16929
  getBlob: () => Promise.resolve(blob),
16851
16930
  width: compositionRef.width,
@@ -16895,7 +16974,8 @@ var ClientRenderQueueProcessor = () => {
16895
16974
  });
16896
16975
  },
16897
16976
  outputTarget: "web-fs",
16898
- licenseKey: job.licenseKey ?? undefined
16977
+ licenseKey: job.licenseKey ?? undefined,
16978
+ allowHtmlInCanvas: job.allowHtmlInCanvas
16899
16979
  });
16900
16980
  return {
16901
16981
  getBlob,
@@ -17573,7 +17653,8 @@ var makeClientRetryPayload = (job) => {
17573
17653
  initialStillImageFormat: job.type === "client-still" ? job.imageFormat : "png",
17574
17654
  initialKeyframeIntervalInSeconds: job.type === "client-video" ? job.keyframeIntervalInSeconds : null,
17575
17655
  initialMuted: job.type === "client-video" ? job.muted : null,
17576
- initialTransparent: job.type === "client-video" ? job.transparent : null
17656
+ initialTransparent: job.type === "client-video" ? job.transparent : null,
17657
+ initialAllowHtmlInCanvas: job.allowHtmlInCanvas
17577
17658
  };
17578
17659
  };
17579
17660
 
@@ -19431,7 +19512,8 @@ var RenderButton = ({
19431
19512
  initialKeyframeIntervalInSeconds: null,
19432
19513
  initialTransparent: null,
19433
19514
  initialMuted: null,
19434
- initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes
19515
+ initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes,
19516
+ initialAllowHtmlInCanvas: defaults.allowHtmlInCanvas
19435
19517
  });
19436
19518
  }, [video, setSelectedModal, getCurrentFrame2, props, inFrame, outFrame]);
19437
19519
  const onClick = useCallback94(() => {
@@ -28864,8 +28946,9 @@ var padding3 = {
28864
28946
  var title6 = {
28865
28947
  fontSize: 14
28866
28948
  };
28949
+ var DESCRIPTION_FONT_SIZE_PX = 14;
28867
28950
  var description = {
28868
- fontSize: 14,
28951
+ fontSize: DESCRIPTION_FONT_SIZE_PX,
28869
28952
  maxWidth: 400
28870
28953
  };
28871
28954
  var link3 = {
@@ -28924,9 +29007,22 @@ var OptionExplainer = ({ option }) => {
28924
29007
  }) : null
28925
29008
  ]
28926
29009
  }),
28927
- /* @__PURE__ */ jsx253("div", {
29010
+ /* @__PURE__ */ jsxs127("div", {
28928
29011
  style: description,
28929
- children: option.description("ssr")
29012
+ children: [
29013
+ /* @__PURE__ */ jsx253("style", {
29014
+ children: `
29015
+ .__remotion-option-explainer-description a,
29016
+ .__remotion-option-explainer-description code {
29017
+ font-size: ${DESCRIPTION_FONT_SIZE_PX}px;
29018
+ }
29019
+ `
29020
+ }),
29021
+ /* @__PURE__ */ jsx253("div", {
29022
+ className: "__remotion-option-explainer-description",
29023
+ children: option.description("ssr")
29024
+ })
29025
+ ]
28930
29026
  })
28931
29027
  ]
28932
29028
  }),
@@ -33044,7 +33140,9 @@ var WebRenderModalAdvanced = ({
33044
33140
  mediaCacheSizeInBytes,
33045
33141
  setMediaCacheSizeInBytes,
33046
33142
  hardwareAcceleration,
33047
- setHardwareAcceleration
33143
+ setHardwareAcceleration,
33144
+ allowHtmlInCanvas,
33145
+ setAllowHtmlInCanvas
33048
33146
  }) => {
33049
33147
  const toggleCustomMediaCacheSizeInBytes = useCallback143(() => {
33050
33148
  setMediaCacheSizeInBytes((previous) => {
@@ -33054,6 +33152,9 @@ var WebRenderModalAdvanced = ({
33054
33152
  return null;
33055
33153
  });
33056
33154
  }, [setMediaCacheSizeInBytes]);
33155
+ const toggleAllowHtmlInCanvas = useCallback143(() => {
33156
+ setAllowHtmlInCanvas((prev) => !prev);
33157
+ }, [setAllowHtmlInCanvas]);
33057
33158
  const changeMediaCacheSizeInBytes = useCallback143((cb) => {
33058
33159
  setMediaCacheSizeInBytes((prev) => {
33059
33160
  if (prev === null) {
@@ -33165,7 +33266,32 @@ var WebRenderModalAdvanced = ({
33165
33266
  })
33166
33267
  })
33167
33268
  ]
33168
- }) : null
33269
+ }) : null,
33270
+ /* @__PURE__ */ jsxs146("div", {
33271
+ style: optionRow,
33272
+ children: [
33273
+ /* @__PURE__ */ jsxs146("div", {
33274
+ style: label6,
33275
+ children: [
33276
+ "Allow HTML-in-canvas ",
33277
+ /* @__PURE__ */ jsx278(Spacing, {
33278
+ x: 0.5
33279
+ }),
33280
+ /* @__PURE__ */ jsx278(OptionExplainerBubble, {
33281
+ id: "allowHtmlInCanvasOption"
33282
+ })
33283
+ ]
33284
+ }),
33285
+ /* @__PURE__ */ jsx278("div", {
33286
+ style: rightRow,
33287
+ children: /* @__PURE__ */ jsx278(Checkbox, {
33288
+ checked: allowHtmlInCanvas,
33289
+ onChange: toggleAllowHtmlInCanvas,
33290
+ name: "allow-html-in-canvas"
33291
+ })
33292
+ })
33293
+ ]
33294
+ })
33169
33295
  ]
33170
33296
  });
33171
33297
  };
@@ -34182,7 +34308,8 @@ var WebRenderModal = ({
34182
34308
  initialHardwareAcceleration,
34183
34309
  initialKeyframeIntervalInSeconds,
34184
34310
  initialTransparent,
34185
- initialMuted
34311
+ initialMuted,
34312
+ initialAllowHtmlInCanvas
34186
34313
  }) => {
34187
34314
  const context = useContext95(ResolvedCompositionContext);
34188
34315
  const { setSelectedModal } = useContext95(ModalsContext);
@@ -34222,6 +34349,7 @@ var WebRenderModal = ({
34222
34349
  const [muted, setMuted] = useState97(initialMuted ?? false);
34223
34350
  const [scale, setScale] = useState97(initialScale ?? 1);
34224
34351
  const [licenseKey, setLicenseKey] = useState97(initialLicenseKey);
34352
+ const [allowHtmlInCanvas, setAllowHtmlInCanvas] = useState97(initialAllowHtmlInCanvas ?? false);
34225
34353
  const encodableAudioCodecs = useEncodableAudioCodecs(container62);
34226
34354
  const encodableVideoCodecs = useEncodableVideoCodecs(container62);
34227
34355
  const effectiveAudioCodec = useMemo147(() => {
@@ -34414,7 +34542,8 @@ var WebRenderModal = ({
34414
34542
  mediaCacheSizeInBytes,
34415
34543
  logLevel,
34416
34544
  licenseKey,
34417
- scale
34545
+ scale,
34546
+ allowHtmlInCanvas
34418
34547
  }, compositionRef);
34419
34548
  } else {
34420
34549
  addClientVideoJob({
@@ -34437,7 +34566,8 @@ var WebRenderModal = ({
34437
34566
  mediaCacheSizeInBytes,
34438
34567
  logLevel,
34439
34568
  licenseKey,
34440
- scale
34569
+ scale,
34570
+ allowHtmlInCanvas
34441
34571
  }, compositionRef);
34442
34572
  }
34443
34573
  setSidebarCollapsedState({ left: null, right: "expanded" });
@@ -34477,7 +34607,8 @@ var WebRenderModal = ({
34477
34607
  setSelectedModal,
34478
34608
  addClientStillJob,
34479
34609
  addClientVideoJob,
34480
- scale
34610
+ scale,
34611
+ allowHtmlInCanvas
34481
34612
  ]);
34482
34613
  return /* @__PURE__ */ jsxs152("div", {
34483
34614
  style: outerModalStyle,
@@ -34672,7 +34803,9 @@ var WebRenderModal = ({
34672
34803
  mediaCacheSizeInBytes,
34673
34804
  setMediaCacheSizeInBytes,
34674
34805
  hardwareAcceleration,
34675
- setHardwareAcceleration
34806
+ setHardwareAcceleration,
34807
+ allowHtmlInCanvas,
34808
+ setAllowHtmlInCanvas
34676
34809
  }) : /* @__PURE__ */ jsx286(WebRenderModalLicense, {
34677
34810
  licenseKey,
34678
34811
  setLicenseKey,
@@ -208,7 +208,7 @@ var renderContent = (Root) => {
208
208
  renderToDOM(/* @__PURE__ */ jsx("div", {
209
209
  children: /* @__PURE__ */ jsx(DelayedSpinner, {})
210
210
  }));
211
- import("./chunk-2dkkw8x5.js").then(({ StudioInternals }) => {
211
+ import("./chunk-ase93hmz.js").then(({ StudioInternals }) => {
212
212
  window.remotion_isStudio = true;
213
213
  window.remotion_isReadOnlyStudio = true;
214
214
  window.remotion_inputProps = "{}";
@@ -196,6 +196,7 @@ const makeClientRetryPayload = (job) => {
196
196
  initialKeyframeIntervalInSeconds: job.type === 'client-video' ? job.keyframeIntervalInSeconds : null,
197
197
  initialMuted: job.type === 'client-video' ? job.muted : null,
198
198
  initialTransparent: job.type === 'client-video' ? job.transparent : null,
199
+ initialAllowHtmlInCanvas: job.allowHtmlInCanvas,
199
200
  };
200
201
  };
201
202
  exports.makeClientRetryPayload = makeClientRetryPayload;
@@ -31,6 +31,7 @@ export type WebRenderModalState = {
31
31
  initialMuted: boolean | null;
32
32
  initialLicenseKey: string | null;
33
33
  initialMediaCacheSizeInBytes: number | null;
34
+ initialAllowHtmlInCanvas: boolean;
34
35
  };
35
36
  export type RenderModalState = {
36
37
  type: 'server-render';
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/studio"
4
4
  },
5
5
  "name": "@remotion/studio",
6
- "version": "4.0.446",
6
+ "version": "4.0.447",
7
7
  "description": "APIs for interacting with the Remotion Studio",
8
8
  "main": "dist",
9
9
  "sideEffects": false,
@@ -26,13 +26,13 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "semver": "7.5.3",
29
- "remotion": "4.0.446",
30
- "@remotion/player": "4.0.446",
31
- "@remotion/media-utils": "4.0.446",
32
- "@remotion/renderer": "4.0.446",
33
- "@remotion/web-renderer": "4.0.446",
34
- "@remotion/studio-shared": "4.0.446",
35
- "@remotion/zod-types": "4.0.446",
29
+ "remotion": "4.0.447",
30
+ "@remotion/player": "4.0.447",
31
+ "@remotion/media-utils": "4.0.447",
32
+ "@remotion/renderer": "4.0.447",
33
+ "@remotion/web-renderer": "4.0.447",
34
+ "@remotion/studio-shared": "4.0.447",
35
+ "@remotion/zod-types": "4.0.447",
36
36
  "mediabunny": "1.39.2",
37
37
  "memfs": "3.4.3",
38
38
  "source-map": "0.7.3",
@@ -43,7 +43,7 @@
43
43
  "react": "19.2.3",
44
44
  "react-dom": "19.2.3",
45
45
  "@types/semver": "^7.3.4",
46
- "@remotion/eslint-config-internal": "4.0.446",
46
+ "@remotion/eslint-config-internal": "4.0.447",
47
47
  "eslint": "9.19.0",
48
48
  "@typescript/native-preview": "7.0.0-dev.20260217.1"
49
49
  },