Four-cut cluster closing out the major ArtistDetail extraction.
707 → 339 LOC (−368).
ArtistDetailHero — the full hero header: back button, lightbox
trigger, avatar with hover upload overlay + camera/loader icon +
hidden file input, glow effect from extractCoverColors onLoad,
title + album count, entity-rating row, Last.fm + Wikipedia +
favourite link row, and the action button strip (play all,
shuffle, radio, share, offline cache with progress / done state).
Subscribes to useOfflineStore / useOfflineJobStore / useAuthStore
directly so the page doesn't have to thread bulk-progress through.
ArtistDetailTopTracks — the four-column tracklist with each row's
inline play-next + preview ring + track cover thumbnail + click
into playTopSongWithContinuation. Subscribes to playerStore /
previewStore / useOrbitSongRowBehavior directly.
ArtistDetailSimilarArtists — section header (with show-more toggle
on mobile), loading spinner, and the chip list (using
serverSimilarArtists vs. similarArtists depending on which path
fed it).
runArtistDetailActions — four parameterized async actions:
runArtistEntityRating (with full / track_only fallback +
saveFailed toast), runArtistToggleStar (optimistic state + revert
on error), runArtistShare (copy link + success / failure toast),
runArtistImageUpload (upload + invalidate cover-art cache + bump
revision).
ArtistDetail drops the inline definitions; formatDuration moves
into the TopTracks component. Pure code move otherwise.