Fix video-seek, leak, and correctness issues found in codebase review - #106
Merged
Conversation
Functional bugs: - Clicking a data-table row did not seek the video. The only model -> video time sync was gated on the `scrubbing` flag, which is set only by the slider's pointer DragListener, so the row-click handler moved the model clock while the displayed frame stayed put. - Keyboard scrubbing had the same root cause: Scenery's Slider routes keyboard input through AccessibleValueHandler's startInput/endInput, not the startDrag/endDrag that set `scrubbing`. Both are fixed by centralizing the sync: VideoPlayerNode now links currentTimeProperty -> videoElement.currentTime, guarded by the scrubbing flag and a half-frame deadband so it does not fight timeupdate during playback. startInput/endInput are wired on the scrubber as well. - PlaybackControlsNode orphaned an HSlider at construction. The duration link fired synchronously before this.children was assigned, so replaceScrubber()'s indexOf() returned -1 and the new slider was dropped undisposed, permanently retaining listeners on currentTimeProperty (measured: +4 per construct/dispose cycle). Fixed by using lazyLink and by disposing an uninsertable replacement instead of dropping it. - The OpenCV worker leaked a cv.Mat on every template capture: the intermediate header from blurred.roi() was never deleted. WASM heap, so it is never reclaimed by the JS GC. - Changing the frame rate after digitizing left stored points labelled with indices from the old rate, so everything keyed on `frame` addressed the wrong point. TrackingModel.retimeTrackPoints() re-derives each index from the point's authoritative timestamp; frame collisions from a lowered rate keep the earlier point, matching addPointToTrack()'s first-wins policy. Correctness and robustness: - TableRenderer.update() claimed to rebuild on colour/locale changes but only compared unit and track IDs, relying on DataTableNode observing one representative colour and one representative string. Those fire while their siblings still hold old values. The renderer now compares the full colour/label snapshot, and DataTableNode observes every contributing property. - OpenCVTracker.send() silently overwrote the pending resolve/reject pair, stranding the earlier Promise forever; a superseded request is now rejected. - The worker's no-template branch posted x/y as null against a response type declaring numbers. - calculateTickSpacing() clamped to rangeLength / 20, which could override the 1-2-5 "nice number" result at targetTicks = 15 (the scrubber path). Spacing now escalates through the nice sequence instead. i18n and accessibility: - The compile-time locale parity check covered only en/fr, leaving Spanish free to drift even though the language picker exposes it. - Four accessible names were snapshotted with .value, so they did not re-translate under supportsDynamicLocale. - KinematicsGraphNode leaked the VBox wrapping the track checkboxes on every rebuild. - CSV export revoked the object URL synchronously after click(), which cancels the download in some browsers. Tests: added specs for TrackExporter, ModelViewTransformFactory (including the retransform round-trip invariant), VideoPlaybackModel, frame retiming, and tick spacing; extended the memory-leak suite with listener-count regressions for PlaybackControlsNode. Each new regression test was verified to fail against the unfixed code. 22 -> 61 tests. Docs: corrected the sample-video path, the stale FFmpeg references in the CSP rationale, and the shipped-locale list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vg2oNMWc4fmuQWmHh6Bxqo
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
Contributor
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Functional bugs:
Clicking a data-table row did not seek the video. The only model -> video
time sync was gated on the
scrubbingflag, which is set only by theslider's pointer DragListener, so the row-click handler moved the model
clock while the displayed frame stayed put.
Keyboard scrubbing had the same root cause: Scenery's Slider routes
keyboard input through AccessibleValueHandler's startInput/endInput, not
the startDrag/endDrag that set
scrubbing.Both are fixed by centralizing the sync: VideoPlayerNode now links
currentTimeProperty -> videoElement.currentTime, guarded by the scrubbing
flag and a half-frame deadband so it does not fight timeupdate during
playback. startInput/endInput are wired on the scrubber as well.
PlaybackControlsNode orphaned an HSlider at construction. The duration
link fired synchronously before this.children was assigned, so
replaceScrubber()'s indexOf() returned -1 and the new slider was dropped
undisposed, permanently retaining listeners on currentTimeProperty
(measured: +4 per construct/dispose cycle). Fixed by using lazyLink and by
disposing an uninsertable replacement instead of dropping it.
The OpenCV worker leaked a cv.Mat on every template capture: the
intermediate header from blurred.roi() was never deleted. WASM heap, so it
is never reclaimed by the JS GC.
Changing the frame rate after digitizing left stored points labelled with
indices from the old rate, so everything keyed on
frameaddressed thewrong point. TrackingModel.retimeTrackPoints() re-derives each index from
the point's authoritative timestamp; frame collisions from a lowered rate
keep the earlier point, matching addPointToTrack()'s first-wins policy.
Correctness and robustness:
only compared unit and track IDs, relying on DataTableNode observing one
representative colour and one representative string. Those fire while
their siblings still hold old values. The renderer now compares the full
colour/label snapshot, and DataTableNode observes every contributing
property.
stranding the earlier Promise forever; a superseded request is now
rejected.
declaring numbers.
the 1-2-5 "nice number" result at targetTicks = 15 (the scrubber path).
Spacing now escalates through the nice sequence instead.
i18n and accessibility:
free to drift even though the language picker exposes it.
re-translate under supportsDynamicLocale.
every rebuild.
cancels the download in some browsers.
Tests: added specs for TrackExporter, ModelViewTransformFactory (including
the retransform round-trip invariant), VideoPlaybackModel, frame retiming,
and tick spacing; extended the memory-leak suite with listener-count
regressions for PlaybackControlsNode. Each new regression test was verified
to fail against the unfixed code. 22 -> 61 tests.
Docs: corrected the sample-video path, the stale FFmpeg references in the
CSP rationale, and the shipped-locale list.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01Vg2oNMWc4fmuQWmHh6Bxqo