feat(0.6.0): pop-out mini-player — continuous background play across admin nav

Until v0.5.0 the audio cut every time you navigated between WP admin
pages — every navigation is a full page reload, which destroys the
<audio> element. v0.6.0 fixes the background-music use case by letting
you pop the player out into a separate browser window that persists
across the parent tab's navigation.

Pop-out window
  - Small "↗ Pop out" button beside the Play button on both the main
    Radio page and the Dashboard widget.
  - Opens a 380×560 standalone window via window.open() with a window
    name of `radio_popout` so a second click re-focuses the existing
    popup rather than spawning a new one.
  - Popup renders at admin-post.php?action=radio_popout&play=1 — a new
    admin_post_radio_popout hook outputs a full standalone HTML page
    (custom DOCTYPE / head / body, no WP admin chrome).
  - Theme follows radio_state['theme'] via body class radio-theme-{...}.
  - Includes the full player (now-playing block with dancing bars +
    Web Audio visualizer, play, mute, volume, station dropdown, error
    slot). Close button calls window.close().

Auto-resume + single-stream invariant
  - cfg.autoPlay is true when the popup URL carries &play=1. radio.js
    auto-calls audio.play() 200 ms after init. Same-origin user-gesture
    popups are exempt from autoplay-blocking on every modern browser.
  - On pop-out click, every other audio surface in the parent tab is
    paused so there is only ever one stream playing.

State stays in sync
  - Popup uses the SAME radio_save_state AJAX + track-logging endpoint.
    Station / volume changes persist to user_meta; track history keeps
    accumulating from whichever surface is playing.

Edge cases
  - Inside the popup, cfg.popoutUrl is '' so bindPopOut hides any
    Pop-out button it finds (would be infinite otherwise).
  - Popup blocked by browser → clear alert tells the user to allow
    popups for this site.

Files
  - radio.php (version, popoutUrl in localized config, admin_post
    handler + full standalone HTML renderer)
  - inc/admin-page.php + inc/dashboard-widget.php (Pop-out button
    beside Play)
  - assets/css/radio.css (.radio-player__popout styling)
  - assets/js/radio.js (bindPopOut function; autoPlay branch in
    bindPlayer)
  - inc/about.php + CHANGELOG.md (history entries)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 00:03:21 +01:00
parent 2bd501d610
commit 0dc3a220d9
7 changed files with 226 additions and 3 deletions
+10
View File
@@ -166,6 +166,16 @@
font-family: "Helvetica Neue", Arial, sans-serif;
}
/* v0.6.0: Pop-out button — small secondary button beside Play. Uses WP
native .button styles; the ↗ glyph signals "opens in another window." */
.radio-player__popout {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 12px;
}
.radio-player__popout span[aria-hidden] { font-size: 13px; line-height: 1; }
.radio-player__volume {
display: flex;
align-items: center;
+38
View File
@@ -412,6 +412,44 @@
}
bindMute(player, audio, volumeIn, volPctEl);
bindPopOut(player);
// v0.6.0: popout auto-resume — when opened with ?play=1, immediately
// pick up where the main tab left off. Same-origin user-gesture popups
// are exempt from autoplay blocking on every modern browser.
if (cfg.autoPlay) {
setTimeout(function () {
if (audio.paused) { audio.play().catch(function () { /* autoplay denied — user just clicks play */ }); }
}, 200);
}
}
/** v0.6.0: Pop-out window button. Opens a 380×560 standalone window
* with just the player chrome (no WP admin), via admin-post.php?
* action=radio_popout&play=1. The popup persists across main-tab
* navigation so background music doesn't cut when you move around
* the WP admin. Pauses every other audio surface in this tab so
* there's only one stream playing at a time. */
function bindPopOut(player) {
var btn = player.querySelector('[data-radio-popout]');
if (!btn) { return; }
if (!cfg.popoutUrl) { btn.style.display = 'none'; return; } // already in popout
btn.addEventListener('click', function () {
var w = window.open(
cfg.popoutUrl + '&play=1',
'radio_popout',
'width=380,height=560,resizable=yes,scrollbars=no,toolbar=no,location=no,menubar=no,status=no'
);
if (!w) {
window.alert('Pop-out blocked by the browser. Allow popups for this site, then try again.');
return;
}
w.focus();
// Pause every audio surface in this tab — the popup is the new player.
document.querySelectorAll('[data-radio-audio]').forEach(function (a) {
if (!a.paused) { a.pause(); }
});
});
}
/** Keep all .radio-player surfaces on the same station. */