Skip to content

[companion] fix(image-viewer): open fitted to the screen, with Fit and 1:1 zoom - #85

Open
andrebrait wants to merge 5 commits into
companion/lightbox-fit-zoom-basefrom
fix/lightbox-fit-zoom
Open

andrebrait wants to merge 5 commits into
companion/lightbox-fit-zoom-basefrom
fix/lightbox-fit-zoom

Conversation

@andrebrait

@andrebrait andrebrait commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Review-only companion to kahme247#226. Identical head and matching upstream base.

Codex and CodeRabbit have current availability evidence on this repository (#83). Upstream is read-only for this account, so review requests go here after upstream CI is green.

Summary by CodeRabbit

  • New Features
    • Image previews now fit the screen by default, with controls to fit the image or view it at actual size.
    • Zoom in and out in smooth steps, with a range from the fitted size (or 10% for smaller images) up to 800%. Zooming keeps the view centered, and you can scroll to every edge.
    • Lightbox controls meet the configured touch-target sizing.

The lightbox opened at 100% on every device and zoomed with a transform
that did not grow the scroll area. Open fitted to the viewport (never
upscaling small images), show zoom relative to real pixels, step by x1.25
down to min(10%, fit), size the image by layout so every edge scrolls into
view, and replace Reset zoom with Fit to screen and 1:1.
Keep the centre of the view across zoom changes, show an image that fails
to load (or has no intrinsic size) instead of leaving it hidden, size only
once both the image and the viewport are measured, and give the viewer
buttons 44px targets under the Touch Targets setting.
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e1a6d9a4-e036-4a31-a9e6-a014fb3027ca

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 9641ee6f-63db-48e1-9234-83c63c89b2d4
📥 Commits

Reviewing files that changed from the base of the PR and between 6c86c13 and 467d96d.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • app/globals.css
  • components/ImageLightbox.test.mjs
  • components/ImageLightbox.tsx
  • lib/i18n/locales/en.json
  • lib/i18n/locales/ja.json
  • lib/i18n/locales/zh-CN.json

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The image lightbox now opens images fitted to the available area and supports stepped zoom, fit-to-screen, and actual-size controls. It sizes images for scrolling, preserves the viewport center during zoom, and updates control styling, touch-target sizing, and translations.

Changes

Image lightbox zoom

Layer / File(s) Summary
Zoom calculations and tests
components/ImageLightbox.tsx, components/ImageLightbox.test.mjs
Added helpers for fit scale and bounded ×1.25 zoom steps. Tests cover fitted and invalid dimensions, zoom limits, and zoom increments.
Measured image layout and controls
components/ImageLightbox.tsx, app/globals.css, lib/i18n/locales/*.json, CHANGELOG.md
The lightbox measures the viewport and image, lays out images at the selected scale, and preserves the fractional viewport center when zoom changes. Fit-to-screen and actual-size controls replace reset zoom. Styling, translations, and the Unreleased changelog entry reflect these controls and touch-target sizing.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  actor Viewer
  participant ImageLightbox
  participant Image
  participant ResizeObserver
  participant Viewport
  Image->>ImageLightbox: report natural dimensions when loading settles
  ResizeObserver->>ImageLightbox: report viewport content size
  ImageLightbox->>Viewport: set image dimensions from fit scale
  Viewer->>ImageLightbox: choose fit, actual size, or stepped zoom
  ImageLightbox->>Viewport: update image dimensions and preserve fractional center
Loading

Suggested reviewers: kahme247

Merge Risk: ⚪ Minimal · up to 467d9

The image viewer now opens images fitted to the viewport and adds stepped zoom, Fit to screen and 1:1 controls. No concrete merge-blocking risk was identified. Quickly check zoom and scroll behavior in a browser before release.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. (5 skipped: 5… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the image viewer’s fitted opening behavior and Fit and 1:1 zoom controls. The “[companion]” prefix is extra, but the title remains concise and specific.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. (5 skipped: 5 unsupported.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@andrebrait

Copy link
Copy Markdown
Owner Author

@codex review

@andrebrait

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-07T07:53:44.489059Z 63bb4de Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 467d96da5a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread components/ImageLightbox.tsx Outdated
Comment on lines +108 to +109
const observer = new ResizeObserver(([entry]) => {
setBox({ width: entry.contentRect.width, height: entry.contentRect.height });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Size the lightbox against the scaled viewport

When Interface Scale is Comfortable or Large, the root uses zoom: 1.1 or 1.2 (app/globals.css:622-628), while this observer measures a dialog whose unchanged dimensions are 100vw × 100dvh. Viewport units are zoom-invariant in this app and therefore paint beyond the window unless divided by --ui-scale (app/globals.css:599-607), so fitZoom fits the image into the off-screen portion of the dialog: the bottom/right can be clipped and the initial image is not actually fitted to the visible screen. Compact scale similarly leaves the viewer and image undersized. Inversely scale the lightbox's viewport-unit dimensions, as the other dialogs do, before using this measurement.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 93d2d96: the viewer's 100vw/100dvh are now divided by --ui-scale, like the other dialogs. Checked at 1400×900: Compact and Large both cover exactly 1400×900, and the fitted image ends inside the window (23% / 17%).

Viewport units ignore the root zoom, so the viewer was sized past the
window (Comfortable/Large) or short of it (Compact) and fit to that.
@andrebrait

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 93d2d96a33

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread components/ImageLightbox.tsx Outdated
// Real layout size, not transform: scale, so a zoomed-in image
// scrolls to every edge. Hidden until it can be sized to fit.
style={ready
? { width: natural.width * scale, height: natural.height * scale }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep actual-size rendering independent of UI scale

When Interface Scale is Compact, Comfortable, or Large, the root zoom still scales the dimensions assigned here, so clicking 1:1 paints a 1000-pixel-wide image at 900, 1100, or 1200 CSS pixels while the toolbar reports 100%; every other displayed percentage is similarly offset. Counteract --ui-scale when converting the natural dimensions into layout dimensions so the new actual-size control and percentages remain relative to the image's real pixels.

AGENTS.md reference: AGENTS.md:L599-L607

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 63bb4de: the fit is computed in screen pixels (contentRect × --ui-scale) and the image is laid out at natural × zoom / --ui-scale. At Compact and at Large, 1:1 measures 6000 screen px for a 6000px image, and fit is about 20% at both scales on a 1400×900 window.

Comment thread app/globals.css
Comment on lines +1436 to +1440
html[data-touch-targets="accessible"] .image-lightbox-stepper,
html[data-touch-targets="accessible"] :is(.image-lightbox-stepper button, .image-lightbox-icon-button, .image-lightbox-zoom-value) {
min-width: 44px;
height: 44px;
line-height: 44px;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reflow accessible controls on narrow scaled viewports

On a 360px-wide phone with Touch Targets set to Accessible and Interface Scale set to Large, the dialog has only about 300 unzoomed CSS pixels and the toolbar content area about 280, but these rules require roughly 288 pixels for the stepper, Fit, 1:1, Close, and their gaps even before the title. Since the stepper also has overflow: hidden and is shrinkable, its zoom controls are clipped; add a narrow responsive layout such as wrapping or a compact toolbar so all controls remain reachable.

AGENTS.md reference: AGENTS.md:L599-L607

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 63bb4de: the toolbar and its action group now wrap, and the stepper no longer shrinks. On a 360×760 phone at Large scale with Accessible targets, all five buttons are fully inside the dialog at 44px.

…lbar

Measure fit and size the image in screen pixels so 1:1 and every
percentage ignore the root zoom, and let the toolbar wrap so large touch
targets stay reachable on narrow screens.
@andrebrait

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: 63bb4ded38

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant