video-mode: a selector trim start never lands after a highlight (fixes main CI) - #40

Merged
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight
Aug 26, 2026
Merged

video-mode: a selector trim start never lands after a highlight (fixes main CI)#40
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight

Conversation

@mmkal

@mmkalmmkal commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Main CI has been red since #38 (a65cda0): reveals an expanding textarea one line at a time at its final geometry fails 2/2 there with frames 0-1 showing the filled text before the reveal — the render window starts after the fill. It never reproduces locally (3/3 green), which fits the cause:

#38 calibrates the render window from the calibration cover. On a slow runner the screencast trails the paints it maps by a few frames, and the cover-based offset inherits that lag — so a selector-driven trim start (trimStart: ["selector", ...]) can land past the first fill. The existing race guard only clamped a trim start back to a highlight when it landed within one frame after it; CI's lag is bigger.

Nothing acts on the app before it's ready, so any trim start after a recorded highlight is measurement error, and starting at the highlight is always harmless (the video opens on the action instead of a beat before it). The guard now clamps unconditionally:

// before: a start ≤ 1 frame after a highlight moved back to it; further = kept → opens post-action// after: a start after any highlight moves back to the earliest such highlight

No new spec: the failure is runner-timing-specific and this PR's CI run is the verification. Unrelated: uses a normal pointer tail after text cursor holds is a pre-existing local flake on main (1/3 fails with --repeat-each=3), untouched here.

Blocks #39's CI (inherited the same red).

🤖 Generated with Claude Code

Session: b7f6f792-6606-44be-9ec3-207eb762c4b6


Note

Medium Risk
Changes render trim boundaries for all selector-driven starts when highlights precede the computed start; behavior is intentional but affects every video-mode render path that sets sourceRange.start.

Overview
Fixes rendered videos that could open after the first recorded action when trimStart uses a selector or cover-based calibration lags on slow CI runners.

During finalization, if sourceRange.start is set but sits past one or more highlight timestamps, the render window start is pulled back to the earliest such highlight. The previous guard only did this when the gap was within one frame; multi-frame calibration/screencast lag is now covered too.

Comments in video-mode.ts spell out the rationale: a trim start after a highlight is treated as timing error, and clamping to the highlight is safe because it keeps the opening frame on the action instead of post-fill footage.

Reviewed by Cursor Bugbot for commit c737189. Bugbot is set up for automated code reviews on this repo. Configure here.

