From 09f37f890f121c3b5aeb1754800236c2d4e992aa Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Sat, 18 Jul 2026 23:28:20 -0400 Subject: [PATCH] fix(mobile): image/video aspect-ratio so media doesn't crop/letterbox (N2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MImage/MVideo pinned AttachmentBox to a fixed height computed for a 400px-wide layout. The box width is responsive (maxWidth:100%) but the height was frozen, so on a phone the box narrows below 400px while keeping desktop height -> images crop (object-fit:cover) and videos letterbox (object-fit:contain). Drive the box by `aspect-ratio: w/h` when intrinsic dimensions are known, so the height tracks the responsive width. On desktop the box stays 400px wide, so the aspect-ratio yields the identical height (algebraically 400*h/w = scaleYDimension(w,400,h)) — pixel-identical. Falls back to the fixed height when dimensions are unknown; the 48px floor and 600px cap are preserved. Uses the same pattern already shipped in this codebase (TwitchThumbnailWrapper, GalleryTile). Two review passes, one empirically measuring the rendered image in Chromium: desktop unchanged, narrow widths keep correct aspect, no collapse. Co-Authored-By: Claude Opus 4.8 --- .../components/message/MsgTypeRenderers.tsx | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/app/components/message/MsgTypeRenderers.tsx b/src/app/components/message/MsgTypeRenderers.tsx index bec299543..fe7f418ae 100644 --- a/src/app/components/message/MsgTypeRenderers.tsx +++ b/src/app/components/message/MsgTypeRenderers.tsx @@ -417,6 +417,22 @@ type RenderImageContentProps = { markedAsSpoiler?: boolean; spoilerReason?: string; }; +// Media frame sizing. When intrinsic width/height are known, drive the box by +// aspect-ratio so its height tracks the responsive (maxWidth:100%) width — a +// fixed pixel height computed for a 400px-wide layout otherwise crops (images, +// object-fit:cover) or letterboxes (videos, object-fit:contain) on phones where +// the box narrows below 400px. On desktop the box stays 400px wide, so the +// aspect-ratio yields the identical height. Falls back to the fixed height when +// dimensions are unknown. +const attachmentMediaStyle = ( + w: number | undefined, + h: number | undefined, + fallbackHeight: number, +): CSSProperties => + w && h + ? { aspectRatio: `${w} / ${h}`, minHeight: toRem(48) } + : { height: toRem(fallbackHeight < 48 ? 48 : fallbackHeight) }; + type MImageProps = { content: IImageContent; renderImageContent: (props: RenderImageContentProps) => ReactNode; @@ -432,11 +448,7 @@ export function MImage({ content, renderImageContent, outlined }: MImageProps) { return ( - + {renderImageContent({ body: content.body || 'Image', info: imgInfo, @@ -498,11 +510,7 @@ export function MVideo({ content, renderAsFile, renderVideoContent, outlined }: } /> - + {renderVideoContent({ body: content.body || 'Video', info: videoInfo,