Repository navigation
feat(ui): image viewer with zoom, pan and gesture support - #597
Merged
Merged
Conversation
Replace the fixed-card image lightbox with an ImageViewer entity on a host-filling dialog. Wheel and pinch zoom about the pointer, drag and touch pan, double-click toggles fit and actual size, keys and toolbar glide between placements, and a click beside the image, Escape or the close button dismiss it. Placement is clamped to the stage and small images are not enlarged at fit. Dialog gains an opt-in fill() mode; the macOS traffic-light inset moves to window_caption as its owner and the shell nav bar uses it; the four Lucide icons the toolbar needs are bundled.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The image lightbox was a 1200px dialog card with a static
imginside. It is now anImageViewerentity on a host-filling dialog that zooms and pans, replacingattachments::open_image_lightboxfor the composer strip, sent-message thumbnails, Markdown images and image-link badges.Gestures, all resolved against the last drawn frame:
Placement is clamped so the image is centred on any axis where it is smaller than the stage and never leaves a gap where it is larger; small images are not enlarged at fit, and 1:1 is always reachable. Keys, buttons and double-click glide via
gpui_base::motion::transition; pointer gestures track directly.Survey before building: neither gpui-base 0.7 nor gpui-kit ships an image viewer, lightbox or zoom/pan primitive (only
CarouselandAttachmentthumbnails), so this composes gpui'sPinchEvent/ScrollWheelEvent/click-count with gpui-baseDialogandtransition.Also in this PR:
Dialog::fill()on Tcode's dialog wrapper, the one addition needed to host a full-window surface on the existing stack.window_caption::traffic_light_inset(window)is now the owner of the macOS traffic-light inset; the shell's nav bar uses it instead of its private constant, and the viewer toolbar uses it too.zoom-in,zoom-out,maximize-2,minimize-2) bundled inassets/icons, since the bundled component icon set does not contain them (they rendered blank on first run).Evidence
Before: fixed-size card, no zoom or pan; a large screenshot was only ever shown shrunk.
After: launched
target/debug/tcodeon a throwawayTCODE_DATA_DIR, pasted a 2400×1600 PNG into the composer and opened it from the thumbnail. Verified: fit at 49%, toolbar clear of the traffic lights,=key → 61% and fit button appears, drag pans the enlarged image, window at 480px wide re-fits to 27% with the compact toolbar, dark mode renders the dark scrim and opaque toolbar. Double-click could not be driven by the automation tool (it posts two single clicks); that path is covered by the headless test.Headless UI tests in
image_viewer::testsdrive the window the way the app does (simulate_eventfor wheel and pinch, mouse down/move/up, keystrokes, clock advance for the glide):cargo fmt --all --check,cargo clippy --workspace --all-targets --locked -- -D warnings,cargo nextest run --workspace --locked(892 passed),cargo machete. Not run here: Windows, Linux, iOS, Android and Web builds (CI).Merge Danger
Door: two-way
Revert restores the previous card; no persisted state or protocol is touched.
Blast Radius: presentation
Every image-open path in the UI now lands in the viewer. The dialog wrapper gains an opt-in
fill()and existing dialogs are laid out exactly as before. The shell nav bar's traffic-light inset is computed by the shared helper with the same values. Pinch on iOS/Android depends on the platform declaring a native pinch recogniser; gpui's portable recogniser does not implement pinch yet, so touch there gets tap, double-tap and pan.