feat: initial commit — RangerHQ Tuner v0.1.0 (Tier 1 MVP)

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.
This commit is contained in:
2026-06-08 23:31:29 +01:00
commit 38b6b8d3f7
20 changed files with 1001 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>RangerHQ Tuner</title>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<header class="tuner-header">
<div class="tuner-brand">
<span class="tuner-dot" aria-hidden="true"></span>
<h1>RangerHQ Tuner</h1>
</div>
<span class="tuner-state" id="state-pill" aria-live="polite">idle</span>
</header>
<section class="now-playing" aria-label="Now playing">
<div class="np-station" id="np-station"></div>
<div class="np-track" id="np-track">Pick a station to begin</div>
</section>
<section class="controls" aria-label="Playback controls">
<button id="btn-play" class="btn btn-primary" aria-pressed="false" disabled>▶ Play</button>
<label class="vol-wrap">
<span class="vol-label">Vol</span>
<input type="range" id="volume" min="0" max="100" value="70" aria-label="Volume">
</label>
</section>
<section class="stations" aria-label="Stations">
<label class="search-wrap">
<input type="search" id="search" placeholder="Filter stations…" aria-label="Filter stations">
</label>
<ul id="station-list" class="station-list" role="listbox">
<li class="station-empty">Loading stations…</li>
</ul>
</section>
<footer class="tuner-footer">
<span>SomaFM • indie radio • no telemetry</span>
</footer>
<script type="module" src="popup.js"></script>
</body>
</html>