feat(device-sync): show album artist in both panels

Adds optional artist field to DeviceSyncSource and renders it inline
next to the album name ("Album · Artist") in the on-device list.
BrowserRow (left panel) uses the same inline format so albums in search,
random picks and under expanded artists all read consistently. Playlists
unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Psychotoxical
2026-04-18 22:58:51 +02:00
parent 2e5a34178b
commit 6b3e809d12
3 changed files with 22 additions and 5 deletions
+10 -5
View File
@@ -872,7 +872,7 @@ export default function DeviceSync() {
{activeTab === 'albums' && (search.trim() ? albumSearchResults : randomAlbums).map(al => (
<BrowserRow key={al.id} name={al.name} meta={al.artist}
selected={sources.some(s => s.id === al.id) && !pendingDeletion.includes(al.id)}
onToggle={() => handleToggleSource({ type: 'album', id: al.id, name: al.name })} />
onToggle={() => handleToggleSource({ type: 'album', id: al.id, name: al.name, artist: al.artist })} />
))}
{activeTab === 'artists' && filteredArtists.map(ar => (
<React.Fragment key={ar.id}>
@@ -896,7 +896,7 @@ export default function DeviceSync() {
<BrowserRow key={al.id} name={al.name} meta={al.year?.toString()}
selected={sources.some(s => s.id === al.id) && !pendingDeletion.includes(al.id)}
indent
onToggle={() => handleToggleSource({ type: 'album', id: al.id, name: al.name })} />
onToggle={() => handleToggleSource({ type: 'album', id: al.id, name: al.name, artist: al.artist || ar.name })} />
))
}
</React.Fragment>
@@ -990,7 +990,10 @@ export default function DeviceSync() {
onChange={() => toggleChecked(s.id)}
disabled={status === 'deletion'}
/>
<span className="device-sync-row-name">{s.name}</span>
<span className="device-sync-row-name">
{s.name}
{s.artist && <span className="device-sync-row-artist"> · {s.artist}</span>}
</span>
<span className="device-sync-source-type">{s.type}</span>
<span className={`device-sync-status-icon ${status}`}>
{status === 'synced' && <CheckCircle2 size={13} />}
@@ -1272,8 +1275,10 @@ function BrowserRow({ name, meta, selected, onToggle, indent }: {
<span className="device-sync-row-check">
{selected ? <CheckCircle2 size={14} /> : <span className="device-sync-row-circle" />}
</span>
<span className="device-sync-row-name">{name}</span>
{meta && <span className="device-sync-row-meta">{meta}</span>}
<span className="device-sync-row-name">
{name}
{meta && <span className="device-sync-row-artist"> · {meta}</span>}
</span>
</button>
);
}
+2
View File
@@ -5,6 +5,8 @@ export interface DeviceSyncSource {
type: 'album' | 'playlist' | 'artist';
id: string;
name: string;
/** Album artist — only set when type === 'album'. Shown as a subtitle in the device list. */
artist?: string;
}
interface DeviceSyncState {
+10
View File
@@ -8478,6 +8478,16 @@ html.no-compositing .fsr-lyric-line.fsrl-active .fsr-lyric-word.active {
.device-sync-row-name {
flex: 1;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.device-sync-row-artist {
color: var(--text-muted);
font-weight: 400;
margin-left: 0.2em;
}
.device-sync-device-row.checked { background: var(--accent-dim); }