Skip to content

Commit 67ca7db

Browse files
authored
Bypass Sharp for animated GIFs in DocImage and ImageGallery (#417)
The default Sharp pipeline re-encodes GIFs to WebP, which drops every frame after the first (losing animation) and also blows past Sharp's default pixel limit on large multi-frame GIFs — e.g. shared-subscription-demo.gif (1846x1004, 317 frames) fails the build with "Input image exceeds pixel limit". GIFs now skip raster processing and are served as-is, the same way SVGs already are.
1 parent baaf748 commit 67ca7db

2 files changed

Lines changed: 25 additions & 1 deletion

File tree

src/components/DocImage.astro

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const svgRawModules = import.meta.glob<string>(
4747
4848
const mod = assetModules[src];
4949
const isSvg = src.endsWith('.svg');
50+
const isGif = src.endsWith('.gif');
5051
const rawSvg = isSvg ? await svgRawModules[src]?.() : undefined;
5152
let interactiveSvg = rawSvg && /<a[\s>]/i.test(rawSvg) ? rawSvg : null;
5253
@@ -79,8 +80,9 @@ if (interactiveSvg) {
7980
return `<a${stripped} target="_blank" rel="noopener noreferrer">`;
8081
});
8182
}
83+
// GIFs bypass Sharp: WebP re-encode drops animation frames after the first.
8284
const optimized =
83-
mod && !isSvg
85+
mod && !isSvg && !isGif
8486
? await getImage({
8587
src: mod.default,
8688
...(numericWidth ? { width: numericWidth } : {}),

src/components/ImageGallery.astro

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ const processed = await Promise.all(
169169
}
170170
171171
const isSvg = lightPath.endsWith('.svg');
172+
const isGif = lightPath.endsWith('.gif');
172173
173174
// SVGs skip raster processing. If they contain `<a>`, inline them so anchors stay clickable.
174175
if (isSvg) {
@@ -196,6 +197,27 @@ const processed = await Promise.all(
196197
};
197198
}
198199
200+
// GIFs bypass Sharp: WebP re-encode drops animation frames after the first.
201+
if (isGif) {
202+
const lightSrc = lightMod.default.src;
203+
const darkMod = hasDark ? assetModules[darkPath] : null;
204+
const darkSrc = darkMod ? darkMod.default.src : lightSrc;
205+
206+
return {
207+
alt,
208+
caption: captionToHtml(caption ?? ''),
209+
thumbSrc: lightSrc,
210+
fullSrc: lightSrc,
211+
darkThumbSrc: darkSrc,
212+
darkFullSrc: darkSrc,
213+
fullWidth: lightMod.default.width,
214+
fullHeight: lightMod.default.height,
215+
hasDark,
216+
interactiveSvg: undefined,
217+
isCdn: false,
218+
};
219+
}
220+
199221
const thumb = await getImage({
200222
src: lightMod.default,
201223
width: 800,

0 commit comments

Comments
 (0)