Never take a post-click screenshot when a pre-click frame exists
Template tests / tests (push) Successful in 1m48s
Template tests / tests (pull_request) Successful in 1m55s

The remaining 'captured slightly after the click' reports came from the
fresh-shot fallback, which grabs the screen when the click is processed
(after it). The previous lead change made that fallback *more* likely: a
frame now had to be >=120ms before the click to qualify, so on machines
where the capture stream can't always keep a frame that old buffered, more
clicks fell through to the post-click shot.

Make the click-lead a two-tier preference instead of a hard gate in
selectFrameForClick:
1. newest frame captured at least leadMs before the click (ideal margin), else
2. newest frame captured before the click at all.
Only when no pre-click frame exists does the caller fresh-shot. leadMs is
threaded through the stream backend to the worker so both selection paths
agree. Verified end to end: frames land ~120-170ms before each click,
markers stay at 0.00%, and the 8-click burst still saves all 8.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Iisyourdad
2026-06-12 08:40:33 -05:00
parent 5b89b5c927
commit 34cc358902
7 changed files with 84 additions and 28 deletions
+29 -8
View File
@@ -125,14 +125,7 @@ function frameUsableForClick(frame, {
return startedAt <= clickTime + startSlackMs;
}
/**
* Best already-buffered frame for a click: the newest frame that qualifies
* under frameUsableForClick. Buffered frames are by definition completed, so
* in-flight acceptance never applies here. Returns null when nothing
* qualifies and the caller must wait for the in-flight grab or fall back to
* a fresh shot.
*/
function selectFrameForClick(frames, opts = {}) {
function newestUsableFrame(frames, opts) {
let best = null;
for (const frame of frames || []) {
if (!frameUsableForClick(frame, { ...opts, allowInFlight: false })) continue;
@@ -141,6 +134,34 @@ function selectFrameForClick(frames, opts = {}) {
return best;
}
/**
* Best already-buffered frame for a click, in two tiers:
* 1. with a click lead (opts.leadMs > 0): the newest frame captured at least
* leadMs *before* the click, so the step shows the screen the user was
* about to act on — clear of the click's own onset;
* 2. failing that, the newest frame captured before the click at all.
*
* The two tiers matter for correctness, not just polish: the lead is a
* *preference*, never a hard gate. If it were a gate, a click with no frame
* old enough to satisfy the lead would fall through to the caller's fresh
* shot — which captures the screen *after* the click. The tier-2 fallback
* guarantees that as long as any pre-click frame exists, we use it rather
* than shooting post-click. Buffered frames are always completed, so
* in-flight acceptance never applies here.
*/
function selectFrameForClick(frames, opts = {}) {
const leadMs = Math.max(0, Number(opts.leadMs) || 0);
const clickAt = Number.isFinite(opts.clickAt) ? opts.clickAt : Date.now();
if (leadMs > 0) {
// Widen the staleness budget by the lead so a frame that was fresh
// enough for the real click is still fresh enough for the lead target.
const maxAgeMs = (opts.maxAgeMs == null ? DEFAULT_MAX_AGE_MS : opts.maxAgeMs) + leadMs;
const led = newestUsableFrame(frames, { ...opts, clickAt: clickAt - leadMs, maxAgeMs });
if (led) return led;
}
return newestUsableFrame(frames, { ...opts, clickAt });
}
const api = {
FrameRing,
frameUsableForClick,