Player: 'Playing from' context header (Radio / <Artist> Radio)

Spotify-style context line above the track title. npSetPlayContext(text) shows/
hides it; set to 'Radio' when radio mode turns on, '<Artist> Radio' from
playArtistRadio (specific label wins over generic), cleared on stop/clearTrack
and when radio mode is turned off. Accent-colored name, uppercase label.

Frontend-only; JS + CSS clean.
This commit is contained in:
BoulderBadgeDad 2026-05-30 15:07:31 -07:00
parent ab2b5c64f4
commit f9bc96bd90
4 changed files with 42 additions and 0 deletions

View file

@ -7033,6 +7033,10 @@
</div>
</div>
<div class="np-track-info">
<div class="np-play-context hidden" id="np-play-context">
<span class="np-play-context-label">Playing from</span>
<span class="np-play-context-name" id="np-play-context-name"></span>
</div>
<div class="np-track-title" id="np-track-title">No track</div>
<div class="np-artist-name" id="np-artist-name">Unknown Artist</div>
<div class="np-album-name" id="np-album-name">Unknown Album</div>

View file

@ -222,6 +222,7 @@ function clearTrack() {
// Clear track state
currentTrack = null;
isPlaying = false;
npSetPlayContext(''); // hide "Playing from" when nothing's playing
const trackTitleElement = document.getElementById('track-title');
trackTitleElement.innerHTML = '<span class="title-text">No track</span>';
@ -1466,6 +1467,13 @@ function npSetRadioMode(enabled, options = {}) {
if (toast) {
showToast(npRadioMode ? 'Radio mode on - similar tracks will auto-queue' : 'Radio mode off', 'success');
}
// Context label: only set the generic "Radio" if a more specific one (e.g.
// "<Artist> Radio") wasn't already set by the caller.
if (npRadioMode) {
if (!npPlayContext || !/radio/i.test(npPlayContext)) npSetPlayContext('Radio');
} else if (/radio/i.test(npPlayContext)) {
npSetPlayContext('');
}
if (npRadioMode && fetchIfNeeded && currentTrack && currentTrack.id && !npLoadingQueueItem && !npQueueHasNext()) {
npEnsureCurrentTrackInQueue();
npFetchRadioTracks();
@ -2114,6 +2122,21 @@ function handleNpRepeat() {
// QUEUE MANAGEMENT
// ===============================
// "Playing from" context shown above the track title (Spotify-style).
let npPlayContext = '';
function npSetPlayContext(text) {
npPlayContext = text || '';
const box = document.getElementById('np-play-context');
const nameEl = document.getElementById('np-play-context-name');
if (!box || !nameEl) return;
if (npPlayContext) {
nameEl.textContent = npPlayContext;
box.classList.remove('hidden');
} else {
box.classList.add('hidden');
}
}
function addToQueue(track) {
npQueue.push(track);
showToast('Added to queue', 'success');

View file

@ -4757,6 +4757,9 @@ async function playArtistRadio() {
// Enable radio mode + immediately seed the queue with similar tracks —
// same path the modal's Radio button uses (fetchIfNeeded also adds the
// current track to the queue first).
if (typeof npSetPlayContext === 'function') {
npSetPlayContext(`${artistName || 'Artist'} Radio`);
}
if (typeof npSetRadioMode === 'function') {
npSetRadioMode(true, { toast: false, fetchIfNeeded: true });
} else {

View file

@ -48664,6 +48664,18 @@ textarea.enhanced-meta-field-input {
box-shadow: 0 30px 92px rgba(0,0,0,0.66), 0 12px 40px rgba(var(--accent-rgb),0.28) !important;
}
/* ── "Playing from" context label (above the title) ── */
.np-play-context { display: flex; align-items: center; gap: 7px; margin-bottom: 10px; }
.np-play-context.hidden { display: none; }
.np-play-context-label {
font-size: 10px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase;
color: rgba(255,255,255,0.32);
}
.np-play-context-name {
font-size: 12px; font-weight: 700; color: rgb(var(--accent-light-rgb));
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 220px;
}
/* ── Track meta ── */
.np-track-info { text-align: left !important; }
.np-track-title { font-size: 28px !important; font-weight: 800 !important; letter-spacing: -0.02em !important; line-height: 1.1 !important; }