player use icons

This commit is contained in:
2026-02-06 03:44:14 -08:00
parent 455699415f
commit c0d4f47d9c
2 changed files with 115 additions and 38 deletions

View File

@@ -35,6 +35,21 @@
} from "$lib/player/player.svelte";
import { createMediaSessionBindings } from "$lib/player/media-session";
import {
ChevronsUpDown,
ListX,
Pause as PauseIcon,
PanelRightClose,
PanelRightOpen,
Play as PlayIcon,
Repeat,
Shuffle,
SkipBack,
SkipForward,
Volume2,
X,
} from "@lucide/svelte";
let audioEl: HTMLAudioElement | null = null;
// Best-effort preload of the upcoming track's URL
@@ -439,12 +454,17 @@
>
<div class="mx-auto flex max-w-4xl items-center gap-2 px-3 py-2">
<button
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
type="button"
onclick={() => toggleUiOpen()}
aria-label={snap.uiOpen ? "Close player" : "Open player"}
title={snap.uiOpen ? "Close player" : "Open player"}
>
{snap.uiOpen ? "Close" : "Player"}
{#if snap.uiOpen}
<X class="h-4 w-4" />
{:else}
<ChevronsUpDown class="h-4 w-4" />
{/if}
</button>
<div
@@ -502,20 +522,24 @@
</div>
<button
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
type="button"
disabled={!canPrev}
aria-label="Previous"
title="Previous"
onclick={() => {
prev(currentTime);
void syncAndAutoplay();
}}
>
Prev
<SkipBack class="h-4 w-4" />
</button>
<button
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
type="button"
aria-label={isPlaying ? "Pause" : "Play"}
title={isPlaying ? "Pause" : "Play"}
onclick={() => {
if (!audioEl) return;
if (audioEl.paused) void audioEl.play();
@@ -523,19 +547,25 @@
}}
disabled={!snap.currentTrack}
>
{isPlaying ? "Pause" : "Play"}
{#if isPlaying}
<PauseIcon class="h-4 w-4" />
{:else}
<PlayIcon class="h-4 w-4" />
{/if}
</button>
<button
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
type="button"
disabled={!canNext}
aria-label="Next"
title="Next"
onclick={() => {
next();
void syncAndAutoplay();
}}
>
Next
<SkipForward class="h-4 w-4" />
</button>
</div>
@@ -544,22 +574,29 @@
<div class="mx-auto max-w-4xl space-y-3">
<div class="flex flex-wrap items-center gap-2">
<button
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
type="button"
onclick={() => toggleShuffle()}
aria-label={snap.shuffleEnabled ? "Shuffle on" : "Shuffle off"}
title={snap.shuffleEnabled ? "Shuffle on" : "Shuffle off"}
>
Shuffle: {snap.shuffleEnabled ? "On" : "Off"}
<Shuffle class="h-4 w-4" />
</button>
<button
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
type="button"
onclick={() => toggleWrap()}
aria-label={snap.wrapEnabled ? "Wrap on" : "Wrap off"}
title={snap.wrapEnabled ? "Wrap on" : "Wrap off"}
>
Wrap: {snap.wrapEnabled ? "On" : "Off"}
<Repeat class="h-4 w-4" />
</button>
<label class="ml-auto flex items-center gap-2 text-sm">
<span class="text-muted-foreground">Vol</span>
<span class="text-muted-foreground" aria-hidden="true">
<Volume2 class="h-4 w-4" />
</span>
<span class="sr-only">Volume</span>
<input
type="range"
min="0"
@@ -623,11 +660,13 @@
</button>
<button
class="rounded border px-2 py-1 text-xs"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
type="button"
onclick={() => removeTrack(item.track.id)}
aria-label="Remove from queue"
title="Remove from queue"
>
Remove
<ListX class="h-4 w-4" />
</button>
</li>
{/each}
@@ -679,12 +718,17 @@
</div>
<button
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
type="button"
onclick={() => toggleUiOpen()}
aria-label={snap.uiOpen ? "Hide player sidebar" : "Show player sidebar"}
title={snap.uiOpen ? "Hide player sidebar" : "Show player sidebar"}
>
{snap.uiOpen ? "Hide" : "Show"}
{#if snap.uiOpen}
<PanelRightClose class="h-4 w-4" />
{:else}
<PanelRightOpen class="h-4 w-4" />
{/if}
</button>
</div>
@@ -697,38 +741,46 @@
</div>
<div class="flex items-center gap-2">
<button
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
type="button"
onclick={() => toggleShuffle()}
aria-label={snap.shuffleEnabled ? "Shuffle on" : "Shuffle off"}
title={snap.shuffleEnabled ? "Shuffle on" : "Shuffle off"}
>
Shuffle: {snap.shuffleEnabled ? "On" : "Off"}
<Shuffle class="h-4 w-4" />
</button>
<button
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
type="button"
onclick={() => toggleWrap()}
aria-label={snap.wrapEnabled ? "Wrap on" : "Wrap off"}
title={snap.wrapEnabled ? "Wrap on" : "Wrap off"}
>
Wrap: {snap.wrapEnabled ? "On" : "Off"}
<Repeat class="h-4 w-4" />
</button>
</div>
</div>
<div class="flex items-center gap-2">
<button
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
type="button"
disabled={!canPrev}
aria-label="Previous"
title="Previous"
onclick={() => {
prev(currentTime);
void syncAndAutoplay();
}}
>
Prev
<SkipBack class="h-4 w-4" />
</button>
<button
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
type="button"
aria-label={isPlaying ? "Pause" : "Play"}
title={isPlaying ? "Pause" : "Play"}
onclick={() => {
if (!audioEl) return;
if (audioEl.paused) void audioEl.play();
@@ -736,23 +788,32 @@
}}
disabled={!snap.currentTrack}
>
{isPlaying ? "Pause" : "Play"}
{#if isPlaying}
<PauseIcon class="h-4 w-4" />
{:else}
<PlayIcon class="h-4 w-4" />
{/if}
</button>
<button
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
type="button"
disabled={!canNext}
aria-label="Next"
title="Next"
onclick={() => {
next();
void syncAndAutoplay();
}}
>
Next
<SkipForward class="h-4 w-4" />
</button>
<label class="ml-auto flex items-center gap-2 text-sm">
<span class="text-muted-foreground">Vol</span>
<span class="text-muted-foreground" aria-hidden="true">
<Volume2 class="h-4 w-4" />
</span>
<span class="sr-only">Volume</span>
<input
type="range"
min="0"
@@ -819,11 +880,13 @@
</button>
<button
class="rounded border px-2 py-1 text-xs"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
type="button"
onclick={() => removeTrack(item.track.id)}
aria-label="Remove from queue"
title="Remove from queue"
>
Remove
<ListX class="h-4 w-4" />
</button>
</li>
{/each}

View File

@@ -1,4 +1,10 @@
<script lang="ts">
import {
ListPlus,
Play as PlayIcon,
SkipForward,
Trash2,
} from "@lucide/svelte";
import {
addToQueue,
hasTrack,
@@ -77,49 +83,57 @@
<div class="mt-2 flex flex-wrap items-center gap-2">
<button
type="button"
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
disabled={!track}
title="Play"
aria-label="Play"
onclick={() => {
if (!track) return;
play(track);
requestGlobalAutoplay();
}}
>
Play
<PlayIcon class="h-4 w-4" />
</button>
<button
type="button"
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
disabled={!track}
title="Play next"
aria-label="Play next"
onclick={() => {
if (!track) return;
playNext(track);
requestGlobalAutoplay();
}}
>
Play next
<SkipForward class="h-4 w-4" />
</button>
<button
type="button"
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
disabled={!track}
title="Add to queue"
aria-label="Add to queue"
onclick={() => {
if (!track) return;
addToQueue(track);
}}
>
Add to queue
<ListPlus class="h-4 w-4" />
</button>
{#if isQueued}
<button
type="button"
class="rounded border px-2 py-1 text-sm"
class="inline-flex h-8 w-8 items-center justify-center rounded border"
title="Remove from queue"
aria-label="Remove from queue"
onclick={() => removeTrack(annSongId)}
>
Remove
<Trash2 class="h-4 w-4" />
</button>
<span class="text-xs text-muted-foreground">Queued</span>