Three small reliability fixes from re-auditing:
1. Playlist.vue used Promise.all, so a flaky /detail endpoint would
blank out the whole page even though /songs returned just fine.
Switch to allSettled and synthesize a stub playlist header from
the song list when only detail fails. User can still play the
playlist; just loses the description/cover.
2. sourceTabs.readAll: typeof null === 'object' AND typeof [] ===
'object', so a corrupted localStorage value (e.g. an array) would
be treated as a record and its missing keys would silently fall
back. Reject explicitly so the failure mode is "clean defaults"
instead of "wrong shape that almost works".
3. Settings.vue loadProfileConfig: a 200 response with empty/wrong
body would set profileConfigs[botId] to a falsy/wrong-shape value,
leaving the row stuck on "加载中..." (because the v-if uses
!profileConfigs[id]). Validate the shape; surface "响应格式异常"
so the retry link is reachable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Recommend playlists, daily songs, and user playlists on Home now show
a [网易云][QQ] tab when both platforms are logged in. Library 我的歌单
gets the same tab. Selection persists per-section in localStorage and
falls back gracefully when the persisted source becomes unavailable
(e.g., user logged out). Removes dead 我的收藏 block from Library that
referenced a non-existent /api/music/user/liked endpoint.
Spec: docs/superpowers/specs/2026-05-06-music-source-tabs-design.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>