feat(v0.5.3-prep): user-customizable Quick Stations + descriptions
David flagged 2026-06-10: '14 stations wraps 3 lines, original idea was 2 lines tidy, but now thought of user selecting themselves' + 'maybe have a description of each station' + 'be great to keep the code clean.' Architecture: pure data, not code. Adds tuner.quickStations as a new chrome.storage.local key. Default 8 (tidy 2-row layout per the v0.4.0 picks). User can pick any subset of the 46 SomaFM channels via a new Options page card. NEW FILE src/lib/quick-stations.js — DEFAULT_QUICK_IDS + storage helpers (getQuickStations, setQuickStations, resetQuickStations) with the same defensive 'fall back if chrome.storage missing' pattern as history.js + theme.js. CHANGED src/newtab/newtab.js — removed hardcoded QUICK_IDS array, replaced with module-level 'let quickIds = []' populated during init from getQuickStations() + re-loaded when chrome.storage.onChanged fires on tuner.quickStations. renderQuick() now uses module-level quickIds and shows 'No Quick Stations picked — set some in Settings' empty state if the array is empty. src/options/options.html — new 'Quick Stations' card between Appearance and Playback. Contains: helper text + selected-count badge + scrollable <ul> + Reset-to-defaults button. src/options/options.css — ~90 lines for the new picker: scrollable list (max-height 360px), checkbox-name-description row layout, accent-coloured count badge, subtle hover background, brand-styled scrollbar. src/options/options.js — initQuickStationsUI() reads cached channel list from tuner.stationsCache, sorts alphabetically, builds one row per channel with checkbox + name + genre pill + description. Toggle handler writes the current pick set to chrome.storage.local. Reset button confirms then calls resetQuickStations() + re-checks boxes to match defaults. Cross-surface sync via storage.onChanged re-syncs check state if changes happen elsewhere. 5 files (1 new), ~250 lines added. No new permissions, no new dependencies.
This commit is contained in:
@@ -367,3 +367,100 @@ html, body {
|
||||
background: var(--danger);
|
||||
color: var(--cream);
|
||||
}
|
||||
|
||||
/* ──────────────────────────────────────────────────────────────────────
|
||||
Quick Stations picker (v0.5.3) — scrollable checkbox list of every
|
||||
SomaFM channel. Toggles persist to chrome.storage.local under
|
||||
tuner.quickStations; NewTab re-renders chip row instantly.
|
||||
────────────────────────────────────────────────────────────────────── */
|
||||
|
||||
.opt-qs-count {
|
||||
color: var(--accent);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.opt-qs-list {
|
||||
margin: 12px 0 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
max-height: 360px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--bg-row);
|
||||
border-radius: var(--radius);
|
||||
background: var(--bg-row);
|
||||
}
|
||||
|
||||
.opt-qs-list::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
.opt-qs-list::-webkit-scrollbar-thumb {
|
||||
background: var(--bg-row-hi);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.opt-qs-list::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.opt-qs-loading {
|
||||
padding: 14px;
|
||||
text-align: center;
|
||||
color: var(--fg-muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.opt-qs-row {
|
||||
border-bottom: 1px solid var(--bg-soft);
|
||||
}
|
||||
|
||||
.opt-qs-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.opt-qs-label {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 10px 14px;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.opt-qs-label:hover {
|
||||
background: var(--bg-row-hi);
|
||||
}
|
||||
|
||||
.opt-qs-label input[type="checkbox"] {
|
||||
margin: 3px 0 0 0;
|
||||
accent-color: var(--accent);
|
||||
flex-shrink: 0;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.opt-qs-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.opt-qs-name {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
.opt-qs-genre {
|
||||
font-size: 10px;
|
||||
color: var(--accent);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.opt-qs-desc {
|
||||
font-size: 11px;
|
||||
color: var(--fg-muted);
|
||||
margin-top: 3px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user