This commit is contained in:
seb
2026-10-10 02:37:38 +02:00
parent 859fec808d
commit c5b46f11f1
7 changed files with 461 additions and 470 deletions

View File

@@ -0,0 +1,49 @@
import { thumbHashToDataURL } from 'thumbhash';
// Module-scoped caches are also stashed on `globalThis` so they survive Vite
// HMR reloads instead of re-decoding / re-fetching every image.
const dataUrlCache = (globalThis.__THUMB_DATA_URL_CACHE__ ??= new Map());
const loadedImageHashes = (globalThis.__LOADED_IMAGE_HASHES__ ??= new Set());
export const imageCache = (globalThis.__IMAGE_CACHE__ ??= new Map());
/** True once the full image for `fullHash` has been loaded (data URL or `<img>`). */
export function isImageLoaded(fullHash) {
if (!fullHash) return false;
return loadedImageHashes.has(fullHash) || imageCache.has(fullHash);
}
/** Records that `fullHash` is loaded, optionally caching the decoded `<img>`. */
export function markImageLoaded(fullHash, img) {
if (!fullHash) return;
loadedImageHashes.add(fullHash);
if (img) {
imageCache.set(fullHash, img);
}
}
/** Returns the cached, already-decoded `<img>` for `fullHash`, or `null`. */
export function getCachedImage(fullHash) {
if (!fullHash) return null;
const img = imageCache.get(fullHash);
if (img && img.complete && img.naturalWidth) {
return img;
}
return null;
}
/** Decodes a ThumbHash string into a data URL (cached per hash). */
export function getThumbHashDataUrl(thumbHash) {
if (!thumbHash || typeof thumbHash !== 'string') return null;
let url = dataUrlCache.get(thumbHash);
if (!url) {
try {
const bytes = Uint8Array.from(atob(thumbHash), (c) => c.charCodeAt(0));
url = thumbHashToDataURL(bytes);
dataUrlCache.set(thumbHash, url);
} catch (err) {
console.warn('[thumbHashCache] thumbHashToDataURL failed:', err);
return null;
}
}
return url;
}