The player dock used to sit fixed over the scrolling page, so it needed an opaque background. The shell is now a fixed-height frame where only the content column scrolls, letting the dock stay transparent like the sidebar and queue. Scroll reset/restore on navigation moves to restoreScroll(), and the stable scrollbar gutter moves from <html> to the content scroller.
85 lines
2.5 KiB
Svelte
85 lines
2.5 KiB
Svelte
<script lang="ts">
|
|
import TrackInfo from './TrackInfo.svelte';
|
|
import TransportControls from './TransportControls.svelte';
|
|
import Slider from '$lib/components/ui/primitives/Slider.svelte';
|
|
import IconButton from '$lib/components/ui/primitives/IconButton.svelte';
|
|
import { player } from '$lib/player/player.svelte';
|
|
import { queuePanel } from '$lib/player/queuePanel.svelte';
|
|
import { fmtTime } from '$lib/utils/format';
|
|
import Volume1 from '@lucide/svelte/icons/volume-1';
|
|
import Volume2 from '@lucide/svelte/icons/volume-2';
|
|
import VolumeX from '@lucide/svelte/icons/volume-x';
|
|
import ListMusic from '@lucide/svelte/icons/list-music';
|
|
|
|
function seek(value: number) {
|
|
if (player.current.duration > 0) player.seekFraction(value / player.current.duration);
|
|
}
|
|
</script>
|
|
|
|
<div
|
|
class="animate-enter pointer-events-auto flex items-center gap-icon-md px-4 py-3 glass-bar sm:hidden"
|
|
>
|
|
<div class="min-w-0 flex-1">
|
|
<TrackInfo compact />
|
|
</div>
|
|
<TransportControls compact />
|
|
</div>
|
|
|
|
<div
|
|
class="animate-enter pointer-events-auto hidden items-center gap-5 px-4 py-3 glass-bar sm:flex"
|
|
>
|
|
<div class="w-(--mf-player-side-w) min-w-0 shrink-0">
|
|
<TrackInfo />
|
|
</div>
|
|
<div class="flex min-w-0 flex-1 flex-col items-center gap-2">
|
|
<TransportControls />
|
|
<div class="flex w-full max-w-hero items-center gap-3">
|
|
<span class="min-w-8 text-right text-xs text-fg-2 tabular-nums">
|
|
{fmtTime(player.progress)}
|
|
</span>
|
|
<Slider
|
|
value={player.progress}
|
|
max={player.current.duration}
|
|
label="Barra de progreso"
|
|
oninput={seek}
|
|
variant="seek"
|
|
/>
|
|
<span class="min-w-8 text-xs text-fg-2 tabular-nums">
|
|
{fmtTime(player.current.duration)}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="hidden w-(--mf-player-side-w) shrink-0 items-center justify-end gap-3.5 sm:flex">
|
|
<IconButton
|
|
label={player.muted ? 'Activar sonido' : 'Silenciar'}
|
|
tone="plain"
|
|
size="xs"
|
|
onclick={() => player.toggleMute()}
|
|
>
|
|
{#if player.muted || player.volume === 0}
|
|
<VolumeX class="size-icon-sm" />
|
|
{:else if player.volume < 50}
|
|
<Volume1 class="size-icon-sm" />
|
|
{:else}
|
|
<Volume2 class="size-icon-sm" />
|
|
{/if}
|
|
</IconButton>
|
|
<Slider
|
|
value={player.muted ? 0 : player.volume}
|
|
max={100}
|
|
label="Volumen"
|
|
oninput={(value) => player.setVolume(value)}
|
|
variant="volume"
|
|
/>
|
|
<IconButton
|
|
label="Cola de reproducción"
|
|
tone="plain"
|
|
size="xs"
|
|
pressed={queuePanel.open}
|
|
onclick={() => queuePanel.toggle()}
|
|
>
|
|
<ListMusic class="size-icon-sm" />
|
|
</IconButton>
|
|
</div>
|
|
</div>
|