-
Editor's Picks
+
+
+
Welcome to Monochrome
+
+ You haven't listened to anything yet. Search for your favorite songs to get started!
+
-
-
-
-
-
-
Recommended Songs
-
-
-
-
-
-
-
Recommended Albums
-
-
-
-
-
-
-
Recommended Artists
-
-
-
-
-
-
-
Jump Back In
-
-
-
-
-
+
+
+
+
+
+
Recommended Songs
+
+
+
+
+
+
+
Recommended Albums
+
+
+
+
+
+
+
Recommended Artists
+
+
+
+
+
+
+
Jump Back In
+
+
+
+
+
+
+
+
+
diff --git a/js/ui.js b/js/ui.js
index 269dab4..8473d9a 100644
--- a/js/ui.js
+++ b/js/ui.js
@@ -1629,6 +1629,7 @@ export class UIRenderer {
try {
this.showPage('home');
+ this.setupHomeTabs();
const welcomeEl = document.getElementById('home-welcome');
const contentEl = document.getElementById('home-content');
@@ -1698,6 +1699,234 @@ export class UIRenderer {
}
}
+ setupHomeTabs() {
+ const tabs = document.querySelectorAll('.home-tab');
+ if (tabs.length === 0) return;
+
+ if (tabs[0].dataset.initialized) return;
+
+ tabs.forEach((tab) => {
+ tab.dataset.initialized = 'true';
+ tab.addEventListener('click', () => {
+ document.querySelectorAll('.home-tab').forEach((t) => t.classList.remove('active'));
+ document.querySelectorAll('.home-view').forEach((v) => {
+ v.style.display = 'none';
+ v.classList.remove('active');
+ });
+
+ tab.classList.add('active');
+ const viewId = `home-view-${tab.dataset.tab}`;
+ const view = document.getElementById(viewId);
+ if (view) {
+ view.style.display = 'block';
+ view.classList.add('active');
+ }
+
+ if (tab.dataset.tab === 'explore') {
+ this.renderExplorePage();
+ }
+ });
+ });
+ }
+
+ async renderExplorePage() {
+ const container = document.getElementById('explore-grid');
+ if (!container) return;
+
+ if (container.children.length > 0) return;
+
+ container.classList.remove('card-grid');
+
+ container.innerHTML = `
${this.createSkeletonCards(12)}
`;
+
+ try {
+ const response = await fetch('https://hot.monochrome.tf/');
+ if (!response.ok) throw new Error('Failed to load explore data');
+ const data = await response.json();
+
+ container.innerHTML = '';
+
+ const GENRES = [
+ { id: 'hip_hop', name: 'Hip Hop / Rap' },
+ { id: 'pop', name: 'Pop' },
+ { id: 'rock', name: 'Rock' },
+ { id: 'electronic', name: 'Electronic' },
+ { id: 'country', name: 'Country' },
+ { id: 'jazz', name: 'Jazz' },
+ { id: 'classical', name: 'Classical' },
+ { id: 'latin', name: 'Latin' },
+ { id: 'reggae', name: 'Reggae / Dancehall' },
+ { id: 'blues', name: 'Blues' },
+ { id: 'soundtrack', name: 'Soundtrack' },
+ { id: 'alternative', name: 'Alternative' },
+ ];
+
+ if (GENRES.length > 0) {
+ const genresSection = document.createElement('section');
+ genresSection.className = 'content-section';
+ genresSection.innerHTML = `
Genres
`;
+
+ const genresGrid = document.createElement('div');
+ genresGrid.className = 'card-grid';
+ genresGrid.innerHTML = GENRES
+ .map(
+ (genre) => `
+
+
${escapeHtml(genre.name)}
+
+ `
+ )
+ .join('');
+
+ genresSection.appendChild(genresGrid);
+ container.appendChild(genresSection);
+
+ genresGrid.querySelectorAll('.genre-card').forEach((card) => {
+ card.addEventListener('click', () => {
+ this.renderGenrePage(card.dataset.genreId, card.dataset.genreName);
+ });
+ });
+ }
+
+ if (data.top_albums && data.top_albums.length > 0) {
+ this.renderExploreSection(container, 'Trending Albums', data.top_albums, 'album');
+ }
+
+ if (data.top_tracks && data.top_tracks.length > 0) {
+ this.renderExploreSection(container, 'Trending Tracks', data.top_tracks, 'track');
+ }
+
+ if (data.featured_playlists && data.featured_playlists.length > 0) {
+ this.renderExploreSection(container, 'Featured Playlists', data.featured_playlists, 'playlist');
+ }
+
+ if (data.sections && data.sections.length > 0) {
+ data.sections.forEach((section) => {
+ if (section.items && section.items.length > 0) {
+ let type = null;
+ if (section.type === 'ALBUM_LIST') type = 'album';
+ else if (section.type === 'TRACK_LIST') type = 'track';
+ else if (section.type === 'PLAYLIST_LIST') type = 'playlist';
+
+ if (type) {
+ this.renderExploreSection(container, section.title, section.items, type);
+ }
+ }
+ });
+ }
+
+ if (container.children.length === 0) {
+ container.innerHTML = createPlaceholder('No explore content available.');
+ }
+ } catch (e) {
+ console.error(e);
+ container.innerHTML = createPlaceholder('Failed to load explore content.');
+ }
+ }
+
+ renderExploreSection(container, title, items, type) {
+ const section = document.createElement('section');
+ section.className = 'content-section';
+ section.innerHTML = `
${title}
`;
+
+ if (type === 'track') {
+ const list = document.createElement('div');
+ list.className = 'track-list';
+ this.renderListWithTracks(list, items, true);
+ section.appendChild(list);
+ } else {
+ const grid = document.createElement('div');
+ grid.className = 'card-grid';
+ grid.innerHTML = items
+ .map((item) => {
+ if (type === 'album') return this.createAlbumCardHTML(item);
+ if (type === 'playlist') return this.createPlaylistCardHTML(item);
+ return '';
+ })
+ .join('');
+
+ items.forEach((item) => {
+ let selector;
+ if (type === 'album') selector = `[data-album-id="${item.id}"]`;
+ if (type === 'playlist') selector = `[data-playlist-id="${item.uuid}"]`;
+
+ if (selector) {
+ const el = grid.querySelector(selector);
+ if (el) {
+ trackDataStore.set(el, item);
+ if (type === 'album') this.updateLikeState(el, 'album', item.id);
+ if (type === 'playlist') this.updateLikeState(el, 'playlist', item.uuid);
+ }
+ }
+ });
+ section.appendChild(grid);
+ }
+ container.appendChild(section);
+ }
+
+ async renderGenrePage(genreId, genreName) {
+ const container = document.getElementById('explore-grid');
+ if (!container) return;
+
+ container.classList.remove('card-grid');
+
+ container.innerHTML = `
+
+
+
${escapeHtml(genreName)}
+
+
${this.createSkeletonCards(12)}
+ `;
+
+ container.querySelector('.explore-back-btn').addEventListener('click', () => {
+ container.innerHTML = '';
+ this.renderExplorePage();
+ });
+
+ try {
+ const response = await fetch(`https://hot.monochrome.tf/explore/genre/?id=${genreId}`);
+ if (!response.ok) throw new Error('Failed to load genre data');
+ const data = await response.json();
+
+ const header = container.firstElementChild;
+ container.innerHTML = '';
+ container.appendChild(header);
+
+ const contentContainer = document.createElement('div');
+ container.appendChild(contentContainer);
+
+ if (data.sections && data.sections.length > 0) {
+ data.sections.forEach((section) => {
+ if (section.items && section.items.length > 0) {
+ let type = null;
+ if (section.type === 'ALBUM_LIST') type = 'album';
+ else if (section.type === 'TRACK_LIST') type = 'track';
+ else if (section.type === 'PLAYLIST_LIST') type = 'playlist';
+
+ if (type) {
+ this.renderExploreSection(contentContainer, section.title, section.items, type);
+ }
+ }
+ });
+ }
+
+ if (contentContainer.children.length === 0) {
+ contentContainer.innerHTML = createPlaceholder('No content found for this genre.');
+ }
+ } catch (e) {
+ console.error(e);
+ const header = container.firstElementChild;
+ container.innerHTML = '';
+ container.appendChild(header);
+ const errorDiv = document.createElement('div');
+ errorDiv.innerHTML = createPlaceholder('Failed to load genre content.');
+ container.appendChild(errorDiv);
+ }
+ }
+
async getSeeds() {
const history = await db.getHistory();
const favorites = await db.getFavorites('track');
diff --git a/styles.css b/styles.css
index 5540c85..cc6ad72 100644
--- a/styles.css
+++ b/styles.css
@@ -7835,3 +7835,35 @@ textarea:focus {
display: flex;
flex-direction: column;
}
+
+.home-header-tabs {
+ display: flex;
+ gap: 1.5rem;
+ margin-bottom: 1.5rem;
+ border-bottom: 1px solid var(--border);
+}
+
+.home-tab {
+ background: transparent;
+ border: none;
+ color: var(--muted-foreground);
+ padding: 0.75rem 0.5rem;
+ cursor: pointer;
+ font-size: 1.1rem;
+ font-weight: 600;
+ border-bottom: 2px solid transparent;
+ transition: all var(--transition);
+}
+
+.home-tab:hover {
+ color: var(--foreground);
+}
+
+.home-tab.active {
+ color: var(--foreground);
+ border-bottom-color: var(--highlight);
+}
+
+.home-view {
+ animation: fade-in 0.3s ease;
+}