import type { MaybeRefOrGetter } from 'vue' import { appendScript, awaitGlobal, normalizeURL, pathKeyToURL } from '@bagelink/vue' import { ref, toValue, watch } from 'vue' declare global { interface Window { heic2any: any } } async function getCachedImage(url: string): Promise { if (!('caches' in window)) { return undefined } try { const imgCache = await window.caches.open('img-cache') const cachedResponse = await imgCache.match(url) if (cachedResponse) { return URL.createObjectURL(await cachedResponse.blob()) } } catch (error) { console.warn('Cache access error:', error) } return undefined } async function cacheImage(url: string, blob: Blob) { if (!('caches' in window)) { return } try { const imgCache = await window.caches.open('img-cache') await imgCache.put(url, new Response(blob)) } catch (error) { console.warn('Cache write error:', error) } } async function convertHeicImage(url: string): Promise { await appendScript('https://cdnjs.cloudflare.com/ajax/libs/heic2any/0.0.1/index.min.js') const heic2any = await awaitGlobal<(opts: { blob: Blob }) => Promise>('heic2any') const response = await fetch(normalizeURL(url)) const blob = await response.blob() const convertedBlob = await heic2any({ blob }) await cacheImage(url, convertedBlob) return URL.createObjectURL(convertedBlob) } /** * Resolve an image source for display: turns a pathKey/URL into a usable URL via * `pathKeyToURL`, and transparently converts `.heic` files to a displayable blob * (with a cache) since browsers can't render HEIC in . Shared by * and the lightbox so both get the same resolution + HEIC handling. * * @param source ref/getter of the raw src or pathKey * @returns `{ imageSrc, loadingError }` reactive refs */ export function useImageSrc(source: MaybeRefOrGetter) { const imageSrc = ref(undefined) const loadingError = ref(false) async function load() { loadingError.value = false const url = pathKeyToURL(toValue(source)) if (url === undefined || url === '') { imageSrc.value = undefined return } try { const ext = url.split('.').pop()?.toLowerCase().split('?')[0] if (ext === 'heic') { const cachedSrc = await getCachedImage(url) if (cachedSrc !== undefined && cachedSrc !== '') { imageSrc.value = cachedSrc return } imageSrc.value = await convertHeicImage(url) } else { imageSrc.value = url } } catch (error) { console.error('Image loading error:', error) loadingError.value = true imageSrc.value = undefined } } watch(() => toValue(source), load, { immediate: true }) return { imageSrc, loadingError } }