43 lines
1.6 KiB
TypeScript
43 lines
1.6 KiB
TypeScript
/**
|
|
* Names the widths the site's own images come in. Every image the API serves
|
|
* (avatars, covers, thumbnails, attachments, proxied images) takes `?w=` and
|
|
* answers a copy scaled down to it, so a card or an avatar doesn't load the
|
|
* upload at full size. The frontend builds those URLs from this list
|
|
* (`imageSrcSet` in `src/utils/shared/urls.ts`) and the server makes no other
|
|
* width (`serveImage` in `api/lib/upload.ts`), which bounds what it keeps.
|
|
*
|
|
* @module
|
|
*/
|
|
|
|
/**
|
|
* The widths, in pixels, smallest first: an avatar on a dense screen, a small
|
|
* cover, a card on a phone, and the widest feed card on a dense screen.
|
|
*/
|
|
export const IMAGE_WIDTHS = [128, 320, 640, 960] as const;
|
|
|
|
/** One of {@link IMAGE_WIDTHS}. */
|
|
export type ImageWidth = typeof IMAGE_WIDTHS[number];
|
|
|
|
/** The largest of {@link IMAGE_WIDTHS}, which a thumbnail never exceeds. */
|
|
export const MAX_IMAGE_WIDTH: ImageWidth =
|
|
IMAGE_WIDTHS[IMAGE_WIDTHS.length - 1];
|
|
|
|
/**
|
|
* Returns the narrowest of {@link IMAGE_WIDTHS} that covers `width` pixels, or
|
|
* the widest when none does.
|
|
*/
|
|
export function snapImageWidth(width: number): ImageWidth {
|
|
return IMAGE_WIDTHS.find((w) => w >= width) ?? MAX_IMAGE_WIDTH;
|
|
}
|
|
|
|
/**
|
|
* Returns the width a `?w=` value asks for, snapped to {@link IMAGE_WIDTHS},
|
|
* or null when it is absent or not a positive number, which asks for the image
|
|
* as it is.
|
|
*/
|
|
export function parseImageWidth(raw: string | null): ImageWidth | null {
|
|
if (raw === null || !/^\d{1,5}$/.test(raw)) return null;
|
|
const width = Number(raw);
|
|
return width > 0 ? snapImageWidth(width) : null;
|
|
}
|