Commit Graph

15 Commits

Author SHA1 Message Date
ranger a9d76decae v0.7.3 — WordPress.org guideline 8 compliance + Privacy section
Adds the Update URI guard pattern so one source tree produces two
compliant build artifacts:

* Gitea install (default): `Update URI:` header points at
  git.davidtkeane.com → wp.org skips this plugin → self-hosted
  updater runs normally.
* WordPress.org submission zip (build script strips that line):
  header is empty → `radio_should_skip_custom_updater()` returns
  true → entire updater file short-circuits at load time → wp.org
  takes over update delivery as guideline 8 requires.

Settings page now guards the Updates panel render with
`function_exists()` because the panel function only exists when
the updater was allowed to load.

Also adds a dedicated `== Privacy ==` section to readme.txt
covering every outbound connection (none from the plugin itself;
SomaFM audio + 30s songs.json poll while playing) and adds an
explicit link to SomaFM's terms of use (somafm.com/legal/).

No user-visible behaviour change in either distribution.
2026-05-30 04:21:25 +01:00
ranger a298a4c217 feat(0.7.2): screenshots + correct wp.org contributor handle
Two small submission-prep moves on top of v0.7.1:

Added
- Five screenshots at the plugin root, per WordPress.org convention:
  screenshot-1.png  Dashboard widget
  screenshot-2.png  Settings page
  screenshot-3.png  History page (star + four search-provider links)
  screenshot-4.png  Pop-out mini-player window
  screenshot-5.png  About page
  Sizes 1087–1422 wide; total ~720KB.

