From 8baac2fd1ec404755c59fcb96232e01fe27a35f2 Mon Sep 17 00:00:00 2001 From: byGalax Date: Tue, 12 May 2026 21:31:26 +0200 Subject: [PATCH] fix(call): tile sizing polish + cinema-mode chrome suppression + spec/plan docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Equal-grid cells no longer set aspect-video — on wide chat panels this forced cell height = width × 9/16 (~400px on a 700px panel) which pushed the row past the section's max-h and ate the controls bar below. n>=2 cells now fill grid tracks normally via auto-rows-fr; the solo case (n=1) keeps a 16:9 silhouette via aspect-video + max-w + justify-self- center so a single-user-alone-calling view doesn't stretch into a full-width slab. Same change applied to the fullscreen-grid path plus +16px bottom-padding (pb-28) so audio-only avatars' name chip clears the floating controls bar. Docked stage strip thumbs (focus + bento) switch from aspect-video shrink-0 to flex-1 min-w-[200px] max-w-[460px] so 2-3 thumbs share the row width evenly under the share above, instead of clinging to the left edge with dead space to the right. Fullscreen-cinema strip keeps the small aspect-video thumbs the user explicitly approved. ScreenShareViewer gains a hideFullscreenToggle prop; cinema mode passes it via a new `cinema` prop on TileRender so the in-share fullscreen icon doesn't visually collide with FullscreenCall's strip-hidden toggle at the same top-right corner. docs/superpowers/specs + plans for the Discord-style tile handling workstream are committed alongside the implementation that completed it. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/desktop/src/components/InCallPanel.tsx | 88 +- .../src/components/ScreenShareViewer.tsx | 8 +- .../2026-05-12-discord-call-tile-handling.md | 892 ++++++++++++++++++ ...05-12-discord-call-tile-handling-design.md | 139 +++ 4 files changed, 1103 insertions(+), 24 deletions(-) create mode 100644 docs/superpowers/plans/2026-05-12-discord-call-tile-handling.md create mode 100644 docs/superpowers/specs/2026-05-12-discord-call-tile-handling-design.md 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