import { useEffect, useState } from 'react'; type Vibrant = { Quantizer: { WebWorker: any }; from: (imageUrl: string) => { getPalette: () => Promise<{ DarkMuted: { getHex: () => string }; DarkVibrant: { getHex: () => string }; LightMuted: { getHex: () => string }; LightVibrant: { getHex: () => string }; Muted: { getHex: () => string }; Vibrant: { getHex: () => string }; }>; }; }; type ImagePalette = { DarkMuted: string; DarkVibrant: string; LightMuted: string; LightVibrant: string; Muted: string; Vibrant: string; }; function loadVibrant(): Promise { return new Promise((resolve) => { const script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/node-vibrant@3.2.1-alpha.1/dist/vibrant.worker.js'; script.onload = () => { resolve((window as any).Vibrant); }; document.body.appendChild(script); }); } let vibrantPromise: Promise; async function generateColor(imageUrl: string): Promise { if (!vibrantPromise) vibrantPromise = loadVibrant(); try { const vibrant = await vibrantPromise; const res = await vibrant.from(imageUrl).getPalette(); if (!res || !res.DarkMuted.getHex) throw new Error('No color'); return { DarkMuted: res.DarkMuted.getHex(), DarkVibrant: res.DarkVibrant.getHex(), LightMuted: res.LightMuted.getHex(), LightVibrant: res.LightVibrant.getHex(), Muted: res.Muted.getHex(), Vibrant: res.Vibrant.getHex(), }; } catch (e) { return { DarkMuted: 'var(--color-neutral-5)', DarkVibrant: 'var(--color-neutral-5)', LightMuted: 'var(--color-neutral-5)', LightVibrant: 'var(--color-neutral-5)', Muted: 'var(--color-neutral-5)', Vibrant: 'var(--color-neutral-5)', }; } } const cache: { [url: string]: Promise } = {}; const listeners: { [url: string]: (res: any) => void } = {}; function getImageColors(imageUrl: string) { if (!cache[imageUrl]) cache[imageUrl] = generateColor(imageUrl); return cache[imageUrl]; } export function useImageColors(imageUrl: string) { const [color, setColor] = useState(null); useEffect(() => { getImageColors(imageUrl).then(setColor); }, [imageUrl]); return color; }