743086d3cb
Two fixes for discussion #348. Carousel-style swipe The lightbox previously snapped to the next photo on swipe, then showed a loading spinner while the new image fetched — choppy compared with the reference video the reporter shared. The current photo is now rendered inside a 3-slide track (prev/current/next). As the finger drags, the track follows; on release the track animates to the neighbouring slot or springs back if the gesture didn't pass the threshold. Because the prev/next AuthenticatedImages render up front, the browser starts fetching them while the user is still on the current photo, so there's no loader flash on commit. - Phase machine ('idle' | 'dragging' | 'committing' | 'springing') drives the track's transform/transition. Commit + spring use a 280ms cubic-bezier ease. - Percentage-based transforms avoid measuring container width before the first paint. Commit threshold (read from the ref on demand) is max(60px, 20% of width) OR a fast flick (>0.5 px/ms with at least 40px of movement). - transitionend advances currentIndex with wrap-around and resets the track in one batch — slot contents rotate and the track snaps from the commit position back to centered with transition: none, so the visible image stays put. No flicker. - Vertical-cancel (>24px dy) abandons the drag and springs back so the user keeps the gesture they intended. - touch-action: none on the carousel container stops the browser fighting us with edge-swipe back navigation and native pinch-zoom. - Pinch starting mid-drag springs the track back smoothly so the image doesn't jerk under the second finger. - onTouchCancel covers system-interrupted gestures (incoming call etc). - dragX === 0 short-circuits to 'idle' instead of 'springing' so taps don't get stuck waiting for a transitionend that never fires. - Neighbour slides use a simplified AuthenticatedImage render (no canvas/fragment-grid pipeline) since they're only on screen during the swipe; the current slide keeps the full protection chain. - Neighbour videos render their thumbnail rather than spinning up a VideoPlayer. When the *current* photo is a video, the carousel is bypassed entirely — single VideoPlayer + no swipe handlers — because sliding a video element during a drag is awkward and adds nothing. - Removed the now-redundant imageLoaded state + spinner; AuthenticatedImage already shows a placeholder while loading. Keyboard arrows and the on-screen Prev/Next buttons still snap (no animation) — animating them would have required input queuing for fast double-presses, and the request was specifically about swipe. "Swipe to navigate" hint Removed the mobile-only overlay text. Swipe is universal in image viewers; the instruction read like training wheels and competed with the photo for attention.