mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-21 01:53:26 +00:00
Fix image format handling and enhance PageCoverImage component (#3527)
Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
This commit is contained in:
@@ -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')}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user