diff --git a/index.html b/index.html
index c2feb8b..43005d4 100644
--- a/index.html
+++ b/index.html
@@ -45,6 +45,7 @@
+
+
+ Show Editor's Picks
+ Display curated album selections on the home page
+
+
+
diff --git a/js/events.js b/js/events.js
index 9d46e89..cbccdee 100644
--- a/js/events.js
+++ b/js/events.js
@@ -1067,6 +1067,15 @@ export async function handleTrackAction(
navigator.clipboard.writeText(url).then(() => {
showNotification('Link copied to clipboard!');
});
+ } else if (action === 'open-in-new-tab') {
+ // Use stored href from card if available, otherwise construct URL
+ const contextMenu = document.getElementById('context-menu');
+ const storedHref = contextMenu?._contextHref;
+ const url = storedHref
+ ? `${window.location.origin}${storedHref}`
+ : `${window.location.origin}/track/${item.id || item.uuid}`;
+
+ window.open(url, '_blank');
} else if (action === 'track-info') {
// Show detailed track info modal
const isTracker = item.isTracker;
diff --git a/js/settings.js b/js/settings.js
index 7007a55..8f37ad2 100644
--- a/js/settings.js
+++ b/js/settings.js
@@ -919,6 +919,14 @@ export function initializeSettings(scrobbler, player, api, ui) {
});
}
+ const showEditorsPicksToggle = document.getElementById('show-editors-picks-toggle');
+ if (showEditorsPicksToggle) {
+ showEditorsPicksToggle.checked = homePageSettings.shouldShowEditorsPicks();
+ showEditorsPicksToggle.addEventListener('change', (e) => {
+ homePageSettings.setShowEditorsPicks(e.target.checked);
+ });
+ }
+
// Sidebar Section Toggles
const sidebarShowHomeToggle = document.getElementById('sidebar-show-home-toggle');
if (sidebarShowHomeToggle) {
diff --git a/js/storage.js b/js/storage.js
index 22fd1e7..6c85fd8 100644
--- a/js/storage.js
+++ b/js/storage.js
@@ -969,6 +969,21 @@ export const homePageSettings = {
setShowJumpBackIn(enabled) {
localStorage.setItem(this.SHOW_JUMP_BACK_IN_KEY, enabled ? 'true' : 'false');
},
+
+ SHOW_EDITORS_PICKS_KEY: 'home-show-editors-picks',
+
+ shouldShowEditorsPicks() {
+ try {
+ const val = localStorage.getItem(this.SHOW_EDITORS_PICKS_KEY);
+ return val === null ? true : val === 'true';
+ } catch {
+ return true;
+ }
+ },
+
+ setShowEditorsPicks(enabled) {
+ localStorage.setItem(this.SHOW_EDITORS_PICKS_KEY, enabled ? 'true' : 'false');
+ },
};
export const sidebarSectionSettings = {
diff --git a/js/ui.js b/js/ui.js
index 3759815..02134e1 100644
--- a/js/ui.js
+++ b/js/ui.js
@@ -1414,6 +1414,7 @@ export class UIRenderer {
this.renderHomeSongs();
this.renderHomeAlbums();
+ this.renderHomeEditorsPicks();
this.renderHomeArtists();
this.renderHomeRecent();
}
@@ -1518,6 +1519,63 @@ export class UIRenderer {
}
}
+ async renderHomeEditorsPicks(forceRefresh = false) {
+ const picksContainer = document.getElementById('home-editors-picks');
+ const section = document.getElementById('home-editors-picks-section');
+
+ if (!homePageSettings.shouldShowEditorsPicks()) {
+ if (section) section.style.display = 'none';
+ return;
+ }
+
+ if (section) section.style.display = '';
+
+ if (picksContainer) {
+ if (forceRefresh) picksContainer.innerHTML = this.createSkeletonCards(6);
+ else if (picksContainer.children.length > 0 && !picksContainer.querySelector('.skeleton')) return;
+
+ try {
+ const response = await fetch('/editors-picks.json');
+ if (!response.ok) throw new Error("Failed to load editor's picks");
+
+ const data = await response.json();
+ const albumIds = data.albums || [];
+
+ if (albumIds.length === 0) {
+ picksContainer.innerHTML = createPlaceholder("No editor's picks available.");
+ return;
+ }
+
+ // Fetch album details
+ const albums = [];
+ for (const albumId of albumIds.slice(0, 12)) {
+ try {
+ const result = await this.api.getAlbum(albumId);
+ if (result && result.album) albums.push(result.album);
+ } catch (e) {
+ console.warn(`Failed to load album ${albumId}:`, e);
+ }
+ }
+
+ if (albums.length > 0) {
+ picksContainer.innerHTML = albums.map((a) => this.createAlbumCardHTML(a)).join('');
+ albums.forEach((a) => {
+ const el = picksContainer.querySelector(`[data-album-id="${a.id}"]`);
+ if (el) {
+ trackDataStore.set(el, a);
+ this.updateLikeState(el, 'album', a.id);
+ }
+ });
+ } else {
+ picksContainer.innerHTML = createPlaceholder("No editor's picks available.");
+ }
+ } catch (e) {
+ console.error("Failed to load editor's picks:", e);
+ picksContainer.innerHTML = createPlaceholder("Failed to load editor's picks.");
+ }
+ }
+ }
+
async renderHomeArtists(forceRefresh = false) {
const artistsContainer = document.getElementById('home-recommended-artists');
const section = artistsContainer?.closest('.content-section');
diff --git a/public/editors-picks.json b/public/editors-picks.json
new file mode 100644
index 0000000..1622ae3
--- /dev/null
+++ b/public/editors-picks.json
@@ -0,0 +1,5 @@
+{
+ "name": "Editor's Picks",
+ "description": "Curated selection of standout albums handpicked by the monochrome team",
+ "albums": ["4527433", "90502209"]
+}
diff --git a/todo.md b/todo.md
index 2a72170..97d3c92 100644
--- a/todo.md
+++ b/todo.md
@@ -2,8 +2,6 @@
Sorted by ease of implementation (easiest to hardest):
-- [ ] Editor's Picks: Create a JSON file of curated album IDs with metadata for the home screen. Include an option to disable in settings to avoid extra API calls.
-
- [ ] Update notifications: Add ability to show the update popup in settings, with an option to automatically update (enabled by default)
- [ ] Volume curve option: Add setting to switch between exponential and linear volume scaling
- [ ] Custom fonts: Allow users to change fonts in settings or add custom fonts from Google Fonts or direct links