Your Library
-
+
diff --git a/js/events.js b/js/events.js
index 9f0c5a5..cebaabf 100644
--- a/js/events.js
+++ b/js/events.js
@@ -354,7 +354,7 @@ export async function handleTrackAction(action, item, player, api, lyricsManager
const container = itemEl.parentElement;
itemEl.remove();
if (container && container.children.length === 0) {
- const msg = type === 'track' ? 'No liked songs yet.' : `No liked ${type}s yet.`;
+ const msg = type === 'track' ? 'No liked tracks yet.' : `No liked ${type}s yet.`;
container.innerHTML = `
${msg}
`;
}
} else if (added && !itemEl && ui && type === 'track') {
@@ -427,7 +427,14 @@ export function initializeTrackInteractions(player, api, mainContent, contextMen
e.stopPropagation();
const trackItem = menuBtn.closest('.track-item');
if (trackItem && !trackItem.dataset.queueIndex) {
- contextTrack = trackDataStore.get(trackItem);
+ const clickedTrack = trackDataStore.get(trackItem);
+
+ if (contextMenu.style.display === 'block' && contextTrack && clickedTrack && contextTrack.id === clickedTrack.id) {
+ contextMenu.style.display = 'none';
+ return;
+ }
+
+ contextTrack = clickedTrack;
if (contextTrack) {
await updateContextMenuLikeState(contextMenu, contextTrack);
const rect = menuBtn.getBoundingClientRect();
diff --git a/js/ui.js b/js/ui.js
index 55ab12f..e58afd5 100644
--- a/js/ui.js
+++ b/js/ui.js
@@ -454,31 +454,16 @@ export class UIRenderer {
async renderLibraryPage() {
this.showPage('library');
- const playlistsContainer = document.getElementById('library-playlists-container');
const tracksContainer = document.getElementById('library-tracks-container');
const albumsContainer = document.getElementById('library-albums-container');
const artistsContainer = document.getElementById('library-artists-container');
-
- // Render Favorites
- const likedPlaylists = await db.getFavorites('playlist');
- if (likedPlaylists.length) {
- playlistsContainer.innerHTML = likedPlaylists.map(p => this.createPlaylistCardHTML(p)).join('');
- likedPlaylists.forEach(playlist => {
- const el = playlistsContainer.querySelector(`[data-playlist-id="${playlist.uuid}"]`);
- if (el) {
- trackDataStore.set(el, playlist);
- this.updateLikeState(el, 'playlist', playlist.uuid);
- }
- });
- } else {
- playlistsContainer.innerHTML = createPlaceholder('No liked playlists yet.');
- }
+ const playlistsContainer = document.getElementById('library-playlists-container');
const likedTracks = await db.getFavorites('track');
if (likedTracks.length) {
this.renderListWithTracks(tracksContainer, likedTracks, true);
} else {
- tracksContainer.innerHTML = createPlaceholder('No liked songs yet.');
+ tracksContainer.innerHTML = createPlaceholder('No liked tracks yet.');
}
const likedAlbums = await db.getFavorites('album');
@@ -508,6 +493,20 @@ export class UIRenderer {
} else {
artistsContainer.innerHTML = createPlaceholder('No liked artists yet.');
}
+
+ const likedPlaylists = await db.getFavorites('playlist');
+ if (likedPlaylists.length) {
+ playlistsContainer.innerHTML = likedPlaylists.map(p => this.createPlaylistCardHTML(p)).join('');
+ likedPlaylists.forEach(playlist => {
+ const el = playlistsContainer.querySelector(`[data-playlist-id="${playlist.uuid}"]`);
+ if (el) {
+ trackDataStore.set(el, playlist);
+ this.updateLikeState(el, 'playlist', playlist.uuid);
+ }
+ });
+ } else {
+ playlistsContainer.innerHTML = createPlaceholder('No liked playlists yet.');
+ }
}
async renderHomePage() {
@@ -528,7 +527,7 @@ export class UIRenderer {
}
});
} else {
- albumsContainer.innerHTML = createPlaceholder("You haven't viewed any albums yet. Search for music to get started!");
+ albumsContainer.innerHTML = createPlaceholder("You haven't viewed any albums yet.");
}
if (recents.artists.length) {
@@ -541,7 +540,7 @@ export class UIRenderer {
}
});
} else {
- artistsContainer.innerHTML = createPlaceholder("You haven't viewed any artists yet. Search for music to get started!");
+ artistsContainer.innerHTML = createPlaceholder("You haven't viewed any artists yet.");
}
if (playlistsContainer) {
@@ -555,7 +554,7 @@ export class UIRenderer {
}
});
} else {
- playlistsContainer.innerHTML = createPlaceholder("You haven't viewed any playlists yet. Search for music to get started!");
+ playlistsContainer.innerHTML = createPlaceholder("You haven't viewed any playlists yet.");
}
}
}
diff --git a/styles.css b/styles.css
index a93f018..bb03500 100644
--- a/styles.css
+++ b/styles.css
@@ -530,8 +530,8 @@ body.has-page-background .track-item:hover {
.card-like-btn {
position: absolute;
- top: 0.5rem;
- right: 0.5rem;
+ top: 2%;
+ right: 2%;
background: rgba(0, 0, 0, 0.25) !important;
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
@@ -947,6 +947,20 @@ body.has-page-background .track-item:hover {
flex-shrink: 0;
}
+#like-album-btn,
+#like-playlist-btn,
+#like-artist-btn {
+ width: auto;
+ height: auto;
+ padding: 0.875rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 50%;
+ flex-shrink: 0;
+ aspect-ratio: 1/1;
+}
+
.btn-secondary {
padding: 0.5rem 1rem;
background-color: var(--secondary);
@@ -1379,6 +1393,7 @@ input:checked + .slider::before {
filter: var(--cover-filter);
z-index: -1;
background-image: var(--bg-image);
+ transition: background-image var(--transition);
}
.fullscreen-cover-content {
@@ -2296,13 +2311,33 @@ input:checked + .slider::before {
.detail-header-actions {
width: auto;
margin-top: 0.5em;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
}
.detail-header-actions .btn-primary {
- width: auto;
- padding: 0.5rem;
+ width: 40px;
+ height: 40px;
+ padding: 0;
border-radius: 50%;
- aspect-ratio: 1/1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ }
+
+ #like-album-btn,
+ #like-playlist-btn,
+ #like-artist-btn {
+ width: 40px;
+ height: 40px;
+ padding: 0;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
}
.detail-header-actions .btn-primary span {
@@ -2669,6 +2704,11 @@ input:checked + .slider::before {
}
@media (hover: none) and (pointer: coarse) {
+ .main-content {
+ padding: var(--spacing-sm);
+ grid-area: main;
+ }
+
.progress-bar,
.volume-bar {
height: 8px;
@@ -2688,20 +2728,23 @@ input:checked + .slider::before {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
- .track-item,
- .queue-track-item {
- padding: var(--spacing-md) var(--spacing-sm);
+ button {
+ min-height: 36px;
+ min-width: 36px;
}
- button {
- min-height: 44px;
- min-width: 44px;
+ .track-item {
+ grid-template-columns: 32px 1fr 40px 36px;
}
.player-controls .buttons button {
min-height: 36px;
min-width: 36px;
}
+
+ .card-like-btn {
+ opacity: 1;
+ }
}
@supports (padding-top: env(safe-area-inset-top)) {
@@ -3161,3 +3204,4 @@ input:checked + .slider::before {
padding-top: max(1.5rem, env(titlebar-area-height, 0));
}
}
+