"use client"; import { useEffect } from "react"; import { buildImageProxyUrl } from "@/lib/image-delivery"; const MIN_DISPLAY_WIDTH = 160; const DEFAULT_DISPLAY_WIDTH = 960; const MAX_DISPLAY_WIDTH = 2200; const OPTIMIZED_MARK = "ossDisplayOptimized"; function isSkippableImageUrl(url: string): boolean { const normalized = url.trim().toLowerCase(); return ( !normalized || normalized.startsWith("data:") || normalized.startsWith("blob:") || normalized.startsWith("/api/image-proxy") || normalized.includes("x-oss-process=") || /\.(svg|gif|ico)(?:[?#]|$)/i.test(normalized) ); } function getDisplayWidth(image: HTMLImageElement): number { const rectWidth = image.getBoundingClientRect().width; const width = rectWidth || image.clientWidth || Number.parseInt(image.getAttribute("width") || "", 10) || DEFAULT_DISPLAY_WIDTH; const dpr = Math.min(2, Math.max(1, window.devicePixelRatio || 1)); return Math.round(Math.max(MIN_DISPLAY_WIDTH, Math.min(MAX_DISPLAY_WIDTH, width * dpr))); } function optimizeImageElement(image: HTMLImageElement): void { if (image.dataset.imageDelivery === "raw" || image.dataset.imageDelivery === "proxy") { return; } const currentSrc = image.currentSrc || image.src || image.getAttribute("src") || ""; if (isSkippableImageUrl(currentSrc)) { return; } const optimizedUrl = buildImageProxyUrl(currentSrc, { width: getDisplayWidth(image), quality: 84, format: "webp", }); if ( !optimizedUrl || optimizedUrl === currentSrc || optimizedUrl.startsWith("/api/image-proxy") ) { return; } image.dataset[OPTIMIZED_MARK] = "1"; image.src = optimizedUrl; } function optimizeAllImages(root: ParentNode = document): void { root.querySelectorAll("img").forEach((image) => { optimizeImageElement(image); }); } export function ImageDeliveryOptimizer() { useEffect(() => { let frameId = 0; const scheduleOptimize = (root: ParentNode = document) => { if (frameId) { cancelAnimationFrame(frameId); } frameId = requestAnimationFrame(() => { frameId = 0; optimizeAllImages(root); }); }; scheduleOptimize(); const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === "attributes") { const target = mutation.target; if (target instanceof HTMLImageElement) { optimizeImageElement(target); } continue; } for (const node of mutation.addedNodes) { if (node instanceof HTMLImageElement) { optimizeImageElement(node); } else if (node instanceof Element) { scheduleOptimize(node); } } } }); observer.observe(document.body, { attributes: true, attributeFilter: ["src"], childList: true, subtree: true, }); const handleResize = () => scheduleOptimize(); window.addEventListener("resize", handleResize, { passive: true }); return () => { if (frameId) { cancelAnimationFrame(frameId); } observer.disconnect(); window.removeEventListener("resize", handleResize); }; }, []); return null; }