refactor: remove Top Results section from explore search

Remove the mixed Top Results section that showed a blend of artists
and recordings. Search results now show three clean categories:
Artists, Albums, Tracks — each sorted by their own scoring.

Removed: ScoredItem interface, TOP_RESULTS_COUNT, getTopResults,
renderTopResults, renderTopCard, and .top-card CSS.
This commit is contained in:
2026-03-28 12:34:40 -04:00
parent 6820e781e7
commit 48a8713091
@@ -16,7 +16,6 @@ const DEBOUNCE_MS = 300;
const MIN_QUERY_LENGTH = 2;
const MAX_SECTION_RESULTS = 10;
const CAA_GROUP_BASE = 'https://coverartarchive.org/release-group';
const TOP_RESULTS_COUNT = 3;
/**
* Build a Cover Art Archive URL for a release-group front cover.
@@ -51,17 +50,6 @@ function extractYear(dateStr: string): string {
return dateStr.substring(0, 4);
}
/**
* Union type for top-results ranking. We can only rank items with
* a score field — MBReleaseGroup lacks one in the Go struct.
*/
interface ScoredItem {
type: 'artist' | 'recording';
score: number;
artist?: MBArtist;
recording?: MBRecording;
}
@customElement('explore-view')
export class ExploreView extends LitElement {
/* ── State ── */
@@ -216,48 +204,6 @@ export class ExploreView extends LitElement {
}
/* ── Top result cards ── */
.top-card {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 14px;
background: var(--yj-bg-surface, #212529);
border-radius: 8px;
cursor: pointer;
min-width: 200px;
max-width: 280px;
flex-shrink: 0;
transition: background 0.15s ease;
}
.top-card:hover {
background: var(--yj-bg-overlay, rgba(255, 255, 255, 0.06));
}
.top-card:active {
transform: scale(0.98);
}
.top-card-info {
flex: 1;
min-width: 0;
}
.top-card-name {
font-weight: 500;
color: var(--yj-text-primary, #fff);
font-size: var(--yj-text-md);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.top-card-meta {
color: var(--yj-text-tertiary, #888);
font-size: var(--yj-text-sm);
margin-top: 2px;
}
/* ── Artist cards ── */
.artist-card {
display: flex;
@@ -733,29 +679,6 @@ export class ExploreView extends LitElement {
}
}
/* ── Top Results ── */
private getTopResults(): ScoredItem[] {
if (!this.results) return [];
const items: ScoredItem[] = [];
if (this.results.artists) {
for (const a of this.results.artists) {
items.push({ type: 'artist', score: a.score, artist: a });
}
}
if (this.results.recordings) {
for (const r of this.results.recordings) {
items.push({ type: 'recording', score: r.score, recording: r });
}
}
items.sort((a, b) => b.score - a.score);
return items.slice(0, TOP_RESULTS_COUNT);
}
/* ── Navigation ── */
private navigateToArtist(artist: MBArtist) {
@@ -871,13 +794,8 @@ export class ExploreView extends LitElement {
</div>`;
}
const topResults = this.getTopResults();
return html`
<div class="results-container">
${topResults.length > 0
? this.renderTopResults(topResults)
: nothing}
${hasArtists
? this.renderArtistsSection(this.results.artists!.slice(0, MAX_SECTION_RESULTS))
: nothing}
@@ -896,74 +814,6 @@ export class ExploreView extends LitElement {
/* ── Section Renderers ── */
private renderTopResults(items: ScoredItem[]) {
return html`
<section>
<h3 class="section-header">Top Results</h3>
<div class="horizontal-row">
${items.map((item) => this.renderTopCard(item))}
</div>
</section>
`;
}
private renderTopCard(item: ScoredItem) {
if (item.type === 'artist' && item.artist) {
const a = item.artist;
const hue = nameToHue(a.name);
const imgURL = this.artistImageCache.get(a.mbid);
return html`
<div
class="top-card"
@click=${() => this.navigateToArtist(a)}
role="button"
tabindex="0"
@keydown=${(e: KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this.navigateToArtist(a);
}
}}
>
<div
class="artist-avatar"
style="background: hsl(${hue}, 45%, 35%)"
>
${imgURL
? html`<img src="${imgURL}" alt="${a.englishName || a.name}" />`
: (a.englishName || a.name).charAt(0).toUpperCase()}
</div>
<div class="top-card-info">
<div class="top-card-name">${a.englishName || a.name}</div>
<div class="top-card-meta">Artist${a.country ? ` · ${a.country}` : ''}</div>
</div>
</div>
`;
}
if (item.type === 'recording' && item.recording) {
const r = item.recording;
return html`
<div class="top-card" role="listitem">
<wa-icon
style="color: var(--yj-text-tertiary, #888); font-size: 18px; flex-shrink: 0;"
name="music"
></wa-icon>
<div class="top-card-info">
<div class="top-card-name">${r.title}</div>
<div class="top-card-meta">
${r.artistCredit}${r.length
? ` · ${formatDuration(r.length)}`
: ''}
</div>
</div>
</div>
`;
}
return nothing;
}
private renderArtistsSection(artists: MBArtist[]) {
return html`
<section>