<!-- AUTO-GENERATED FILE: DO NOT EDIT DIRECTLY -->
<!-- Generated by generate_collection_web_navigator_tool.py on 2025-07-15T10:28:00 -->
<!-- To regenerate, run the MCP tool. Customize theme via _index.css. -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Music Collection Navigator</title>
<link rel="stylesheet" href="_working_index.css">
<style id="fallback-style">
/* Minimal fallback styles for layout and readability */
body { font-family: sans-serif; background: #181818; color: #f8f8f8; margin: 0; }
header, footer { background: #222; padding: 1em; text-align: center; }
aside { background: #232323; padding: 1em; width: 220px; float: left; height: 100vh; overflow-y: auto; }
main { margin-left: 220px; padding: 2em; min-height: 80vh; }
@media (max-width: 700px) {
aside { width: 100%; float: none; height: auto; }
main { margin-left: 0; }
}
</style>
</head>
<body>
<header>
<h1 id="collection-title">Music Collection</h1>
<div id="collection-stats"></div>
<button id="theme-switcher" style="display:none">Switch Theme</button>
</header>
<aside>
<input id="band-search" type="text" placeholder="Search bands..." style="width:100%;margin-bottom:1em;">
<nav id="band-list"></nav>
</aside>
<main id="main-content">
<div id="loading">Loading collection...</div>
</main>
<footer>
<span id="version-info"></span> | <a href="#" id="docs-link">Documentation</a>
</footer>
<script>
// JS logic for dynamic data loading, rendering, navigation
const COLLECTION_INDEX = '.collection_index.json';
const BAND_METADATA_SUFFIX = '.band_metadata.json';
const CSS_PATH = '_index.css';
let state = {
currentView: 'overview',
selectedBand: null,
selectedAlbum: null,
searchQuery: '',
filters: {},
collection: null,
bandData: null
};
function showLoading(msg) {
document.getElementById('main-content').innerHTML = `<div id="loading">${msg}</div>`;
}
function showError(msg) {
document.getElementById('main-content').innerHTML = `<div id="error">${msg}</div>`;
}
function fetchJSON(path, cb) {
// Try fetch, fallback to XMLHttpRequest for file://
fetch(path).then(r => {
if (!r.ok) throw new Error('HTTP ' + r.status);
return r.json();
}).then(cb).catch(() => {
// Fallback for file://
try {
const xhr = new XMLHttpRequest();
xhr.open('GET', path, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 0) {
try { cb(JSON.parse(xhr.responseText)); } catch (e) { showError('Corrupt JSON: ' + path); }
} else {
showError('Failed to load: ' + path);
}
}
};
xhr.send();
} catch (e) { showError('Cannot load: ' + path); }
});
}
function renderOverview() {
const c = state.collection;
if (!c) return;
document.getElementById('main-content').innerHTML = `
<h2>Collection Overview</h2>
<div>Bands: ${c.total_bands} | Albums: ${c.total_albums} | Completion: ${c.completion_percentage || '?' }%</div>
<div>Genres: ${Object.keys(c.genre_distribution || {}).join(', ')}</div>
`;
}
function renderBandList() {
const nav = document.getElementById('band-list');
if (!state.collection) return;
let bands = state.collection.bands || [];
if (state.searchQuery) {
const q = state.searchQuery.toLowerCase();
bands = bands.filter(b => b.name.toLowerCase().includes(q));
}
nav.innerHTML = bands.map(b => `<div class="band-link" tabindex="0" data-band="${b.name}">${b.name}</div>`).join('') || '<div>No bands found.</div>';
// Add click listeners
nav.querySelectorAll('.band-link').forEach(el => {
el.onclick = () => selectBand(el.dataset.band);
el.onkeydown = e => { if (e.key === 'Enter') selectBand(el.dataset.band); };
});
}
function renderBandDetails() {
const b = state.bandData;
if (!b) return;
document.getElementById('main-content').innerHTML = `
<h2>${b.band_name}</h2>
<div>Formed: ${b.formed || '?' }</div>
<div>Genres: ${(b.genres || []).join(', ')}</div>
<div>Origin: ${b.origin || ''}</div>
<div>Members: ${b.members || ''}</div>
<div>Description: ${b.description}</div>
<div>Albums Count: ${b.albums_count}</div>
<h3>Albums</h3>
<ul>${(b.albums || []).map(a => `<li>${a.year || ''} - ${a.album_name} <span style="font-size:smaller">(${a.type || ''}${a.edition ? ', ' + a.edition : ''})</span></li>`).join('')}</ul>
<h3>Missing Albums</h3>
<ul>${(b.albums_missing || []).map(a => `<li>${a.year || ''} - ${a.album_name} <span style="font-size:smaller">(${a.type || ''}${a.edition ? ', ' + a.edition : ''})</span></li>`).join('')}</ul>
<h3>Analysis</h3>
<div>${b.analyze ? b.analyze.review || '' : ''}</div>
<div>Rating: ${b.analyze ? b.analyze.rate || '' : ''}</div>
<h4>Similar Bands in Collection</h4>
<ul>${(b.analyze && b.analyze.similar_bands || []).map(s => `<li>${s}</li>`).join('')}</ul>
<h4>Similar Bands Not in Collection</h4>
<ul>${(b.analyze && b.analyze.similar_bands_missing || []).map(s => `<li>${s}</li>`).join('')}</ul>
`;
}
function selectBand(bandName) {
state.currentView = 'band';
state.selectedBand = bandName;
state.selectedAlbum = null;
showLoading('Loading band...');
fetchJSON(bandName + '/' + BAND_METADATA_SUFFIX, data => {
state.bandData = data;
renderBandDetails();
window.location.hash = 'band=' + encodeURIComponent(bandName);
});
}
function handleHashChange() {
const hash = window.location.hash.replace(/^#/, '');
if (!hash) {
state.currentView = 'overview';
state.selectedBand = null;
state.selectedAlbum = null;
renderOverview();
return;
}
const params = Object.fromEntries(hash.split('&').map(s => s.split('=')));
if (params.band) {
selectBand(decodeURIComponent(params.band));
} else {
state.currentView = 'overview';
renderOverview();
}
}
document.getElementById('band-search').oninput = function(e) {
state.searchQuery = e.target.value;
renderBandList();
};
document.getElementById('docs-link').onclick = function() {
alert('See README.md for documentation.');
};
window.onhashchange = handleHashChange;
// Initial load
showLoading('Loading collection...');
fetchJSON(COLLECTION_INDEX, data => {
state.collection = data;
document.getElementById('collection-title').textContent = data.title || 'Music Collection';
document.getElementById('collection-stats').textContent = `Bands: ${data.total_bands} | Albums: ${data.total_albums}`;
document.getElementById('version-info').textContent = 'Version: ' + (data.version || 'N/A');
renderBandList();
handleHashChange();
});
</script>
</body>
</html>