Changed
- Contributors: in readme.txt updated from `davidtkeane` (placeholder)
  to `ir240474` (actual wp.org username, profile confirmed at
  https://profiles.wordpress.org/ir240474/).
- Stable tag: bumped to 0.7.2.
- Screenshots section in readme.txt rewritten to match the five
  actual screenshots.

Notes
- Source images were in an images/ working folder; moved + renamed to
  plugin root for wp.org convention, working folder removed.
- After this release the only PCP residue is the .gitignore
  hidden-file warning — unavoidable on the dev install, won't be in
  the submission zip.

Files: radio.php (version), readme.txt (Contributors / Stable Tag /
Screenshots section / new 0.7.2 changelog + upgrade notice),
CHANGELOG.md, inc/about.php (rotate v0.7.2 → latest),
5 new screenshot-*.png files at plugin root, images/ removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 04:06:50 +01:00
ranger c7b2258471 fix(0.7.1): Plugin Check follow-up — tested-up-to bump + .DS_Store re-cleanup
Re-ran PCP after v0.7.0 — issue count 169 → 4. Closes the only real
one of the four:

- Tested up to: 6.7 → 7.0 in readme.txt (PCP outdated_tested_upto_header).
  WordPress 7.0 is current.
- Stable Tag bumped to 0.7.1 in readme.txt.

Removed (again)
- .DS_Store files (root + assets/). macOS Finder regenerated them
  between PCP runs; they will not be present in the submission zip.

Known PCP residue (not addressable in source)
- .gitignore triggers a hidden_files WARNING on PCP. Keeping it is
  needed for git; will be excluded from the submission zip per WP.org
  packaging conventions.

Files: radio.php (version), readme.txt (Tested-up-to + Stable Tag +
new 0.7.1 changelog + upgrade notice), CHANGELOG.md,
inc/about.php (rotate v0.7.1 → latest, v0.7.0 → earlier list),
.DS_Store files deleted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 03:01:25 +01:00
ranger 09b61cc950 feat(0.7.0): WordPress.org submission prep — full Plugin Check clean
Ran the official Plugin Check (PCP) against v0.6.3 — surfaced 169
issues. This release closes all of them so the plugin is submission-
ready for the WordPress.org plugin directory.

Branding
  - Plugin Name renamed: "Radio" → "RangerHQ Radio". Removes the
    trademarked "SomaFM" from the plugin name surface (PCP
    trademarked_term). Lines up with the RangerHQ plugin family.
    SomaFM credited in Description + About as the data source.
    Folder/slug stays `a-radio` — no install path changes; existing
    user_meta keys (radio_state / radio_history / radio_favourites)
    untouched.
  - Text Domain header renamed: `radio` → `a-radio` (matches slug).
  - Requires at least bumped: 5.0 → 5.3 (matches wp_date() usage).
  - File docstring header dropped "SomaFM" from prominent line.

Code (mass-mechanical)
  - 134 i18n call sites rewritten from `'radio'` text domain to
    `'a-radio'` across 7 PHP files. Single sed pass on the unique
    pattern `, 'radio' )` — the 6 menu-slug `'radio'` references in
    add_*_page() were correctly left alone (those are URL slugs).

Security
  - 8 × MissingUnslash + 8 × InputNotSanitized in the v0.5.0 history
    endpoints (radio_ajax_log_track, radio_ajax_toggle_favourite).
    All four $_POST['artist|title|station|station_id'] access points
    are now wrapped sanitize_text_field( wp_unslash( $_POST['…'] ) )
    (or sanitize_key for station_id) at the access point.

Translator comments
  - 6 × printf / sprintf calls with placeholders now carry
    /* translators: ... */ comments.

Pop-out window refactor
  - Inline <link> stylesheets, <style> block, and <script> tag in
    radio_render_popout_page() replaced with wp_enqueue_style() +
    wp_enqueue_script() + wp_localize_script() registered before HTML
    output, then wp_print_styles() in <head> and wp_print_footer_
    scripts() at end of <body>.
  - Popup-specific CSS moved out of inline <style> and into radio.css
    under body.radio-popout scope so it only fires inside the popup.

Removed
  - .DS_Store files (root + assets/). PCP hidden_files.

Distribution
  - New readme.txt in proper WordPress.org format: Plugin headers,
    Contributors, Donate link, Tags, Requires-at-least, Tested-up-to,
    Stable Tag, Requires-PHP, License, Description, Installation,
    FAQ, Screenshots, Changelog, Upgrade Notice.

Compat
  - No behaviour change for users; user_meta preserved.
  - Displayed Plugin Name in Plugins → Installed changes from "Radio"
    to "RangerHQ Radio" — only visible difference on update.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 02:51:09 +01:00
ranger 224fccd6c4 feat(0.6.3): discreet "buy me a coffee" support link
Tiny footer line inside the Updates panel on Settings + matching line
at the bottom of the Credits + thanks card on About. Pulled from a
single RADIO_SUPPORT_URL constant so both stay in sync. Conditional
render — if the constant is empty, the link is silently hidden, so
forks can strip funding with one line.

Design intent: muted (12px, admin-theme-coloured, subtle top divider).
Reads as housekeeping not a sales pitch — no yellow BMC brand chrome.
Dark-theme divider override so it stays subtle on the dark surface.

Files: radio.php (version, RADIO_SUPPORT_URL constant default
https://buymeacoffee.com/davidtkeane), inc/updater.php (link in
Updates panel), inc/about.php (link in Credits + thanks card; rotate
v0.6.3 → latest, v0.6.2 → earlier list), assets/css/radio.css
(.radio-support-link + dark-theme override), CHANGELOG.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 00:44:44 +01:00
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
ranger 7a747b829b fix(0.6.1): About page restructure — balance the layout
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.

- Three short cards on top (What / Who / Credits) — equal-height,
  balanced row. Credits gets equal billing instead of being a fourth
  card buried under a wall of version notes.
- Version history is its own full-width card below. Only the latest
  release is shown in full; earlier releases collapse to one line
  each. 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 via
  the existing "View the full CHANGELOG.md →" link. Single source of
  truth.

Files: 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 (drop dead .radio-about-card--versions rules;
add .radio-about-versions + __latest + __earlier; dark-theme overrides).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 00:25:12 +01:00
ranger 0dc3a220d9 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>
2026-05-30 00:03:21 +01:00
ranger 2bd501d610 feat(0.5.0): track history + favourites with four search providers
Quietly log every track that scrolls past the player to a per-user
history (capped 500). A star button promotes the good ones to a
separate Favourites tab that doesn't age out. Each row has four
search-provider deep links so you can find the track on whichever
service you use.

Storage
  Two new user_meta keys (separate from radio_state so frequent
  inserts don't churn the player-state blob):
    - radio_history    — capped FIFO of last 500 played tracks
    - radio_favourites — uncapped user-starred list

New page: Radio → History
  - History tab: capped FIFO list, newest first.
  - Favourites tab: starred tracks (uncapped).
  - Each row: when (relative + full timestamp on hover) / station /
    artist — title / four search links / favourite-star toggle.
  - Filter by artist/title (live, client-side) + by station (dropdown).
  - Clear history button (favourites preserved).
  - Search providers: Spotify, YouTube, Apple Music, Bandcamp.
    Deep-link URLs only — no API keys, no third-party JS.

Auto-logging during playback
  fetchTrack (existing 30s SomaFM poll loop) now hands new tracks to
  logTrackIfNew → POST to wp_ajax_radio_log_track. Dedup client-side
  (lastLoggedSig) AND server-side (against last entry in user_meta).
  Junk filtered server-side: (unknown) artist, missing artist/title.

New AJAX endpoints
  - radio_log_track          (nonce radio_save_state — player pages)
  - radio_toggle_favourite   (nonce radio_history — history page only)
  - radio_clear_history      (nonce radio_history — history page only)

Files
  - radio.php (version, require new include, submenu page, asset
    enqueue hook adds radio_page_radio-history, three AJAX endpoints,
    three new localized strings)
  - inc/history.php (NEW — storage helpers + admin page renderer)
  - assets/css/radio.css (history table, toolbar, search-link pills,
    favourite star, dark-theme overrides)
  - assets/js/radio.js (logTrackIfNew wired into fetchTrack;
    bindHistoryPage handles filter/favourite/clear)
  - inc/about.php (history entry)
  - CHANGELOG.md (full entry)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 23:55:18 +01:00
ranger f5feca7dfa feat(0.4.0): now-playing indicator — dancing bars + Web Audio visualizer
Two-layer "this is playing right now" visual:

(1) CSS dancing bars — four tiny vertical bars next to the "Now Playing"
    label, staggered `@keyframes` pulse while audio plays. Pure CSS, no
    JS dependency, tints to the user's WP admin colour scheme via
    var(--wp-admin-theme-color). Driven by a single `.is-playing` class
    on `.radio-player` toggled from the existing play/pause/error
    handlers. Always works.

(2) Web Audio frequency visualizer (progressive upgrade) — on first play,
    builds AudioContext + AnalyserNode + canvas drawing pipeline. When
    the analyser starts returning real (non-zero) data, hides the bars
    and shows the canvas with live frequency bars. Falls back to bars
    if AudioContext is unavailable, createMediaElementSource throws, or
    the analyser returns all-zeros for >2s (CORS silently blocking).
    State machine on player._vizState: no-webaudio / init-failed /
    cors-blocked / ok.

`<audio>` element gained `crossorigin="anonymous"` so Web Audio can read
the stream data (SomaFM serves the CORS headers).

Files: radio.php (version), inc/admin-page.php + inc/dashboard-widget.php
(.radio-player__indicator with .radio-player__bars + canvas; crossorigin
on audio), assets/css/radio.css (indicator, bars, radio-bars-dance
keyframes, canvas size), assets/js/radio.js (tryVisualizer,
startVizLoop, stopVizLoop; play/pause/error handlers wire the loop and
toggle is-playing), inc/about.php (history entry).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 23:42:09 +01:00
ranger a56fd7aff7 fix(0.3.2): play-button glyph baseline — drop dashicon for Unicode ▶ / ‖
The dashicon used for the play/pause icon was rendering visibly below
the button text — dashicon font metrics sit the glyph low inside its
own box, so even with `inline-flex` centering the symbol looked like
it was on a separate row from the word "Play".

Swap dashicons-controls-play / dashicons-controls-pause for plain
Unicode glyphs (▶ / ‖) which render on the text baseline like any
other character. Flex container changed to align-items: baseline.
font-variant-emoji: text on the glyph span to keep platforms that
might otherwise pick up an emoji variant of ▶ as monochrome text.

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

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 23:19:04 +01:00
ranger 774e7f9958 fix(0.3.1): My Radio layout polish + drop dark-auto
Real-screen review of v0.3.0 surfaced a contrast bug and several layout
issues. Quick patch.

- Drop @media prefers-color-scheme dark for theme=auto. WP admin has
  no native dark mode, so OS-dark made our light text sit on the still-
  white WP postbox = unreadable. `auto` now behaves as light; `dark`
  remains as an explicit choice.
- theme=dark now actually reads: the player surface itself goes dark
  (#1d2327 bg + subtle border + padding) so the light text has somewhere
  to land instead of fighting the white WP postbox.
- .radio-wrap max-width 880px — player no longer stretches edge-to-edge.
- Drop the .radio-intro max-width:720px cap so the intro fits one line.
- Volume slider fixed at flex:0 0 auto / width:220px — % label sits next
  to the slider instead of pinned to the far edge.
- Station dropdown capped at 360px (was width:100%).
- Play-button dashicon shrunk 18px → 14px so it sits on the button-text
  baseline instead of looking like a second row.

Files: radio.php, assets/css/radio.css, inc/about.php, CHANGELOG.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 23:11:06 +01:00
ranger c5a4b28b29 feat(0.3.0): dark theme, mute, media keys, current-track + 2nd-look fixes
Closes the gaps from a UI review of v0.2.0.

Added
- Mute toggle: speaker icon is now a button; remembers prior volume.
- MediaSession API: OS media keys / headphone buttons / lock-screen
  widget play/pause the radio. Metadata exposes station + SomaFM + genre.
- Current-track display: polls https://somafm.com/songs/{code}.json every
  30s while playing; shown as `♪ Title — Artist` under the description.
  Best-effort — silently hidden if CORS-blocked / unreachable.

Fixed (2nd-look)
- Dark theme now actually renders. v0.2.0 saved the dropdown but had no
  CSS — add `admin_body_class` filter + `radio-theme-{auto,light,dark}`
  CSS for the player + about-cards. `auto` follows OS prefers-color-scheme.
- Settings-page volume slider: removed inline `oninput`; wired in radio.js
  via `bindSettingsSlider()`. Cleaner under strict CSP.
- Save errors surface as a transient notice instead of being swallowed.
- Gitea changelog URL moved into `RADIO_GITEA_URL` constant.
- Genre badge restyled as an inline pill (was using `margin-left: auto`
  which wrapped poorly on narrow widget widths).

Files
- radio.php (version, constant, strings, body-class filter)
- inc/about.php (use constant, add 0.3.0 history entry)
- inc/settings.php (drop inline oninput)
- inc/admin-page.php + inc/dashboard-widget.php (mute button, track slot)
- assets/css/radio.css (pill, mute, track, dark-theme rules)
- assets/js/radio.js (rewrite: mute, MediaSession, track polling,
  settings slider, save-error surfacing)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 22:56:57 +01:00
ranger 3e6994461e feat: v0.2.0 — UI rebuilt to WordPress admin standards
v0.1.0 worked but felt like a third-party React widget bolted on
top of WordPress. v0.2.0 makes the player visually native to the
WP admin: postbox container, standard Play/Pause button with text
label, admin-colour-scheme aware accents, dashboard widget no
longer renders a card inside a card.

CHANGES
- inc/admin-page.php: main page now wraps the player in a
  .postbox > .postbox-header (with h2.hndle) > .inside structure.
  Custom rounded card / shadow stripped.
- inc/dashboard-widget.php: bare .radio-player content; WP already
  wraps dashboard widgets in a postbox, was double-card before.
- inc/about.php: version-history card promotes v0.2.0 to latest,
  demotes v0.1.0.
- assets/css/radio.css: rewrite. Strip custom shadows + oversized
  typography. Adopt WP body-text defaults. Use
  var(--wp-admin-theme-color, #2271b1) for volume-slider accent +
  link colours so the plugin picks up whichever admin colour
  scheme the user has chosen. About-page cards now use the
  postbox-style gray header + 1px border pattern.
- assets/js/radio.js: setPlayIcon() also flips the visible text
  label ("Play" ↔ "Pause"), not just the icon class. mirrorSelection()
  also updates the [data-radio-genre] element so the genre label
  stays in sync across surfaces.
- radio.php: Version: 0.1.0 -> 0.2.0; BUDDY_VERSION constant
  bumped likewise.
- CHANGELOG.md: new [0.2.0] entry explaining the visual overhaul.

NET EFFECT
- Same 44 stations, same audio path, same persistence, same
  updater, same AJAX endpoint. Pure visual change.
- The plugin now looks like part of WordPress admin instead of a
  guest widget.
- Closer to WP.org submission criteria — plugin reviewers look
  for native-styled plugins.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 10:03:51 +01:00
ranger a22ddfb6d3 chore: initial commit — Radio v0.1.0 (Phase A complete)
First release of a-radio — a free, focused SomaFM player for the
WordPress admin. Extracted-and-rebuilt from RangerPlex's RadioPlayer
component, simplified along the way (no Node CORS proxy needed; the
browser plays SomaFM streams directly).

Phase A deliverables — all in this commit:

PLAYER SURFACES
- Dashboard widget on WP Admin → Dashboard (compact: play/pause +
  station select + volume).
- Dedicated admin page at WP Admin → Radio → My Radio (larger
  layout, station genre badge, volume %% display).
- Both surfaces share the same JS — picking a station on one mirrors
  to the other within the same admin session.

STATIONS
- 44 SomaFM stations grouped by 10 genres (Ambient / Electronic /
  Lounge / Rock / Metal / Jazz / World / Reggae / Holiday / Specials).
- Ported verbatim from RangerPlex's RadioPlayer.tsx.
- Default station: Groove Salad (most popular SomaFM channel, safe
  ambient/coding pick).

STATE
- Per-user state stored in user_meta under key `radio_state`:
  { station_id, volume, theme, last_played_at }.
- Settings page lets the user pick default station, default volume,
  theme (auto/light/dark), and hide the dashboard widget.
- AJAX endpoint `radio_save_state` persists changes without a page
  reload. Nonce-protected, capability-checked, only known keys
  accepted, station validated against the list, volume clamped to
  [0,1].

UPDATES
- Self-hosted updater wired to Gitea (ranger/a-radio) from commit 1.
- Direct port of Buddy's inc/updater.php with BUDDY_* → RADIO_* and
  buddy_* → radio_* renames. Same 12h-success / 1h-error caching.

ARCHITECTURE
- No `wp` prefix (trademark policy).
- GPL v2+ public Gitea repo from day one.
- Vanilla JS only — no React, no webpack, no minified-only files.
- CSS-only animations, all assets local.
- Single H1 per admin page.
- Direct HTML5 <audio> playback (SomaFM has CORS headers; no PHP
  audio proxy needed). This is the key simplification vs RangerPlex.

COMPLIANCE
- "Powered by SomaFM" credit displayed on both player surfaces with
  link to somafm.com. About page invites users to donate to SomaFM
  directly.

PHASE PROGRESSION (not in this commit)
- Phase B — Settings polish + retry on transient stream errors +
  README.md formatted for WP.org submission.
- Phase C — Now-playing metadata via SomaFM's per-station song
  history endpoint (this is where the RangerPlex proxy logic ports
  to — server-side, for metadata not audio).
- Phase D — [ranger_radio] shortcode for frontend embedding.
- Phase E — Favorites system.
- Phase F — Multi-provider (Radio Paradise / NTS / KEXP / BBC).

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