From 90ef4ce025665390f2c794ebbfb36994c96d2f61 Mon Sep 17 00:00:00 2001 From: Cyril Date: Fri, 24 Apr 2026 13:36:15 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B(frontend)=20fix=20PiP=20tile=20cor?= =?UTF-8?q?ner=20clipping=20and=20unify=204px=20radius?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set a single clipping owner per PiP layout and use a consistent 4px radius. --- .../src/features/pip/components/PipView.tsx | 12 +- .../pip/components/layouts/PipFocusLayout.tsx | 160 ++++++++-------- .../pip/components/layouts/PipGridLayout.tsx | 164 ++++++++--------- .../pip/components/layouts/PipStage.tsx | 174 +++++++++--------- 4 files changed, 253 insertions(+), 257 deletions(-) diff --git a/src/frontend/src/features/pip/components/PipView.tsx b/src/frontend/src/features/pip/components/PipView.tsx index e1facf03..0c321663 100644 --- a/src/frontend/src/features/pip/components/PipView.tsx +++ b/src/frontend/src/features/pip/components/PipView.tsx @@ -54,23 +54,17 @@ const PipContainer = styled('div', { display: 'grid', gridTemplateRows: 'minmax(0, 1fr) auto auto', backgroundColor: 'primaryDark.50', + // Disable LiveKit's own border-radius on tiles so our containers + // (GridCell, Thumbnail, StageFrame) own the clipping exclusively. + '--lk-border-radius': '4px', '& .lk-participant-tile': { height: '100%', - overflow: 'hidden', - backgroundColor: 'primaryDark.100', }, '& .lk-participant-media': { height: '100%', - overflow: 'hidden', - borderRadius: 0, - backgroundColor: 'primaryDark.100', }, '& .lk-participant-media-video': { - display: 'block', - width: '100%', height: '100%', - borderRadius: 0, - backgroundColor: 'primaryDark.100', objectFit: 'cover', }, '& .lk-grid-layout': { diff --git a/src/frontend/src/features/pip/components/layouts/PipFocusLayout.tsx b/src/frontend/src/features/pip/components/layouts/PipFocusLayout.tsx index 285d557c..9cd10aea 100644 --- a/src/frontend/src/features/pip/components/layouts/PipFocusLayout.tsx +++ b/src/frontend/src/features/pip/components/layouts/PipFocusLayout.tsx @@ -1,79 +1,81 @@ -import { memo } from 'react' -import type { TrackReferenceOrPlaceholder } from '@livekit/components-core' -import { styled } from '@/styled-system/jsx' -import { ParticipantTile } from '@/features/rooms/livekit/components/ParticipantTile' -import { getTrackKey } from '../../utils/pipTrackSelection' - -type PipFocusLayoutProps = { - mainTrack: TrackReferenceOrPlaceholder - thumbnailTrack?: TrackReferenceOrPlaceholder -} - -/** - * Focus layout used when 1-2 tracks are visible in the PiP window. - * - * The main tile is letterboxed (object-fit: contain) so the camera is - * never stretched to a non-video aspect and leaves dark padding - * above/below when the window shape doesn't match the source. - * The thumbnail keeps the usual cover fill. - */ -export const PipFocusLayout = memo(function PipFocusLayout({ - mainTrack, - thumbnailTrack, -}: PipFocusLayoutProps) { - return ( - - - - - {thumbnailTrack && ( - - - - )} - - ) -}) - -const FocusContainer = styled('div', { - base: { - position: 'relative', - width: '100%', - height: '100%', - backgroundColor: 'primaryDark.100', - }, -}) - -const MainSlot = styled('div', { - base: { - width: '100%', - height: '100%', - '& .lk-participant-media-video': { - objectFit: 'contain', - }, - }, -}) - -const Thumbnail = styled('div', { - base: { - position: 'absolute', - right: '1rem', - bottom: '1rem', - width: '42%', - maxWidth: '220px', - minWidth: '140px', - aspectRatio: '16 / 9', - borderRadius: 'md', - overflow: 'hidden', - boxShadow: 'md', - zIndex: 2, - }, -}) +import { memo } from 'react' +import type { TrackReferenceOrPlaceholder } from '@livekit/components-core' +import { styled } from '@/styled-system/jsx' +import { ParticipantTile } from '@/features/rooms/livekit/components/ParticipantTile' +import { getTrackKey } from '../../utils/pipTrackSelection' + +type PipFocusLayoutProps = { + mainTrack: TrackReferenceOrPlaceholder + thumbnailTrack?: TrackReferenceOrPlaceholder +} + +/** + * Focus layout used when 1-2 tracks are visible in the PiP window. + * + * The main tile is letterboxed (object-fit: contain) so the camera is + * never stretched to a non-video aspect and leaves dark padding + * above/below when the window shape doesn't match the source. + * The thumbnail keeps the usual cover fill. + */ +export const PipFocusLayout = memo(function PipFocusLayout({ + mainTrack, + thumbnailTrack, +}: PipFocusLayoutProps) { + return ( + + + + + {thumbnailTrack && ( + + + + )} + + ) +}) + +const FocusContainer = styled('div', { + base: { + position: 'relative', + width: '100%', + height: '100%', + borderRadius: '4px', + overflow: 'hidden', + backgroundColor: 'primaryDark.100', + }, +}) + +const MainSlot = styled('div', { + base: { + width: '100%', + height: '100%', + '& .lk-participant-media-video': { + objectFit: 'contain', + }, + }, +}) + +const Thumbnail = styled('div', { + base: { + position: 'absolute', + right: '1rem', + bottom: '1rem', + width: '42%', + maxWidth: '220px', + minWidth: '140px', + aspectRatio: '16 / 9', + borderRadius: '4px', + overflow: 'hidden', + boxShadow: 'md', + zIndex: 2, + }, +}) diff --git a/src/frontend/src/features/pip/components/layouts/PipGridLayout.tsx b/src/frontend/src/features/pip/components/layouts/PipGridLayout.tsx index 2e749946..350f86cd 100644 --- a/src/frontend/src/features/pip/components/layouts/PipGridLayout.tsx +++ b/src/frontend/src/features/pip/components/layouts/PipGridLayout.tsx @@ -1,82 +1,82 @@ -import { memo, useMemo, useRef } from 'react' -import type { TrackReferenceOrPlaceholder } from '@livekit/components-core' -import { styled } from '@/styled-system/jsx' -import { ParticipantTile } from '@/features/rooms/livekit/components/ParticipantTile' -import { usePipElementSize } from '../../hooks/usePipElementSize' -import { usePipFlipAnimations } from '../../hooks/usePipFlipAnimations' -import { computePipGridLayout } from '../../utils/pipGrid' -import { getTrackKey } from '../../utils/pipTrackSelection' - -type PipGridLayoutProps = { - tracks: TrackReferenceOrPlaceholder[] -} - -/** - * Adaptive grid used when 3+ tracks are visible in the PiP window. - * - * All grid math (shape choice + partial-row stretching) is delegated to - * `computePipGridLayout`. This component only measures the container, - * applies the returned placements, and plays a FLIP animation when the - * tile set or grid shape changes (participant joins/leaves or shape shift). - * - * Tiles keep a stable key so resizing never remounts