diff --git a/app/layout.tsx b/app/layout.tsx index 853ec3208..64b3b13ac 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from "next"; import { Toaster } from "sonner"; import { ThemeProvider } from "@/components/theme-provider"; import { FaviconUpdater } from "@/components/favicon-updater"; +import { ImageDeliveryOptimizer } from "@/components/image-delivery-optimizer"; import "./globals.css"; import { SessionProvider } from "next-auth/react"; @@ -171,6 +172,7 @@ export default async function RootLayout({ + { + 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; +} diff --git a/lib/image-delivery.ts b/lib/image-delivery.ts index e9238d220..99bb21543 100644 --- a/lib/image-delivery.ts +++ b/lib/image-delivery.ts @@ -99,6 +99,7 @@ export function buildImageProxyUrl(url: string, options: ImageProxyOptions = {}) if (directDisplayUrl) { return directDisplayUrl; } + return source; } const width = normalizeWidth(options.width);