Skip to content

Play videos in place over the slide's still - #408

Merged
lstein merged 6 commits into
masterfrom
lstein/feature/video-player-in-place
Sep 29, 2026
Merged

lstein merged 6 commits into
masterfrom
lstein/feature/video-player-in-place

Conversation

@lstein

@lstein lstein commented Sep 29, 2026

Copy link
Copy Markdown
Owner

Summary

The video player no longer jumps to a different size when you press play, and moving to another slide no longer takes two steps.

Swiper view. The player is pinned over the exact on-screen rectangle of the poster's picture (letterboxing excluded), with no backdrop, so the still turns into the moving picture in place. It follows the slide on resize, drawer changes and partial drags. Navigating closes the player and moves on in one step: the swiper's ◀ ▶ buttons, ←/→/PageUp/PageDown, or a horizontal swipe/drag across the video (drags starting on the native control bar are left to scrubbing). × / Esc / Backspace still close without moving.

Grid view. Videos play in their tile the same way. Pressing play on a tile also selects it. localStorage["photomap.gridVideoInTile"] = "false" restores the lightbox for comparison.

While a video plays in place:

  • The Control/Search panels fade out, because the browser offers no way to move the native control bar above them.
  • The playing slide's favourites star and play badge are hidden; the star sat under the player's close button.

Fullscreen from the native controls.

  • Closing the player always exits fullscreen first. Before, a hidden fullscreen element left an invisible layer that swallowed every click.
  • While fullscreen, grid rebuilds, slideChanged, swipes and the navigate-away keys are ignored, so the arrows seek again.
  • If the poster disappears or goes off screen, the player waits up to 4s for the same image's tile to come back after a rebuild and re-pins to it, adopting the grid's new Swiper instance. It also keeps that instance's keyboard disabled.

Grid resize fix (pre-existing bug). Any resize that changed the grid's shape, including entering and leaving fullscreen, brought the grid back one page early and left a duplicate of every tile. The cause: the resize handler reset the slides before creating the new Swiper instance, so the new instance started at slide 0, on the prepended previous page. It now creates the instance first, then resets, the same order the thumbnail-size handler uses. As a side effect this also stops the handler passing a global index where loadBatch expects a search index.

Test plan

  • npm test: 952 pass, including new tests for in-place pinning, following, grace/re-pin, keyboard and swipe navigation, fullscreen, the slideshow-resume rule and badge selection. Lint and format are clean.
  • Driven in headless Chromium against a real video album:
    • frame rect equals the poster's picture rect;
    • the arrow key, next button, mouse drag and resize behave as described;
    • a grid tile on page 4 survives a resize and fullscreen round trip on the same page and re-pins;
    • the page is clickable after exiting fullscreen;
    • a resize with no video returns to the same page with no duplicates.
  • Adversarial fresh-context review; all confirmed findings fixed in the last commit.
  • Manual: real fullscreen button in Chrome/Firefox, and Safari on iPad (native video fullscreen path).

🤖 Generated with Claude Code

lstein and others added 6 commits September 27, 2026 22:26
Pressing play in the swiper now pins the player over the exact rectangle
the poster occupies, with no backdrop, so the still turns into the moving
picture without resizing. Navigation leaves the video: the swiper's
arrows, the arrow/Page keys and a swipe across the video close the player
and move to the neighbouring slide. Grid tiles play in their tile too
(localStorage photomap.gridVideoInTile=false restores the lightbox).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in place

Dropping the Control/Search panels beneath the video looked broken where
it covered only part of one, and the browser gives no way to move the
native control bar above them. Fade them out for the duration instead.
The playing slide's favourites star is hidden too: on a grid tile it sat
under the player's close button.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Entering fullscreen from the native controls resizes the window, which
makes the grid discard every tile and its Swiper instance. The follow
loop saw its tile vanish and closed the player while the video was still
the fullscreen element, leaving an invisible top-layer element over the
page that swallowed every click.

- Closing the player now exits fullscreen first.
- While the player is fullscreen, rebuilds, slideChanged, and the
  navigate-away keys/swipe are ignored (the arrows seek again).
- A vanished poster is looked up again by global index in the same
  swiper container, for a few seconds, and the player re-pins to it and
  adopts the new Swiper instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The resize handler reset the slides before creating the new Swiper
instance. The reset built the pages at the old geometry and scrolled past
the page it prepends; the instance created afterwards started at slide 0,
i.e. that previous page, and because creating it clears loadedImageIndices
but keeps the slides, the batch loads that followed appended a duplicate
of every tile. Any resize that changed the grid's shape - including
entering and leaving fullscreen - brought the grid back one page early.

Create the instance first, then reset, as the thumbnail-size handler does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A poster that is found but off screen is waited for, like a missing
  one, instead of closing at once: a grid rebuild puts its new instance at
  slide 0 over the old slides before the reset settles. Real navigation
  still closes immediately via slideChanged.
- Playing a grid tile selects it, so rebuilds reopen on its page and the
  drawer describes the clip that is playing.
- Keep the keyboard of whatever Swiper instance now owns the container
  disabled, every frame, in lightbox mode and fullscreen too; a recreated
  grid instance otherwise paged the grid on the arrows meant to seek.
- Open as a lightbox when the poster has no on-screen picture, instead of
  closing synchronously and leaving an invisible, audible player.
- Leaving a video by navigating no longer resumes a paused slideshow;
  only an explicit dismissal does.
- Capture a mouse drag once it moves so a swipe can end outside a small
  tile; scale the control-bar exclusion to the frame.
- The panel fade-in transition rule has zero specificity, so it no
  longer overrides .hidden-fullscreen's transition.
- Test reset clears body/slide classes and returns the close button.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lstein
lstein enabled auto-merge (squash) September 29, 2026 01:07
@lstein
lstein merged commit 087c3fe into master Sep 29, 2026
10 checks passed
@lstein
lstein deleted the lstein/feature/video-player-in-place branch September 29, 2026 01:20
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