fix: image compression checkbox now shows for all raster image types

The checkbox was only shown for image/jpeg and image/png. Users
uploading WebP, GIF, AVIF, BMP, TIFF, HEIC (iPhone photos) or any
other raster format never saw the checkbox at all.

Fix: isCompressible now checks file.type.startsWith('image/') and
excludes only image/svg+xml (vector — would rasterise) and empty type
strings. compressImage signature widened to File | Blob so it matches
the TUploadContent type without unsafe casts.

The send-path guard in handleSendUpload was also widened from the
hardcoded jpeg/png check to use isCompressible(), keeping the two gates
in sync. The Blob-safe id attribute uses the .name fallback so it
doesn't break when originalFile is a Blob without a name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-05 20:22:38 -04:00
parent a78cb46bfe
commit 58b19995b8
3 changed files with 23 additions and 20 deletions
+16 -9
View File
@@ -6,19 +6,26 @@ export type CompressionResult = {
height: number;
};
const COMPRESSIBLE_TYPES = ['image/jpeg', 'image/png'];
// Any raster image the browser can render to canvas can be compressed to JPEG.
// SVG is vector and must stay as-is; GIF animation is lost on canvas but static
// frames compress fine. Empty type string (undetected MIME) is excluded.
const isCompressibleType = (type: string): boolean =>
type.startsWith('image/') && type !== 'image/svg+xml' && type !== '';
/** Returns true if this file type can be compressed (JPEG or PNG, any size). */
export function isCompressible(file: File): boolean {
return COMPRESSIBLE_TYPES.includes(file.type);
/** Returns true if this file can be compressed via canvas (any raster image type). */
export function isCompressible(file: File | Blob): boolean {
return isCompressibleType(file.type);
}
/**
* Compress an image file via canvas.toBlob.
* Returns null if the file type is not compressible (GIF, SVG, WebP, video, audio, etc.).
* Compress an image file via canvas.toBlob → JPEG at the given quality.
* Returns null if the browser cannot render the image (e.g. unsupported codec).
*/
export async function compressImage(file: File, quality = 0.82): Promise<CompressionResult | null> {
if (!COMPRESSIBLE_TYPES.includes(file.type)) return null;
export async function compressImage(
file: File | Blob,
quality = 0.82,
): Promise<CompressionResult | null> {
if (!isCompressibleType(file.type)) return null;
const img = await loadImage(file);
const canvas = document.createElement('canvas');
@@ -48,7 +55,7 @@ export async function compressImage(file: File, quality = 0.82): Promise<Compres
});
}
function loadImage(file: File): Promise<HTMLImageElement> {
function loadImage(file: File | Blob): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const url = URL.createObjectURL(file);
const img = new Image();