refactor(immersive): clean up controls and improve split view layout

- Remove track info (artwork, title, artist) from ImmersiveControls bottom bar
- Add track title and artist below album artwork in split view mode
- Change Lyrics Focus icon from MessageSquareText to Mic (microphone)
- Remove unused compact mode from ImmersiveControls
- Simplify props and cleanup CSS
This commit is contained in:
vicrodh committed 2026-01-30 23:11:06 -06:00
1 parent 0fc0723804
commit 4d5a78ff24
3 files changed
+63 -103

No files matched your search

@@ -6,10 +6,6 @@
interface Props { interface Props {
visible?: boolean; visible?: boolean;
// Track info
artwork: string;
trackTitle: string;
artist: string;
// Playback state // Playback state
isPlaying: boolean; isPlaying: boolean;
currentTime: number; currentTime: number;
@@ -32,15 +28,10 @@
onToggleRepeat: () => void; onToggleRepeat: () => void;
onToggleFavorite: () => void; onToggleFavorite: () => void;
onVolumeChange: (volume: number) => void; onVolumeChange: (volume: number) => void;
// Compact mode (hide some elements)
compact?: boolean;
} }
let { let {
visible = true, visible = true,
artwork,
trackTitle,
artist,
isPlaying, isPlaying,
currentTime, currentTime,
duration, duration,
@@ -58,23 +49,11 @@
onToggleShuffle, onToggleShuffle,
onToggleRepeat, onToggleRepeat,
onToggleFavorite, onToggleFavorite,
onVolumeChange, onVolumeChange
compact = false
}: Props = $props(); }: Props = $props();
</script> </script>
<div class="immersive-controls" class:visible class:compact> <div class="immersive-controls" class:visible>
<!-- Left: Track Info -->
{#if !compact}
<div class="track-info">
<img src={artwork} alt="" class="mini-artwork" />
<div class="track-meta">
<div class="track-title">{trackTitle}</div>
<div class="track-artist">{artist}</div>
</div>
</div>
{/if}
<!-- Center: Playback Controls + Progress --> <!-- Center: Playback Controls + Progress -->
<div class="playback-section"> <div class="playback-section">
<div class="controls"> <div class="controls">
@@ -136,31 +115,29 @@
</div> </div>
<!-- Right: Volume + Actions --> <!-- Right: Volume + Actions -->
{#if !compact} <div class="right-section">
<div class="right-section"> <button
<button class="action-btn"
class="action-btn" class:active={isFavorite}
class:active={isFavorite} onclick={onToggleFavorite}
onclick={onToggleFavorite} title={isFavorite ? 'Remove from Favorites' : 'Add to Favorites'}
title={isFavorite ? 'Remove from Favorites' : 'Add to Favorites'} >
> <Heart
<Heart size={18}
size={18} fill={isFavorite ? 'currentColor' : 'none'}
fill={isFavorite ? 'currentColor' : 'none'} />
/> </button>
</button>
<div class="volume-wrapper"> <div class="volume-wrapper">
<VolumeSlider {volume} {onVolumeChange} /> <VolumeSlider {volume} {onVolumeChange} />
</div>
{#if quality}
<div class="quality-wrapper">
<QualityBadge {quality} {bitDepth} {samplingRate} />
</div>
{/if}
</div> </div>
{/if}
{#if quality}
<div class="quality-wrapper">
<QualityBadge {quality} {bitDepth} {samplingRate} />
</div>
{/if}
</div>
</div> </div>
<style> <style>
@@ -193,46 +170,6 @@
pointer-events: auto; pointer-events: auto;
} }
/* Track Info (Left) */
.track-info {
display: flex;
align-items: center;
gap: 12px;
min-width: 200px;
max-width: 280px;
}
.mini-artwork {
width: 48px;
height: 48px;
border-radius: 6px;
object-fit: cover;
flex-shrink: 0;
}
.track-meta {
min-width: 0;
flex: 1;
}
.track-title {
font-size: 14px;
font-weight: 600;
color: var(--text-primary, white);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.track-artist {
font-size: 12px;
color: var(--alpha-60, rgba(255, 255, 255, 0.6));
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-top: 2px;
}
/* Playback Section (Center) */ /* Playback Section (Center) */
.playback-section { .playback-section {
flex: 1; flex: 1;
@@ -351,14 +288,8 @@
margin-left: 8px; margin-left: 8px;
} }
/* Compact mode */
.immersive-controls.compact .playback-section {
max-width: 100%;
}
/* Responsive */ /* Responsive */
@media (max-width: 900px) { @media (max-width: 900px) {
.track-info,
.right-section { .right-section {
display: none; display: none;
} }
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { X, Disc3, LayoutGrid, MessageSquareText, ListMusic, Music2, Info, Radio } from 'lucide-svelte'; import { X, Disc3, LayoutGrid, Mic, ListMusic, Music2, Info, Radio } from 'lucide-svelte';
import { t } from '$lib/i18n'; import { t } from '$lib/i18n';
export type ImmersiveTab = 'lyrics' | 'credits' | 'suggestions' | 'queue'; export type ImmersiveTab = 'lyrics' | 'credits' | 'suggestions' | 'queue';
@@ -39,7 +39,7 @@
const displayModes: { id: DisplayMode; icon: typeof Disc3; title: string }[] = [ const displayModes: { id: DisplayMode; icon: typeof Disc3; title: string }[] = [
{ id: 'coverflow', icon: Disc3, title: 'Coverflow (1)' }, { id: 'coverflow', icon: Disc3, title: 'Coverflow (1)' },
{ id: 'split', icon: LayoutGrid, title: 'Split View (2)' }, { id: 'split', icon: LayoutGrid, title: 'Split View (2)' },
{ id: 'lyrics-focus', icon: MessageSquareText, title: 'Lyrics Focus (3)' }, { id: 'lyrics-focus', icon: Mic, title: 'Lyrics Focus (3)' },
{ id: 'queue-focus', icon: ListMusic, title: 'Queue Focus (4)' }, { id: 'queue-focus', icon: ListMusic, title: 'Queue Focus (4)' },
]; ];
@@ -271,10 +271,14 @@
</div> </div>
{:else} {:else}
<!-- Split: Artwork + Panel side by side --> <!-- Split: Artwork + Panel side by side -->
<div class="main-content"> <div class="immersive-main">
<!-- Left: Artwork --> <!-- Left: Artwork + Track Info -->
<div class="artwork-section"> <div class="artwork-section">
<ImmersiveArtwork {artwork} {trackTitle} variant="floating" /> <ImmersiveArtwork {artwork} {trackTitle} variant="floating" />
<div class="split-track-info">
<h2 class="split-track-title">{trackTitle}</h2>
<p class="split-track-artist">{artist}</p>
</div>
</div> </div>
<!-- Right: Active Panel --> <!-- Right: Active Panel -->
@@ -307,9 +311,6 @@
<!-- Bottom Controls --> <!-- Bottom Controls -->
<ImmersiveControls <ImmersiveControls
visible={showUI} visible={showUI}
{artwork}
{trackTitle}
{artist}
{isPlaying} {isPlaying}
{currentTime} {currentTime}
{duration} {duration}
@@ -354,7 +355,7 @@
} }
/* Split mode layout */ /* Split mode layout */
.main-content { .immersive-main {
position: absolute; position: absolute;
top: 0; top: 0;
left: 50%; left: 50%;
@@ -373,8 +374,36 @@
.artwork-section { .artwork-section {
flex: 0 0 auto; flex: 0 0 auto;
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 20px;
}
.split-track-info {
text-align: center;
max-width: 380px;
}
.split-track-title {
font-size: clamp(18px, 2.5vw, 24px);
font-weight: 700;
color: var(--text-primary, white);
margin: 0 0 6px 0;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
/* Truncate long titles */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.split-track-artist {
font-size: clamp(14px, 1.8vw, 16px);
color: var(--alpha-70, rgba(255, 255, 255, 0.7));
margin: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.panel-section { .panel-section {
@@ -412,7 +441,7 @@
/* Responsive */ /* Responsive */
@media (max-width: 1200px) { @media (max-width: 1200px) {
.main-content { .immersive-main {
padding: 70px 32px 130px; padding: 70px 32px 130px;
gap: 40px; gap: 40px;
} }
@@ -427,7 +456,7 @@
} }
@media (max-width: 900px) { @media (max-width: 900px) {
.main-content { .immersive-main {
flex-direction: column; flex-direction: column;
padding: 70px 24px 140px; padding: 70px 24px 140px;
gap: 24px; gap: 24px;
@@ -454,7 +483,7 @@
} }
@media (max-width: 600px) { @media (max-width: 600px) {
.main-content { .immersive-main {
padding: 60px 16px 130px; padding: 60px 16px 130px;
gap: 20px; gap: 20px;
} }