Skip to content

fix(slideshow): stop on last slide in linear mode; reshuffle endlessly in shuffle mode#293

Merged
lstein merged 2 commits into
masterfrom
lstein/fix/slideshow-last-slide-wrap
Jun 4, 2026
Merged

fix(slideshow): stop on last slide in linear mode; reshuffle endlessly in shuffle mode#293
lstein merged 2 commits into
masterfrom
lstein/fix/slideshow-last-slide-wrap

Conversation

@lstein
Copy link
Copy Markdown
Owner

@lstein lstein commented Jun 4, 2026

Problem

The autoplay slideshow misbehaved at the end of the slide list:

  1. Linear mode — on reaching the last image, the slideshow jumped back ~10 slides instead of stopping. Swiper 11's autoplay, on reaching the end with loop: false, calls slideTo(0) — which lands on the first slide of the windowed in-memory buffer (a subset, not the album's first image), so it appeared to leap backward.
  2. Shuffle mode — on a small album it showed every image once and then froze (the play indicator still showed "running" but slides stopped changing). The random picker refused any image already loaded, so once all images were in the buffer nothing could be appended.

Fix

Linear end-of-list — We now own end-of-list behavior instead of relying on Swiper's autoplay. When slideState.resolveOffset(+1) reports there is no next slide (genuine end of list, wrap off), the slideNextTransitionStart handler stops autoplay so the slideshow rests on the last slide. stopOnLastSlide: true is kept as a config backstop for the "start the slideshow while already parked on the last slide" case. With wrap on, resolveOffset(+1) returns the wrapped index, so the next slide is appended ahead and the slideshow loops to the start.

Shuffle reshuffling deck — Replaced the "pick a random index not already loaded" logic with a reshuffling bag: every image is dealt exactly once per cycle in random order; when the bag empties it refills and reshuffles into a fresh order, so shuffle runs indefinitely. A cross-cycle guard prevents an image repeating twice in a row (including avoiding an immediate repeat of the on-screen image when shuffle first starts). trimShuffleBacklog keeps the buffer bounded by the high-water mark — re-dealing images across cycles would otherwise grow the DOM without limit — and does so without stop/starting autoplay (so the play/pause icon doesn't flicker every slide).

Behavior after the fix

  • Linear + wrap off → stops on the last slide
  • Linear + wrap on → wraps to first and continues
  • Shuffle → runs forever: shows all images, reshuffles, repeats in a new order

Testing

  • Reworked the shuffle unit tests for the new bag semantics (deals every image once per cycle then reshuffles with no boundary repeat; appends a fresh slide even when all images are already loaded — the no-stall regression).
  • Added end-of-list coverage: linear stop at genuine end (wrap off), wrapped-slide append (wrap on), shuffle never stops at the last index.
  • Full frontend suite passes (343 tests); npm run lint and npm run format:check clean.
  • Verified live in the app across all three modes.

🤖 Generated with Claude Code

lstein and others added 2 commits June 4, 2026 00:02
…shuffle)

The autoplay slideshow misbehaved at the end of the slide list:

- Linear mode reaching the last image jumped back ~10 slides instead of
  stopping. Swiper's autoplay, on reaching the end with loop off, calls
  slideTo(0) — the first slide of the windowed in-memory buffer, not the
  album start. We now own end-of-list: when resolveOffset(+1) reports no
  next slide (genuine end, wrap off), the slideNextTransitionStart handler
  stops autoplay so the show rests on the last slide. stopOnLastSlide:true
  is kept as a config backstop for the start-while-already-on-last case.
  Wrap on continues to append the wrapped slide ahead and loops.

- Shuffle mode on a small album showed every image once and then froze
  (indicator still "running") because the old random picker refused any
  image already loaded — once all were in the buffer, nothing could be
  appended. Replaced it with a reshuffling "bag": every image is dealt
  once per cycle in random order, then the bag refills and reshuffles for
  a fresh order, so shuffle runs indefinitely. A cross-cycle guard avoids
  immediate repeats. trimShuffleBacklog bounds the buffer (re-dealing would
  otherwise grow the DOM without limit) without stop/starting autoplay.

Tests: reworked swiper-shuffle tests for the bag semantics and added
end-of-list coverage (linear stop, wrap append, shuffle no-stall).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@lstein lstein enabled auto-merge (squash) June 4, 2026 04:06
@lstein lstein merged commit b4f9b6c into master Jun 4, 2026
10 checks passed
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