fix(player): clamp scrubber progress when postMessage frame exceeds duration (#700)

The postMessage state path set `_currentTime` without clamping, while the
direct timeline path already used `Math.min(currentTime, _duration)`. A
final-frame state message with a frame count slightly past the end would
set `_currentTime > _duration`, causing the progress bar (position:
absolute, no overflow guard) to bleed out of the scrubber track and
visually cover the volume button, and the time display to show values
like "0:05 / 0:04".

- Clamp `_currentTime` in `_onMessage` to match the direct timeline path
- Clamp defensively in `updateTime` so the display layer never overflows
- Add `overflow: hidden` + `min-width: 0` to `.hfp-scrubber` as a CSS
  safety net; remove now-redundant `border-radius` from `.hfp-progress`
  (parent `overflow: hidden` handles clipping to the rounded shape)
- Apply the same `overflow: hidden` fix to `.hfp-volume-slider` for
  consistency; remove redundant `border-radius` from `.hfp-volume-fill`
- Add regression test covering the postMessage over-duration case

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
terencecho
2026-05-09 22:23:19 -07:00
committed by GitHub
co-authored by Claude Sonnet 4.6
parent 811f309ea5
commit dd375e2784
4 changed files with 34 additions and 5 deletions
+3 -2
View File
@@ -247,11 +247,13 @@ export const PLAYER_STYLES = /* css */ `
.hfp-scrubber {
flex: 1;
min-width: 0;
height: var(--hfp-scrubber-height, 4px);
background: var(--hfp-scrubber-bg, rgba(255, 255, 255, 0.3));
border-radius: var(--hfp-scrubber-radius, 2px);
cursor: pointer;
position: relative;
overflow: hidden;
}
.hfp-scrubber:hover {
@@ -264,7 +266,6 @@ export const PLAYER_STYLES = /* css */ `
left: 0;
height: 100%;
background: var(--hfp-accent, #fff);
border-radius: var(--hfp-scrubber-radius, 2px);
pointer-events: none;
}
@@ -401,6 +402,7 @@ export const PLAYER_STYLES = /* css */ `
border-radius: var(--hfp-scrubber-radius, 2px);
cursor: pointer;
position: relative;
overflow: hidden;
margin-left: 4px;
margin-right: 4px;
}
@@ -411,7 +413,6 @@ export const PLAYER_STYLES = /* css */ `
left: 0;
height: 100%;
background: var(--hfp-accent, #fff);
border-radius: var(--hfp-scrubber-radius, 2px);
pointer-events: none;
}
`;