The Chrome Web Store enforces a 132-character limit on the manifest
description field. The v0.3.0 description was 195 chars and got
rejected on upload. Trimmed to the same wording as the listing short
description in WEB_STORE_SUBMISSION.md §2.
The longer 1500-char marketing description still lives in §3 of the
submission notes and goes in the Dashboard's 'Detailed description'
field at listing time.
Both generated from the RangerHQ helmet logo + Arial typography on the
extension's own #0f1411 dark palette so the Dashboard preview blends
naturally with the screenshots.
- promo-tile-440x280.png: helmet anchor left, "RangerHQ Tuner" cream
bold + "Indie radio in your toolbar" accent green + "SomaFM · No
telemetry · GPL v2+" muted footer. Web Store requires this size for
search/browse listing tile.
- marquee-1400x560.png: same idea scaled up. Optional asset; Google
promotes extensions with marquees more often.
ImageMagick recipe documented in store/screenshots/README.md.
Web Store-spec screenshots for Dashboard upload, generated from
David's screen grabs taken on the live v0.3.0 build.
Screenshots (1280×800 PNG, the Chrome Web Store required size):
1. Stations tab — DEF CON Radio + quick chips + browse list
2. History tab — the headline 4-button-search feature with 4
tracks already logged (Spotify / YouTube / Apple / Bandcamp)
3. Favourites tab — starred-track persistence demo
4. Toolbar popup — composed on a dark canvas, helmet icon
visible in the Chrome address bar above
ImageMagick recipe:
- NTP shots: crop bottom Chrome chrome (1378x950+0+0), resize
to width 1280, north-anchored extent to 1280x800
- Popup: resize to height 740, center on 1280x800 canvas
with palette-matching #0f1411 background
Source originals archived under store/source/ for regeneration.
No code changes — extension behaviour unchanged.
Prep for Chrome Web Store first submission. No code changes — v0.3.0
behaviour unchanged.
- LICENSE — full GPL v2 text, matches RangerHQ WP family.
- PRIVACY.md — canonical privacy policy. "Collects nothing, stores
everything locally" using Google's data-category vocabulary so the
Dashboard Privacy Practices tab can be ticked uniformly "does not
collect" across all 9 categories.
- WEB_STORE_SUBMISSION.md — paste-ready Dashboard copy: single-purpose
statement, 132-char short + 1500-char detailed description, permission
justifications for offscreen + storage + somafm.com host, build ZIP
command, screenshot brief, post-approval update workflow.
The public version of the privacy policy will live at
davidtkeane.com/rangerhq-tuner/privacy (HTML mirror of PRIVACY.md).
Web Store submission target. Mirrors rangerhq-radio's track-history pattern
(inc/history.php) so the family stays coherent across surfaces.
Highlights
- New Tab Page override (Tier 2.5) — Chrome's default new tab replaced with
a RangerHQ-branded landing showing the player, current track, quick chips,
searchable browse list, and now history + favourites tabs.
- Track history + favourites — capped FIFO 500, dedup against last entry,
skip "(unknown)" artist (SomaFM dead-air). Stored in chrome.storage.local
under tuner.history + tuner.favourites.
- 4-button search per entry — Spotify / YouTube / Apple Music / Bandcamp.
Pure public-search-URL link-outs in a new tab, NO auth, NO API keys, NO
quota, NO third-party SDK embedded.
- Options page (chrome://extensions → details → options) — live stats,
history cap slider (50-500), Clear history / Clear favourites / Clear
EVERYTHING buttons, About panel with Gitea + davidtkeane.com links.
- Popup nav row — Open in tab / History (#hash deep link) / Settings,
using chrome.tabs.create + chrome.runtime.openOptionsPage. No new perms.
- Cross-surface sync — popup ↔ newtab listen on chrome.storage.onChanged
for tuner.currentStationId / tuner.isPlaying / history / favourites.
- Storage gateway — offscreen doc can't reliably reach chrome.storage in
some Chrome versions, so it sends LOG_TRACK_REQUEST to the SW which
does the write. history.js also defensively guards every storage call.
- Metadata latency fix — polling now starts immediately on PLAY, in
parallel with audio buffer fill. First track display drops from
~10-15s to ~1-2s.
Permissions unchanged
- Still ["offscreen", "storage"] + somafm.com host only.
- chrome.tabs.create works on our own extension URLs without "tabs" perm.
- No webRequest, no <all_urls>, no third-party SDK.
Bumped from 0.1.0 (last tag on Gitea) directly to 0.3.0.
v0.2.0 (newtab + clock) was a working local build but never tagged;
its features ship together with v0.3.0 in this single commit.
Chrome MV3 extension, browser-resident sibling to rangerhq-radio
(WP plugin). Plays SomaFM via the chrome.offscreen API + a source-
adapter pattern at src/sources/.
Architecture highlights:
- Audio runs in offscreen document — SW would get killed.
- Source-adapter pattern locks Tier 1 contract (RadioSource interface
in src/sources/base-source.js). Adding a network = drop a file +
register one line in src/sources/index.js.
- Vanilla JS, no build step. Pure ES modules.
- No telemetry, no third-party JS. Outbound only to somafm.com.
- Narrow permissions: offscreen + storage + somafm.com host_perms.
No tabs, no <all_urls>, no webRequest.
22 files, ~30 min build following the saved plan at
~/.ranger-memory/projects/rangerhq-tuner-plan.md.
Tier 2 + Tier 3 (Web Store submission) not started.