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"; } from "$lib/player/player.svelte";
import { createMediaSessionBindings } from "$lib/player/media-session"; 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; let audioEl: HTMLAudioElement | null = null;
// Best-effort preload of the upcoming track's URL // 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"> <div class="mx-auto flex max-w-4xl items-center gap-2 px-3 py-2">
<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" type="button"
onclick={() => toggleUiOpen()} onclick={() => toggleUiOpen()}
aria-label={snap.uiOpen ? "Close player" : "Open player"} 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> </button>
<div <div
@@ -502,20 +522,24 @@
</div> </div>
<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" type="button"
disabled={!canPrev} disabled={!canPrev}
aria-label="Previous"
title="Previous"
onclick={() => { onclick={() => {
prev(currentTime); prev(currentTime);
void syncAndAutoplay(); void syncAndAutoplay();
}} }}
> >
Prev <SkipBack class="h-4 w-4" />
</button> </button>
<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" type="button"
aria-label={isPlaying ? "Pause" : "Play"}
title={isPlaying ? "Pause" : "Play"}
onclick={() => { onclick={() => {
if (!audioEl) return; if (!audioEl) return;
if (audioEl.paused) void audioEl.play(); if (audioEl.paused) void audioEl.play();
@@ -523,19 +547,25 @@
}} }}
disabled={!snap.currentTrack} disabled={!snap.currentTrack}
> >
{isPlaying ? "Pause" : "Play"} {#if isPlaying}
<PauseIcon class="h-4 w-4" />
{:else}
<PlayIcon class="h-4 w-4" />
{/if}
</button> </button>
<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" type="button"
disabled={!canNext} disabled={!canNext}
aria-label="Next"
title="Next"
onclick={() => { onclick={() => {
next(); next();
void syncAndAutoplay(); void syncAndAutoplay();
}} }}
> >
Next <SkipForward class="h-4 w-4" />
</button> </button>
</div> </div>
@@ -544,22 +574,29 @@
<div class="mx-auto max-w-4xl space-y-3"> <div class="mx-auto max-w-4xl space-y-3">
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
<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" type="button"
onclick={() => toggleShuffle()} 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>
<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" type="button"
onclick={() => toggleWrap()} 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> </button>
<label class="ml-auto flex items-center gap-2 text-sm"> <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 <input
type="range" type="range"
min="0" min="0"
@@ -623,11 +660,13 @@
</button> </button>
<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" type="button"
onclick={() => removeTrack(item.track.id)} onclick={() => removeTrack(item.track.id)}
aria-label="Remove from queue"
title="Remove from queue"
> >
Remove <ListX class="h-4 w-4" />
</button> </button>
</li> </li>
{/each} {/each}
@@ -679,12 +718,17 @@
</div> </div>
<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" type="button"
onclick={() => toggleUiOpen()} onclick={() => toggleUiOpen()}
aria-label={snap.uiOpen ? "Hide player sidebar" : "Show player sidebar"} 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> </button>
</div> </div>
@@ -697,38 +741,46 @@
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<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" type="button"
onclick={() => toggleShuffle()} 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>
<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" type="button"
onclick={() => toggleWrap()} 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> </button>
</div> </div>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<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" type="button"
disabled={!canPrev} disabled={!canPrev}
aria-label="Previous"
title="Previous"
onclick={() => { onclick={() => {
prev(currentTime); prev(currentTime);
void syncAndAutoplay(); void syncAndAutoplay();
}} }}
> >
Prev <SkipBack class="h-4 w-4" />
</button> </button>
<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" type="button"
aria-label={isPlaying ? "Pause" : "Play"}
title={isPlaying ? "Pause" : "Play"}
onclick={() => { onclick={() => {
if (!audioEl) return; if (!audioEl) return;
if (audioEl.paused) void audioEl.play(); if (audioEl.paused) void audioEl.play();
@@ -736,23 +788,32 @@
}} }}
disabled={!snap.currentTrack} disabled={!snap.currentTrack}
> >
{isPlaying ? "Pause" : "Play"} {#if isPlaying}
<PauseIcon class="h-4 w-4" />
{:else}
<PlayIcon class="h-4 w-4" />
{/if}
</button> </button>
<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" type="button"
disabled={!canNext} disabled={!canNext}
aria-label="Next"
title="Next"
onclick={() => { onclick={() => {
next(); next();
void syncAndAutoplay(); void syncAndAutoplay();
}} }}
> >
Next <SkipForward class="h-4 w-4" />
</button> </button>
<label class="ml-auto flex items-center gap-2 text-sm"> <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 <input
type="range" type="range"
min="0" min="0"
@@ -819,11 +880,13 @@
</button> </button>
<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" type="button"
onclick={() => removeTrack(item.track.id)} onclick={() => removeTrack(item.track.id)}
aria-label="Remove from queue"
title="Remove from queue"
> >
Remove <ListX class="h-4 w-4" />
</button> </button>
</li> </li>
{/each} {/each}

View File

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