Summary
Small markdown images (badges, icons) are upscaled to fill the fallback reserve box on first view, rendering a ~20px shields-style badge as a huge blurry block. The same image renders at its correct natural size on the second view, because the decoded size cache then supplies real dimensions. The bug is the inconsistency between first view and every subsequent view.
Environment
- Buzz Desktop 0.5.3 (macOS, Apple Silicon), also reproduced on 0.5.2
- Any channel, plain markdown message
Repro
Post a message containing a small dim-less image, e.g.:

Expected: the badge renders at its intrinsic size (roughly 46x20).
Actual: it renders as a giant block filling a ~384x256 area, visibly upscaled. After the decoded size has been cached (e.g. remount of the message list), the same message renders the badge at its natural size.
Related symptoms from the same mechanism:
- A wide image (e.g. 1200x240) is scaled down into the same box, making its text unreadably small.
- Small raster images are upscaled and pixelated.
Root cause (from reading main)
desktop/src/shared/ui/markdown/utils.ts:
- A dim-less image with no cached decode gets
DEFAULT_IMAGE_RESERVE = { height: 256, width: 384 } and useFixedReserveBox: true (resolveImageReserveBox).
useFrozenImageReserve deliberately freezes that box for the lifetime of the mount (sound layout-shift reasoning).
desktop/src/shared/ui/markdown/ProgressiveImage.tsx:
IMAGE_CLASS uses object-contain inside the frame. object-contain scales UP as well as down, so a 46x20 badge is stretched to fill the 384x256 frame for the entire first view.
rememberDecodedImageDimensions caches the natural size on load, which is why the second view is correct — the first view never benefits from the decode that happens inside it.
Suggested fix
Either of these preserves the layout-shift guarantee for large images:
- When a dim-less image decodes and its natural size fits entirely inside the reserve box, adopt the natural size immediately (collapse the frame). First view then matches the cached second-view behavior the code already intends. Shift is shrink-only and only for small images.
- Alternatively/minimally:
object-scale-down instead of object-contain on the inline image, so small images never upscale (they would sit centered in the reserve box until decode-cache kicks in on later views).
Happy to open a PR for option 1 if the direction is acceptable.
Summary
Small markdown images (badges, icons) are upscaled to fill the fallback reserve box on first view, rendering a ~20px shields-style badge as a huge blurry block. The same image renders at its correct natural size on the second view, because the decoded size cache then supplies real dimensions. The bug is the inconsistency between first view and every subsequent view.
Environment
Repro
Post a message containing a small dim-less image, e.g.:
Expected: the badge renders at its intrinsic size (roughly 46x20).
Actual: it renders as a giant block filling a ~384x256 area, visibly upscaled. After the decoded size has been cached (e.g. remount of the message list), the same message renders the badge at its natural size.
Related symptoms from the same mechanism:
Root cause (from reading main)
desktop/src/shared/ui/markdown/utils.ts:DEFAULT_IMAGE_RESERVE = { height: 256, width: 384 }anduseFixedReserveBox: true(resolveImageReserveBox).useFrozenImageReservedeliberately freezes that box for the lifetime of the mount (sound layout-shift reasoning).desktop/src/shared/ui/markdown/ProgressiveImage.tsx:IMAGE_CLASSusesobject-containinside the frame.object-containscales UP as well as down, so a 46x20 badge is stretched to fill the 384x256 frame for the entire first view.rememberDecodedImageDimensionscaches the natural size on load, which is why the second view is correct — the first view never benefits from the decode that happens inside it.Suggested fix
Either of these preserves the layout-shift guarantee for large images:
object-scale-downinstead ofobject-containon the inline image, so small images never upscale (they would sit centered in the reserve box until decode-cache kicks in on later views).Happy to open a PR for option 1 if the direction is acceptable.