@@ -52,11 +52,12 @@ export interface WebcamLayoutPresetDefinition {
5252export interface WebcamCompositeLayout {
5353 screenRect : RenderRect ;
5454 webcamRect : StyledRenderRect | null ;
55+ /** When true, the video should be scaled to cover screenRect (cropping overflow). */
56+ screenCover ?: boolean ;
5557}
5658
5759const MAX_STAGE_FRACTION = 0.18 ;
5860const MARGIN_FRACTION = 0.02 ;
59- const MIN_SIZE = 96 ;
6061const MAX_BORDER_RADIUS = 24 ;
6162const WEBCAM_LAYOUT_PRESET_MAP : Record < WebcamLayoutPreset , WebcamLayoutPresetDefinition > = {
6263 "picture-in-picture" : {
@@ -65,8 +66,8 @@ const WEBCAM_LAYOUT_PRESET_MAP: Record<WebcamLayoutPreset, WebcamLayoutPresetDef
6566 type : "overlay" ,
6667 maxStageFraction : MAX_STAGE_FRACTION ,
6768 marginFraction : MARGIN_FRACTION ,
68- minMargin : 12 ,
69- minSize : MIN_SIZE ,
69+ minMargin : 0 ,
70+ minSize : 0 ,
7071 } ,
7172 borderRadius : {
7273 max : MAX_BORDER_RADIUS ,
@@ -134,7 +135,6 @@ export function computeCompositeLayout(params: {
134135 webcamPosition,
135136 } = params ;
136137 const { width : canvasWidth , height : canvasHeight } = canvasSize ;
137- const { width : maxContentWidth , height : maxContentHeight } = maxContentSize ;
138138 const { width : screenWidth , height : screenHeight } = screenSize ;
139139 const webcamWidth = webcamSize ?. width ;
140140 const webcamHeight = webcamSize ?. height ;
@@ -146,52 +146,37 @@ export function computeCompositeLayout(params: {
146146
147147 if ( preset . transform . type === "stack" ) {
148148 if ( ! webcamWidth || ! webcamHeight || webcamWidth <= 0 || webcamHeight <= 0 ) {
149+ // No webcam — screen fills the entire canvas (cover mode)
149150 return {
150- screenRect : centerRect ( {
151- canvasSize,
152- size : screenSize ,
153- maxSize : maxContentSize ,
154- } ) ,
151+ screenRect : { x : 0 , y : 0 , width : canvasWidth , height : canvasHeight } ,
155152 webcamRect : null ,
153+ screenCover : true ,
156154 } ;
157155 }
158156
159- const gap = preset . transform . gap ;
160- const normalizedWebcamHeight = webcamHeight * ( screenWidth / webcamWidth ) ;
161- const combinedHeight = screenHeight + gap + normalizedWebcamHeight ;
162- const scale = Math . min ( maxContentWidth / screenWidth , maxContentHeight / combinedHeight , 1 ) ;
163- const clampedScale = Number . isFinite ( scale ) && scale > 0 ? scale : 1 ;
164- const resolvedScreenHeight = Math . round ( screenHeight * clampedScale ) ;
165- const resolvedScreenWidth = Math . round ( screenWidth * clampedScale ) ;
166- const resolvedWebcamHeight = Math . round ( normalizedWebcamHeight * clampedScale ) ;
167- const resolvedGap = Math . round ( gap * clampedScale ) ;
168- const totalHeight = resolvedScreenHeight + resolvedGap + resolvedWebcamHeight ;
169- const top = Math . max ( 0 , Math . floor ( ( canvasHeight - totalHeight ) / 2 ) ) ;
170- const left = Math . max ( 0 , Math . floor ( ( canvasWidth - resolvedScreenWidth ) / 2 ) ) ;
171- const screenRect = {
172- x : left ,
173- y : top ,
174- width : resolvedScreenWidth ,
175- height : resolvedScreenHeight ,
176- } ;
157+ // Webcam: full width at the bottom, maintaining its aspect ratio
158+ const webcamAspect = webcamWidth / webcamHeight ;
159+ const resolvedWebcamWidth = canvasWidth ;
160+ const resolvedWebcamHeight = Math . round ( canvasWidth / webcamAspect ) ;
161+
162+ // Screen: fills remaining space at the top (cover mode — may crop sides)
163+ const screenRectHeight = canvasHeight - resolvedWebcamHeight ;
177164
178165 return {
179- screenRect,
166+ screenRect : {
167+ x : 0 ,
168+ y : 0 ,
169+ width : canvasWidth ,
170+ height : Math . max ( 0 , screenRectHeight ) ,
171+ } ,
180172 webcamRect : {
181- x : left ,
182- y : top + resolvedScreenHeight + resolvedGap ,
183- width : resolvedScreenWidth ,
173+ x : 0 ,
174+ y : Math . max ( 0 , screenRectHeight ) ,
175+ width : resolvedWebcamWidth ,
184176 height : resolvedWebcamHeight ,
185- borderRadius : Math . min (
186- preset . borderRadius . max ,
187- Math . max (
188- preset . borderRadius . min ,
189- Math . round (
190- Math . min ( resolvedScreenWidth , resolvedWebcamHeight ) * preset . borderRadius . fraction ,
191- ) ,
192- ) ,
193- ) ,
177+ borderRadius : 0 ,
194178 } ,
179+ screenCover : true ,
195180 } ;
196181 }
197182
0 commit comments