feat(ui): add "In Your Library" section to artist detail page
Show liked tracks and playlist tracks by the artist with source labels, collapsible chevron toggle, and real-time updates via favorites-changed and playlist-tracks-changed events.
This commit is contained in:
parent
433f859513
commit
52d5166363
4 changed files with 300 additions and 9 deletions
22
index.html
22
index.html
|
|
@ -1115,6 +1115,19 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="goto-playlist-modal" class="modal">
|
||||||
|
<div class="modal-overlay"></div>
|
||||||
|
<div class="modal-content">
|
||||||
|
<h3>Go to Playlist</h3>
|
||||||
|
<div id="goto-playlist-list" class="modal-list">
|
||||||
|
<!-- Options will be injected here -->
|
||||||
|
</div>
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button id="goto-playlist-cancel" class="btn-secondary">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="shortcuts-modal" class="modal">
|
<div id="shortcuts-modal" class="modal">
|
||||||
<div class="modal-overlay"></div>
|
<div class="modal-overlay"></div>
|
||||||
<div class="modal-content medium">
|
<div class="modal-content medium">
|
||||||
|
|
@ -3283,6 +3296,15 @@
|
||||||
<h2 class="section-title">Popular Tracks</h2>
|
<h2 class="section-title">Popular Tracks</h2>
|
||||||
<div class="track-list" id="artist-detail-tracks"></div>
|
<div class="track-list" id="artist-detail-tracks"></div>
|
||||||
</section>
|
</section>
|
||||||
|
<section class="content-section" id="artist-section-in-library" style="display: none">
|
||||||
|
<h2 class="section-title in-library-toggle" id="in-library-toggle" style="cursor: pointer; display: flex; align-items: center; gap: 0.5rem; user-select: none;">
|
||||||
|
In Your Library
|
||||||
|
<svg id="in-library-chevron" xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="transition: transform 0.25s ease; flex-shrink: 0;">
|
||||||
|
<polyline points="9 18 15 12 9 6"></polyline>
|
||||||
|
</svg>
|
||||||
|
</h2>
|
||||||
|
<div class="track-list" id="artist-detail-in-library" style="display: none;"></div>
|
||||||
|
</section>
|
||||||
<section class="content-section">
|
<section class="content-section">
|
||||||
<h2 class="section-title">Albums</h2>
|
<h2 class="section-title">Albums</h2>
|
||||||
<div class="card-grid" id="artist-detail-albums"></div>
|
<div class="card-grid" id="artist-detail-albums"></div>
|
||||||
|
|
|
||||||
5
js/db.js
5
js/db.js
|
|
@ -172,11 +172,13 @@ export class MusicDatabase {
|
||||||
|
|
||||||
if (exists) {
|
if (exists) {
|
||||||
await this.performTransaction(storeName, 'readwrite', (store) => store.delete(key));
|
await this.performTransaction(storeName, 'readwrite', (store) => store.delete(key));
|
||||||
|
window.dispatchEvent(new CustomEvent('favorites-changed'));
|
||||||
return false; // Removed
|
return false; // Removed
|
||||||
} else {
|
} else {
|
||||||
const minified = this._minifyItem(type, item);
|
const minified = this._minifyItem(type, item);
|
||||||
const entry = { ...minified, addedAt: Date.now() };
|
const entry = { ...minified, addedAt: Date.now() };
|
||||||
await this.performTransaction(storeName, 'readwrite', (store) => store.put(entry));
|
await this.performTransaction(storeName, 'readwrite', (store) => store.put(entry));
|
||||||
|
window.dispatchEvent(new CustomEvent('favorites-changed'));
|
||||||
return true; // Added
|
return true; // Added
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -600,6 +602,7 @@ export class MusicDatabase {
|
||||||
await this.performTransaction('user_playlists', 'readwrite', (store) => store.put(playlist));
|
await this.performTransaction('user_playlists', 'readwrite', (store) => store.put(playlist));
|
||||||
|
|
||||||
this._dispatchPlaylistSync('update', playlist);
|
this._dispatchPlaylistSync('update', playlist);
|
||||||
|
window.dispatchEvent(new CustomEvent('playlist-tracks-changed'));
|
||||||
|
|
||||||
return playlist;
|
return playlist;
|
||||||
}
|
}
|
||||||
|
|
@ -623,6 +626,7 @@ export class MusicDatabase {
|
||||||
this._updatePlaylistMetadata(playlist);
|
this._updatePlaylistMetadata(playlist);
|
||||||
await this.performTransaction('user_playlists', 'readwrite', (store) => store.put(playlist));
|
await this.performTransaction('user_playlists', 'readwrite', (store) => store.put(playlist));
|
||||||
this._dispatchPlaylistSync('update', playlist);
|
this._dispatchPlaylistSync('update', playlist);
|
||||||
|
window.dispatchEvent(new CustomEvent('playlist-tracks-changed'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return playlist;
|
return playlist;
|
||||||
|
|
@ -643,6 +647,7 @@ export class MusicDatabase {
|
||||||
await this.performTransaction('user_playlists', 'readwrite', (store) => store.put(playlist));
|
await this.performTransaction('user_playlists', 'readwrite', (store) => store.put(playlist));
|
||||||
|
|
||||||
this._dispatchPlaylistSync('update', playlist);
|
this._dispatchPlaylistSync('update', playlist);
|
||||||
|
window.dispatchEvent(new CustomEvent('playlist-tracks-changed'));
|
||||||
|
|
||||||
return playlist;
|
return playlist;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
227
js/ui.js
227
js/ui.js
|
|
@ -3072,10 +3072,10 @@ export class UIRenderer {
|
||||||
dateDisplay =
|
dateDisplay =
|
||||||
window.innerWidth > 768
|
window.innerWidth > 768
|
||||||
? releaseDate.toLocaleDateString('en-US', {
|
? releaseDate.toLocaleDateString('en-US', {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
})
|
})
|
||||||
: year;
|
: year;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3927,6 +3927,8 @@ export class UIRenderer {
|
||||||
const epsSection = document.getElementById('artist-section-eps');
|
const epsSection = document.getElementById('artist-section-eps');
|
||||||
const similarContainer = document.getElementById('artist-detail-similar');
|
const similarContainer = document.getElementById('artist-detail-similar');
|
||||||
const similarSection = document.getElementById('artist-section-similar');
|
const similarSection = document.getElementById('artist-section-similar');
|
||||||
|
const inLibraryContainer = document.getElementById('artist-detail-in-library');
|
||||||
|
const inLibrarySection = document.getElementById('artist-section-in-library');
|
||||||
const dlBtn = document.getElementById('download-discography-btn');
|
const dlBtn = document.getElementById('download-discography-btn');
|
||||||
if (dlBtn) dlBtn.innerHTML = `${SVG_DOWNLOAD}<span>Download Discography</span>`;
|
if (dlBtn) dlBtn.innerHTML = `${SVG_DOWNLOAD}<span>Download Discography</span>`;
|
||||||
|
|
||||||
|
|
@ -3948,6 +3950,14 @@ export class UIRenderer {
|
||||||
if (loadUnreleasedSection) loadUnreleasedSection.style.display = 'none';
|
if (loadUnreleasedSection) loadUnreleasedSection.style.display = 'none';
|
||||||
if (similarContainer) similarContainer.innerHTML = this.createSkeletonCards(6, true);
|
if (similarContainer) similarContainer.innerHTML = this.createSkeletonCards(6, true);
|
||||||
if (similarSection) similarSection.style.display = 'block';
|
if (similarSection) similarSection.style.display = 'block';
|
||||||
|
if (inLibrarySection) inLibrarySection.style.display = 'none';
|
||||||
|
if (inLibraryContainer) {
|
||||||
|
inLibraryContainer.innerHTML = '';
|
||||||
|
inLibraryContainer.style.display = 'none';
|
||||||
|
}
|
||||||
|
// Reset chevron state
|
||||||
|
const chevronEl = document.getElementById('in-library-chevron');
|
||||||
|
if (chevronEl) chevronEl.style.transform = 'rotate(0deg)';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const artist = await this.api.getArtist(artistId, provider);
|
const artist = await this.api.getArtist(artistId, provider);
|
||||||
|
|
@ -4168,9 +4178,9 @@ export class UIRenderer {
|
||||||
<span>${artist.popularity}% popularity</span>
|
<span>${artist.popularity}% popularity</span>
|
||||||
<div class="artist-tags">
|
<div class="artist-tags">
|
||||||
${(artist.artistRoles || [])
|
${(artist.artistRoles || [])
|
||||||
.filter((role) => role.category)
|
.filter((role) => role.category)
|
||||||
.map((role) => `<span class="artist-tag">${role.category}</span>`)
|
.map((role) => `<span class="artist-tag">${role.category}</span>`)
|
||||||
.join('')}
|
.join('')}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
@ -4182,6 +4192,205 @@ export class UIRenderer {
|
||||||
|
|
||||||
this.renderListWithTracks(tracksContainer, artist.tracks, true);
|
this.renderListWithTracks(tracksContainer, artist.tracks, true);
|
||||||
|
|
||||||
|
// "In your library" section: find liked tracks and playlist tracks for this artist
|
||||||
|
if (inLibraryContainer && inLibrarySection) {
|
||||||
|
const artistNameLower = artist.name.toLowerCase();
|
||||||
|
|
||||||
|
const isTrackByArtist = (track) => {
|
||||||
|
if (track.artists && Array.isArray(track.artists)) {
|
||||||
|
return track.artists.some(
|
||||||
|
(a) => a && a.name && a.name.toLowerCase() === artistNameLower
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (track.artist) {
|
||||||
|
const artistStr = typeof track.artist === 'string' ? track.artist : track.artist.name;
|
||||||
|
if (artistStr && artistStr.toLowerCase() === artistNameLower) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshInLibrary = async () => {
|
||||||
|
try {
|
||||||
|
const seenIds = new Set();
|
||||||
|
const libraryTracks = [];
|
||||||
|
const trackSourceMap = new Map(); // trackId -> Array<{ label, href }>
|
||||||
|
|
||||||
|
const addSource = (trackId, source) => {
|
||||||
|
if (!trackSourceMap.has(trackId)) {
|
||||||
|
trackSourceMap.set(trackId, []);
|
||||||
|
}
|
||||||
|
trackSourceMap.get(trackId).push(source);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get liked tracks
|
||||||
|
const likedTracks = await db.getFavorites('track');
|
||||||
|
for (const track of likedTracks) {
|
||||||
|
if (isTrackByArtist(track)) {
|
||||||
|
if (!seenIds.has(track.id)) {
|
||||||
|
seenIds.add(track.id);
|
||||||
|
libraryTracks.push(track);
|
||||||
|
}
|
||||||
|
addSource(track.id, { label: 'Liked Tracks', href: '/library' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get tracks from user playlists
|
||||||
|
const userPlaylists = await db.getPlaylists(true);
|
||||||
|
for (const playlist of userPlaylists) {
|
||||||
|
if (playlist.tracks && Array.isArray(playlist.tracks)) {
|
||||||
|
for (const track of playlist.tracks) {
|
||||||
|
if (isTrackByArtist(track)) {
|
||||||
|
if (!seenIds.has(track.id)) {
|
||||||
|
seenIds.add(track.id);
|
||||||
|
libraryTracks.push(track);
|
||||||
|
}
|
||||||
|
const label = playlist.name || playlist.title || 'Playlist';
|
||||||
|
addSource(track.id, {
|
||||||
|
label,
|
||||||
|
href: `/userplaylist/${playlist.id}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort alphabetically by title
|
||||||
|
libraryTracks.sort((a, b) => (a.title || '').localeCompare(b.title || ''));
|
||||||
|
|
||||||
|
if (libraryTracks.length > 0) {
|
||||||
|
inLibrarySection.style.display = 'block';
|
||||||
|
this.renderListWithTracks(inLibraryContainer, libraryTracks, true);
|
||||||
|
|
||||||
|
// Inject source labels into each track's .artist div
|
||||||
|
const trackElements = inLibraryContainer.querySelectorAll('.track-item');
|
||||||
|
trackElements.forEach((el, idx) => {
|
||||||
|
const track = libraryTracks[idx];
|
||||||
|
if (!track) return;
|
||||||
|
const sources = trackSourceMap.get(track.id);
|
||||||
|
if (!sources || sources.length === 0) return;
|
||||||
|
const artistDiv = el.querySelector('.track-item-details .artist');
|
||||||
|
if (!artistDiv) return;
|
||||||
|
|
||||||
|
// Extract artist name and year from existing content
|
||||||
|
const artistLinks = artistDiv.querySelectorAll('.artist-link');
|
||||||
|
const artistNames = Array.from(artistLinks).map(a => a.textContent).join(', ');
|
||||||
|
const truncatedArtist = artistNames.length > 15 ? artistNames.slice(0, 20) + '…' : artistNames;
|
||||||
|
|
||||||
|
// Extract year from text content (pattern: " • 2024")
|
||||||
|
const fullText = artistDiv.textContent;
|
||||||
|
const yearMatch = fullText.match(/\s•\s(\d{4})/);
|
||||||
|
const yearText = yearMatch ? ` • ${yearMatch[1]}` : '';
|
||||||
|
|
||||||
|
// Build source content
|
||||||
|
const sourceSpan = document.createElement('span');
|
||||||
|
sourceSpan.className = 'library-source';
|
||||||
|
|
||||||
|
if (sources.length === 1) {
|
||||||
|
const srcLabel = sources[0].label.length > 15 ? sources[0].label.slice(0, 15) + '…' : sources[0].label;
|
||||||
|
sourceSpan.innerHTML = `<span class="library-source-label">· Source: </span><span class="library-source-link">${srcLabel}</span>`;
|
||||||
|
sourceSpan.style.cursor = 'pointer';
|
||||||
|
sourceSpan.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
navigate(sources[0].href);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
sourceSpan.innerHTML = `<span class="library-source-label">· Source: </span><span class="library-source-link">Multiple Playlists</span>`;
|
||||||
|
sourceSpan.style.cursor = 'pointer';
|
||||||
|
sourceSpan.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const modal = document.getElementById('goto-playlist-modal');
|
||||||
|
const list = document.getElementById('goto-playlist-list');
|
||||||
|
const cancelBtn = document.getElementById('goto-playlist-cancel');
|
||||||
|
const overlay = modal.querySelector('.modal-overlay');
|
||||||
|
|
||||||
|
list.innerHTML = sources.map((s) =>
|
||||||
|
`<div class="modal-option" data-href="${s.href}">
|
||||||
|
<span>${s.label}</span>
|
||||||
|
</div>`
|
||||||
|
).join('');
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
modal.classList.remove('active');
|
||||||
|
};
|
||||||
|
|
||||||
|
list.onclick = (ev) => {
|
||||||
|
const option = ev.target.closest('.modal-option');
|
||||||
|
if (!option) return;
|
||||||
|
const href = option.dataset.href;
|
||||||
|
closeModal();
|
||||||
|
if (href) navigate(href);
|
||||||
|
};
|
||||||
|
|
||||||
|
cancelBtn.onclick = closeModal;
|
||||||
|
overlay.onclick = closeModal;
|
||||||
|
modal.classList.add('active');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rebuild artist div with structured layout
|
||||||
|
artistDiv.innerHTML = '';
|
||||||
|
artistDiv.classList.add('library-artist-flex');
|
||||||
|
|
||||||
|
const artistNameSpan = document.createElement('span');
|
||||||
|
artistNameSpan.className = 'library-artist-name';
|
||||||
|
artistNameSpan.textContent = truncatedArtist;
|
||||||
|
|
||||||
|
const yearSpan = document.createElement('span');
|
||||||
|
yearSpan.className = 'library-year';
|
||||||
|
yearSpan.textContent = yearText;
|
||||||
|
|
||||||
|
artistDiv.appendChild(artistNameSpan);
|
||||||
|
artistDiv.appendChild(yearSpan);
|
||||||
|
artistDiv.appendChild(sourceSpan);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
inLibrarySection.style.display = 'none';
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('Failed to load library tracks for artist:', err);
|
||||||
|
inLibrarySection.style.display = 'none';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initial load
|
||||||
|
refreshInLibrary().then(() => {
|
||||||
|
inLibraryContainer.style.display = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Setup chevron toggle (once)
|
||||||
|
const toggle = document.getElementById('in-library-toggle');
|
||||||
|
const chevron = document.getElementById('in-library-chevron');
|
||||||
|
if (toggle) {
|
||||||
|
toggle.onclick = () => {
|
||||||
|
const isOpen = inLibraryContainer.style.display !== 'none';
|
||||||
|
inLibraryContainer.style.display = isOpen ? 'none' : '';
|
||||||
|
if (chevron) {
|
||||||
|
chevron.style.transform = isOpen ? 'rotate(0deg)' : 'rotate(90deg)';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Real-time updates: refresh when favorites or playlists change
|
||||||
|
let refreshTimeout;
|
||||||
|
const debouncedRefresh = () => {
|
||||||
|
clearTimeout(refreshTimeout);
|
||||||
|
refreshTimeout = setTimeout(() => refreshInLibrary(), 300);
|
||||||
|
};
|
||||||
|
window.addEventListener('favorites-changed', debouncedRefresh);
|
||||||
|
window.addEventListener('playlist-tracks-changed', debouncedRefresh);
|
||||||
|
|
||||||
|
// Cleanup listeners when navigating away
|
||||||
|
const cleanupOnNav = () => {
|
||||||
|
window.removeEventListener('favorites-changed', debouncedRefresh);
|
||||||
|
window.removeEventListener('playlist-tracks-changed', debouncedRefresh);
|
||||||
|
window.removeEventListener('popstate', cleanupOnNav);
|
||||||
|
};
|
||||||
|
window.addEventListener('popstate', cleanupOnNav, { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
// Update header like button
|
// Update header like button
|
||||||
const artistLikeBtn = document.getElementById('like-artist-btn');
|
const artistLikeBtn = document.getElementById('like-artist-btn');
|
||||||
if (artistLikeBtn) {
|
if (artistLikeBtn) {
|
||||||
|
|
@ -4812,13 +5021,13 @@ export class UIRenderer {
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
${
|
${
|
||||||
isUser
|
isUser
|
||||||
? `
|
? `
|
||||||
<button class="delete-instance" title="Delete Instance">
|
<button class="delete-instance" title="Delete Instance">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<path d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
<path d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>`
|
</button>`
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
<button class="move-up" title="Move Up" ${index === 0 ? 'disabled' : ''}>
|
<button class="move-up" title="Move Up" ${index === 0 ? 'disabled' : ''}>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
|
|
||||||
55
styles.css
55
styles.css
|
|
@ -1988,6 +1988,58 @@ input[type='search']::-webkit-search-cancel-button {
|
||||||
gap: 2px var(--spacing-xl);
|
gap: 2px var(--spacing-xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#artist-detail-in-library {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(580px, 1fr));
|
||||||
|
gap: 2px var(--spacing-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-source {
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
font-size: inherit;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 0;
|
||||||
|
flex-shrink: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-artist-flex {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35em;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-artist-name {
|
||||||
|
flex-shrink: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-year {
|
||||||
|
flex-shrink: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-source-label {
|
||||||
|
flex-shrink: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-source-link {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
min-width: 0;
|
||||||
|
transition: color var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-source:hover .library-source-link {
|
||||||
|
color: var(--highlight);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
#playlist-detail-recommended .track-item {
|
#playlist-detail-recommended .track-item {
|
||||||
grid-template-columns: 40px 1fr 32px auto;
|
grid-template-columns: 40px 1fr 32px auto;
|
||||||
}
|
}
|
||||||
|
|
@ -1995,6 +2047,7 @@ input[type='search']::-webkit-search-cancel-button {
|
||||||
@media (max-width: 1100px) {
|
@media (max-width: 1100px) {
|
||||||
#home-recommended-songs,
|
#home-recommended-songs,
|
||||||
#artist-detail-tracks,
|
#artist-detail-tracks,
|
||||||
|
#artist-detail-in-library,
|
||||||
#playlist-detail-recommended {
|
#playlist-detail-recommended {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
@ -2313,6 +2366,8 @@ input[type='search']::-webkit-search-cancel-button {
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
|
word-break: break-word;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-header-info .title.long-title {
|
.detail-header-info .title.long-title {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue