let masonryTimer = null; function scheduleMasonryRelayout(grid) { clearTimeout(masonryTimer); masonryTimer = setTimeout(() => { relayoutMasonry(grid); }, 50); } export function createImageCard(id, tags, imageUrl, thumbnailUrl, type, options = {}) { const { showMessage, showTagModal, favoriteIds = new Set() } = options; const card = document.createElement('div'); card.className = 'image-entry loading newly-added'; card.dataset.id = id; card.dataset.type = type; card.title = tags.join(', '); const img = document.createElement('img'); img.src = thumbnailUrl || imageUrl; img.loading = 'lazy'; img.alt = tags.join(' '); img.onload = () => { img.classList.add('loaded'); card.classList.remove('loading'); if (type !== 'book') { setTimeout(() => resizeMasonryItem(card), 20); scheduleMasonryRelayout(card.parentElement); } setTimeout(() => { card.classList.remove('newly-added'); }, 400); }; img.onerror = () => { card.classList.remove('loading'); img.classList.add('loaded'); }; card.appendChild(img); if (type === 'book') { const readOverlay = document.createElement('div'); readOverlay.className = 'book-read-overlay'; readOverlay.innerHTML = ` Click To Read `; card.appendChild(readOverlay); return card; } const buttonsOverlay = document.createElement('div'); buttonsOverlay.className = 'image-buttons'; const favBtn = document.createElement('button'); favBtn.className = 'heart-button'; favBtn.dataset.id = id; const isFavorited = favoriteIds.has(String(id)); updateHeartIcon(favBtn, isFavorited); favBtn.onclick = async (e) => { e.stopPropagation(); e.preventDefault(); const currentlyFavorited = favoriteIds.has(String(id)); if (currentlyFavorited) { const success = await window.api.removeFavorite(id); if (success) { favoriteIds.delete(String(id)); updateHeartIcon(favBtn, false); showMessage('Removed from favorites', 'success'); if (window.location.pathname.includes('favorites.html')) { card.remove(); if (options.applyLayoutToGallery && options.favoritesGallery) { options.applyLayoutToGallery(options.favoritesGallery, options.currentLayout); } } } else { showMessage('Failed to remove favorite', 'error'); } } else { const favoriteData = { id: String(id), image_url: imageUrl, thumbnail_url: thumbnailUrl, tags: tags.join(','), title: card.title || 'Unknown' }; const success = await window.api.addFavorite(favoriteData); if (success) { favoriteIds.add(String(id)); updateHeartIcon(favBtn, true); showMessage('Added to favorites', 'success'); } else { showMessage('Failed to save favorite', 'error'); } } }; const tagBtn = document.createElement('button'); tagBtn.innerHTML = ``; tagBtn.title = "View Tags"; tagBtn.onclick = (e) => { e.stopPropagation(); showTagModal(tags); }; buttonsOverlay.appendChild(tagBtn); buttonsOverlay.appendChild(favBtn); card.appendChild(buttonsOverlay); return card; } function resizeMasonryItem(item) { if (item.dataset.type === 'book') return; const grid = item.parentElement; const rowHeight = parseInt(getComputedStyle(grid).gridAutoRows); const rowGap = parseInt(getComputedStyle(grid).gap); const img = item.querySelector("img"); if (!img) return; if (!img.complete || img.naturalWidth === 0) { return; } const width = img.clientWidth; const imageHeight = (img.naturalHeight / img.naturalWidth) * width; const extra = 2; const totalHeight = imageHeight + extra; const rowSpan = Math.ceil((totalHeight + rowGap) / (rowHeight + rowGap)); item.style.gridRowEnd = "span " + rowSpan; item.style.height = totalHeight + "px"; } function updateHeartIcon(btn, isFavorited) { if (isFavorited) { btn.innerHTML = ``; btn.title = "Remove from Favorites"; } else { btn.innerHTML = ``; btn.title = "Add to Favorites"; } } function relayoutMasonry(grid) { const items = grid.querySelectorAll('.image-entry:not([data-type="book"])'); items.forEach(item => resizeMasonryItem(item)); } export function populateTagModal(container, tags) { container.innerHTML = ''; if (!tags || tags.length === 0) { container.innerHTML = '
No tags available.
'; return; } tags.forEach(tag => { const span = document.createElement('span'); span.textContent = tag; container.appendChild(span); }); }