Skip to content

feat(media): navigate attachment bundles and galleries in the viewer - #1968

Open
Th-Underscore wants to merge 1 commit into
SableClient:devfrom
Th-Underscore:feat/media-viewer-navigation
Open

feat(media): navigate attachment bundles and galleries in the viewer#1968
Th-Underscore wants to merge 1 commit into
SableClient:devfrom
Th-Underscore:feat/media-viewer-navigation

Conversation

@Th-Underscore

Copy link
Copy Markdown

Description

The zoom-in media viewer gains prev/next navigation on desktop:

  • Attachment bundles: Zooming into an image that's part of a grouped run of consecutive media messages enables ArrowLeft/ArrowRight (and chevron buttons) to move through that run. Bundles derive from the timeline's own rendered grouping, so what you navigate matches what the timeline collapsed.
  • Multi-image galleries: A single gallery message (multiple images sent as one event) navigates the same way.
  • Wrap-around with edge hints: Next at the last image wraps to the first (and vice versa); the button pointing off a bundle edge rests dimmed (~20% opacity) but brightens on approach so it stays clearly clickable.
  • Hover-revealed controls: On desktop the chevrons stay invisible until the cursor is over the modal (150 ms fade); arrow keys never summon them; Tab focus reveals them for keyboard users. The buttons deliberately don't inherit Sable's global button:hover lift, which previously caused an infinite hover-bounce where the hitbox chased the cursor.
  • Neighbour preloading without memory spikes: A bounded sliding window warms 2 images ahead then 2 behind, recomputed as you navigate and cancelled on move/close - it only fills Sable's media caches / the HTTP cache and never retains blobs or object URLs, so even a large bundle costs at most ~2 extra images beyond what's visible. Web + encrypted bundles are skipped (their cache can't be warmed without duplicate downloads).
  • Keep-stale rendering: The current image stays mounted underneath until the next resolves (spinner overlaid), eliminating blank-frame flicker; combined with preloading most swaps are instant.

Staged composer attachments: clicking a staged image preview opens the same viewer for all staged images as their own local bundle - newest-first matching the tile strip, wrap-around, edge-dimmed buttons. Save works directly from the local File (no download round-trip); removing attachments while open closes cleanly; non-image attachments don't open the viewer.

Tested on the desktop app, Ubuntu 24.04.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation - .changeset
  • My changes generate no new warnings

AI disclosure:

  • Partially AI assisted (clarify which code was AI assisted and briefly explain what it does).
  • Fully AI generated (explain what all the generated code does in moderate detail).

I designed and directed the feature and tested it in the GUI; the code itself was AI-generated.

Bundle membership reuses the timeline collapse predicate (same sender + media type + time window) over the rendered processed rows (mediaBundle.ts pure helpers) rather than raw event arrays, as raw arrays interleave non-media rows, which broke naive adjacency. Gallery items come from the gallery content's item list in one pass. Navigation arithmetic is shared via a small wrapIndex helper in $utils/common; keyboard handling ignores events while inputs/buttons/menu items hold focus. Preloading resolves each neighbour through Sable's normal media pipeline so encryption keys register and the loopback cache warms exactly as if it had been displayed.

@Th-Underscore
Th-Underscore force-pushed the feat/media-viewer-navigation branch from 0cdbfdc to 95f188a Compare August 27, 2026 03:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant