Fix up gallery loader a bit.

This commit is contained in:
Arcum42
2026-07-18 08:39:56 -07:00
parent a6bc5c4826
commit d64d4b0253
3 changed files with 82 additions and 66 deletions
+26 -48
View File
@@ -7,7 +7,8 @@
import { getThumbnailSize } from "./config.js";
import { formatFileSize } from "../reports/reportGenerator.js";
import { actions, selectors } from "./stateManager.js";
import { copyImageToClipboard, generateThumbnail } from "./imageUtils.js";
import { copyImageToClipboard } from "./imageUtils.js";
import { getLazyLoader } from "./lazyImageLoader.js";
import { showFullImage } from "./imageViewer.js";
/**
@@ -52,58 +53,35 @@ export function createImageItem(image, showImageContextMenu, showMetadata) {
background: #333;
`;
// Set thumbnail source with error handling
// Set thumbnail source with lazy loader and error handling
const thumbnailSizeConfig = getThumbnailSize(selectors.thumbnailSize());
// Load thumbnail using centralized loader
const loadThumbnailUrl = async () => {
try {
const sizeName = thumbnailSizeConfig.width <= 120 ? 'small' :
thumbnailSizeConfig.width <= 200 ? 'medium' : 'large';
return await generateThumbnail(image, sizeName);
} catch (error) {
console.error('Error generating thumbnail:', error);
return null;
}
};
const sizeName = thumbnailSizeConfig.width <= 120 ? 'small' :
thumbnailSizeConfig.width <= 200 ? 'medium' : 'large';
const errorDiv = document.createElement('div');
errorDiv.style.cssText = `
width: 100%;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
background: #333;
color: #999;
font-size: 12px;
position: absolute;
inset: 0;
`;
errorDiv.textContent = 'Thumbnail failed';
errorDiv.style.display = 'none';
item.appendChild(errorDiv);
loadThumbnailUrl().then(thumbnailUrl => {
if (thumbnailUrl) {
thumbnail.src = thumbnailUrl;
} else {
thumbnail.style.display = 'none';
const errorDiv = document.createElement('div');
errorDiv.style.cssText = `
width: 100%;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
background: #333;
color: #999;
font-size: 12px;
`;
errorDiv.textContent = 'Thumbnail failed';
item.appendChild(errorDiv);
}
});
thumbnail.addEventListener('error', () => {
thumbnail.style.display = 'none';
const errorDiv = document.createElement('div');
errorDiv.style.cssText = `
width: 100%;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
background: #333;
color: #888;
font-size: 12px;
`;
errorDiv.textContent = '❌ Thumbnail failed';
item.insertBefore(errorDiv, thumbnail.nextSibling);
errorDiv.style.display = 'flex';
});
const lazyLoader = getLazyLoader();
lazyLoader.observe(thumbnail, image, sizeName);
// Create overlay with filename and info
const overlay = document.createElement('div');
+14 -2
View File
@@ -9,6 +9,12 @@
import { loadThumbnail } from './imageLoader.js';
function getImagePath(imageInput) {
if (!imageInput) return null;
if (typeof imageInput === 'string') return imageInput;
return imageInput.path || imageInput.relative_path || imageInput.name || null;
}
/**
* Configuration for lazy loading and retry behavior
*/
@@ -79,11 +85,17 @@ export class LazyImageLoader {
* @param {string} imagePath - Path to the image
* @param {string} thumbnailSize - Size of thumbnail to load
*/
observe(img, imagePath, thumbnailSize = 'large') {
observe(img, imageInput, thumbnailSize = 'large') {
if (!this.observer) {
this.init();
}
const imagePath = getImagePath(imageInput);
if (!imagePath) {
console.warn('[LazyImageLoader] No image path available for lazy load', imageInput);
return;
}
// Store metadata on element
img.dataset.imagePath = imagePath;
img.dataset.thumbnailSize = thumbnailSize;
@@ -149,7 +161,7 @@ export class LazyImageLoader {
try {
// Attempt to load thumbnail
const thumbnailUrl = await loadThumbnail({ path: imagePath }, thumbnailSize);
const thumbnailUrl = await loadThumbnail(imagePath, thumbnailSize);
// Set the image source
img.src = thumbnailUrl;
+42 -16
View File
@@ -311,7 +311,27 @@ function setupGalleryEventHandlers(folderAndControls, unused, grid, metadata, he
// Show loading indicator in grid with immediate count update
grid.imageCountSpan.textContent = '(Loading...)';
let progressStarted = false;
const ensureProgressContainer = () => {
if (!progressContainer || !grid.gridContainer.contains(progressContainer)) {
if (progressContainer && progressContainer.parentNode) {
progressContainer.remove();
}
if (!progressStarted) {
grid.gridContainer.innerHTML = '';
}
progressContainer = document.createElement('div');
progressContainer.className = 'gallery-loading-progress';
if (grid.gridContainer.firstChild) {
grid.gridContainer.insertBefore(progressContainer, grid.gridContainer.firstChild);
} else {
grid.gridContainer.appendChild(progressContainer);
}
progressStarted = true;
}
};
// Create a progress update function
const updateProgress = async (current, total, message = '') => {
// Don't update if aborted
@@ -319,12 +339,7 @@ function setupGalleryEventHandlers(folderAndControls, unused, grid, metadata, he
return;
}
if (!progressContainer) {
grid.gridContainer.innerHTML = '';
progressContainer = document.createElement('div');
progressContainer.className = 'gallery-loading-progress';
grid.gridContainer.appendChild(progressContainer);
}
ensureProgressContainer();
const percentage = total > 0 ? Math.round((current / total) * 100) : 0;
@@ -337,6 +352,7 @@ function setupGalleryEventHandlers(folderAndControls, unused, grid, metadata, he
const fill = progressContainer.querySelector('.gallery-progress-bar__fill');
if (fill) {
fill.style.setProperty('--fill-width', `${percentage}%`);
fill.style.width = `${percentage}%`;
}
};
@@ -396,6 +412,15 @@ function setupGalleryEventHandlers(folderAndControls, unused, grid, metadata, he
renderImageGrid(currentImages, currentFolders, loadImagesWrapper)
.catch(err => console.warn('Gallery: Final re-render after metadata completion failed:', err));
}
// Remove any remaining loading banner after metadata completes
if (progressContainer && progressContainer.parentNode) {
setTimeout(() => {
if (progressContainer && progressContainer.parentNode) {
progressContainer.remove();
}
}, 300);
}
}
};
@@ -422,14 +447,14 @@ function setupGalleryEventHandlers(folderAndControls, unused, grid, metadata, he
// Update UI immediately with actual counts
grid.imageCountSpan.textContent = `${images.length} images / ${folders.length} folders`;
// Clear the progress container if it exists
// Render image grid with both images and folders immediately
await renderImageGrid(images, folders, loadImagesWrapper);
// Remove the gallery load progress banner after initial grid render
if (progressContainer && progressContainer.parentNode) {
progressContainer.remove();
}
// Render image grid with both images and folders immediately
await renderImageGrid(images, folders, loadImagesWrapper);
// Auto-show metadata for the first image if available
if (images && images.length > 0) {
showImageMetadata(images[0]);
@@ -485,7 +510,11 @@ function setupGalleryEventHandlers(folderAndControls, unused, grid, metadata, he
// Only clear if not doing incremental update
if (!incremental) {
const loadingProgress = grid.gridContainer.querySelector('.gallery-loading-progress');
grid.gridContainer.innerHTML = '';
if (loadingProgress) {
grid.gridContainer.appendChild(loadingProgress);
}
}
if (!images || (images.length === 0 && folders.length === 0)) {
@@ -611,6 +640,7 @@ function setupGalleryEventHandlers(folderAndControls, unused, grid, metadata, he
const batchSize = 20; // Process 20 images at a time
const totalImages = images.length;
let processedCount = 0;
let renderedCount = incremental ? grid.gridContainer.querySelectorAll('.gallery-image-item').length : 0;
// Find or create progress container
let progressContainer = grid.gridContainer.querySelector('.gallery-progress-container');
@@ -640,13 +670,9 @@ function setupGalleryEventHandlers(folderAndControls, unused, grid, metadata, he
const fill = progressContainer.querySelector('.gallery-thumbnail-progress__fill');
if (fill) {
fill.style.setProperty('--fill-width', `${percentage}%`);
fill.style.width = `${percentage}%`;
}
};
await updateProgress();
// Track which images have been rendered (by index)
let renderedCount = incremental ? grid.gridContainer.querySelectorAll('.gallery-image-item').length : 0;
}
// For large folders, show skeleton loaders first for better perceived performance
const skeletonThreshold = 100; // Show skeletons for folders with 100+ images