Clamp goPrev / goNext instead of wrapping the index, and hide the
left chevron on the first image and the right chevron on the last
one. Arrow keys, swipe gestures, and the on-screen buttons all
behave like a linear gallery now, so users get a clear cue that
they have reached the end instead of unexpectedly looping back.
- MessageInlineVideo (new): custom-controlled inline video that disables
the iOS Safari / Chromium native overlays entirely and reimplements
the essentials: tap-to-play, centered play affordance while paused,
bottom bar with play/pause + current time + drag-to-scrub progress
bar + remaining time + fullscreen. Pointer events with pointer
capture cover both mouse and touch scrubbing, including dragging
past the bar's bounds. The element listens to 'seeked' as well as
'timeupdate' so external currentTime writes paint the bar even when
the video is paused, and the goFullscreen callback synchronously
syncs React state on close so the inline progress reflects the user's
fullscreen playhead with no perceptible delay.
- VideoBubble: replace the inline <video controls> with
MessageInlineVideo and thread postId through openVideo so the
fullscreen overlay can attach the download pill to the right post.
- VideoPlayer overlay: replace its <video controls> with
MessageInlineVideo size='lg', removing the iOS native arrows / PiP /
mute / overflow controls. The overlay supplies its own large
download pill and a beefier close button.
- AttachmentDownloadPill: new 'size' prop ('sm' default 30 px, 'lg'
44 px with 22 px icon and text-[14px]) for overlay surfaces where
the affordance can breathe and should feel touch-friendly.
- ImageLightbox: drop the inline LightboxDownloadButton and use the
shared AttachmentDownloadPill size='lg' instead, with a matching
larger close button. Unused imports cleaned up.
Successful downloads no longer pop a 'downloadOk' toast across all
the download sites (PopularRankList, RecommendedCard, FileDocBubble,
ImageLightbox). The browser's native download UI already confirms
the action, and the toast was redundant noise. Failure toasts and
the spinner state remain so users still see errors.
Bump the previous/next chevron buttons from translucent white tint
to a solid-ish black with a subtle ring + shadow, so the controls
stay legible on light or busy image backgrounds.