diff --git a/src/components/player/NowPlayingOverlay.tsx b/src/components/player/NowPlayingOverlay.tsx index 2479f71..abc5b98 100644 --- a/src/components/player/NowPlayingOverlay.tsx +++ b/src/components/player/NowPlayingOverlay.tsx @@ -210,7 +210,10 @@ export function NowPlayingOverlay() { const collaborators = splitCollaborators(track.artist); return buildArtistNameTokens( collaborators.length > 0 ? collaborators : [track.artist] - ); + ).map((token) => ({ + ...token, + separator: token.separator ? ', ' : null, + })); }, [track]); const albumKey = track?.albumIdentityKey ?? libraryTrack?.album_identity_key; @@ -572,6 +575,7 @@ export function NowPlayingOverlay() { ({ width: '100%', alignItems: 'center', }, + middleStackCentered: { + justifyContent: 'center', + }, artButton: { alignItems: 'center', justifyContent: 'center', diff --git a/src/components/player/nowPlayingLayout.test.mts b/src/components/player/nowPlayingLayout.test.mts index 7692765..148d6d4 100644 --- a/src/components/player/nowPlayingLayout.test.mts +++ b/src/components/player/nowPlayingLayout.test.mts @@ -10,9 +10,9 @@ const BASELINES = [ [320, 568, true, 296, 96, 204, 58], [360, 640, false, 328, 206, 214, 58], [360, 640, true, 328, 96, 214, 58], - [393, 852, false, 361, 361, 361, 76], + [393, 852, false, 361, 336, 361, 76], [393, 852, true, 361, 234, 361, 76], - [412, 915, false, 380, 380, 380, 82], + [412, 915, false, 380, 336, 380, 82], [412, 915, true, 380, 248, 380, 82], [600, 840, false, 520, 394, 394, 58], [600, 840, true, 520, 262, 394, 58], @@ -47,6 +47,17 @@ test('keeps the lower-content anchor stable when the analyzer toggles', () => { } }); +test('caps visualizer-off artwork on roomy phones without shrinking the media stage', () => { + for (const [width, height] of [ + [393, 852], + [412, 915], + ]) { + const hidden = getNowPlayingLayout(width, height, false); + assert.equal(hidden.artSize, 336); + assert.ok(hidden.mediaStackHeight > hidden.artSize); + } +}); + test('adds the companion only to roomy tablet canvases', () => { for (const [width, height] of [ [320, 568], diff --git a/src/components/player/nowPlayingLayout.ts b/src/components/player/nowPlayingLayout.ts index 9ea5933..f339729 100644 --- a/src/components/player/nowPlayingLayout.ts +++ b/src/components/player/nowPlayingLayout.ts @@ -7,6 +7,7 @@ const NARROW_CONTENT_SIDE_PADDING = spacing.md; const MEDIA_AREA_MIN = 220; const TABLET_MAX_CONTENT_WIDTH = 520; const TABLET_ART_SIZE_MAX = 440; +const PHONE_SCOPE_OFF_ART_SIZE_MAX = 336; const WIDE_MAX_CONTENT_WIDTH = 960; export const NOW_PLAYING_WIDE_PANE_GAP = spacing.xxl; const WIDE_RIGHT_PANE_MIN = 300; @@ -183,14 +184,19 @@ export function getNowPlayingLayout( NOW_PLAYING_SUB_BUTTON_SIZE + MIN_FLOATING_SPACE; const bound = availableHeight - fixedHeightBase - mediaBottomGap; - const scopeOffArt = Math.round( + const uncappedScopeOffArt = Math.round( clamp(bound, Math.min(mediaMin, Math.max(96, bound)), mediaMax) ); - const offSurplus = Math.max(0, bound - scopeOffArt); + const scopeOffArt = isTabletColumn + ? uncappedScopeOffArt + : Math.min(uncappedScopeOffArt, PHONE_SCOPE_OFF_ART_SIZE_MAX); + const offSurplus = Math.max(0, bound - uncappedScopeOffArt); const stretchUnit = Math.min(Math.floor(offSurplus / 5), spacing.md); const waveformHeight = NOW_PLAYING_WAVEFORM_HEIGHT + stretchUnit * 2; const scopeBlockHeight = VISUALIZER_TOP_GAP + scopeHeight + VISUALIZER_BOTTOM_GAP; - const mediaStackHeight = Math.max(scopeOffArt, 96 + scopeBlockHeight); + // Keep the old stage height even when the visualizer-off artwork is capped. + // This preserves the metadata/lower-content anchor across the toggle. + const mediaStackHeight = Math.max(uncappedScopeOffArt, 96 + scopeBlockHeight); const scopeOnArt = mediaStackHeight - scopeBlockHeight; const artSize = showVisualizer ? scopeOnArt : scopeOffArt; const visualizerTopGap = showVisualizer ? VISUALIZER_TOP_GAP : 0;