fix: cmdPrev retry-skip + show SongCard actions on touch devices

Two corner-case fixes for the prev-history + play-next feature:

1. cmdPrev only tried queue.prev() once. instance.playNext's auto-
   advance retry-skip pushes failed songs into the same history stack,
   so a single prev frequently lands on an unplayable song — returning
   "Cannot play previous song" while leaving queue.currentIndex stuck
   mid-failure (causing next() to skip past the actually-playing song).
   Retry up to 4 times so prev finds a playable history entry, matching
   the retry budget already used by playNext for auto-advance.

2. SongCard.song-actions has opacity:0 by default and is revealed via
   parent :hover. Touch devices have no hover, so all three action
   buttons (Play / Play Next / Add) were invisible to phone/tablet
   users. Add @media (pointer: coarse) → opacity:1 to always show on
   touch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
saopig1andClaude Opus 4.7 committed 2026-05-06 17:06:18 +08:00
1 parent fab8c194e3
commit 73f3f7749a
2 files changed
+17 -4

No files matched your search

+8 -4
View File
@@ -498,13 +498,17 @@ export class BotInstance extends EventEmitter {
}
private async cmdPrev(): Promise<string> {
// Retry-skip up to 4 attempts: history can include failed songs
// that playNext's auto-advance retry-skipped past, so a single
// prev would otherwise land on an unplayable song and leave the
// queue's currentIndex stuck mid-failure.
for (let i = 0; i < 4; i++) {
const prev = this.queue.prev();
if (prev) {
if (!prev) return "No previous song";
const ok = await this.resolveAndPlay(prev);
if (!ok) return "Cannot play previous song";
return `Now playing: ${prev.name} - ${prev.artist}`;
if (ok) return `Now playing: ${prev.name} - ${prev.artist}`;
}
return "No previous song";
return "Cannot play any previous songs (all failed to resolve)";
}
private cmdVol(cmd: ParsedCommand): string {
+9
View File
@@ -156,6 +156,15 @@ function formatDuration(seconds: number): string {
transition: opacity var(--transition-fast);
}
// Touch devices have no :hover, so the parent-hover-reveals-actions
// pattern leaves all action buttons invisible. Always show on coarse-
// pointer (touch) inputs — this is also where bigger tap targets matter.
@media (pointer: coarse) {
.song-actions {
opacity: 1;
}
}
.action-btn {
font-size: 18px;
padding: 4px;