Since #38 the render window is calibrated from the calibration cover.
On a slow runner the screencast trails the paints it maps by a few
frames, and the cover-based offset inherits that lag, so a selector-
driven trim start could land past the first fill - the render then
opened on footage from AFTER the action (the filled textarea before its
reveal). CI has been red on main since #38 for exactly this
('reveals an expanding textarea one line at a time at its final
geometry', frames 0-1 showing dark text); it never reproduced locally.
The existing race guard clamped a trim start back to a highlight only
when it landed within one frame after it. Nothing acts on the app before
it is ready, so ANY trim start after a recorded highlight is measurement
error, and starting at the highlight is always harmless. Clamp
unconditionally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pkg-pr-new

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/middlewright@40

commit: c737189

@mmkal
mmkal marked this pull request as ready for review August 26, 2026 21:39
@mmkal
mmkal merged commit df84cb1 into mainAug 26, 2026
3 checks passed
mmkal added a commit that referenced this pull request Aug 28, 2026
…ghlight (main CI flake) (#41)
Follow-up to #40, which fixed the *after* case. Main still flakes on
`reveals an expanding textarea one line at a time at its final geometry`
(1/2 on main's latest run, 3/3 on #39) with the mirror case.
Diagnosed from the CI run's own artifacts (`gh run download`): the
selector trim start lands a sliver **before** the first highlight (36ms
/ 92ms / 54ms across the three attempts), so the render opens with ~1
frame of raw footage before the waitFor hold. On a slow runner the
screencast starts so late that its very first frames already show the
fill's result — the CI raw video is 2.0s long with text present at
t=0.04s — so that opening frame is the *filled, expanded* textarea, one
frame before the reveal's empty still:
First six rendered frames from the failing CI run — frame 0 is the
filled, expanded textarea; frames 1-5 are the hold with the empty
textarea:
![ci-textarea-first-frames.png](https://github.com/user-attachments/assets/ef479360-88d6-4f55-bc01-1be92891e6ec)
A lead-in shorter than the existing fill stabilization window (`max(0,
timelineOffset) + 3 frames`) isn't worth keeping and can't be trusted on
a lagging recorder, so the start now snaps forward to the first
highlight. Longer lead-ins are untouched; #40's after-case clamp stays.
Runner-timing-specific like #40 — this PR's CI run is the verification.
Blocks #39.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Session: `b7f6f792-6606-44be-9ec3-207eb762c4b6`
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes rendered video trim boundaries for selector-based starts on
slow runners; scoped to short lead-ins but affects pixel-accurate video
assertions if they depend on exact first frames.
> > **Overview**
> Extends **selector trim start** correction in `video-mode` render
finalization: after the existing clamp when trim start lands *after* a
highlight (#40), it now handles the mirror case when trim start sits a
**short sliver before** the first highlight.
> > If `sourceRange.start` is within the fill stabilization window
(`max(0, timelineOffset) + 3 frame durations`), it **snaps forward** to
`firstHighlightStart` instead of keeping ~1 frame of raw screencast.
That avoids opening on lagging-recorder frames that already show
post-fill UI (e.g. expanded textarea) before a fill-reveal hold.
> > Longer lead-ins are unchanged; only sub-tolerance gaps are adjusted.
> > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
52054c5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto 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.

1 participant

@mmkal
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

video-mode: a selector trim start never lands after a highlight (fixes main CI) - #40

Merged
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight
Aug 26, 2026
Merged

video-mode: a selector trim start never lands after a highlight (fixes main CI)#40
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight

Conversation

@mmkal

@mmkalmmkal commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Main CI has been red since #38 (a65cda0): reveals an expanding textarea one line at a time at its final geometry fails 2/2 there with frames 0-1 showing the filled text before the reveal — the render window starts after the fill. It never reproduces locally (3/3 green), which fits the cause:

#38 calibrates the render window from the calibration cover. On a slow runner the screencast trails the paints it maps by a few frames, and the cover-based offset inherits that lag — so a selector-driven trim start (trimStart: ["selector", ...]) can land past the first fill. The existing race guard only clamped a trim start back to a highlight when it landed within one frame after it; CI's lag is bigger.

Nothing acts on the app before it's ready, so any trim start after a recorded highlight is measurement error, and starting at the highlight is always harmless (the video opens on the action instead of a beat before it). The guard now clamps unconditionally:

// before: a start ≤ 1 frame after a highlight moved back to it; further = kept → opens post-action// after: a start after any highlight moves back to the earliest such highlight

No new spec: the failure is runner-timing-specific and this PR's CI run is the verification. Unrelated: uses a normal pointer tail after text cursor holds is a pre-existing local flake on main (1/3 fails with --repeat-each=3), untouched here.

Blocks #39's CI (inherited the same red).

🤖 Generated with Claude Code

Session: b7f6f792-6606-44be-9ec3-207eb762c4b6


Note

Medium Risk
Changes render trim boundaries for all selector-driven starts when highlights precede the computed start; behavior is intentional but affects every video-mode render path that sets sourceRange.start.

Overview
Fixes rendered videos that could open after the first recorded action when trimStart uses a selector or cover-based calibration lags on slow CI runners.

During finalization, if sourceRange.start is set but sits past one or more highlight timestamps, the render window start is pulled back to the earliest such highlight. The previous guard only did this when the gap was within one frame; multi-frame calibration/screencast lag is now covered too.

Comments in video-mode.ts spell out the rationale: a trim start after a highlight is treated as timing error, and clamping to the highlight is safe because it keeps the opening frame on the action instead of post-fill footage.

Reviewed by Cursor Bugbot for commit c737189. Bugbot is set up for automated code reviews on this repo. Configure here.

Since #38 the render window is calibrated from the calibration cover.
On a slow runner the screencast trails the paints it maps by a few
frames, and the cover-based offset inherits that lag, so a selector-
driven trim start could land past the first fill - the render then
opened on footage from AFTER the action (the filled textarea before its
reveal). CI has been red on main since #38 for exactly this
('reveals an expanding textarea one line at a time at its final
geometry', frames 0-1 showing dark text); it never reproduced locally.
The existing race guard clamped a trim start back to a highlight only
when it landed within one frame after it. Nothing acts on the app before
it is ready, so ANY trim start after a recorded highlight is measurement
error, and starting at the highlight is always harmless. Clamp
unconditionally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pkg-pr-new

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/middlewright@40

commit: c737189

@mmkal
mmkal marked this pull request as ready for review August 26, 2026 21:39
@mmkal
mmkal merged commit df84cb1 into mainAug 26, 2026
3 checks passed
mmkal added a commit that referenced this pull request Aug 28, 2026
…ghlight (main CI flake) (#41)
Follow-up to #40, which fixed the *after* case. Main still flakes on
`reveals an expanding textarea one line at a time at its final geometry`
(1/2 on main's latest run, 3/3 on #39) with the mirror case.
Diagnosed from the CI run's own artifacts (`gh run download`): the
selector trim start lands a sliver **before** the first highlight (36ms
/ 92ms / 54ms across the three attempts), so the render opens with ~1
frame of raw footage before the waitFor hold. On a slow runner the
screencast starts so late that its very first frames already show the
fill's result — the CI raw video is 2.0s long with text present at
t=0.04s — so that opening frame is the *filled, expanded* textarea, one
frame before the reveal's empty still:
First six rendered frames from the failing CI run — frame 0 is the
filled, expanded textarea; frames 1-5 are the hold with the empty
textarea:
![ci-textarea-first-frames.png](https://github.com/user-attachments/assets/ef479360-88d6-4f55-bc01-1be92891e6ec)
A lead-in shorter than the existing fill stabilization window (`max(0,
timelineOffset) + 3 frames`) isn't worth keeping and can't be trusted on
a lagging recorder, so the start now snaps forward to the first
highlight. Longer lead-ins are untouched; #40's after-case clamp stays.
Runner-timing-specific like #40 — this PR's CI run is the verification.
Blocks #39.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Session: `b7f6f792-6606-44be-9ec3-207eb762c4b6`
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes rendered video trim boundaries for selector-based starts on
slow runners; scoped to short lead-ins but affects pixel-accurate video
assertions if they depend on exact first frames.
> > **Overview**
> Extends **selector trim start** correction in `video-mode` render
finalization: after the existing clamp when trim start lands *after* a
highlight (#40), it now handles the mirror case when trim start sits a
**short sliver before** the first highlight.
> > If `sourceRange.start` is within the fill stabilization window
(`max(0, timelineOffset) + 3 frame durations`), it **snaps forward** to
`firstHighlightStart` instead of keeping ~1 frame of raw screencast.
That avoids opening on lagging-recorder frames that already show
post-fill UI (e.g. expanded textarea) before a fill-reveal hold.
> > Longer lead-ins are unchanged; only sub-tolerance gaps are adjusted.
> > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
52054c5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto 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.

1 participant

@mmkal
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

video-mode: a selector trim start never lands after a highlight (fixes main CI) - #40

Merged
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight
Aug 26, 2026
Merged

video-mode: a selector trim start never lands after a highlight (fixes main CI)#40
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight

Conversation

@mmkal

@mmkalmmkal commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Main CI has been red since #38 (a65cda0): reveals an expanding textarea one line at a time at its final geometry fails 2/2 there with frames 0-1 showing the filled text before the reveal — the render window starts after the fill. It never reproduces locally (3/3 green), which fits the cause:

#38 calibrates the render window from the calibration cover. On a slow runner the screencast trails the paints it maps by a few frames, and the cover-based offset inherits that lag — so a selector-driven trim start (trimStart: ["selector", ...]) can land past the first fill. The existing race guard only clamped a trim start back to a highlight when it landed within one frame after it; CI's lag is bigger.

Nothing acts on the app before it's ready, so any trim start after a recorded highlight is measurement error, and starting at the highlight is always harmless (the video opens on the action instead of a beat before it). The guard now clamps unconditionally:

// before: a start ≤ 1 frame after a highlight moved back to it; further = kept → opens post-action// after: a start after any highlight moves back to the earliest such highlight

No new spec: the failure is runner-timing-specific and this PR's CI run is the verification. Unrelated: uses a normal pointer tail after text cursor holds is a pre-existing local flake on main (1/3 fails with --repeat-each=3), untouched here.

Blocks #39's CI (inherited the same red).

🤖 Generated with Claude Code

Session: b7f6f792-6606-44be-9ec3-207eb762c4b6


Note

Medium Risk
Changes render trim boundaries for all selector-driven starts when highlights precede the computed start; behavior is intentional but affects every video-mode render path that sets sourceRange.start.

Overview
Fixes rendered videos that could open after the first recorded action when trimStart uses a selector or cover-based calibration lags on slow CI runners.

During finalization, if sourceRange.start is set but sits past one or more highlight timestamps, the render window start is pulled back to the earliest such highlight. The previous guard only did this when the gap was within one frame; multi-frame calibration/screencast lag is now covered too.

Comments in video-mode.ts spell out the rationale: a trim start after a highlight is treated as timing error, and clamping to the highlight is safe because it keeps the opening frame on the action instead of post-fill footage.

Reviewed by Cursor Bugbot for commit c737189. Bugbot is set up for automated code reviews on this repo. Configure here.

Since #38 the render window is calibrated from the calibration cover.
On a slow runner the screencast trails the paints it maps by a few
frames, and the cover-based offset inherits that lag, so a selector-
driven trim start could land past the first fill - the render then
opened on footage from AFTER the action (the filled textarea before its
reveal). CI has been red on main since #38 for exactly this
('reveals an expanding textarea one line at a time at its final
geometry', frames 0-1 showing dark text); it never reproduced locally.
The existing race guard clamped a trim start back to a highlight only
when it landed within one frame after it. Nothing acts on the app before
it is ready, so ANY trim start after a recorded highlight is measurement
error, and starting at the highlight is always harmless. Clamp
unconditionally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pkg-pr-new

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/middlewright@40

commit: c737189

@mmkal
mmkal marked this pull request as ready for review August 26, 2026 21:39
@mmkal
mmkal merged commit df84cb1 into mainAug 26, 2026
3 checks passed
mmkal added a commit that referenced this pull request Aug 28, 2026
…ghlight (main CI flake) (#41)
Follow-up to #40, which fixed the *after* case. Main still flakes on
`reveals an expanding textarea one line at a time at its final geometry`
(1/2 on main's latest run, 3/3 on #39) with the mirror case.
Diagnosed from the CI run's own artifacts (`gh run download`): the
selector trim start lands a sliver **before** the first highlight (36ms
/ 92ms / 54ms across the three attempts), so the render opens with ~1
frame of raw footage before the waitFor hold. On a slow runner the
screencast starts so late that its very first frames already show the
fill's result — the CI raw video is 2.0s long with text present at
t=0.04s — so that opening frame is the *filled, expanded* textarea, one
frame before the reveal's empty still:
First six rendered frames from the failing CI run — frame 0 is the
filled, expanded textarea; frames 1-5 are the hold with the empty
textarea:
![ci-textarea-first-frames.png](https://github.com/user-attachments/assets/ef479360-88d6-4f55-bc01-1be92891e6ec)
A lead-in shorter than the existing fill stabilization window (`max(0,
timelineOffset) + 3 frames`) isn't worth keeping and can't be trusted on
a lagging recorder, so the start now snaps forward to the first
highlight. Longer lead-ins are untouched; #40's after-case clamp stays.
Runner-timing-specific like #40 — this PR's CI run is the verification.
Blocks #39.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Session: `b7f6f792-6606-44be-9ec3-207eb762c4b6`
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes rendered video trim boundaries for selector-based starts on
slow runners; scoped to short lead-ins but affects pixel-accurate video
assertions if they depend on exact first frames.
> > **Overview**
> Extends **selector trim start** correction in `video-mode` render
finalization: after the existing clamp when trim start lands *after* a
highlight (#40), it now handles the mirror case when trim start sits a
**short sliver before** the first highlight.
> > If `sourceRange.start` is within the fill stabilization window
(`max(0, timelineOffset) + 3 frame durations`), it **snaps forward** to
`firstHighlightStart` instead of keeping ~1 frame of raw screencast.
That avoids opening on lagging-recorder frames that already show
post-fill UI (e.g. expanded textarea) before a fill-reveal hold.
> > Longer lead-ins are unchanged; only sub-tolerance gaps are adjusted.
> > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
52054c5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto 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.

1 participant

@mmkal
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

video-mode: a selector trim start never lands after a highlight (fixes main CI) - #40

Merged
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight
Aug 26, 2026
Merged

video-mode: a selector trim start never lands after a highlight (fixes main CI)#40
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight

Conversation

@mmkal

@mmkalmmkal commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Main CI has been red since #38 (a65cda0): reveals an expanding textarea one line at a time at its final geometry fails 2/2 there with frames 0-1 showing the filled text before the reveal — the render window starts after the fill. It never reproduces locally (3/3 green), which fits the cause:

#38 calibrates the render window from the calibration cover. On a slow runner the screencast trails the paints it maps by a few frames, and the cover-based offset inherits that lag — so a selector-driven trim start (trimStart: ["selector", ...]) can land past the first fill. The existing race guard only clamped a trim start back to a highlight when it landed within one frame after it; CI's lag is bigger.

Nothing acts on the app before it's ready, so any trim start after a recorded highlight is measurement error, and starting at the highlight is always harmless (the video opens on the action instead of a beat before it). The guard now clamps unconditionally:

// before: a start ≤ 1 frame after a highlight moved back to it; further = kept → opens post-action// after: a start after any highlight moves back to the earliest such highlight

No new spec: the failure is runner-timing-specific and this PR's CI run is the verification. Unrelated: uses a normal pointer tail after text cursor holds is a pre-existing local flake on main (1/3 fails with --repeat-each=3), untouched here.

Blocks #39's CI (inherited the same red).

🤖 Generated with Claude Code

Session: b7f6f792-6606-44be-9ec3-207eb762c4b6


Note

Medium Risk
Changes render trim boundaries for all selector-driven starts when highlights precede the computed start; behavior is intentional but affects every video-mode render path that sets sourceRange.start.

Overview
Fixes rendered videos that could open after the first recorded action when trimStart uses a selector or cover-based calibration lags on slow CI runners.

During finalization, if sourceRange.start is set but sits past one or more highlight timestamps, the render window start is pulled back to the earliest such highlight. The previous guard only did this when the gap was within one frame; multi-frame calibration/screencast lag is now covered too.

Comments in video-mode.ts spell out the rationale: a trim start after a highlight is treated as timing error, and clamping to the highlight is safe because it keeps the opening frame on the action instead of post-fill footage.

Reviewed by Cursor Bugbot for commit c737189. Bugbot is set up for automated code reviews on this repo. Configure here.

Since #38 the render window is calibrated from the calibration cover.
On a slow runner the screencast trails the paints it maps by a few
frames, and the cover-based offset inherits that lag, so a selector-
driven trim start could land past the first fill - the render then
opened on footage from AFTER the action (the filled textarea before its
reveal). CI has been red on main since #38 for exactly this
('reveals an expanding textarea one line at a time at its final
geometry', frames 0-1 showing dark text); it never reproduced locally.
The existing race guard clamped a trim start back to a highlight only
when it landed within one frame after it. Nothing acts on the app before
it is ready, so ANY trim start after a recorded highlight is measurement
error, and starting at the highlight is always harmless. Clamp
unconditionally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pkg-pr-new

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/middlewright@40

commit: c737189

@mmkal
mmkal marked this pull request as ready for review August 26, 2026 21:39
@mmkal
mmkal merged commit df84cb1 into mainAug 26, 2026
3 checks passed
mmkal added a commit that referenced this pull request Aug 28, 2026
…ghlight (main CI flake) (#41)
Follow-up to #40, which fixed the *after* case. Main still flakes on
`reveals an expanding textarea one line at a time at its final geometry`
(1/2 on main's latest run, 3/3 on #39) with the mirror case.
Diagnosed from the CI run's own artifacts (`gh run download`): the
selector trim start lands a sliver **before** the first highlight (36ms
/ 92ms / 54ms across the three attempts), so the render opens with ~1
frame of raw footage before the waitFor hold. On a slow runner the
screencast starts so late that its very first frames already show the
fill's result — the CI raw video is 2.0s long with text present at
t=0.04s — so that opening frame is the *filled, expanded* textarea, one
frame before the reveal's empty still:
First six rendered frames from the failing CI run — frame 0 is the
filled, expanded textarea; frames 1-5 are the hold with the empty
textarea:
![ci-textarea-first-frames.png](https://github.com/user-attachments/assets/ef479360-88d6-4f55-bc01-1be92891e6ec)
A lead-in shorter than the existing fill stabilization window (`max(0,
timelineOffset) + 3 frames`) isn't worth keeping and can't be trusted on
a lagging recorder, so the start now snaps forward to the first
highlight. Longer lead-ins are untouched; #40's after-case clamp stays.
Runner-timing-specific like #40 — this PR's CI run is the verification.
Blocks #39.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Session: `b7f6f792-6606-44be-9ec3-207eb762c4b6`
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes rendered video trim boundaries for selector-based starts on
slow runners; scoped to short lead-ins but affects pixel-accurate video
assertions if they depend on exact first frames.
> > **Overview**
> Extends **selector trim start** correction in `video-mode` render
finalization: after the existing clamp when trim start lands *after* a
highlight (#40), it now handles the mirror case when trim start sits a
**short sliver before** the first highlight.
> > If `sourceRange.start` is within the fill stabilization window
(`max(0, timelineOffset) + 3 frame durations`), it **snaps forward** to
`firstHighlightStart` instead of keeping ~1 frame of raw screencast.
That avoids opening on lagging-recorder frames that already show
post-fill UI (e.g. expanded textarea) before a fill-reveal hold.
> > Longer lead-ins are unchanged; only sub-tolerance gaps are adjusted.
> > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
52054c5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto 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.

1 participant

@mmkal
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

video-mode: a selector trim start never lands after a highlight (fixes main CI) - #40

Merged
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight
Aug 26, 2026
Merged

video-mode: a selector trim start never lands after a highlight (fixes main CI)#40
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight

Conversation

@mmkal

@mmkalmmkal commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Main CI has been red since #38 (a65cda0): reveals an expanding textarea one line at a time at its final geometry fails 2/2 there with frames 0-1 showing the filled text before the reveal — the render window starts after the fill. It never reproduces locally (3/3 green), which fits the cause:

#38 calibrates the render window from the calibration cover. On a slow runner the screencast trails the paints it maps by a few frames, and the cover-based offset inherits that lag — so a selector-driven trim start (trimStart: ["selector", ...]) can land past the first fill. The existing race guard only clamped a trim start back to a highlight when it landed within one frame after it; CI's lag is bigger.

Nothing acts on the app before it's ready, so any trim start after a recorded highlight is measurement error, and starting at the highlight is always harmless (the video opens on the action instead of a beat before it). The guard now clamps unconditionally:

// before: a start ≤ 1 frame after a highlight moved back to it; further = kept → opens post-action// after: a start after any highlight moves back to the earliest such highlight

No new spec: the failure is runner-timing-specific and this PR's CI run is the verification. Unrelated: uses a normal pointer tail after text cursor holds is a pre-existing local flake on main (1/3 fails with --repeat-each=3), untouched here.

Blocks #39's CI (inherited the same red).

🤖 Generated with Claude Code

Session: b7f6f792-6606-44be-9ec3-207eb762c4b6


Note

Medium Risk
Changes render trim boundaries for all selector-driven starts when highlights precede the computed start; behavior is intentional but affects every video-mode render path that sets sourceRange.start.

Overview
Fixes rendered videos that could open after the first recorded action when trimStart uses a selector or cover-based calibration lags on slow CI runners.

During finalization, if sourceRange.start is set but sits past one or more highlight timestamps, the render window start is pulled back to the earliest such highlight. The previous guard only did this when the gap was within one frame; multi-frame calibration/screencast lag is now covered too.

Comments in video-mode.ts spell out the rationale: a trim start after a highlight is treated as timing error, and clamping to the highlight is safe because it keeps the opening frame on the action instead of post-fill footage.

Reviewed by Cursor Bugbot for commit c737189. Bugbot is set up for automated code reviews on this repo. Configure here.

Since #38 the render window is calibrated from the calibration cover.
On a slow runner the screencast trails the paints it maps by a few
frames, and the cover-based offset inherits that lag, so a selector-
driven trim start could land past the first fill - the render then
opened on footage from AFTER the action (the filled textarea before its
reveal). CI has been red on main since #38 for exactly this
('reveals an expanding textarea one line at a time at its final
geometry', frames 0-1 showing dark text); it never reproduced locally.
The existing race guard clamped a trim start back to a highlight only
when it landed within one frame after it. Nothing acts on the app before
it is ready, so ANY trim start after a recorded highlight is measurement
error, and starting at the highlight is always harmless. Clamp
unconditionally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pkg-pr-new

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/middlewright@40

commit: c737189

@mmkal
mmkal marked this pull request as ready for review August 26, 2026 21:39
@mmkal
mmkal merged commit df84cb1 into mainAug 26, 2026
3 checks passed
mmkal added a commit that referenced this pull request Aug 28, 2026
…ghlight (main CI flake) (#41)
Follow-up to #40, which fixed the *after* case. Main still flakes on
`reveals an expanding textarea one line at a time at its final geometry`
(1/2 on main's latest run, 3/3 on #39) with the mirror case.
Diagnosed from the CI run's own artifacts (`gh run download`): the
selector trim start lands a sliver **before** the first highlight (36ms
/ 92ms / 54ms across the three attempts), so the render opens with ~1
frame of raw footage before the waitFor hold. On a slow runner the
screencast starts so late that its very first frames already show the
fill's result — the CI raw video is 2.0s long with text present at
t=0.04s — so that opening frame is the *filled, expanded* textarea, one
frame before the reveal's empty still:
First six rendered frames from the failing CI run — frame 0 is the
filled, expanded textarea; frames 1-5 are the hold with the empty
textarea:
![ci-textarea-first-frames.png](https://github.com/user-attachments/assets/ef479360-88d6-4f55-bc01-1be92891e6ec)
A lead-in shorter than the existing fill stabilization window (`max(0,
timelineOffset) + 3 frames`) isn't worth keeping and can't be trusted on
a lagging recorder, so the start now snaps forward to the first
highlight. Longer lead-ins are untouched; #40's after-case clamp stays.
Runner-timing-specific like #40 — this PR's CI run is the verification.
Blocks #39.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Session: `b7f6f792-6606-44be-9ec3-207eb762c4b6`
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes rendered video trim boundaries for selector-based starts on
slow runners; scoped to short lead-ins but affects pixel-accurate video
assertions if they depend on exact first frames.
> > **Overview**
> Extends **selector trim start** correction in `video-mode` render
finalization: after the existing clamp when trim start lands *after* a
highlight (#40), it now handles the mirror case when trim start sits a
**short sliver before** the first highlight.
> > If `sourceRange.start` is within the fill stabilization window
(`max(0, timelineOffset) + 3 frame durations`), it **snaps forward** to
`firstHighlightStart` instead of keeping ~1 frame of raw screencast.
That avoids opening on lagging-recorder frames that already show
post-fill UI (e.g. expanded textarea) before a fill-reveal hold.
> > Longer lead-ins are unchanged; only sub-tolerance gaps are adjusted.
> > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
52054c5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto 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.

1 participant

@mmkal
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

video-mode: a selector trim start never lands after a highlight (fixes main CI) - #40

Merged
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight
Aug 26, 2026
Merged

video-mode: a selector trim start never lands after a highlight (fixes main CI)#40
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight

Conversation

@mmkal

@mmkalmmkal commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Main CI has been red since #38 (a65cda0): reveals an expanding textarea one line at a time at its final geometry fails 2/2 there with frames 0-1 showing the filled text before the reveal — the render window starts after the fill. It never reproduces locally (3/3 green), which fits the cause:

#38 calibrates the render window from the calibration cover. On a slow runner the screencast trails the paints it maps by a few frames, and the cover-based offset inherits that lag — so a selector-driven trim start (trimStart: ["selector", ...]) can land past the first fill. The existing race guard only clamped a trim start back to a highlight when it landed within one frame after it; CI's lag is bigger.

Nothing acts on the app before it's ready, so any trim start after a recorded highlight is measurement error, and starting at the highlight is always harmless (the video opens on the action instead of a beat before it). The guard now clamps unconditionally:

// before: a start ≤ 1 frame after a highlight moved back to it; further = kept → opens post-action// after: a start after any highlight moves back to the earliest such highlight

No new spec: the failure is runner-timing-specific and this PR's CI run is the verification. Unrelated: uses a normal pointer tail after text cursor holds is a pre-existing local flake on main (1/3 fails with --repeat-each=3), untouched here.

Blocks #39's CI (inherited the same red).

🤖 Generated with Claude Code

Session: b7f6f792-6606-44be-9ec3-207eb762c4b6


Note

Medium Risk
Changes render trim boundaries for all selector-driven starts when highlights precede the computed start; behavior is intentional but affects every video-mode render path that sets sourceRange.start.

Overview
Fixes rendered videos that could open after the first recorded action when trimStart uses a selector or cover-based calibration lags on slow CI runners.

During finalization, if sourceRange.start is set but sits past one or more highlight timestamps, the render window start is pulled back to the earliest such highlight. The previous guard only did this when the gap was within one frame; multi-frame calibration/screencast lag is now covered too.

Comments in video-mode.ts spell out the rationale: a trim start after a highlight is treated as timing error, and clamping to the highlight is safe because it keeps the opening frame on the action instead of post-fill footage.

Reviewed by Cursor Bugbot for commit c737189. Bugbot is set up for automated code reviews on this repo. Configure here.

Since #38 the render window is calibrated from the calibration cover.
On a slow runner the screencast trails the paints it maps by a few
frames, and the cover-based offset inherits that lag, so a selector-
driven trim start could land past the first fill - the render then
opened on footage from AFTER the action (the filled textarea before its
reveal). CI has been red on main since #38 for exactly this
('reveals an expanding textarea one line at a time at its final
geometry', frames 0-1 showing dark text); it never reproduced locally.
The existing race guard clamped a trim start back to a highlight only
when it landed within one frame after it. Nothing acts on the app before
it is ready, so ANY trim start after a recorded highlight is measurement
error, and starting at the highlight is always harmless. Clamp
unconditionally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pkg-pr-new

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/middlewright@40

commit: c737189

@mmkal
mmkal marked this pull request as ready for review August 26, 2026 21:39
@mmkal
mmkal merged commit df84cb1 into mainAug 26, 2026
3 checks passed
mmkal added a commit that referenced this pull request Aug 28, 2026
…ghlight (main CI flake) (#41)
Follow-up to #40, which fixed the *after* case. Main still flakes on
`reveals an expanding textarea one line at a time at its final geometry`
(1/2 on main's latest run, 3/3 on #39) with the mirror case.
Diagnosed from the CI run's own artifacts (`gh run download`): the
selector trim start lands a sliver **before** the first highlight (36ms
/ 92ms / 54ms across the three attempts), so the render opens with ~1
frame of raw footage before the waitFor hold. On a slow runner the
screencast starts so late that its very first frames already show the
fill's result — the CI raw video is 2.0s long with text present at
t=0.04s — so that opening frame is the *filled, expanded* textarea, one
frame before the reveal's empty still:
First six rendered frames from the failing CI run — frame 0 is the
filled, expanded textarea; frames 1-5 are the hold with the empty
textarea:
![ci-textarea-first-frames.png](https://github.com/user-attachments/assets/ef479360-88d6-4f55-bc01-1be92891e6ec)
A lead-in shorter than the existing fill stabilization window (`max(0,
timelineOffset) + 3 frames`) isn't worth keeping and can't be trusted on
a lagging recorder, so the start now snaps forward to the first
highlight. Longer lead-ins are untouched; #40's after-case clamp stays.
Runner-timing-specific like #40 — this PR's CI run is the verification.
Blocks #39.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Session: `b7f6f792-6606-44be-9ec3-207eb762c4b6`
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes rendered video trim boundaries for selector-based starts on
slow runners; scoped to short lead-ins but affects pixel-accurate video
assertions if they depend on exact first frames.
> > **Overview**
> Extends **selector trim start** correction in `video-mode` render
finalization: after the existing clamp when trim start lands *after* a
highlight (#40), it now handles the mirror case when trim start sits a
**short sliver before** the first highlight.
> > If `sourceRange.start` is within the fill stabilization window
(`max(0, timelineOffset) + 3 frame durations`), it **snaps forward** to
`firstHighlightStart` instead of keeping ~1 frame of raw screencast.
That avoids opening on lagging-recorder frames that already show
post-fill UI (e.g. expanded textarea) before a fill-reveal hold.
> > Longer lead-ins are unchanged; only sub-tolerance gaps are adjusted.
> > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
52054c5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto 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.

1 participant

@mmkal
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

video-mode: a selector trim start never lands after a highlight (fixes main CI) - #40

Merged
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight
Aug 26, 2026
Merged

video-mode: a selector trim start never lands after a highlight (fixes main CI)#40
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight

Conversation

@mmkal

@mmkalmmkal commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Main CI has been red since #38 (a65cda0): reveals an expanding textarea one line at a time at its final geometry fails 2/2 there with frames 0-1 showing the filled text before the reveal — the render window starts after the fill. It never reproduces locally (3/3 green), which fits the cause:

#38 calibrates the render window from the calibration cover. On a slow runner the screencast trails the paints it maps by a few frames, and the cover-based offset inherits that lag — so a selector-driven trim start (trimStart: ["selector", ...]) can land past the first fill. The existing race guard only clamped a trim start back to a highlight when it landed within one frame after it; CI's lag is bigger.

Nothing acts on the app before it's ready, so any trim start after a recorded highlight is measurement error, and starting at the highlight is always harmless (the video opens on the action instead of a beat before it). The guard now clamps unconditionally:

// before: a start ≤ 1 frame after a highlight moved back to it; further = kept → opens post-action// after: a start after any highlight moves back to the earliest such highlight

No new spec: the failure is runner-timing-specific and this PR's CI run is the verification. Unrelated: uses a normal pointer tail after text cursor holds is a pre-existing local flake on main (1/3 fails with --repeat-each=3), untouched here.

Blocks #39's CI (inherited the same red).

🤖 Generated with Claude Code

Session: b7f6f792-6606-44be-9ec3-207eb762c4b6


Note

Medium Risk
Changes render trim boundaries for all selector-driven starts when highlights precede the computed start; behavior is intentional but affects every video-mode render path that sets sourceRange.start.

Overview
Fixes rendered videos that could open after the first recorded action when trimStart uses a selector or cover-based calibration lags on slow CI runners.

During finalization, if sourceRange.start is set but sits past one or more highlight timestamps, the render window start is pulled back to the earliest such highlight. The previous guard only did this when the gap was within one frame; multi-frame calibration/screencast lag is now covered too.

Comments in video-mode.ts spell out the rationale: a trim start after a highlight is treated as timing error, and clamping to the highlight is safe because it keeps the opening frame on the action instead of post-fill footage.

Reviewed by Cursor Bugbot for commit c737189. Bugbot is set up for automated code reviews on this repo. Configure here.

Since #38 the render window is calibrated from the calibration cover.
On a slow runner the screencast trails the paints it maps by a few
frames, and the cover-based offset inherits that lag, so a selector-
driven trim start could land past the first fill - the render then
opened on footage from AFTER the action (the filled textarea before its
reveal). CI has been red on main since #38 for exactly this
('reveals an expanding textarea one line at a time at its final
geometry', frames 0-1 showing dark text); it never reproduced locally.
The existing race guard clamped a trim start back to a highlight only
when it landed within one frame after it. Nothing acts on the app before
it is ready, so ANY trim start after a recorded highlight is measurement
error, and starting at the highlight is always harmless. Clamp
unconditionally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pkg-pr-new

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/middlewright@40

commit: c737189

@mmkal
mmkal marked this pull request as ready for review August 26, 2026 21:39
@mmkal
mmkal merged commit df84cb1 into mainAug 26, 2026
3 checks passed
mmkal added a commit that referenced this pull request Aug 28, 2026
…ghlight (main CI flake) (#41)
Follow-up to #40, which fixed the *after* case. Main still flakes on
`reveals an expanding textarea one line at a time at its final geometry`
(1/2 on main's latest run, 3/3 on #39) with the mirror case.
Diagnosed from the CI run's own artifacts (`gh run download`): the
selector trim start lands a sliver **before** the first highlight (36ms
/ 92ms / 54ms across the three attempts), so the render opens with ~1
frame of raw footage before the waitFor hold. On a slow runner the
screencast starts so late that its very first frames already show the
fill's result — the CI raw video is 2.0s long with text present at
t=0.04s — so that opening frame is the *filled, expanded* textarea, one
frame before the reveal's empty still:
First six rendered frames from the failing CI run — frame 0 is the
filled, expanded textarea; frames 1-5 are the hold with the empty
textarea:
![ci-textarea-first-frames.png](https://github.com/user-attachments/assets/ef479360-88d6-4f55-bc01-1be92891e6ec)
A lead-in shorter than the existing fill stabilization window (`max(0,
timelineOffset) + 3 frames`) isn't worth keeping and can't be trusted on
a lagging recorder, so the start now snaps forward to the first
highlight. Longer lead-ins are untouched; #40's after-case clamp stays.
Runner-timing-specific like #40 — this PR's CI run is the verification.
Blocks #39.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Session: `b7f6f792-6606-44be-9ec3-207eb762c4b6`
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes rendered video trim boundaries for selector-based starts on
slow runners; scoped to short lead-ins but affects pixel-accurate video
assertions if they depend on exact first frames.
> > **Overview**
> Extends **selector trim start** correction in `video-mode` render
finalization: after the existing clamp when trim start lands *after* a
highlight (#40), it now handles the mirror case when trim start sits a
**short sliver before** the first highlight.
> > If `sourceRange.start` is within the fill stabilization window
(`max(0, timelineOffset) + 3 frame durations`), it **snaps forward** to
`firstHighlightStart` instead of keeping ~1 frame of raw screencast.
That avoids opening on lagging-recorder frames that already show
post-fill UI (e.g. expanded textarea) before a fill-reveal hold.
> > Longer lead-ins are unchanged; only sub-tolerance gaps are adjusted.
> > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
52054c5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto 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.

1 participant

@mmkal
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

video-mode: a selector trim start never lands after a highlight (fixes main CI) - #40

Merged
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight
Aug 26, 2026
Merged

video-mode: a selector trim start never lands after a highlight (fixes main CI)#40
mmkal merged 1 commit into
mainfrom
fix-selector-trim-after-highlight

Conversation

@mmkal

@mmkalmmkal commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Main CI has been red since #38 (a65cda0): reveals an expanding textarea one line at a time at its final geometry fails 2/2 there with frames 0-1 showing the filled text before the reveal — the render window starts after the fill. It never reproduces locally (3/3 green), which fits the cause:

#38 calibrates the render window from the calibration cover. On a slow runner the screencast trails the paints it maps by a few frames, and the cover-based offset inherits that lag — so a selector-driven trim start (trimStart: ["selector", ...]) can land past the first fill. The existing race guard only clamped a trim start back to a highlight when it landed within one frame after it; CI's lag is bigger.

Nothing acts on the app before it's ready, so any trim start after a recorded highlight is measurement error, and starting at the highlight is always harmless (the video opens on the action instead of a beat before it). The guard now clamps unconditionally:

// before: a start ≤ 1 frame after a highlight moved back to it; further = kept → opens post-action// after: a start after any highlight moves back to the earliest such highlight

No new spec: the failure is runner-timing-specific and this PR's CI run is the verification. Unrelated: uses a normal pointer tail after text cursor holds is a pre-existing local flake on main (1/3 fails with --repeat-each=3), untouched here.

Blocks #39's CI (inherited the same red).

🤖 Generated with Claude Code

Session: b7f6f792-6606-44be-9ec3-207eb762c4b6


Note

Medium Risk
Changes render trim boundaries for all selector-driven starts when highlights precede the computed start; behavior is intentional but affects every video-mode render path that sets sourceRange.start.

Overview
Fixes rendered videos that could open after the first recorded action when trimStart uses a selector or cover-based calibration lags on slow CI runners.

During finalization, if sourceRange.start is set but sits past one or more highlight timestamps, the render window start is pulled back to the earliest such highlight. The previous guard only did this when the gap was within one frame; multi-frame calibration/screencast lag is now covered too.

Comments in video-mode.ts spell out the rationale: a trim start after a highlight is treated as timing error, and clamping to the highlight is safe because it keeps the opening frame on the action instead of post-fill footage.

Reviewed by Cursor Bugbot for commit c737189. Bugbot is set up for automated code reviews on this repo. Configure here.

Since #38 the render window is calibrated from the calibration cover.
On a slow runner the screencast trails the paints it maps by a few
frames, and the cover-based offset inherits that lag, so a selector-
driven trim start could land past the first fill - the render then
opened on footage from AFTER the action (the filled textarea before its
reveal). CI has been red on main since #38 for exactly this
('reveals an expanding textarea one line at a time at its final
geometry', frames 0-1 showing dark text); it never reproduced locally.
The existing race guard clamped a trim start back to a highlight only
when it landed within one frame after it. Nothing acts on the app before
it is ready, so ANY trim start after a recorded highlight is measurement
error, and starting at the highlight is always harmless. Clamp
unconditionally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pkg-pr-new

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/middlewright@40

commit: c737189

@mmkal
mmkal marked this pull request as ready for review August 26, 2026 21:39
@mmkal
mmkal merged commit df84cb1 into mainAug 26, 2026
3 checks passed
mmkal added a commit that referenced this pull request Aug 28, 2026
…ghlight (main CI flake) (#41)
Follow-up to #40, which fixed the *after* case. Main still flakes on
`reveals an expanding textarea one line at a time at its final geometry`
(1/2 on main's latest run, 3/3 on #39) with the mirror case.
Diagnosed from the CI run's own artifacts (`gh run download`): the
selector trim start lands a sliver **before** the first highlight (36ms
/ 92ms / 54ms across the three attempts), so the render opens with ~1
frame of raw footage before the waitFor hold. On a slow runner the
screencast starts so late that its very first frames already show the
fill's result — the CI raw video is 2.0s long with text present at
t=0.04s — so that opening frame is the *filled, expanded* textarea, one
frame before the reveal's empty still:
First six rendered frames from the failing CI run — frame 0 is the
filled, expanded textarea; frames 1-5 are the hold with the empty
textarea:
![ci-textarea-first-frames.png](https://github.com/user-attachments/assets/ef479360-88d6-4f55-bc01-1be92891e6ec)
A lead-in shorter than the existing fill stabilization window (`max(0,
timelineOffset) + 3 frames`) isn't worth keeping and can't be trusted on
a lagging recorder, so the start now snaps forward to the first
highlight. Longer lead-ins are untouched; #40's after-case clamp stays.
Runner-timing-specific like #40 — this PR's CI run is the verification.
Blocks #39.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Session: `b7f6f792-6606-44be-9ec3-207eb762c4b6`
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes rendered video trim boundaries for selector-based starts on
slow runners; scoped to short lead-ins but affects pixel-accurate video
assertions if they depend on exact first frames.
> > **Overview**
> Extends **selector trim start** correction in `video-mode` render
finalization: after the existing clamp when trim start lands *after* a
highlight (#40), it now handles the mirror case when trim start sits a
**short sliver before** the first highlight.
> > If `sourceRange.start` is within the fill stabilization window
(`max(0, timelineOffset) + 3 frame durations`), it **snaps forward** to
`firstHighlightStart` instead of keeping ~1 frame of raw screencast.
That avoids opening on lagging-recorder frames that already show
post-fill UI (e.g. expanded textarea) before a fill-reveal hold.
> > Longer lead-ins are unchanged; only sub-tolerance gaps are adjusted.
> > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
52054c5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto 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.

1 participant

@mmkal