You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The dashboard has exactly two full-viewport surfaces that own their own magnification, and after #6117 they still offer different ways to reach it:
pinch
double-tap
buttons
keys
Lightbox (image viewer)
yes
no
− / reset / +
+ / -
DiagramLightbox (mermaid viewer)
yes
yes
no
no
Both gaps were raised by the UX lane on #6117 and dispositioned there — the doc claim that implied the image viewer had a double-tap was corrected to describe shipped behaviour, and these two remain.
Why it matters
A user who learns the double-tap on a diagram tries it on an image and gets nothing. Nothing is broken, but the gesture vocabulary is inconsistent across two surfaces that look and behave alike.
In the diagram viewer, every desktop input has no working way to magnify — mouse, keyboard, and trackpad alike. Measured, because two earlier framings of this issue were wrong:
No controls exist.DiagramLightbox renders exactly one <button> (the close X, ~line 220) and its only key handler is Escape. The − / reset / + cluster and the +/-/0 bindings are the image viewer's (MarkdownRenderer.tsx:3237-3238, 3406-3412, 3496), not this one's.
Trackpad pinch does not reach the hook. A trackpad pinch produces no touch pointers: Blink reports it as ctrl+wheel, WebKit as gesturestart/gesturechange. DiagramLightbox monitors neither (grep -nE "onWheel|ctrlKey|gesturestart" → no matches) and usePinchZoom returns early on pointerType === 'mouse'. So "trackpad works, only mouse is unsupported" is not the situation.
Page zoom is not a fallback, which corrects what this issue said when filed. Page zoom was never disabled on desktop (fix(dashboard): stop the mobile shell page-zooming on a pinch #6000's suppression installs only under (pointer: coarse) and hooks only gesturestart/gesturechange/gestureend/touchmove; the viewport meta governs the touch visual viewport) — but it cannot magnify fit-to-viewport content. At 200% the viewport's CSS-pixel width halves, the fixed inset-0 box halves with it, and the SVG re-fits to that smaller box while each CSS pixel now covers 2 device pixels. The two cancel exactly: the labels come out the same apparent size.
Net: the viewer opens at the state where its labels are smallest and a desktop user has no way out of it. The UX lane raised this on three heads of #6117; the third framing is the one that holds.
Buttons plus keys cover all three inputs in one change — a control is input-agnostic, so it answers mouse, keyboard and trackpad together. Handling ctrl+wheel and gesturestart separately would be a second code path for the trackpad alone; only add it if a pinch specifically is wanted after the controls exist.
Neither is a regression — desktop was never able to magnify a fit-scaled diagram, before #6000 or after — but the second is a genuine dead end rather than a rough edge, and calling it merely "coarse" was wrong. The maintainer has ruled that this work lands here rather than in #6117, which ships with the touch path only; that PR carries the ruling and the measurements above.
Task
Double-tap on Lightbox.usePinchZoom already carries the tap arithmetic used by DiagramLightbox, so the zoom half is small. The design step is the conflict: Lightbox owns one-finger swipe-dismiss, and a double-tap is two one-finger contacts in quick succession. Decide how a second tap is distinguished from the start of a swipe before wiring it, and keep MarkdownRenderer.lightboxSwipe.test.tsx (20 specs, 6 of them pinch) green — that suite is the contract for the existing gesture.
Zoom buttons + keys in DiagramLightbox. Reuse the image viewer's − / reset / + cluster and +/- bindings, driving the same setZoom the hook returns. Note DiagramLightbox renders through createPortal (Lightbox does not), so tests query document.body, not the render container.
Worth doing together: both are "make the two magnification surfaces offer the same affordances", and doing one alone leaves the table above asymmetric in the other direction.
Notes
usePinchZoom's helpers are module-private on purpose and its returned surface is exactly what the two consumers destructure. If this work needs another field, add it back deliberately rather than re-exporting the internals.
The hook does not own one-finger gestures — that seam is why Lightbox keeps swipe-dismiss and DiagramLightbox keeps drag-pan in their own components. Item 1 should respect it rather than folding swipe thresholds into the hook.
Problem
The dashboard has exactly two full-viewport surfaces that own their own magnification, and after #6117 they still offer different ways to reach it:
Lightbox(image viewer)+/-DiagramLightbox(mermaid viewer)Both gaps were raised by the UX lane on #6117 and dispositioned there — the doc claim that implied the image viewer had a double-tap was corrected to describe shipped behaviour, and these two remain.
Why it matters
A user who learns the double-tap on a diagram tries it on an image and gets nothing. Nothing is broken, but the gesture vocabulary is inconsistent across two surfaces that look and behave alike.
In the diagram viewer, every desktop input has no working way to magnify — mouse, keyboard, and trackpad alike. Measured, because two earlier framings of this issue were wrong:
DiagramLightboxrenders exactly one<button>(the close X, ~line 220) and its only key handler isEscape. The − / reset / + cluster and the+/-/0bindings are the image viewer's (MarkdownRenderer.tsx:3237-3238,3406-3412,3496), not this one's.ctrl+wheel, WebKit asgesturestart/gesturechange.DiagramLightboxmonitors neither (grep -nE "onWheel|ctrlKey|gesturestart"→ no matches) andusePinchZoomreturns early onpointerType === 'mouse'. So "trackpad works, only mouse is unsupported" is not the situation.(pointer: coarse)and hooks onlygesturestart/gesturechange/gestureend/touchmove; the viewport meta governs the touch visual viewport) — but it cannot magnify fit-to-viewport content. At 200% the viewport's CSS-pixel width halves, thefixed inset-0box halves with it, and the SVG re-fits to that smaller box while each CSS pixel now covers 2 device pixels. The two cancel exactly: the labels come out the same apparent size.Net: the viewer opens at the state where its labels are smallest and a desktop user has no way out of it. The UX lane raised this on three heads of #6117; the third framing is the one that holds.
Buttons plus keys cover all three inputs in one change — a control is input-agnostic, so it answers mouse, keyboard and trackpad together. Handling
ctrl+wheelandgesturestartseparately would be a second code path for the trackpad alone; only add it if a pinch specifically is wanted after the controls exist.Neither is a regression — desktop was never able to magnify a fit-scaled diagram, before #6000 or after — but the second is a genuine dead end rather than a rough edge, and calling it merely "coarse" was wrong. The maintainer has ruled that this work lands here rather than in #6117, which ships with the touch path only; that PR carries the ruling and the measurements above.
Task
Lightbox.usePinchZoomalready carries the tap arithmetic used byDiagramLightbox, so the zoom half is small. The design step is the conflict:Lightboxowns one-finger swipe-dismiss, and a double-tap is two one-finger contacts in quick succession. Decide how a second tap is distinguished from the start of a swipe before wiring it, and keepMarkdownRenderer.lightboxSwipe.test.tsx(20 specs, 6 of them pinch) green — that suite is the contract for the existing gesture.DiagramLightbox. Reuse the image viewer's − / reset / + cluster and+/-bindings, driving the samesetZoomthe hook returns. NoteDiagramLightboxrenders throughcreatePortal(Lightboxdoes not), so tests querydocument.body, not the render container.Worth doing together: both are "make the two magnification surfaces offer the same affordances", and doing one alone leaves the table above asymmetric in the other direction.
Notes
usePinchZoom's helpers are module-private on purpose and its returned surface is exactly what the two consumers destructure. If this work needs another field, add it back deliberately rather than re-exporting the internals.Lightboxkeeps swipe-dismiss andDiagramLightboxkeeps drag-pan in their own components. Item 1 should respect it rather than folding swipe thresholds into the hook.