Skip to content

Commit ddb3062

Browse files
karngyanclaude
andcommitted
fix(ui): scrubber drag listener leak and caption boundary
Scrubber attached pointermove/pointerup to window on drag start but only removed them on pointerup. If the component unmounted mid-drag (e.g. compact <-> desktop layout swap remounts a different Scrubber), the listeners outlived it and kept calling actions.seek. Track the active drag cleanup in a ref and run it on unmount; also release on pointercancel for touch. Caption fallback scan treated a cue as active at exactly its end time (now <= endTime). The HTML active-cue rule is start <= now < end, so the ending cue could briefly stack with the next one. Use strict <. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 396917b commit ddb3062

2 files changed

Lines changed: 9 additions & 1 deletion

File tree

src/ui/scrubber.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ export function Scrubber() {
2020

2121
const trackRef = useRef<HTMLDivElement | null>(null)
2222
const previewRef = useRef<HTMLDivElement | null>(null)
23+
// Cleanup for an in-flight drag, so window listeners don't outlive the
24+
// component if it unmounts mid-drag (e.g. layout swaps compact <-> desktop).
25+
const dragCleanup = useRef<(() => void) | null>(null)
26+
useEffect(() => () => dragCleanup.current?.(), [])
2327
const [hover, setHover] = useState<{ x: number; time: number } | null>(null)
2428
const [sb, setSb] = useState<Storyboard | null>(null)
2529
// measured (post-transform) preview width + track width, to keep the preview
@@ -55,9 +59,13 @@ export function Scrubber() {
5559
const up = () => {
5660
window.removeEventListener("pointermove", move)
5761
window.removeEventListener("pointerup", up)
62+
window.removeEventListener("pointercancel", up)
63+
dragCleanup.current = null
5864
}
65+
dragCleanup.current = up
5966
window.addEventListener("pointermove", move)
6067
window.addEventListener("pointerup", up)
68+
window.addEventListener("pointercancel", up)
6169
}
6270

6371
const onPointerMove = (e: ReactPointerEvent<HTMLDivElement>) => {

src/util/captions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export function activeCueText(
3434
const hits: TextTrackCue[] = []
3535
for (let j = 0; j < all.length; j++) {
3636
const c = all[j]
37-
if (c && c.startTime <= now && now <= c.endTime) hits.push(c)
37+
if (c && c.startTime <= now && now < c.endTime) hits.push(c)
3838
}
3939
const text = joinCues(hits)
4040
if (text) return text

0 commit comments

Comments
 (0)