fix(desktop): use object-scale-down for inline markdown images (#4686)

Fixes #4384.

A dim-less markdown image with no cached decode gets
`DEFAULT_IMAGE_RESERVE = { height: 256, width: 384 }` and
`useFixedReserveBox: true` in `resolveImageReserveBox`. The frame is
that big on first view, and the inline `<img>` uses `object-contain`
which scales UP to fill the frame — so a 46×20 shields badge renders
as a giant blurry block. The same message renders at the natural
size on the second view because `rememberDecodedImageDimensions`
populates the cache, but the inconsistency between first and
subsequent view is the bug.

Switches `object-contain` → `object-scale-down` on
`ProgressiveImage.tsx`'s shared `IMAGE_CLASS`. Both `object-contain`
and `object-scale-down` scale down to fit, but only
`object-scale-down` also refuses to scale up — a small image now sits
centered in the reserve box on first view, never upscaled. The
layout-shift guarantee for large images is preserved (they still
scale down to fit the reserve), matching the issue's stated
constraint.

Chose option 2 ("Alternatively/minimally") from the issue over
option 1 because:

- It's a one-class-name change with zero new state, zero new
  effects, and zero risk to the existing layout-shift guarantee
  that the surrounding `useFrozenImageReserve` is explicitly
  designed to preserve.
- Option 1 (collapse the frame on decode) is the issue author's
  preferred direction but requires deciding whether the layout
  shift is acceptable *shrink*-only — a direction question that
  should be confirmed with the maintainer first. The issue
  explicitly says "Happy to open a PR for option 1 if the
  direction is acceptable" — that's the sign-off step, not
  this PR.

If maintainers want the full first-view-equals-second-view
behavior, option 1 is a follow-up that builds on this base.

Not added: a regression test. The current desktop test infra
(`desktop/src/shared/ui/markdown/markdown.test.mjs` and
siblings) doesn't cover `ProgressiveImage` directly; a
DOM-style snapshot would need a new test harness. The change
is one CSS class name; the visual regression is plain to
eyeball in the screenshots CI already captures.

Verified:
- `pnpm check` (biome + file-sizes + px-text + pubkey-truncation) —
clean
- `npx tsc --noEmit` — clean

Signed-off-by: kushaim <carlossilvajimenez@gmail.com>
Co-authored-by: kushaim <carlossilvajimenez@gmail.com>
This commit is contained in:
Carlos Jimenez
2026-10-05 11:54:35 -07:00
committed by GitHub
parent 964b556a42
commit fcf5f044b3

View File

@@ -2,8 +2,11 @@ import * as React from "react";
import { cn } from "@/shared/lib/cn";
// `object-scale-down` (not `object-contain`) so small dim-less badges don't
// upscale to fill the reserve box on first view — the decoded size only
// stabilizes after the cache hits. See #4384 for the full trade-off.
const IMAGE_CLASS =
"absolute inset-0 block h-full w-full rounded-2xl object-contain";
"absolute inset-0 block h-full w-full rounded-2xl object-scale-down";
function isSameImageSource(
left: string | undefined,