diff --git a/index.html b/index.html index 8b82374..f755577 100644 --- a/index.html +++ b/index.html @@ -47,7 +47,7 @@
${formatTime(track.duration)}
@@ -57,13 +84,21 @@ export class UIRenderer { createAlbumCardHTML(album) { const explicitBadge = hasExplicitContent(album) ? this.createExplicitBadge() : ''; + let yearDisplay = ''; + if (album.releaseDate) { + const date = new Date(album.releaseDate); + if (!isNaN(date.getTime())) { + yearDisplay = `${date.getFullYear()}`; + } + } return `
${album.title}

${album.title} ${explicitBadge}

-

Album • ${album.artist?.name ?? ''}

+

${album.artist?.name ?? ''}

+

${yearDisplay}

`; } @@ -75,7 +110,6 @@ export class UIRenderer { ${artist.name}

${artist.name}

-

Artist

`; } @@ -100,7 +134,7 @@ export class UIRenderer {
-
+ ${!isArtist ? '
' : ''}
`; } @@ -117,8 +151,11 @@ export class UIRenderer { const fragment = document.createDocumentFragment(); const tempDiv = document.createElement('div'); + // Check if there are multiple discs in the tracks array + const hasMultipleDiscs = tracks.some(t => (t.volumeNumber || t.discNumber || 1) > 1); + tempDiv.innerHTML = tracks.map((track, i) => - this.createTrackItemHTML(track, i, showCover) + this.createTrackItemHTML(track, i, showCover, hasMultipleDiscs) ).join(''); while (tempDiv.firstChild) { @@ -245,12 +282,14 @@ export class UIRenderer { const imageEl = document.getElementById('album-detail-image'); const titleEl = document.getElementById('album-detail-title'); const metaEl = document.getElementById('album-detail-meta'); + const prodEl = document.getElementById('album-detail-producer'); const tracklistContainer = document.getElementById('album-detail-tracklist'); imageEl.src = ''; imageEl.style.backgroundColor = 'var(--muted)'; titleEl.innerHTML = '
'; metaEl.innerHTML = '
'; + prodEl.innerHTML = '
'; tracklistContainer.innerHTML = `
# @@ -269,16 +308,29 @@ export class UIRenderer { const explicitBadge = hasExplicitContent(album) ? this.createExplicitBadge() : ''; titleEl.innerHTML = `${album.title} ${explicitBadge}`; - const totalDuration = calculateTotalDuration(tracks); - const releaseDate = new Date(album.releaseDate); - const year = releaseDate.getFullYear(); + this.adjustTitleFontSize(titleEl, album.title); - const dateDisplay = window.innerWidth > 768 - ? releaseDate.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) - : year; + const totalDuration = calculateTotalDuration(tracks); + let dateDisplay = ''; + if (album.releaseDate) { + const releaseDate = new Date(album.releaseDate); + if (!isNaN(releaseDate.getTime())) { + const year = releaseDate.getFullYear(); + dateDisplay = window.innerWidth > 768 + ? releaseDate.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) + : year; + } + } + + const firstCopyright = tracks.find(track => track.copyright)?.copyright; metaEl.innerHTML = - `By ${album.artist.name} • ${dateDisplay} • ${tracks.length} tracks • ${formatDuration(totalDuration)}`; + (dateDisplay ? `${dateDisplay} • ` : '') + + `${tracks.length} tracks • ${formatDuration(totalDuration)}`; + + prodEl.innerHTML = + `By ${album.artist.name}` + + (firstCopyright ? ` • ${firstCopyright}` : ''); tracklistContainer.innerHTML = `
@@ -288,7 +340,12 @@ export class UIRenderer {
`; - tracks.sort((a, b) => a.trackNumber - b.trackNumber); + tracks.sort((a, b) => { + const discA = a.volumeNumber ?? a.discNumber ?? 1; + const discB = b.volumeNumber ?? b.discNumber ?? 1; + if (discA !== discB) return discA - discB; + return a.trackNumber - b.trackNumber; + }); this.renderListWithTracks(tracklistContainer, tracks, false); recentActivityManager.addAlbum(album); @@ -332,6 +389,8 @@ async renderPlaylistPage(playlistId) { titleEl.textContent = playlist.title; + this.adjustTitleFontSize(titleEl, playlist.title); + const totalDuration = calculateTotalDuration(tracks); metaEl.textContent = `${playlist.numberOfTracks} tracks • ${formatDuration(totalDuration)}`; @@ -377,6 +436,9 @@ async renderPlaylistPage(playlistId) { imageEl.src = this.api.getArtistPictureUrl(artist.picture, '750'); imageEl.style.backgroundColor = ''; nameEl.textContent = artist.name; + + this.adjustTitleFontSize(nameEl, artist.name); + metaEl.textContent = `${artist.popularity} popularity`; this.renderListWithTracks(tracksContainer, artist.tracks, true); diff --git a/styles.css b/styles.css index 830bbdd..bf91bcd 100644 --- a/styles.css +++ b/styles.css @@ -114,6 +114,27 @@ --explicit-badge: #f59e0b; } +:root[data-theme="light"] { + --background: #ffffff; + --foreground: #000000; + --card: #f4f4f5; + --card-foreground: #000000; + --primary: #2563eb; + --primary-foreground: #ffffff; + --secondary: #e4e4e7; + --secondary-foreground: #000000; + --muted: #e4e4e7; + --muted-foreground: #71717a; + --border: #e4e4e7; + --input: #e4e4e7; + --ring: #2563eb; + --highlight: #2563eb; + --highlight-rgb: 37, 99, 235; + --active-highlight: var(--highlight); + --explicit-badge: #ef4444; + --explicit-badge-foreground: #ffffff; +} + *, *::before, *::after { box-sizing: border-box; margin: 0; @@ -166,19 +187,18 @@ kbd { height: 100dvh; grid-template: "sidebar main" 1fr - "player player" auto / 280px 1fr; + "player player" auto / 190px 1fr; } .sidebar { grid-area: sidebar; background-color: var(--background); border-right: 1px solid var(--border); - padding: 1.5rem; + padding: 1.25rem; display: flex; flex-direction: column; gap: 2rem; transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); - z-index: 2000; } .main-content { @@ -190,7 +210,7 @@ kbd { .now-playing-bar { grid-area: player; - background-color: #050505; + background-color: var(--card); border-top: 1px solid var(--border); padding: var(--spacing-md) var(--spacing-lg); display: grid; @@ -491,7 +511,7 @@ kbd { align-items: center; justify-content: center; background-color: var(--explicit-badge); - color: #000; + color: var(--explicit-badge-foreground, #000); font-size: 0.65rem; font-weight: 700; padding: 0.15rem 0.35rem; @@ -682,6 +702,15 @@ kbd { align-items: center; gap: 1rem; flex-wrap: wrap; + word-break: break-word; +} + +.detail-header-info .title.long-title { + font-size: 2.5rem; +} + +.detail-header-info .title.very-long-title { + font-size: 1.75rem; } .detail-header-info .meta { @@ -1805,7 +1834,7 @@ input:checked + .slider::before { /* Responsive Design */ @media (max-width: 1024px) { .app-container { - grid-template-columns: 240px 1fr; + grid-template-columns: 160px 1fr; } .card-grid { @@ -1817,6 +1846,14 @@ input:checked + .slider::before { font-size: 3rem; } + .detail-header-info .title.long-title { + font-size: 2rem; + } + + .detail-header-info .title.very-long-title { + font-size: 1.35rem; + } + .main-content { padding: var(--spacing-lg); } @@ -1850,6 +1887,7 @@ input:checked + .slider::before { height: 100%; transform: translateX(-100%); box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); + z-index: 2000; } .sidebar.is-open { @@ -1900,6 +1938,14 @@ input:checked + .slider::before { line-height: 1.2; } + .detail-header-info .title.long-title { + font-size: 1.5rem; + } + + .detail-header-info .title.very-long-title { + font-size: 1.25rem; + } + .detail-header-info .meta { font-size: 0.85rem; gap: 0.35rem; @@ -2136,6 +2182,14 @@ input:checked + .slider::before { font-size: 1.75rem; } + .detail-header-info .title.long-title { + font-size: 1.35rem; + } + + .detail-header-info .title.very-long-title { + font-size: 1.1rem; + } + .search-tab { padding: var(--spacing-sm) var(--spacing-md); font-size: 0.9rem;