Fix image format handling and enhance PageCoverImage component (#3527)

Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
This commit is contained in:
conico974
2025-08-08 18:30:00 +02:00
committed by GitHub
parent ff96bb5787
commit 28f7fbaa8a
3 changed files with 46 additions and 24 deletions
@@ -43,31 +43,30 @@ export async function PageCover(props: {
const getImage = async (resolved: ResolvedContentRef | null, returnNull = false) => {
if (!resolved && returnNull) return;
const [attrs, size] = await Promise.all([
getImageAttributes({
sizes,
source: resolved
? {
src: resolved.href,
size: resolved.file?.dimensions ?? null,
}
: {
src: defaultPageCover.src,
size: {
width: defaultPageCover.width,
height: defaultPageCover.height,
},
// If we don't have a size for the image, we want to calculate it so that we can use srcSet
const size =
resolved?.file?.dimensions ??
(await context.imageResizer?.getImageSize(resolved?.href || defaultPageCover.src, {}));
const attrs = await getImageAttributes({
sizes,
source: resolved
? {
src: resolved.href,
size: size ?? null,
}
: {
src: defaultPageCover.src,
size: {
width: defaultPageCover.width,
height: defaultPageCover.height,
},
quality: 100,
resize: context.imageResizer ?? false,
}),
context.imageResizer
?.getImageSize(resolved?.href || defaultPageCover.src, {})
.then((size) => size ?? undefined),
]);
},
quality: 100,
resize: context.imageResizer ?? false,
});
return {
...attrs,
size,
size: size ?? undefined,
};
};
@@ -44,6 +44,8 @@ export function PageCoverImage({ imgs, y }: { imgs: Images; y: number }) {
<div className="h-full w-full overflow-hidden" ref={containerRef}>
<img
src={imgs.light.src}
srcSet={imgs.light.srcSet}
sizes={imgs.light.sizes}
fetchPriority="high"
alt="Page cover"
className={tcls('w-full', 'object-cover', imgs.dark ? 'dark:hidden' : '')}
@@ -55,6 +57,8 @@ export function PageCoverImage({ imgs, y }: { imgs: Images; y: number }) {
{imgs.dark && (
<img
src={imgs.dark.src}
srcSet={imgs.dark.srcSet}
sizes={imgs.dark.sizes}
fetchPriority="low"
alt="Page cover"
className={tcls('w-full', 'object-cover', 'dark:inline', 'hidden')}
+21 -2
View File
@@ -87,6 +87,8 @@ export async function serveResizedImage(
options.height = Number(height);
}
const longestEdgeValue = Math.max(options.width || 0, options.height || 0);
const dpr = requestURL.searchParams.get('dpr');
if (dpr) {
options.dpr = Number(dpr);
@@ -99,10 +101,14 @@ export async function serveResizedImage(
// Check the Accept header to handle content negotiation
const accept = request.headers.get('accept');
if (accept && /image\/avif/.test(accept)) {
// We use transform image, max size for avif should be 1600
// https://developers.cloudflare.com/images/transform-images/#limits-per-format
if (accept && /image\/avif/.test(accept) && longestEdgeValue <= 1600) {
options.format = 'avif';
} else if (accept && /image\/webp/.test(accept)) {
options.dpr = chooseDPR(longestEdgeValue, 1600, options.dpr);
} else if (accept && /image\/webp/.test(accept) && longestEdgeValue <= 1920) {
options.format = 'webp';
options.dpr = chooseDPR(longestEdgeValue, 1920, options.dpr);
}
try {
@@ -119,6 +125,19 @@ export async function serveResizedImage(
}
}
/**
* Choose the appropriate device pixel ratio (DPR) based on the longest edge of the image.
* This function ensures that the DPR is within a reasonable range (1 to 3).
* This is only used for AVIF/WebP formats to avoid issues with Cloudflare resizing.
* It means that dpr may not be respected for avif/webp formats, but it will also improve the cache hit ratio.
*/
function chooseDPR(longestEdgeValue: number, maxAllowedSize: number, wantedDpr?: number): number {
const maxDprBySize = Math.floor(maxAllowedSize / longestEdgeValue);
const clampedDpr = Math.min(wantedDpr ?? 1, 3); // Limit to a maximum of 3, default to 1 if not specified
// Ensure that the DPR is within the allowed range
return Math.max(1, Math.min(maxDprBySize, clampedDpr));
}
/**
* Parse the image signature version from a query param. Returns null if the version is invalid.
*/