feat: add 'No Webcam' layout preset to hide webcam in final recording

Adds a new 'No Webcam' option to the webcam layout preset dropdown in the editor. When selected, the webcam feed is completely hidden from both the preview and the exported video, allowing users who recorded with a webcam to exclude it from the final output.

- Add 'no-webcam' to WebcamLayoutPreset type union and preset map

- Handle 'no-webcam' in computeCompositeLayout (returns webcamRect: null)

- Add 'no-webcam' case in project persistence normalization

- Add 'No Webcam' option to the layout preset dropdown in SettingsPanel

- Add 'noWebcam' i18n translation key (en)
This commit is contained in:
Ayusman Singhal
2026-05-07 12:19:48 +05:30
parent 899504f8e2
commit ada1f434f7
4 changed files with 37 additions and 2 deletions
@@ -821,6 +821,7 @@ export function SettingsPanel({
<SelectContent>
{WEBCAM_LAYOUT_PRESETS.filter((preset) => {
if (preset.value === "picture-in-picture") return true;
if (preset.value === "no-webcam") return true;
if (preset.value === "vertical-stack") return isPortraitCanvas;
return !isPortraitCanvas;
}).map((preset) => (
@@ -829,7 +830,9 @@ export function SettingsPanel({
? t("layout.pictureInPicture")
: preset.value === "vertical-stack"
? t("layout.verticalStack")
: t("layout.dualFrame")}
: preset.value === "no-webcam"
? t("layout.noWebcam")
: t("layout.dualFrame")}
</SelectItem>
))}
</SelectContent>
@@ -100,6 +100,7 @@ function computeNormalizedWebcamLayoutPreset(
): WebcamLayoutPreset {
switch (webcamLayoutPreset) {
case "picture-in-picture":
case "no-webcam":
return webcamLayoutPreset;
case "vertical-stack":
return isPortraitAspectRatio(normalizedAspectRatio)
+1
View File
@@ -35,6 +35,7 @@
"pictureInPicture": "Picture in Picture",
"verticalStack": "Vertical Stack",
"dualFrame": "Dual Frame",
"noWebcam": "No Webcam",
"webcamShape": "Camera Shape",
"webcamSize": "Webcam Size"
},
+31 -1
View File
@@ -15,7 +15,11 @@ export interface Size {
height: number;
}
export type WebcamLayoutPreset = "picture-in-picture" | "vertical-stack" | "dual-frame";
export type WebcamLayoutPreset =
| "picture-in-picture"
| "vertical-stack"
| "dual-frame"
| "no-webcam";
/** Webcam size as a percentage of the canvas reference dimension (1050). */
export type WebcamSizePreset = number;
@@ -126,6 +130,21 @@ const WEBCAM_LAYOUT_PRESET_MAP: Record<WebcamLayoutPreset, WebcamLayoutPresetDef
},
shadow: null,
},
"no-webcam": {
label: "No Webcam",
transform: {
type: "overlay",
marginFraction: 0,
minMargin: 0,
minSize: 0,
},
borderRadius: {
max: 0,
min: 0,
fraction: 0,
},
shadow: null,
},
};
export const WEBCAM_LAYOUT_PRESETS = Object.entries(WEBCAM_LAYOUT_PRESET_MAP).map(
@@ -172,6 +191,17 @@ export function computeCompositeLayout(params: {
} = params;
const { width: canvasWidth, height: canvasHeight } = canvasSize;
const { width: screenWidth, height: screenHeight } = screenSize;
// "no-webcam" preset: hide the webcam entirely, screen fills the canvas normally
if (layoutPreset === "no-webcam") {
const screenRect = centerRect({
canvasSize,
size: screenSize,
maxSize: maxContentSize,
});
return { screenRect, webcamRect: null };
}
const webcamWidth = webcamSize?.width;
const webcamHeight = webcamSize?.height;
const preset = getWebcamLayoutPresetDefinition(layoutPreset);