Skip to content

fix(player): stabilize iOS Safari controls toggle on tap#632

Merged
stackia merged 4 commits into
mainfrom
cursor/fix-ios-player-controls-6e0a
Jul 9, 2026
Merged

fix(player): stabilize iOS Safari controls toggle on tap#632
stackia merged 4 commits into
mainfrom
cursor/fix-ios-player-controls-6e0a

Conversation

@stackia

@stackia stackia commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Summary

Simplify player controls visibility to fix flaky show/hide on iOS Safari.

Previously, video click toggle raced with touch-synthesized mousemove/mouseleave, so a single tap could show then immediately hide controls. The new model drops click toggle and leave-based hide entirely.

Controls visibility

Show / keep alive (resets 3s timer):

  • mouseenter / mousemove on the player surface (includes iOS tap-synthesized mouse events)
  • mouseenter on the controls toolbar
  • Keyboard digit entry / Escape when hidden
  • Channel switch / Document PiP entry

Hide:

  • 3s idle timer (only automatic path)
  • Escape when controls are visible

Test results (local)

Verified on Chromium against devlab + rtp2httpd (http://127.0.0.1:5140/player, channel HLS-TS (h264-mp2)), with strict timing (~0.4s after each action):

Scenario Result
Idle ~3s → auto-hide PASS
mousemove over video → show PASS
Leave to sidebar → stay visible; hide after ~3s idle PASS
Click video center → no toggle; hide after ~3s idle PASS
Escape → show/hide PASS
Still mouse on toolbar → hides after ~3s (expected: enter resets once, no hover-hold) PASS

Not retested on a physical iOS Safari device in this run; touch wake relies on WebKit synthesizing mousemove from taps, which is the same path exercised above for show/keep-alive.

Open in Web Open in Cursor 

Ignore touch-synthesized mouse move/leave events that were racing the
video click toggle, keep showControls in a ref for consistent toggles,
and disable pointer events on hidden overlays so taps reach the video.

Co-authored-by: Stackie Jia <jsq2627@gmail.com>
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Documentation preview

The documentation preview has been deployed for this pull request.

cursoragent and others added 2 commits July 9, 2026 09:16
Stop hiding player controls on mouse leave; auto-hide is driven solely
by the idle timer. Drop the redundant controls mouseenter handler.

Co-authored-by: Stackie Jia <jsq2627@gmail.com>
Drop video click toggle and touch-mousemove suppression. Showing
controls relies on mouseenter/mousemove (including iOS tap-synthesized
move); hiding stays timer-only plus Escape.

Co-authored-by: Stackie Jia <jsq2627@gmail.com>
@stackia stackia marked this pull request as ready for review July 9, 2026 09:21
Co-authored-by: Stackie Jia <jsq2627@gmail.com>
@stackia stackia merged commit dc0f457 into main Jul 9, 2026
11 checks passed
@stackia stackia deleted the cursor/fix-ios-player-controls-6e0a branch July 9, 2026 09:48
stackia added a commit that referenced this pull request Jul 9, 2026
…ols (#634)

## Summary

Reworks player controls visibility so it matches expected behavior on
both desktop and mobile, while keeping the iOS tap stability from #632.

#632 fixed flaky iOS show/hide by dropping **click-toggle** and
**leave-to-hide** entirely, leaving only `mousemove` + a 3s timer. That
regressed those interactions on every platform. This PR brings them back
with a cleaner model.

### Root cause of the original iOS flicker
A single tap on iOS makes WebKit synthesize *compatibility mouse events*
— `mousemove`, `mouseleave`, **and** `click` all fire from one tap. The
old `onMouseMove`=show + `onMouseLeave`=hide + `onClick`=toggle raced
through all three, so a tap showed then immediately hid controls.

### The fix
Drive hover through **Pointer Events gated on `pointerType`**. iOS
compatibility mouse events are legacy `MouseEvent`s and never
re-dispatch as `PointerEvent`s, so ignoring `pointerType === "touch"` in
the hover handlers removes the race with no timing heuristics or
conflict detection.

## Behavior

| Action | Desktop (mouse/pen) | Mobile (touch) |
|---|---|---|
| Pointer enters player | show | — (no hover) |
| Move inside | reset 3s timer | — |
| Leave player | hide | — |
| Click / tap on video | toggle | toggle |
| Idle 3s | auto-hide | auto-hide |

On mobile there is no hover state, so tap-to-toggle is the natural
equivalent and the standard mobile video pattern.

## Notes
- Click handler stays on the `<video>` element so clicks on
toolbar/overlay buttons don't bubble into a toggle.
- `tsc --noEmit` and `biome check` both pass.
- `src/embedded_web_data.h` is intentionally not included; rebuild the
Web UI to regenerate it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.

2 participants