Files
rangerhq-radio/CHANGELOG.md
T
ranger 32a3040e39 feat(0.6.2): current version badge on Settings page
Small grey pill follows the "Radio — Settings" H1 — v{RADIO_VERSION}.
Visible at a glance so you don't have to hover the plugin row in
Plugins → Installed or open About just to check what version you're on.
Dark-theme variant so it stays readable when theme=dark.

Files: radio.php (version), inc/settings.php (<span> inside H1),
assets/css/radio.css (.radio-version-badge + dark override),
inc/about.php (rotate v0.6.2 into latest expanded slot, v0.6.1 into
earlier-releases list), CHANGELOG.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 00:30:14 +01:00

22 KiB
Raw Blame History

Changelog

All notable changes to Radio are documented here. Format: Keep a Changelog 1.1.0 — versioning: SemVer.


[Unreleased]


[0.6.2] — 2026-05-30 — Current version badge on Settings

Added

  • Small grey pill follows the "Radio — Settings" heading: v{RADIO_VERSION}. Visible at a glance so you don't have to hover the plugin row in Plugins → Installed or open About just to check what version you're on.
  • Dark-theme variant of the badge (#2c3338 background, #c3c4c7 text) so it stays readable when theme=dark.

Files changed: radio.php (version), inc/settings.php (<span class="radio-version-badge">v…</span> inside the H1), assets/css/radio.css (.radio-version-badge styling + dark-theme override).


[0.6.1] — 2026-05-30 — About page restructure

By v0.6.0 the About page had eight version-history entries, each a full paragraph, dwarfing the other cards and pushing Credits + thanks off the visible area. v0.6.1 rebalances the layout.

Changed

  • Three short cards on top (What / Who / Credits) — equal-height, balanced row. Credits is no longer a fourth card buried under the version history; it sits beside What and Who where it belongs.
  • Version history is its own full-width card below. Only the latest release is shown in full; earlier releases collapse to one line each (version + date + headline). The card now stays compact however many versions ship — adding a future release adds one line, not a paragraph.
  • Full prose for older versions lives in CHANGELOG.md on Gitea — the "View the full CHANGELOG.md →" link does the heavy lifting. Single source of truth, no duplication.

Files changed: radio.php (version), inc/about.php (3-card top + new .radio-about-versions block with __latest / __earlier sub-elements; 9 versions in the earlier-releases list incl. v0.1.0), assets/css/radio.css (removed dead .radio-about-card--versions rules; added .radio-about-versions + __latest + __earlier rules; dark-theme overrides for the new selectors).


[0.6.0] — 2026-05-30 — Pop-out mini-player (continuous background play)

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.

Added — Pop out button + standalone popup player

  • Small ↗ Pop out button beside the Play button on both the main Radio page and the Dashboard widget. Click it and a 380×560 standalone window opens with just the player chrome (no WP admin sidebar / nav).
  • The popup lives at admin-post.php?action=radio_popout&play=1 — a new server-side route that renders a full standalone HTML page outside the WP admin shell (custom <!DOCTYPE>, head, body, no admin chrome).
  • Popup is radio_popout-named so a second click on Pop out re-focuses the existing window instead of opening a new one.
  • The popup's <audio> element is never destroyed by parent-tab navigation, so the music keeps playing while you click around Plugins, Posts, Users, etc.

Auto-resume — pick up where the main tab left off

  • The Pop-out button URL carries &play=1. radio.js reads it from the localized config and auto-calls audio.play() 200 ms after init. Same-origin user-gesture popups are exempt from autoplay-blocking on every modern browser, so it just works.
  • On opening the popup, every other audio surface in the main tab is paused so you don't end up with two streams running simultaneously.

Popup details

  • Theme follows the user's saved choice (radio_state['theme']) — light by default, dark if explicitly set; the popup body gets the radio-theme-dark class so the existing dark-mode CSS rules apply.
  • The popup includes everything you need to listen and switch: now-playing block (with dancing bars + Web Audio visualizer), play/pause, mute, volume, full station dropdown grouped by genre, error slot.
  • Close button () in the top-right calls window.close().
  • Pop out button does not appear inside the popup itself (would be infinite); the JS detects popoutUrl === '' in the localized config and hides any Pop-out button it finds.
  • Popup blocked? The button shows a clear alert: "Pop-out blocked by the browser. Allow popups for this site, then try again."

State stays in sync

  • The popup uses the same radio_save_state AJAX endpoint as the main player. If you change station or volume in the popup, it persists to user_meta; the next time you load any Radio page in the main tab it picks up the new values.
  • Track history continues to log from whichever surface is playing — the popup polls SomaFM and POSTs to radio_log_track exactly like the main player.

Files changed: radio.php (version, popoutUrl added to localized config, new admin_post_radio_popout action + radio_render_popout_page handler with full standalone HTML), inc/admin-page.php + inc/dashboard-widget.php (Pop-out button beside Play), assets/css/radio.css (Pop-out button styling), assets/js/radio.js (bindPopOut opens the window + pauses other surfaces, autoplay branch reads cfg.autoPlay), inc/about.php (history entry).


[0.5.0] — 2026-05-29 — Track history + favourites

SomaFM plays deep cuts you'll never hear again. v0.5.0 quietly logs every track that scrolls past so you can find it again later — and a star button keeps the ones worth keeping forever.

Added — Radio → History admin page (per-user)

  • History tab — capped FIFO list of the last 500 played tracks. Each row shows when (relative time, full timestamp on hover), station, artist — title, four search links, and a favourite-star toggle.
  • Favourites tab — uncapped list of starred tracks. Same row layout. Survives even when the history rolls over.
  • Filter by artist/title (live, client-side) and by station (dropdown). Clear history button on the History tab — favourites preserved.
  • Four search providers per row, brand-tinted on hover: Spotify (green), YouTube (red), Apple Music (pink), Bandcamp (teal). Deep-link search URLs only — no API keys, no third-party JS.
  • Empty-state messages on both tabs.

Added — automatic logging during playback

  • fetchTrack (the existing 30s SomaFM polling loop) now hands every new track to a new logTrackIfNew helper that POSTs it to wp_ajax_radio_log_track.
  • Dedup: client-side via lastLoggedSig so the 30s polling doesn't re-log the same song; server-side against the last entry in user_meta as belt-and-braces.
  • Junk filtering: (unknown) artists and entries missing artist or title are dropped server-side in radio_sanitize_entry.

Added — per-user storage

  • Two new user_meta keys, separate from radio_state so frequent track inserts don't churn the player-state blob:
    • radio_history — capped at 500 entries (~5080 KB max).
    • radio_favourites — uncapped, expected to stay small (user-curated).

Added — three new AJAX endpoints

  • wp_ajax_radio_log_track — append a track (nonce: radio_save_state; player-page only).
  • wp_ajax_radio_toggle_favourite — toggle a track in favourites (nonce: radio_history; History-page only).
  • wp_ajax_radio_clear_history — clear the history list (nonce: radio_history; History-page only). Favourites untouched.

Notes

  • Per-user — nothing is shared, nothing leaves the site. Just your own listening history on your own WP account.
  • No PII concern — entries are public station/artist/title strings from SomaFM's own JSON.
  • Search-link UI tints toward each provider's brand colour on hover only — keeps the row visually calm in the default state.

Files changed: radio.php (version, require, submenu, asset enqueue hook, three AJAX endpoints, three new localized strings), new inc/history.php (storage helpers + page renderer), assets/css/radio.css (history-page table, toolbar, search-link pills, favourite star, dark-theme overrides), assets/js/radio.js (logTrackIfNew wired into fetchTrack; bindHistoryPage for filter/favourite/clear), inc/about.php (history entry).


[0.4.0] — 2026-05-29 — Now-playing indicator: dancing bars + Web Audio visualizer

A small visual that instantly says "this is playing right now." Two layers — a reliable CSS-only indicator that always works, and a progressive Web Audio upgrade that draws actual frequency data when the browser allows.

Added — dancing bars (always on, CSS only)

  • Four tiny vertical bars next to the "Now Playing" label that pulse with a staggered @keyframes animation while the audio is playing, settling to a low static state when paused. Pure CSS — no JS dependency, no audio analysis.
  • Bars use var(--wp-admin-theme-color) so they tint to whichever WP admin colour scheme the user has chosen.
  • Driven by a single .is-playing class toggled on the .radio-player surface from the existing play / pause / error audio handlers.

Added — Web Audio frequency visualizer (progressive upgrade)

  • On first play, tryVisualizer builds an AudioContext + AnalyserNode chain on the <audio> element and starts drawing live frequency bars on a <canvas> next to "Now Playing."
  • <audio> now carries crossorigin="anonymous" so the Web Audio analyser can actually read the stream data (SomaFM serves the CORS headers).
  • Graceful fallback: if AudioContext isn't available, or createMediaElementSource throws, or the analyser returns all-zeros for 2 s (CORS silently blocking), the visualizer state flips to cors-blocked / init-failed / no-webaudio and the CSS dancing bars remain — the plugin never loses its indicator.
  • Canvas is sized to its CSS box × devicePixelRatio so it stays crisp on retina screens.

State machine on player._vizState

Value Meaning
undefined not yet attempted
no-webaudio browser lacks AudioContext
init-failed createMediaElementSource threw
cors-blocked analyser returned zeros for >2 s
ok live frequency data flowing → canvas shown, bars hidden

Files changed: radio.php (version), inc/admin-page.php + inc/dashboard-widget.php (added .radio-player__indicator with .radio-player__bars + <canvas data-radio-viz>, plus crossorigin="anonymous" on the <audio>), assets/css/radio.css (indicator container, bars + radio-bars-dance keyframes, canvas size), assets/js/radio.js (tryVisualizer / startVizLoop / stopVizLoop, play/pause/error handlers toggle is-playing class and drive the loop), inc/about.php (history entry).


[0.3.2] — 2026-05-29 — Play-button glyph baseline fix

The dashicon used for the play/pause icon was rendering visibly below the button text baseline — the dashicon font sits the glyph low inside its own box, and even with inline-flex centering the result looked like the symbol was on a separate row from the word "Play".

Fixed

  • Play/pause icon now sits on the text baseline. Swapped the dashicon (dashicons-controls-play / dashicons-controls-pause) for a plain Unicode glyph (▶ / ‖) which renders on the text baseline like any other character.
  • Flex container changed from align-items: center to align-items: baseline for the same reason.
  • font-variant-emoji: text set so platforms that might otherwise pick up a colour-emoji variant for ▶ keep it as monochrome text.

Files changed: radio.php (version), inc/admin-page.php + inc/dashboard-widget.php (dashicon span → glyph span), assets/css/radio.css (drop the dashicon-sizing rule, add .radio-player__play-glyph styling, change play-button flex alignment to baseline), assets/js/radio.js (setPlayIcon now swaps glyph textContent instead of dashicon className), inc/about.php (history entry).


[0.3.1] — 2026-05-29 — My Radio layout polish + drop dark-auto

Quick patch after a real-screen review of the My Radio admin page. v0.3.0 added a lot of features but stretched the player to fill the full admin width and — embarrassingly — introduced a real contrast bug via the dark-auto CSS.

Fixed

  • Dropped @media (prefers-color-scheme: dark) for theme=auto. WordPress admin has no native dark mode, so when the OS was dark, our dark text rendered on the still-white WP postbox = unreadable. auto now behaves as light (matching WP's actual scheme); dark is still available as an explicit choice.
  • theme=dark now actually reads. The player surface goes dark (#1d2327 background + subtle border + padding) so the light text has somewhere to sit, instead of fighting the white WP postbox.
  • Player no longer stretches edge-to-edge. .radio-wrap { max-width: 880px; } keeps the player a focused settings-page card.
  • Intro paragraph one-line on normal widths. Removed the max-width: 720px cap that was forcing the wrap.
  • Volume slider no longer dominates the row. Fixed width 220px (flex: 0 0 auto) — the percent label now sits next to the slider instead of pinned to the far right edge.
  • Station dropdown capped at 360px (was full-width) — typical WP form-control width.
  • Play button icon shrunk from 18px to 14px so the ▶ glyph sits on the button-text baseline instead of looking like a separate row.

Files changed: radio.php (version), assets/css/radio.css (wrap width, intro, play icon, volume, station-select, dark-auto block deleted, dark-surface added), inc/about.php (history entry).


[0.3.0] — 2026-05-29 — Dark theme + mute + media keys + current-track display

A polish pass that closes the gaps surfaced by a UI review. Two categories: 2nd-look fixes (things the previous release implied but didn't actually deliver) and nice-to-haves (small upgrades that lift the admin-player feel).

Added — features

  • Mute toggle on the speaker icon. The icon next to the volume slider is now a button. Click to mute (icon flips to dashicons-controls-volumeoff, tinted red); click again to restore the prior volume. Remembers volume across mute/unmute via data-prev-volume.
  • MediaSession API integration. OS media keys (F8/F9 on Mac, Bluetooth headphone buttons, lock-screen widget on supported platforms) now play/pause the radio. The currently-playing station name, "SomaFM" as artist, and the genre as album are exposed as MediaMetadata so they show on the OS overlays.
  • Current-track display. Polls https://somafm.com/songs/{code}.json every 30 seconds while playing only and shows the track as ♪ Title — Artist under the station description. Best-effort: silently hidden if the endpoint is unreachable / CORS-blocked, so the plugin keeps working regardless.

Fixed — 2nd-look

  • Dark theme is now actually wired through. v0.2.0 saved the Theme dropdown (auto / light / dark) but had no CSS to render anything other than light. v0.3.0 adds admin_body_class filter → radio-theme-{auto,light,dark} body class → corresponding dark-palette CSS for the player + about-cards. auto follows the OS via prefers-color-scheme: dark.
  • Settings-page volume slider no longer uses an inline oninput="" handler — the listener moved into assets/js/radio.js (bindSettingsSlider). Cleaner under strict-CSP environments.
  • Save errors are surfaced. AJAX state-save failures were previously swallowed silently — the local UI updated but the user had no signal if the server dropped the request. The plugin now shows a brief notice ("Preferences not saved — check your connection.") in the player's error slot for 3.5 s, then auto-clears.
  • Hardcoded Gitea URL in the About page replaced with a RADIO_GITEA_URL constant defined in radio.php. One place to update if the repo ever moves.
  • Genre badge layout fix. Was using margin-left: auto inside a wrap-enabled flex row, which caused it to land on its own line on narrow widget widths. Now styled as a small inline pill (rounded rgba(0,0,0,0.06) background) that flows naturally next to the station name.

Other

  • Plugin version bumped to 0.3.0.
  • New localized strings: mute, unmute, saveError (for the JS-driven UI).
  • The mute button has a visible focus ring (outline: 2px solid var(--wp-admin-theme-color)) for keyboard navigation.
  • Volume slider input now also exits mute state (sets audio.muted = false on drag), so dragging the slider always overrides a prior mute click.

Files changed: radio.php (version, constant, strings, admin_body_class filter), inc/about.php (constant for changelog URL), inc/settings.php (removed inline oninput), inc/admin-page.php + inc/dashboard-widget.php (speaker icon → mute button, added track slot), assets/css/radio.css (genre badge pill, mute button, track display, dark-theme rules incl. prefers-color-scheme for auto), assets/js/radio.js (full rewrite incl. bindMute, bindSettingsSlider, startTrackPolling/stopTrackPolling, updateMediaSession, save-error surfacing).


[0.2.0] — 2026-05-26

Changed — UI rebuilt to WordPress admin standards

v0.1.0 worked but looked like a third-party React widget sitting on top of WordPress rather than part of it. v0.2.0 rebuilds the player UI to use WP-native patterns end-to-end.

  • Main page now uses the standard .postbox container (gray header bar + .inside body) instead of a custom rounded-shadow card.
  • Play button is now a standard .button .button-primary with both icon AND text label (Play / Pause), matching every other admin button. Replaces the giant blue circular icon-only button.
  • Now Playing uses left-aligned default body text with .description muted-gray for the station tagline. Replaces the centered large-typography card.
  • Genre badge moved to a small .radio-player__station-genre text label aligned right of the station name. Replaces the custom pill.
  • Volume slider now uses accent-color: var(--wp-admin-theme-color) — adapts to whichever admin colour scheme the user has chosen (Default / Light / Modern / Blue / Coffee / Ectoplasm / Midnight / Ocean / Sunrise).
  • All link colours likewise adapt to the user's admin theme via var(--wp-admin-theme-color, #2271b1).
  • Dashboard widget content sits bare inside its .inside — WordPress already wraps it in a postbox. v0.1.0 was rendering a card inside a card.
  • About page cards now use postbox-style gray header bars + WP-standard 1px border + subtle shadow. Replaces the custom rounded grid.
  • Credit footer uses .description class, smaller and more native.

Net effect

The plugin feels like part of WordPress now, not bolted onto it. Picks up your admin colour scheme automatically. Closer to WP.org submission criteria — they look for native-styled plugins during plugin review.

Not changed

  • Functionality identical to v0.1.0 — same 44 stations, same audio path, same user_meta persistence, same updater, same AJAX endpoint.
  • No behaviour change for end users; this is purely visual.
  • About page version-history card promotes v0.2.0 to "latest", demotes v0.1.0.

[0.1.0] — 2026-05-26

Radio is born. First release of a new standalone WordPress plugin extracted-and-rebuilt from the radio feature that lives inside RangerPlex. Radio stands on its own as a focused, friendly companion plugin for the WordPress dashboard — a tab of background music while you work.

Added — Phase A complete (player exists)

  • Dashboard widget at WP Admin → Dashboard showing a compact mini-player with play/pause, station select grouped by genre, and a volume slider.
  • Dedicated admin page at WP Admin → Radio → My Radio showing the same controls in a larger format with the station's genre badge and description.
  • 44 SomaFM stations across 10 genres (Ambient, Electronic, Lounge, Rock, Metal, Jazz, World, Reggae, Holiday, Specials). Stream URLs use SomaFM's public 128kbps MP3 endpoints — no proxy server required.
  • Per-user state storage via user_meta (key: radio_state). Each WordPress admin remembers their own station choice, volume, and theme preference.
  • Settings page at WP Admin → Radio → Settings with default station, default volume, theme (auto/light/dark), and an opt-out for the dashboard widget. Updates panel shown to admins with manage_options.
  • About page at WP Admin → Radio → About with plain-language explanation of what the plugin does, who it's for, version history, and credits to SomaFM.
  • Self-hosted update checker wired up to the Gitea repo (ranger/a-radio) from commit 1. Polls /api/v1/repos/ranger/a-radio/releases/latest with a /tags?limit=1 fallback. 12h success cache, 1h negative cache.
  • AJAX endpoint radio_save_state for persisting station/volume changes without a page reload. Nonce-protected, capability-checked.
  • Custom admin-menu icon (dashicons-format-audio).
  • Direct HTML5 <audio> playback — no Node proxy, no PHP stream-passthrough, no server-side resource cost per listener. SomaFM's CORS headers make this work out of the box in modern browsers.

Architecture (locked from day one)

  • Single-word brand name Radio — no "WP" prefix, no marketplace trademark hurdle.
  • Public GPL v2+ Gitea repo at ranger/a-radio on git.davidtkeane.com.
  • Per-user state in user_meta under key radio_state.
  • Vanilla JS only — no React, no build step, no bundler. ~200 lines of JS controlling all interactions.
  • CSS-only animations, all assets local — bundle stays sub-100KB.
  • Single H1 per admin page, no nested toggle boxes — Tier-1 discipline carried forward from the Logbook + Buddy lineage.
  • Sanitize on input, escape on output throughout. Every AJAX endpoint nonce-protected and capability-checked.

Compliance

  • Station list and stream URLs are SomaFM's public, freely-published endpoints. Their terms allow redistribution with attribution.
  • "Powered by SomaFM" credit displayed in both player surfaces, linking to somafm.com.
  • The About page invites users to donate to SomaFM directly.

Not in this release (planned)

  • Phase B — Settings polish + README for WP.org submission + retry logic for transient stream errors.
  • Phase C — Now-playing metadata via SomaFM's per-station song-history endpoint.
  • Phase D — [ranger_radio] shortcode so the player can be embedded in posts/pages.
  • Phase E — Favorites system.
  • Phase F — Multi-provider (Radio Paradise, NTS Radio, KEXP, BBC) with a provider abstraction.