Quality badge primitives + Settings thresholds
Stacked on PR #39. Adds the visual layer that the upcoming Library page (PR 3) and artist detail page (PR 4) will lean on, plus a small immediate win on the existing List view.
What changes
-
$lib/qualityColor.ts— shared util mapping a score togreen | yellow | red | grayagainst configurable thresholds. Defaults match the cutoffs already used bycomputeQualityScore(FLAC 16-bit = 80 → green, MP3 320 = 50 → yellow, lower → red, score 0 / null → gray). -
getQualityThresholds/setQualityThresholds— backed by the existing settings key/value table (JSON-serialized). -
+layout.server.ts— exposes thresholds on every page's load data so the badge can color itself without a fetch. -
/api/settings/quality-thresholds— GET/POST. Server validatesgreenMin > yellowMin. -
<QualityBadge>— three sizes:-
dot(10px ring) for cramped overlay placements like cover thumbnails -
sm(10px text pill) for list rows -
md(12px text pill) for headers
-
-
<SegmentedControl>— extracted from the inline pill-button pattern inAlbumList.svelte, generic over the value type. PR 3 needs three of these stacked (Source, Type, View) so consolidating here. - Settings: Quality thresholds card — two number inputs with a four-badge live preview (Lossless / MP3 320 / MP3 192 / Unknown) that recolors as you adjust the cutoffs.
- AlbumCard — gets a colored dot on the cover and the quality label appended to the artist/year metadata line. Immediate visual upgrade on the existing List view; the dot is dimensioned to fit the 48px cover.
Verification
-
pnpm run buildpasses. -
/shows colored dots on cards. Albums without aquality_scoreshow gray (covered by thescore === 0branch). - Open
/settings, scroll to Quality thresholds, change the green min to 90, save, reload/. Cards previously green at 80 should now show as yellow. - POST
/api/settings/quality-thresholdswith{greenMin: 50, yellowMin: 60}returns 400.
Risk
UI-only and additive. Nothing existing reads qualityColor or thresholds yet, so the worst-case regression is the dot rendering at the wrong corner of a card. The Settings page picks up one new card between Playback and API Connections — no removals.