diff --git a/themes/one-pager/assets/css/style.css b/themes/one-pager/assets/css/style.css
index ad243d7..fda17de 100644
--- a/themes/one-pager/assets/css/style.css
+++ b/themes/one-pager/assets/css/style.css
@@ -2724,3 +2724,107 @@ button {
font-size: 0.85rem;
}
}
+/* ============================================
+ * NAV STRIP (sub-page header)
+ * ============================================ */
+
+.nav-strip {
+ position: sticky;
+ top: 0;
+ z-index: 998;
+ background: rgba(0, 0, 0, 0.55);
+ backdrop-filter: blur(20px);
+ -webkit-backdrop-filter: blur(20px);
+ border-bottom: 3px solid var(--accent-yellow);
+ padding: 0.6rem 2rem;
+}
+
+.nav-strip-inner {
+ max-width: 1200px;
+ margin: 0 auto;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1.5rem;
+ flex-wrap: wrap;
+}
+
+.nav-strip-brand {
+ display: flex;
+ align-items: center;
+ gap: 0.8rem;
+ text-decoration: none;
+ color: var(--text-primary);
+ transition: all 0.3s ease;
+}
+
+.nav-strip-brand:hover {
+ color: var(--accent-yellow);
+}
+
+.nav-strip-avatar {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ object-fit: cover;
+ border: 2px solid var(--accent-yellow);
+ filter: grayscale(100%);
+ transition: filter 0.3s ease;
+}
+
+.nav-strip-brand:hover .nav-strip-avatar {
+ filter: grayscale(0%);
+}
+
+.nav-strip-name {
+ font-family: 'Poppins', sans-serif;
+ font-weight: 700;
+ font-size: 0.9rem;
+ letter-spacing: -0.5px;
+ text-transform: uppercase;
+}
+
+.nav-strip-links {
+ display: flex;
+ gap: 0.6rem;
+ flex-wrap: wrap;
+}
+
+.nav-strip-links a {
+ color: var(--text-primary);
+ text-decoration: none;
+ font-family: 'Poppins', sans-serif;
+ font-weight: 600;
+ font-size: 0.7rem;
+ letter-spacing: 2px;
+ text-transform: uppercase;
+ padding: 0.4rem 0.9rem;
+ border: 2px solid transparent;
+ border-radius: 6px;
+ transition: all 0.3s ease;
+ background: rgba(0, 0, 0, 0.3);
+}
+
+.nav-strip-links a:hover {
+ color: var(--accent-yellow);
+ border-color: var(--accent-yellow);
+ transform: scale(1.05);
+}
+
+/* Responsive */
+@media (max-width: 640px) {
+ .nav-strip {
+ padding: 0.5rem 1rem;
+ }
+ .nav-strip-inner {
+ gap: 0.6rem;
+ }
+ .nav-strip-name {
+ display: none;
+ }
+ .nav-strip-links a {
+ font-size: 0.6rem;
+ padding: 0.3rem 0.6rem;
+ letter-spacing: 1px;
+ }
+}
diff --git a/themes/one-pager/assets/js/scripts.js b/themes/one-pager/assets/js/scripts.js
index 4c36a1c..90960cc 100644
--- a/themes/one-pager/assets/js/scripts.js
+++ b/themes/one-pager/assets/js/scripts.js
@@ -2,7 +2,7 @@
// 1. UTILITY FUNCTIONS
// ============================================
-// Function to shuffle array (Fisher-Yates)
+// Fisher-Yates shuffle
function shuffleArray(array) {
const shuffled = [...array];
for (let i = shuffled.length - 1; i > 0; i--) {
@@ -12,27 +12,48 @@ function shuffleArray(array) {
return shuffled;
}
-// Artist filter list
+// Artist whitelist — only tracks by these artists are shown
const allowedArtists = [
'徒 Setto セット',
'Settoshi Tonami',
'Sethy Bowoy',
'reSet Sakrecoer',
- 'Sakrecoer'
+ 'Sakrecoer',
'Vigesimal'
];
-// Function to check if a track is by an allowed artist
function isAllowedArtist(artist) {
if (!artist) return false;
const normalizedArtist = artist.trim().toLowerCase();
return allowedArtists.some(allowed => {
const normalizedAllowed = allowed.trim().toLowerCase();
return normalizedArtist.includes(normalizedAllowed) ||
- normalizedAllowed.includes(normalizedArtist);
+ normalizedAllowed.includes(normalizedArtist);
});
}
+// Find a track's raw Nostr event by its audio URL — used to look up `content` (lyrics/info)
+// Checks homepage data first, then falls back to playlist-page data.
+function findRawTrack(url) {
+ if (!url) return null;
+
+ const fromHome = (window.allMusicData || []).find(t => {
+ if (!t.tags) return false;
+ return t.tags.some(tag => tag[0] === 'url' && tag[1] === url);
+ });
+ if (fromHome) return fromHome;
+
+ const fromPlaylist = (window.playlistOriginalTracks || []).find(t => t.url === url);
+ return fromPlaylist || null;
+}
+
+function formatTime(seconds) {
+ if (!seconds || isNaN(seconds)) return '0:00';
+ const mins = Math.floor(seconds / 60);
+ const secs = Math.floor(seconds % 60);
+ return `${mins}:${secs.toString().padStart(2, '0')}`;
+}
+
// ============================================
// 2. PHOTO LIGHTBOX
// ============================================
@@ -65,7 +86,6 @@ function openPhotoLightbox(index) {
});
if (dateStr) metaText += `📅 ${dateStr}`;
-
lightboxMeta.textContent = metaText || '📅 No date available';
lightboxCounter.textContent = `${currentPhotoIndex + 1} / ${allPicturesArray.length}`;
@@ -92,13 +112,12 @@ function nextPhoto() {
function initPhotoLightbox() {
const lightbox = document.getElementById('customLightbox');
+ if (!lightbox) return;
+
const lightboxClose = document.getElementById('lightboxClose');
const lightboxPrev = document.getElementById('lightboxPrev');
const lightboxNext = document.getElementById('lightboxNext');
- if (!lightbox) return;
-
- // Remove old listeners by cloning
const newClose = lightboxClose.cloneNode(true);
const newPrev = lightboxPrev.cloneNode(true);
const newNext = lightboxNext.cloneNode(true);
@@ -107,29 +126,23 @@ function initPhotoLightbox() {
lightboxPrev.parentNode.replaceChild(newPrev, lightboxPrev);
lightboxNext.parentNode.replaceChild(newNext, lightboxNext);
- // Add new listeners
newClose.addEventListener('click', closePhotoLightbox);
newPrev.addEventListener('click', prevPhoto);
newNext.addEventListener('click', nextPhoto);
- // Click outside to close
lightbox.removeEventListener('click', handlePhotoClick);
lightbox.addEventListener('click', handlePhotoClick);
- // Keyboard controls
document.removeEventListener('keydown', handlePhotoKeydown);
document.addEventListener('keydown', handlePhotoKeydown);
- // Setup photo click listeners
document.querySelectorAll('.photo-item img').forEach((img) => {
img.removeEventListener('click', img._photoClick);
img._photoClick = function(e) {
e.preventDefault();
const url = this.src;
const fullIndex = allPicturesArray.findIndex(p => p.url === url);
- if (fullIndex !== -1) {
- openPhotoLightbox(fullIndex);
- }
+ if (fullIndex !== -1) openPhotoLightbox(fullIndex);
};
img.addEventListener('click', img._photoClick);
});
@@ -139,16 +152,9 @@ function handlePhotoKeydown(e) {
const lightbox = document.getElementById('customLightbox');
if (!lightbox || !lightbox.classList.contains('active')) return;
- if (e.key === 'Escape') {
- e.preventDefault();
- closePhotoLightbox();
- } else if (e.key === 'ArrowLeft') {
- e.preventDefault();
- prevPhoto();
- } else if (e.key === 'ArrowRight') {
- e.preventDefault();
- nextPhoto();
- }
+ if (e.key === 'Escape') { e.preventDefault(); closePhotoLightbox(); }
+ else if (e.key === 'ArrowLeft') { e.preventDefault(); prevPhoto(); }
+ else if (e.key === 'ArrowRight') { e.preventDefault(); nextPhoto(); }
}
function handlePhotoClick(e) {
@@ -157,7 +163,7 @@ function handlePhotoClick(e) {
}
// ============================================
-// 3. MUSIC RENDERER
+// 3. MUSIC RENDERER (homepage cards)
// ============================================
function renderMusicTracks(tracks, containerId, randomize = false) {
@@ -167,9 +173,7 @@ function renderMusicTracks(tracks, containerId, randomize = false) {
const filteredTracks = tracks.filter(track => {
let artist = '';
if (track.tags) {
- track.tags.forEach(tag => {
- if (tag[0] === 'artist') artist = tag[1];
- });
+ track.tags.forEach(tag => { if (tag[0] === 'artist') artist = tag[1]; });
}
return isAllowedArtist(artist);
});
@@ -179,11 +183,10 @@ function renderMusicTracks(tracks, containerId, randomize = false) {
return;
}
- // Build playlist from ALL filtered tracks
+ // Build global playlist from all filtered tracks
window.musicPlaylist = [];
filteredTracks.forEach(track => {
let title = '', url = '', image = '', artist = '', album = '', duration = '';
- // ADD THIS: Store the track ID
const trackId = track.id || '';
if (track.tags) {
@@ -200,9 +203,9 @@ function renderMusicTracks(tracks, containerId, randomize = false) {
}
if (url) {
window.musicPlaylist.push({
- id: trackId, // ADD THIS LINE
+ id: trackId,
title: title || 'Untitled',
- url: url,
+ url,
image: image || '',
artist: artist || 'Unknown Artist',
album: album || '',
@@ -212,16 +215,14 @@ function renderMusicTracks(tracks, containerId, randomize = false) {
}
});
- // Randomize or sort the playlist
if (randomize) {
window.musicPlaylist = shuffleArray(window.musicPlaylist);
} else {
window.musicPlaylist.sort((a, b) => (b.created_at || 0) - (a.created_at || 0));
}
- // Render only 6 cards
+ // Only render the first 6 cards
const displayTracks = window.musicPlaylist.slice(0, 6);
-
container.innerHTML = '';
displayTracks.forEach((track, index) => {
@@ -229,72 +230,50 @@ function renderMusicTracks(tracks, containerId, randomize = false) {
card.className = 'music-card clickable';
card.dataset.playlistIndex = index;
- // Click on card opens lightbox (except when clicking on info button)
card.addEventListener('click', function(e) {
- // Don't trigger if clicking on lyrics-toggle or inside lyrics
- if (e.target.closest('.lyrics-toggle') || e.target.closest('.lyrics')) {
- return;
- }
+ if (e.target.closest('.lyrics-toggle') || e.target.closest('.lyrics')) return;
const idx = parseInt(this.dataset.playlistIndex);
- if (!isNaN(idx) && idx >= 0) {
- openMusicLightbox(idx);
- }
+ if (!isNaN(idx) && idx >= 0) openMusicLightbox(idx);
});
- // Find genre tags from the original track data
+ // Collect genre tags + info from the raw event
let genreTags = [];
let content = '';
- let alt = '';
-
- // Find the original track data to get tags and content
const originalTrack = filteredTracks.find(t => {
- let url = '';
- if (t.tags) {
- t.tags.forEach(tag => {
- if (tag[0] === 'url') url = tag[1];
- });
- }
- return url === track.url;
+ if (!t.tags) return false;
+ return t.tags.some(tag => tag[0] === 'url' && tag[1] === track.url);
});
if (originalTrack && originalTrack.tags) {
originalTrack.tags.forEach(tag => {
- const key = tag[0];
- const value = tag[1];
- if (key === 't') genreTags.push(value);
- if (key === 'alt') alt = value;
+ if (tag[0] === 't') genreTags.push(tag[1]);
});
- content = originalTrack.content || '';
+ content = originalTrack.content || '';
}
- // Build the card HTML
- let cardHTML = `
-
-
- ${track.image ? `

` : ''}
-
- ▶
-
-
-
- ${genreTags.length > 0 ? `
-
- ${genreTags.map(t => `${t}`).join('')}
-
- ` : ''}
- ${content ? `
-
-
${content.replace(/\n/g, '
')}
- ` : ''}
+ card.innerHTML = `
+
+
+ ${track.image ? `

` : ''}
+
▶
- `;
-
- card.innerHTML = cardHTML;
- container.appendChild(card);
+
+ ${genreTags.length > 0 ? `
+
+ ${genreTags.map(t => `${t}`).join('')}
+
+ ` : ''}
+ ${content ? `
+
+
${content.replace(/\n/g, '
')}
+ ` : ''}
+
+ `;
+ container.appendChild(card);
});
initMusicEventListeners();
@@ -308,18 +287,12 @@ function initMusicEventListeners() {
}
function handleLyricsToggle(e) {
- // Stop the click from bubbling up to the card
e.stopPropagation();
-
const lyrics = this.nextElementSibling;
if (lyrics && lyrics.classList.contains('lyrics')) {
- if (lyrics.style.display === 'none') {
- lyrics.style.display = 'block';
- this.innerHTML = '
Hide Info';
- } else {
- lyrics.style.display = 'none';
- this.innerHTML = '
Show Info';
- }
+ const hidden = lyrics.style.display === 'none';
+ lyrics.style.display = hidden ? 'block' : 'none';
+ this.innerHTML = `
${hidden ? 'Hide Info' : 'Show Info'}`;
}
}
@@ -331,21 +304,14 @@ function renderRandomPictures(picturesData, containerId, randomize = false) {
const container = document.getElementById(containerId);
if (!container) return;
- // First, flatten all pictures from all events but keep track of which event they belong to
let allPictures = [];
let allEvents = [];
picturesData.forEach(event => {
- const imetaTags = [];
- event.tags.forEach(tag => {
- if (tag[0] === 'imeta') {
- imetaTags.push(tag);
- }
- });
+ const imetaTags = event.tags.filter(tag => tag[0] === 'imeta');
if (imetaTags.length > 0) {
let firstImage = null;
- let eventImages = [];
imetaTags.forEach(imetaTag => {
let imageUrl = '';
@@ -353,28 +319,20 @@ function renderRandomPictures(picturesData, containerId, randomize = false) {
imetaTag.forEach(item => {
if (typeof item === 'string') {
- if (item.startsWith('url ')) {
- imageUrl = item.replace('url ', '').trim();
- }
- if (item.startsWith('alt ')) {
- alt = item.replace('alt ', '').trim();
- }
+ if (item.startsWith('url ')) imageUrl = item.replace('url ', '').trim();
+ if (item.startsWith('alt ')) alt = item.replace('alt ', '').trim();
}
});
if (imageUrl) {
const pictureData = {
- url: imageUrl,
- alt: alt,
+ url: imageUrl, alt,
created_at: event.created_at,
pubkey: event.pubkey,
event_id: event.id
};
- eventImages.push(pictureData);
allPictures.push(pictureData);
- if (!firstImage) {
- firstImage = pictureData;
- }
+ if (!firstImage) firstImage = pictureData;
}
});
@@ -382,13 +340,11 @@ function renderRandomPictures(picturesData, containerId, randomize = false) {
allEvents.push({
event_id: event.id,
created_at: event.created_at,
- firstImage: firstImage
+ firstImage
});
}
} else {
- let imageUrl = '';
- let alt = event.content || '';
-
+ let imageUrl = '', alt = event.content || '';
event.tags.forEach(tag => {
if (tag[0] === 'imeta') {
const imetaStr = tag[1];
@@ -398,11 +354,9 @@ function renderRandomPictures(picturesData, containerId, randomize = false) {
if (altMatch) alt = altMatch[1];
}
});
-
if (imageUrl) {
const pictureData = {
- url: imageUrl,
- alt: alt,
+ url: imageUrl, alt,
created_at: event.created_at,
pubkey: event.pubkey,
event_id: event.id
@@ -417,24 +371,16 @@ function renderRandomPictures(picturesData, containerId, randomize = false) {
}
});
- // Store ALL pictures in the global array for the lightbox
allPicturesArray = allPictures;
- // Sort events by created_at (newest first)
const sortedEvents = [...allEvents].sort((a, b) => b.created_at - a.created_at);
-
- // Select which events to display (6 max)
- let displayEvents;
- if (randomize) {
- const shuffled = shuffleArray([...sortedEvents]);
- displayEvents = shuffled.slice(0, 6);
- } else {
- displayEvents = sortedEvents.slice(0, 6);
- }
+ const displayEvents = randomize
+ ? shuffleArray([...sortedEvents]).slice(0, 6)
+ : sortedEvents.slice(0, 6);
container.innerHTML = '';
- displayEvents.forEach((event, index) => {
+ displayEvents.forEach(event => {
const picture = event.firstImage;
if (!picture) return;
@@ -442,8 +388,6 @@ function renderRandomPictures(picturesData, containerId, randomize = false) {
item.className = 'photo-item';
item.dataset.image = picture.url;
item.dataset.alt = picture.alt;
- item.dataset.index = index;
- item.dataset.eventId = event.event_id;
const date = new Date(picture.created_at * 1000);
const dateStr = date.toLocaleDateString('en-US', {
@@ -453,14 +397,12 @@ function renderRandomPictures(picturesData, containerId, randomize = false) {
item.dataset.pubkey = picture.pubkey || '';
item.innerHTML = `
- ${picture.url ? `

` : ''}
-
📅 ${dateStr}
+ ${picture.url ? `

` : ''}
+
📅 ${dateStr}
`;
-
container.appendChild(item);
});
- // Initialize photo lightbox
initPhotoLightbox();
}
@@ -472,14 +414,9 @@ function renderRandomVideos(videosData, containerId, randomize = false) {
const container = document.getElementById(containerId);
if (!container) return;
- let selected;
- if (randomize) {
- const shuffled = shuffleArray(videosData);
- selected = shuffled.slice(0, 2);
- } else {
- const sorted = [...videosData].sort((a, b) => b.created_at - a.created_at);
- selected = sorted.slice(0, 2);
- }
+ const selected = randomize
+ ? shuffleArray(videosData).slice(0, 2)
+ : [...videosData].sort((a, b) => b.created_at - a.created_at).slice(0, 2);
container.innerHTML = '';
const posterImage = '/images/videoposter.png';
@@ -514,37 +451,246 @@ function renderRandomVideos(videosData, containerId, randomize = false) {
});
const wrapperStyle = isVertical
- ? 'style="position:relative;padding-bottom:177.78%;height:0;overflow:hidden;background:var(--accent-black);"'
- : '';
+ ? 'style="position:relative;padding-bottom:177.78%;height:0;overflow:hidden;background:var(--accent-black);"'
+ : '';
const videoStyle = isVertical
- ? 'style="position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;"'
- : 'style="width:100%;"';
+ ? 'style="position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;"'
+ : 'style="width:100%;"';
- const html = `
-
- ${videoUrl ? `
-
-
+ container.insertAdjacentHTML('beforeend', `
+
+ ${videoUrl ? `
+
+
+
+ ` : ''}
+
+
${alt}
+
+ 📅 ${dateStr}
+ ${dim ? `📐 ${dim}` : ''}
+
+
- ` : ''}
-
-
${alt}
-
- 📅 ${dateStr}
- ${dim ? `📐 ${dim}` : ''}
-
-
-
- `;
- container.insertAdjacentHTML('beforeend', html);
+ `);
});
}
// ============================================
-// 6. REFRESH FUNCTIONS
+// 6. MUSIC LIGHTBOX
+// ============================================
+
+let currentMusicIndex = 0;
+let musicPlayer = null;
+
+function openMusicLightbox(index) {
+ if (!window.musicPlaylist || window.musicPlaylist.length === 0) return;
+
+ if (musicPlayer) {
+ musicPlayer.pause();
+ musicPlayer.remove();
+ musicPlayer = null;
+ }
+
+ if (index < 0) index = window.musicPlaylist.length - 1;
+ if (index >= window.musicPlaylist.length) index = 0;
+ currentMusicIndex = index;
+
+ const track = window.musicPlaylist[currentMusicIndex];
+ if (!track) return;
+
+ const lightbox = document.getElementById('musicLightbox');
+ const bg = document.getElementById('musicLightboxBg');
+ const img = document.getElementById('musicLightboxImage');
+ const title = document.getElementById('musicLightboxTitle');
+ const artist = document.getElementById('musicLightboxArtist');
+ const album = document.getElementById('musicLightboxAlbum');
+ const durationDisplay = document.getElementById('musicLightboxDuration');
+ const currentTimeEl = document.getElementById('musicLightboxCurrentTime');
+ const progressFill = document.getElementById('musicLightboxProgressFill');
+ const playIcon2 = document.getElementById('musicPlayIcon2');
+ const lyricsDisplay = document.getElementById('musicLightboxLyrics');
+ const shareBtn = document.getElementById('musicShareBtn');
+
+ // --- Share button ---
+ let feedbackEl = document.getElementById('shareFeedbackPopout');
+ if (!feedbackEl) {
+ feedbackEl = document.createElement('div');
+ feedbackEl.id = 'shareFeedbackPopout';
+ feedbackEl.className = 'share-feedback';
+ feedbackEl.innerHTML = '
✓ Copied!';
+ document.body.appendChild(feedbackEl);
+ }
+ if (shareBtn) {
+ shareBtn.onclick = function() {
+ if (!track.id) {
+ feedbackEl.textContent = '⚠️ No ID';
+ feedbackEl.classList.add('visible');
+ setTimeout(() => feedbackEl.classList.remove('visible'), 2000);
+ return;
+ }
+ const shareUrl = `https://nostr.basspistol.org/notes/${track.id}`;
+
+ if (navigator.share) {
+ navigator.share({
+ title: track.title || 'Music Track',
+ text: `Check out "${track.title}" by ${track.artist || 'Unknown Artist'}`,
+ url: shareUrl
+ }).catch(err => console.log('Share cancelled or failed:', err));
+ } else {
+ const showCopied = () => {
+ feedbackEl.innerHTML = '
✓ Copied!';
+ feedbackEl.classList.add('visible');
+ setTimeout(() => feedbackEl.classList.remove('visible'), 2000);
+ };
+ if (navigator.clipboard) {
+ navigator.clipboard.writeText(shareUrl).then(showCopied).catch(() => {
+ const ta = document.createElement('textarea');
+ ta.value = shareUrl;
+ document.body.appendChild(ta);
+ ta.select();
+ try { document.execCommand('copy'); showCopied(); } catch(e) {}
+ document.body.removeChild(ta);
+ });
+ }
+ }
+ };
+ }
+
+ // --- Album art + info ---
+ const artUrl = track.image || '/images/default-album.jpg';
+ img.src = artUrl;
+ bg.style.backgroundImage = `url(${artUrl})`;
+ title.textContent = track.title || 'Untitled';
+ artist.textContent = track.artist || 'Unknown Artist';
+ album.textContent = track.album || '';
+
+ // --- Lyrics lookup (works on homepage & playlist pages) ---
+ let lyricsContent = track.content || '';
+ if (!lyricsContent) {
+ const raw = findRawTrack(track.url);
+ if (raw && raw.content) lyricsContent = raw.content;
+ }
+ if (lyricsContent) {
+ lyricsDisplay.innerHTML = lyricsContent.replace(/\n/g, '
');
+ lyricsDisplay.style.display = 'block';
+ } else {
+ lyricsDisplay.style.display = 'none';
+ }
+
+ // --- Progress ---
+ progressFill.style.width = '0%';
+ currentTimeEl.textContent = '0:00';
+ durationDisplay.textContent = track.duration ? formatTime(track.duration) : '0:00';
+ playIcon2.textContent = '▶';
+
+ // --- Audio player ---
+ musicPlayer = new Audio(track.url);
+ musicPlayer.preload = 'metadata';
+
+ musicPlayer.addEventListener('loadedmetadata', function() {
+ durationDisplay.textContent = formatTime(this.duration);
+ });
+ musicPlayer.addEventListener('ended', nextMusicTrack);
+ musicPlayer.addEventListener('timeupdate', function() {
+ const percent = (this.currentTime / this.duration) * 100;
+ progressFill.style.width = `${percent}%`;
+ currentTimeEl.textContent = formatTime(this.currentTime);
+ });
+
+ lightbox.classList.add('active');
+ document.body.style.overflow = 'hidden';
+ playMusic();
+}
+
+function playMusic() {
+ if (!musicPlayer) return;
+ musicPlayer.play()
+ .then(() => { document.getElementById('musicPlayIcon2').textContent = '⏸️'; })
+ .catch(e => console.log('Playback prevented:', e));
+}
+
+function pauseMusic() {
+ if (!musicPlayer) return;
+ musicPlayer.pause();
+ document.getElementById('musicPlayIcon2').textContent = '▶️';
+}
+
+function toggleMusic() {
+ if (!musicPlayer) return;
+ musicPlayer.paused ? playMusic() : pauseMusic();
+}
+
+function nextMusicTrack() { openMusicLightbox(currentMusicIndex + 1); }
+function prevMusicTrack() { openMusicLightbox(currentMusicIndex - 1); }
+
+function closeMusicLightbox() {
+ const lightbox = document.getElementById('musicLightbox');
+ if (!lightbox) return;
+ if (musicPlayer) {
+ musicPlayer.pause();
+ musicPlayer.remove();
+ musicPlayer = null;
+ }
+ lightbox.classList.remove('active');
+ document.body.style.overflow = '';
+}
+
+function initMusicLightbox() {
+ const lightbox = document.getElementById('musicLightbox');
+ if (!lightbox) return;
+
+ const closeBtn = document.getElementById('musicLightboxClose');
+ const playBtn2 = document.getElementById('musicLightboxPlayBtn2');
+ const prevBtn = document.getElementById('musicLightboxPrev');
+ const nextBtn = document.getElementById('musicLightboxNext');
+ const progressBar = document.getElementById('musicLightboxProgressBar');
+
+ const newClose = closeBtn.cloneNode(true);
+ const newPlayBtn2 = playBtn2.cloneNode(true);
+ const newPrev = prevBtn.cloneNode(true);
+ const newNext = nextBtn.cloneNode(true);
+ const newProgress = progressBar.cloneNode(true);
+
+ closeBtn.parentNode.replaceChild(newClose, closeBtn);
+ playBtn2.parentNode.replaceChild(newPlayBtn2, playBtn2);
+ prevBtn.parentNode.replaceChild(newPrev, prevBtn);
+ nextBtn.parentNode.replaceChild(newNext, nextBtn);
+ progressBar.parentNode.replaceChild(newProgress, progressBar);
+
+ newClose.addEventListener('click', closeMusicLightbox);
+
+ lightbox.addEventListener('click', function(e) {
+ if (e.target === lightbox) closeMusicLightbox();
+ });
+
+ newPlayBtn2.addEventListener('click', function(e) { e.stopPropagation(); toggleMusic(); });
+ newPrev.addEventListener('click', function(e) { e.stopPropagation(); prevMusicTrack(); });
+ newNext.addEventListener('click', function(e) { e.stopPropagation(); nextMusicTrack(); });
+
+ newProgress.addEventListener('click', function(e) {
+ if (!musicPlayer) return;
+ const rect = this.getBoundingClientRect();
+ const percent = (e.clientX - rect.left) / rect.width;
+ musicPlayer.currentTime = percent * musicPlayer.duration;
+ });
+
+ document.addEventListener('keydown', function(e) {
+ const lb = document.getElementById('musicLightbox');
+ if (!lb || !lb.classList.contains('active')) return;
+
+ if (e.key === 'Escape') { e.preventDefault(); closeMusicLightbox(); }
+ else if (e.key === ' ' || e.key === 'Space') { e.preventDefault(); toggleMusic(); }
+ else if (e.key === 'ArrowLeft') { e.preventDefault(); prevMusicTrack(); }
+ else if (e.key === 'ArrowRight') { e.preventDefault(); nextMusicTrack(); }
+ });
+}
+
+// ============================================
+// 7. REFRESH / RANDOMIZE HELPERS
// ============================================
function refreshMusic() {
@@ -563,366 +709,38 @@ function refreshVideos() {
}
// ============================================
-// 7. MUSIC LIGHTBOX
-// ============================================
-
-let currentMusicIndex = 0;
-let musicPlayer = null;
-
-// Open music lightbox
-function openMusicLightbox(index) {
-
-
- if (!window.musicPlaylist || window.musicPlaylist.length === 0) return;
-
- // Stop any existing playback
- if (musicPlayer) {
- musicPlayer.pause();
- musicPlayer.remove();
- musicPlayer = null;
- }
-
- if (index < 0) index = window.musicPlaylist.length - 1;
- if (index >= window.musicPlaylist.length) index = 0;
- currentMusicIndex = index;
-
- const track = window.musicPlaylist[currentMusicIndex];
- if (!track) return;
-
- // Get elements
- const lightbox = document.getElementById('musicLightbox');
- const bg = document.getElementById('musicLightboxBg');
- const img = document.getElementById('musicLightboxImage');
- const title = document.getElementById('musicLightboxTitle');
- const artist = document.getElementById('musicLightboxArtist');
- const album = document.getElementById('musicLightboxAlbum');
- const durationDisplay = document.getElementById('musicLightboxDuration');
- const currentTimeDisplay = document.getElementById('musicLightboxCurrentTime');
- const progressFill = document.getElementById('musicLightboxProgressFill');
- const playBtn2 = document.getElementById('musicLightboxPlayBtn2');
- const playIcon2 = document.getElementById('musicPlayIcon2');
- // Get the lyrics element
- const lyricsDisplay = document.getElementById('musicLightboxLyrics');
-
- // Share button - in its own row
- const shareBtn = document.getElementById('musicShareBtn');
-
- // Create feedback element if it doesn't exist
- let feedbackEl = document.getElementById('shareFeedbackPopout');
- if (!feedbackEl) {
- feedbackEl = document.createElement('div');
- feedbackEl.id = 'shareFeedbackPopout';
- feedbackEl.className = 'share-feedback';
- feedbackEl.innerHTML = '
✓ Copied!';
- document.body.appendChild(feedbackEl);
- }
-
- if (shareBtn) {
- shareBtn.onclick = function() {
- if (!track.id) {
- feedbackEl.textContent = '⚠️ No ID';
- feedbackEl.classList.add('visible');
- setTimeout(() => {
- feedbackEl.classList.remove('visible');
- }, 2000);
- return;
- }
-
- const shareUrl = `https://nostr.basspistol.org/notes/${track.id}`;
-
- if (navigator.share) {
- navigator.share({
- title: track.title || 'Music Track',
- text: `Check out "${track.title}" by ${track.artist || 'Unknown Artist'}`,
- url: shareUrl
- }).catch(err => {
- console.log('Share cancelled or failed:', err);
- });
- } else {
- navigator.clipboard.writeText(shareUrl).then(() => {
- feedbackEl.innerHTML = '
✓ Copied!';
- feedbackEl.classList.add('visible');
- setTimeout(() => {
- feedbackEl.classList.remove('visible');
- }, 2000);
- }).catch(err => {
- console.error('Could not copy text: ', err);
- const textArea = document.createElement('textarea');
- textArea.value = shareUrl;
- document.body.appendChild(textArea);
- textArea.select();
- try {
- document.execCommand('copy');
- feedbackEl.innerHTML = '
✓ Copied!';
- feedbackEl.classList.add('visible');
- setTimeout(() => {
- feedbackEl.classList.remove('visible');
- }, 2000);
- } catch (err) {
- console.error('Fallback copy failed:', err);
- feedbackEl.textContent = '⚠️ Copy failed';
- feedbackEl.classList.add('visible');
- setTimeout(() => {
- feedbackEl.classList.remove('visible');
- }, 2000);
- }
- document.body.removeChild(textArea);
- });
- }
- };
- }
-
- // Set album art
- const artUrl = track.image || '/images/default-album.jpg';
- img.src = artUrl;
- bg.style.backgroundImage = `url(${artUrl})`;
-
- // Set info
- title.textContent = track.title || 'Untitled';
- artist.textContent = track.artist || 'Unknown Artist';
- album.textContent = track.album || '';
-
- // Set lyrics - find the original track data to get content
- let lyricsContent = '';
- // Find the original track data
- const originalTrack = window.allMusicData ? window.allMusicData.find(t => {
- let url = '';
- if (t.tags) {
- t.tags.forEach(tag => {
- if (tag[0] === 'url') url = tag[1];
- });
- }
- return url === track.url;
- }) : null;
-
- if (originalTrack && originalTrack.content) {
- lyricsContent = originalTrack.content;
- }
-
- // Display lyrics if they exist
- if (lyricsContent) {
- lyricsDisplay.innerHTML = lyricsContent.replace(/\n/g, '
');
- lyricsDisplay.style.display = 'block';
- } else {
- lyricsDisplay.style.display = 'none';
- }
-
- // Reset progress
- progressFill.style.width = '0%';
- currentTimeDisplay.textContent = '0:00';
- durationDisplay.textContent = track.duration ? formatTime(track.duration) : '0:00';
-
- // Set play buttons to play state
- playIcon2.textContent = '▶';
-
- // Create audio player
- musicPlayer = new Audio(track.url);
- musicPlayer.preload = 'metadata';
-
- // When metadata loaded, update duration
- musicPlayer.addEventListener('loadedmetadata', function() {
- durationDisplay.textContent = formatTime(this.duration);
- });
-
- // Track ended - go to next
- musicPlayer.addEventListener('ended', function() {
- nextMusicTrack();
- });
-
- // Update progress
- musicPlayer.addEventListener('timeupdate', function() {
- const percent = (this.currentTime / this.duration) * 100;
- progressFill.style.width = `${percent}%`;
- currentTimeDisplay.textContent = formatTime(this.currentTime);
- });
-
- // Show lightbox
- lightbox.classList.add('active');
- document.body.style.overflow = 'hidden';
-
- // Auto-play
- playMusic();
-}
-
-// Play music
-function playMusic() {
- if (!musicPlayer) return;
-
- musicPlayer.play()
- .then(() => {
- document.getElementById('musicPlayIcon2').textContent = '⏸️';
- })
- .catch(e => {
- console.log('Playback prevented:', e);
- });
-}
-
-// Pause music
-function pauseMusic() {
- if (!musicPlayer) return;
- musicPlayer.pause();
- document.getElementById('musicPlayIcon2').textContent = '▶️';
-}
-
-// Toggle play/pause
-function toggleMusic() {
- if (!musicPlayer) return;
- if (musicPlayer.paused) {
- playMusic();
- } else {
- pauseMusic();
- }
-}
-
-// Next track
-function nextMusicTrack() {
- openMusicLightbox(currentMusicIndex + 1);
-}
-
-// Previous track
-function prevMusicTrack() {
- openMusicLightbox(currentMusicIndex - 1);
-}
-
-// Close music lightbox
-function closeMusicLightbox() {
- const lightbox = document.getElementById('musicLightbox');
- if (!lightbox) return;
-
- if (musicPlayer) {
- musicPlayer.pause();
- musicPlayer.remove();
- musicPlayer = null;
- }
-
- lightbox.classList.remove('active');
- document.body.style.overflow = '';
-}
-
-// Format time (seconds to MM:SS)
-function formatTime(seconds) {
- if (!seconds || isNaN(seconds)) return '0:00';
- const mins = Math.floor(seconds / 60);
- const secs = Math.floor(seconds % 60);
- return `${mins}:${secs.toString().padStart(2, '0')}`;
-}
-
-// Initialize music lightbox event listeners
-function initMusicLightbox() {
- const lightbox = document.getElementById('musicLightbox');
- const closeBtn = document.getElementById('musicLightboxClose');
- const playBtn2 = document.getElementById('musicLightboxPlayBtn2');
- const prevBtn = document.getElementById('musicLightboxPrev');
- const nextBtn = document.getElementById('musicLightboxNext');
- const progressBar = document.getElementById('musicLightboxProgressBar');
-
- if (!lightbox) return;
-
- // Remove old listeners by cloning
- const newClose = closeBtn.cloneNode(true);
- const newPlayBtn2 = playBtn2.cloneNode(true);
- const newPrev = prevBtn.cloneNode(true);
- const newNext = nextBtn.cloneNode(true);
- const newProgressBar = progressBar.cloneNode(true);
-
- closeBtn.parentNode.replaceChild(newClose, closeBtn);
- playBtn2.parentNode.replaceChild(newPlayBtn2, playBtn2);
- prevBtn.parentNode.replaceChild(newPrev, prevBtn);
- nextBtn.parentNode.replaceChild(newNext, nextBtn);
- progressBar.parentNode.replaceChild(newProgressBar, progressBar);
-
- // Close button
- newClose.addEventListener('click', closeMusicLightbox);
-
- // Click outside to close (on the background)
- lightbox.addEventListener('click', function(e) {
- if (e.target === lightbox) {
- closeMusicLightbox();
- }
- });
-
- // Play button
- newPlayBtn2.addEventListener('click', function(e) {
- e.stopPropagation();
- toggleMusic();
- });
-
- // Previous/Next
- newPrev.addEventListener('click', function(e) {
- e.stopPropagation();
- prevMusicTrack();
- });
-
- newNext.addEventListener('click', function(e) {
- e.stopPropagation();
- nextMusicTrack();
- });
-
- // Progress bar click to seek
- newProgressBar.addEventListener('click', function(e) {
- if (!musicPlayer) return;
- const rect = this.getBoundingClientRect();
- const x = e.clientX - rect.left;
- const percent = x / rect.width;
- musicPlayer.currentTime = percent * musicPlayer.duration;
- });
-
- // Keyboard controls
- document.addEventListener('keydown', function(e) {
- const lightbox = document.getElementById('musicLightbox');
- if (!lightbox || !lightbox.classList.contains('active')) return;
-
- if (e.key === 'Escape') {
- e.preventDefault();
- closeMusicLightbox();
- } else if (e.key === ' ' || e.key === 'Space') {
- e.preventDefault();
- toggleMusic();
- } else if (e.key === 'ArrowLeft') {
- e.preventDefault();
- prevMusicTrack();
- } else if (e.key === 'ArrowRight') {
- e.preventDefault();
- nextMusicTrack();
- }
- });
-}
-
-// ============================================
-// 8. DOM CONTENT LOADED
+// 8. INIT
// ============================================
document.addEventListener('DOMContentLoaded', function() {
- // Sticky Navigation
+ // --- Sticky navigation ---
const stickyNav = document.getElementById('stickyNav');
const mainHeader = document.querySelector('.main-header');
- let lastScrollY = window.scrollY;
let ticking = false;
function updateStickyNav() {
const scrollY = window.scrollY;
const headerHeight = mainHeader ? mainHeader.offsetHeight : 400;
- if (scrollY > headerHeight - 100) {
- stickyNav.classList.add('visible');
- } else {
- stickyNav.classList.remove('visible');
+ if (stickyNav) {
+ if (scrollY > headerHeight - 100) stickyNav.classList.add('visible');
+ else stickyNav.classList.remove('visible');
}
- lastScrollY = scrollY;
}
- window.addEventListener('scroll', function() {
- if (!ticking) {
- window.requestAnimationFrame(function() {
- updateStickyNav();
- ticking = false;
- });
- ticking = true;
- }
- });
+ if (stickyNav) {
+ window.addEventListener('scroll', function() {
+ if (!ticking) {
+ window.requestAnimationFrame(function() {
+ updateStickyNav();
+ ticking = false;
+ });
+ ticking = true;
+ }
+ });
+ setTimeout(updateStickyNav, 100);
+ }
- setTimeout(updateStickyNav, 100);
-
- // Read More toggle for blog posts
+ // --- Read more toggles ---
document.querySelectorAll('.read-more-toggle').forEach(button => {
button.addEventListener('click', function() {
const content = this.nextElementSibling;
@@ -936,45 +754,35 @@ document.addEventListener('DOMContentLoaded', function() {
});
});
- // Smooth scrolling for navigation links (only for # anchors)
+ // --- Smooth scroll for # anchors ---
document.querySelectorAll('nav a').forEach(link => {
link.addEventListener('click', function(e) {
const href = this.getAttribute('href');
- // Only intercept if it's a # anchor link
if (href && href.startsWith('#')) {
e.preventDefault();
- const targetId = href;
- const targetElement = document.querySelector(targetId);
+ const targetElement = document.querySelector(href);
if (targetElement) {
- const navHeight = stickyNav.offsetHeight;
+ const navHeight = stickyNav ? stickyNav.offsetHeight : 0;
const targetPosition = targetElement.getBoundingClientRect().top + window.pageYOffset - navHeight - 20;
- window.scrollTo({
- top: targetPosition,
- behavior: 'smooth'
- });
+ window.scrollTo({ top: targetPosition, behavior: 'smooth' });
}
}
- // If it's a regular link (like /playlists/), let it navigate normally
});
});
- // Initialize Music (chronological, not randomized)
+ // --- Initialize renderers ---
if (window.allMusicData && window.allMusicData.length > 0) {
renderMusicTracks(window.allMusicData, 'musicGrid', false);
}
-
- // Initialize Pictures (chronological, not randomized)
if (window.allPicturesData && window.allPicturesData.length > 0) {
renderRandomPictures(window.allPicturesData, 'photoGrid', false);
}
-
- // Initialize Videos (chronological, not randomized)
if (window.allVideosData && window.allVideosData.length > 0) {
renderRandomVideos(window.allVideosData, 'videoGrid', false);
}
- // Initialize music lightbox
+ // --- Initialize the music lightbox controls ---
initMusicLightbox();
});
-console.log('✅ Site initialized successfully!');
+console.log('✅ Site initialized successfully!');
\ No newline at end of file
diff --git a/themes/one-pager/layouts/_default/baseof.html b/themes/one-pager/layouts/_default/baseof.html
index a5bf90d..95b34af 100644
--- a/themes/one-pager/layouts/_default/baseof.html
+++ b/themes/one-pager/layouts/_default/baseof.html
@@ -12,25 +12,16 @@
+ {{ block "header" . }}{{ end }}
+
+ {{ block "main" . }}{{ end }}
+
+ {{ partial "footer.html" . }}
+ {{ partial "photo-lightbox.html" . }}
+ {{ partial "music-lightbox.html" . }}
- {{ block "header" . }}{{ end }}
-
-
-
-
- {{ block "main" . }}{{ end }}
-
-
-
- {{ partial "footer.html" . }}
-
-
-
-
- {{ $js := resources.Get "js/scripts.js" | resources.Minify | fingerprint }}
-
-
-
+ {{ $js := resources.Get "js/scripts.js" | resources.Minify | fingerprint }}
+