I-022 — Receipt previewer: fit to window height, vary width by ratio, scroll long receipts¶
Symptom¶
The "Click Here" links in the exported Register of Expense Receipts XLSX open
the no-login viewer (/v/<shortId>, kind: 'receipt'). The receipt image
(Processed and Original) was shown in a fixed A4-portrait iframe, so larger
photos — or any photo whose ratio isn't A4 — were cropped into a tiny scroll box,
forcing the viewer to scroll around inside it to read the receipt.
Root cause¶
Receipts are served as image/jpeg bytes by /api/v/[id]/pdf, but /v/[id]
rendered them with the same SharedWopcView it uses for WOPC / archive PDFs —
an <iframe> hard-locked to aspectRatio: 210 / 297 (A4). That's right for PDFs,
wrong for receipt photos of arbitrary size and ratio.
Fix (commit below)¶
components/share/SharedDocumentView.tsx—SharedWopcViewgains amedia?: 'pdf' | 'image'prop.'image'renders an<img>sized from the image's natural ratio, measured client-side on load + resize:- Default — fit to the viewport HEIGHT, width following the natural ratio
(
max-height: calc(100vh − header − padding),width:auto,object-fit:contain). Tall → narrow column; wide → short banner; small → natural size. No scrolling. - Long/narrow receipts — once fitting to height would shrink the width below
a readable floor (
MIN_READABLE_IMG_WIDTH_PX = 400), the width is pinned at that floor and the image is allowed to run past the bottom of the screen, scrolling vertically (width:400; height:auto; stageoverflow-y:auto). The header isposition:sticky, so it stays put while the receipt scrolls. - PDFs keep the A4 iframe (
media:'pdf', the default). pages/v/[id].tsx— passmedia="image"forkind === 'receipt'.
Owner's framing (verbatim): "if an image hits the minimum width to accommodate for the height, then we increase height for scrolling instead."
Verification¶
Rendered the exact image branch (72px header + 16px stage padding + the new
client-measured <img> logic) in headless Chromium at 1440×900:
- Normal 1000×1400 → FIT-HEIGHT, img 505×707, no scroll.
- Wide 2100×680 → FIT-HEIGHT banner, width-capped, no scroll.
- Long 560×2600 → SCROLL mode, img pinned 398×1848 (readable width), the
stage/page scrolls to reveal the rest; sticky header stays.
Project-pinned tsc — clean. Real-data confirmation is owner-on-deploy (re-export the Receipt register, click a Processed/Original "Click Here").
Decision log¶
2026-06-30 — ✅ Read AGENTS.md before working this ticket. Source: Records (Infrastructure) · https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD
Owner reported the receipt previewer was "fixated" while large images needed major
scrolling; asked for a height-fit previewer whose width varies with image ratio.
Scoped to kind: 'receipt' (the reported case); archive-doc PDFs keep the A4
iframe. Ticket-only (UI fix; no structural T-task).