fix(media viewer): lightbox focuses itself on open (keys worked only after a click); timeline viewer gets +/-/0 keys, double-click zoom and dialog semantics (#164)
CI / Build & Quality Checks (push) Successful in 1m31s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 5s
CI / Trigger Desktop Build (push) Successful in 7s
CI / Playwright smoke (e2e) (push) Successful in 2m19s

Gallery lightbox: FocusTrap had initialFocus:false, so focus stayed on the
tile behind the overlay and ←/→/Esc/+/- were dead until the user clicked
inside. Timeline ImageViewer: no keyboard zoom, no role/aria-modal/label,
focus landed on the <img>; now mirrors the lightbox (+ = / - / 0,
double-click toggles 1×↔2×, role=dialog aria-modal labelled by the file
name, focuses on open).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
2026-09-18 21:29:24 -04:00
co-authored by Claude Opus 5
parent f2f498425b
commit 53823f5466
2 changed files with 35 additions and 3 deletions
+7 -1
View File
@@ -302,6 +302,7 @@ function Lightbox({
// Pan is only active for a zoomed-in image; usePan resets its offset when this
// flips false (i.e. back to 1x, on navigation, or on a video).
const { pan, cursor, onMouseDown, onTouchStart } = usePan(isImage && zoom !== 1);
const dialogRef = useRef<HTMLDivElement>(null);
const toggleZoom = useCallback(() => setZoom((z) => (z === 1 ? 2 : 1)), [setZoom]);
// Reset zoom when navigating to another item (and thus pan, via usePan).
@@ -346,12 +347,17 @@ function Lightbox({
<Overlay open backdrop={<OverlayBackdrop />}>
<FocusTrap
focusTrapOptions={{
initialFocus: false,
// Focus the dialog itself on open: with `initialFocus: false` focus
// stayed on the gallery tile behind the overlay, so ←/→/Esc/+/-
// (handled by onKeyDown below) did nothing until the user clicked
// inside the viewer (Gitea #164).
initialFocus: () => dialogRef.current ?? false,
clickOutsideDeactivates: false,
escapeDeactivates: false,
}}
>
<div
ref={dialogRef}
role="dialog"
aria-modal
aria-label="Media viewer"