block / block/buzz

Markdown images without dim are upscaled to the fallback reserve box on first view (badges render huge)

Open
#4,384 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

## 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.:

```markdown
![](https://img.shields.io/badge/OPEN-b91c1c.svg?style=flat-square)
```

**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:

1. 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.
2. 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.

Contributor guide

Open the contributing guide

Research direction

Start in desktop/src/shared/ui/markdown/utils.ts with resolveImageReserveBox and useFrozenImageReserve, then inspect IMAGE_CLASS and image loading in desktop/src/shared/ui/markdown/ProgressiveImage.tsx. Reproduce the first and subsequent views with the shields.io badge; done means dim-less small images render at intrinsic size on first view without regressing large-image reserve behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
desktop, frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.