From c04eac06c43d5c1e1693cf24ad8637ed94708035 Mon Sep 17 00:00:00 2001 From: th Date: Sun, 12 Apr 2026 11:51:08 +0200 Subject: [PATCH] assets: unified tile render, empty-filter placeholder * Orphaned items now use mediaTile() instead of a hand-written triple- ternary, so audio and PDF files render with the same icon/player tile as linked items. Eliminates the code duplication that caused audio files to fall through to the path in the orphaned section. * applyFilter() tracks visibleCount after toggling item display. When the active filter hides every item (e.g. "Audio" tab with no audio files) but total assets exist, a type-specific placeholder is shown: matching icon (ph-image / ph-music-note / ph-file-pdf) + "no {type} assets" text. Hidden again on the next filter switch that has results. * Section header hide logic narrowed to [data-section]:not(.gallery-item) so it only targets the header divs, not the items themselves. chore: bump version to 0.9.19 Co-Authored-By: Claude Opus 4.6 (1M context) --- config.php | 2 +- public/js/modules/gallery.js | 39 +++++++++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/config.php b/config.php index 2ff0140..7d7576b 100644 --- a/config.php +++ b/config.php @@ -1,7 +1,7 @@ { t.classList.toggle('active', t.dataset.filter === filter); }); + let visibleCount = 0; grid.querySelectorAll('.gallery-item').forEach(item => { const url = item.dataset.url || ''; const type = fileType(url); const show = filter === 'all' || type === filter; item.style.display = show ? '' : 'none'; + if (show) visibleCount++; }); + // Hide section headers when no items in that section are visible + grid.querySelectorAll('[data-section]:not(.gallery-item)').forEach(hdr => { + const section = hdr.dataset.section; + const hasVisible = !!grid.querySelector(`.gallery-item[data-section="${section}"]:not([style*="display: none"])`); + hdr.style.display = hasVisible ? '' : 'none'; + }); + // Show/hide the empty-filter placeholder + let placeholder = grid.querySelector('.gallery-filter-empty'); + if (visibleCount === 0 && total > 0) { + if (!placeholder) { + placeholder = document.createElement('div'); + placeholder.className = 'gallery-filter-empty col-span-full text-center py-16'; + const icons = { image: 'ph-image', audio: 'ph-music-note', pdf: 'ph-file-pdf' }; + const icon = icons[filter] || 'ph-folder-open'; + placeholder.innerHTML = `

no ${filter} assets

`; + grid.appendChild(placeholder); + } + placeholder.style.display = ''; + } else if (placeholder) { + placeholder.style.display = 'none'; + } }; document.querySelectorAll('#gallery-tabs .gallery-tab').forEach(t => { t.onclick = () => applyFilter(t.dataset.filter); @@ -137,15 +160,11 @@ export async function openGallery() { let html = ''; if (orphaned.length) { - html += `
orphaned (not used by any card)
`; + html += `
orphaned (not used by any card)
`; html += orphaned.map(f => ` -