diff --git a/apps/desktop/src/components/InCallPanel.tsx b/apps/desktop/src/components/InCallPanel.tsx index 9eaa867..07890b3 100644 --- a/apps/desktop/src/components/InCallPanel.tsx +++ b/apps/desktop/src/components/InCallPanel.tsx @@ -921,6 +921,7 @@ function TileRender({ conversationMembers, size, focused, + cinema, onClick, onDoubleClick, onContextMenu, @@ -932,6 +933,10 @@ function TileRender({ conversationMembers: StageProps['conversationMembers']; size?: 'default' | 'small'; focused?: boolean; + /** True when rendered inside FullscreenCall's big-tile slot. Drives + * chrome-suppression on the inner ScreenShareViewer so its toggle + * doesn't visually collide with the cinema-mode strip-hidden button. */ + cinema?: boolean; onClick?: () => void; onDoubleClick?: () => void; onContextMenu?: (e: React.MouseEvent) => void; @@ -954,6 +959,7 @@ function TileRender({ share={share} avatarUrl={member?.profile?.avatarUrl ?? tile.avatarUrl} displayName={member?.profile?.displayName ?? tile.displayName} + hideFullscreenToggle={cinema === true} /> ); @@ -1017,7 +1023,11 @@ function CallStage({ {others.map((p) => (
(
=2, and only enforce + // a 16:9 silhouette (capped width, centred) for the solo-user case. + const isSolo = tiles.length === 1; return (
{tiles.map((p) => (
div]:h-full [&>div]:w-full' + : 'min-h-0 min-w-0 [&>div]:h-full [&>div]:w-full' + } > - {/* Content area. pb-24 reserves ~96px space at the bottom for the - floating controls bar so tiles never sit behind it. */} -
+ {/* Content area. pb-28 reserves ~112px space at the bottom for the + floating controls bar plus extra clearance so the tiles' bottom + name-chip (positioned `bottom-2` inside each tile) doesn't sit + directly underneath the controls — pb-24 was tight enough that + on wide screens with audio-only avatars the chip got eclipsed. */} +
{hasFocus ? ( <>
onTileContextMenu(speaker!, e) } : {})} @@ -1394,24 +1420,40 @@ function FullscreenCall({
- {visibleTiles.map((p) => ( -
- onFocusTile(p.id)} - {...(onTileContextMenu - ? { onContextMenu: (e: React.MouseEvent) => onTileContextMenu(p, e) } - : {})} - /> -
- ))} + {visibleTiles.map((p) => { + const isSolo = visibleTiles.length === 1; + return ( +
=2 fill the grid + // tracks normally; for solo, cap width + centre. + className={ + isSolo + ? 'aspect-video w-full max-w-[720px] justify-self-center [&>div]:h-full [&>div]:w-full' + : 'min-h-0 min-w-0 [&>div]:h-full [&>div]:w-full' + } + > + onFocusTile(p.id)} + {...(onTileContextMenu + ? { onContextMenu: (e: React.MouseEvent) => onTileContextMenu(p, e) } + : {})} + /> +
+ ); + })}
{pageCount > 1 && (
diff --git a/apps/desktop/src/components/ScreenShareViewer.tsx b/apps/desktop/src/components/ScreenShareViewer.tsx index 5ce09ec..e62cb62 100644 --- a/apps/desktop/src/components/ScreenShareViewer.tsx +++ b/apps/desktop/src/components/ScreenShareViewer.tsx @@ -9,6 +9,11 @@ interface ScreenShareViewerProps { share: RemoteScreenShare; avatarUrl: string | null; displayName: string; + /** Suppress the in-share fullscreen toggle button. Used inside cinema + * mode where (a) the window is already OS-fullscreen and (b) the + * toggle collides visually with FullscreenCall's strip-hidden button + * at the same top-right corner. */ + hideFullscreenToggle?: boolean; } // Click-to-watch gate, live