"use client"; import { memo, useEffect, useMemo, useRef, useState, type ImgHTMLAttributes, } from "react"; import { buildImageProxyUrl } from "@/lib/image-delivery"; type AgentPreviewImageProps = Omit< ImgHTMLAttributes, "src" | "loading" | "decoding" > & { src?: string | null; previewWidth?: number; previewQuality?: number; previewFormat?: "webp" | "avif" | "jpeg" | "png"; rootMargin?: string; eager?: boolean; raw?: boolean; }; const EMPTY_PIXEL = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="; function buildPreviewUrl(params: { src: string; width: number; quality: number; format: "webp" | "avif" | "jpeg" | "png"; raw?: boolean; }) { if (params.raw) return params.src; return ( buildImageProxyUrl(params.src, { width: params.width, quality: params.quality, format: params.format, }) || params.src ); } function AgentPreviewImageBase({ src, previewWidth = 480, previewQuality = 78, previewFormat = "webp", rootMargin = "640px 0px", eager = false, raw = false, onError, onLoad, ...props }: AgentPreviewImageProps) { const imageRef = useRef(null); const [shouldLoad, setShouldLoad] = useState(eager); const [useOriginal, setUseOriginal] = useState(false); const normalizedSrc = String(src || "").trim(); useEffect(() => { setUseOriginal(false); setShouldLoad(eager); }, [eager, normalizedSrc]); useEffect(() => { if (eager || shouldLoad || !normalizedSrc) return; const image = imageRef.current; if (!image) return; if (!("IntersectionObserver" in window)) { setShouldLoad(true); return; } const observer = new IntersectionObserver( (entries) => { if (entries.some((entry) => entry.isIntersecting)) { setShouldLoad(true); observer.disconnect(); } }, { rootMargin, threshold: 0.01 } ); observer.observe(image); return () => observer.disconnect(); }, [eager, normalizedSrc, rootMargin, shouldLoad]); const displaySrc = useMemo(() => { if (!normalizedSrc || !shouldLoad) return EMPTY_PIXEL; if (useOriginal) return normalizedSrc; return buildPreviewUrl({ src: normalizedSrc, width: previewWidth, quality: previewQuality, format: previewFormat, raw, }); }, [ normalizedSrc, previewFormat, previewQuality, previewWidth, raw, shouldLoad, useOriginal, ]); return ( { if (normalizedSrc && !useOriginal && displaySrc !== normalizedSrc) { setUseOriginal(true); return; } onError?.(event); }} onLoad={onLoad} /> ); } export const AgentPreviewImage = memo(AgentPreviewImageBase);