feat(web): AlbumModal follow everywhere + Settings polish (#10)

Follow affordance in ALL album modals (user request): thread artistMbid through
the remaining callers — Library, Wanted, and artist Discography. /api/library
and /api/wanted now expose artistMbid (from the joined MonitoredRelease); the
artist-detail route already exposed mbid. So every album modal now links the
artist name to their page + shows a Follow/Following button.

Settings polish (#10):
- Qobuz tab: drop the email/password inputs (the engine prefers the more
  reliable user-id + auth-token path); a "Clear legacy login" affordance appears
  only when old email/password creds are still stored, and the PUT no longer
  sends them.
- Clear-button alignment fixed: .field label is a flex row and .field-grid
  bottom-aligns inputs, so a set-secret field (with its · set + Clear) lines up
  with a plain sibling field.
- Library tab gains a read-only Staging path panel (/staging, STAGING_DIR, why
  fast local disk, rebuild-to-change).
- Monitor + Discovery tabs gain grounded helptext (quality-class 1/2/3 cutoff,
  poll/retry/upgrade-window; sweep cadence + seeds-per-chunk + ListenBrainz URL).

web 152 tests, tsc + build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-14 12:09:52 +02:00
parent 17bed331a9
commit 24d4ba6098
7 changed files with 84 additions and 34 deletions
@@ -17,7 +17,7 @@ type Release = {
state: string;
currentQualityClass: number | null;
};
type Detail = { id: string; name: string; autoMonitorFuture: boolean; showAllTypes: boolean; releases: Release[] };
type Detail = { id: string; mbid: string; name: string; autoMonitorFuture: boolean; showAllTypes: boolean; releases: Release[] };
function badge(r: Release): { label: string; cls: string } {
if (r.currentQualityClass !== null) return { label: `Have q${r.currentQualityClass}`, cls: "chip done" };
@@ -127,6 +127,7 @@ export function DiscographyClient({ id }: { id: string }) {
artist: detail.name,
year: openAlbum.firstReleaseDate?.slice(0, 4) ?? null,
type: openAlbum.primaryType,
artistMbid: detail.mbid,
}}
status={<span className={badge(openAlbum).cls}>{badge(openAlbum).label}</span>}
actions={