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