Skip to content

[codex] Fix cursor sync after recording pause#399

Merged
webadderall merged 2 commits intomainfrom
codex/fix-cursor-pause-resume-sync
May 1, 2026
Merged

[codex] Fix cursor sync after recording pause#399
webadderall merged 2 commits intomainfrom
codex/fix-cursor-pause-resume-sync

Conversation

@webadderall
Copy link
Copy Markdown
Collaborator

@webadderall webadderall commented May 1, 2026

What changed

Fix cursor telemetry timing so pausing and resuming a recording no longer desynchronizes the cursor in the editor.

Why

The video timeline already removes paused time, but cursor telemetry was still being timestamped against wall-clock elapsed time. After a pause/resume boundary, cursor events drifted later than the recorded video.

Root cause

Cursor sampling and cursor interaction events continued to measure elapsed time from the original recording start without subtracting paused duration.

Implementation

  • add a pause-aware cursor capture clock in the Electron recording state
  • stop advancing cursor sampling and interaction timestamps while recording is paused
  • wire pause/resume from the recorder hook into cursor capture IPC
  • add a regression test covering paused elapsed-time accounting
  • remove an unused local in useScreenRecorder so the branch typechecks cleanly

Impact

Recordings that include pauses should keep cursor motion and click effects aligned with the video timeline in the editor after resume.

Validation

  • npx tsc -p tsconfig.json --noEmit
  • npx vitest --run electron/ipc/cursor/telemetry.test.ts src/hooks/useScreenRecorder.test.ts

Summary by CodeRabbit

  • New Features
    • Added pause and resume controls for cursor capture during recording sessions; paused time is excluded from the recording timeline.
  • Bug Fixes / Reliability
    • Improved pause/resume coordination so recording state and cursor sampling stay in sync; failures now roll back to preserve recorder state.
  • Tests
    • Added tests to validate pause/resume timing and prevent duplicate state transitions.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 1, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5896c443-700c-48bc-b33e-9c153917d25e

📥 Commits

Reviewing files that changed from the base of the PR and between 667efdc and 9df44c7.

📒 Files selected for processing (5)
  • electron/electron-env.d.ts
  • electron/ipc/cursor/telemetry.ts
  • electron/ipc/register/recording.ts
  • electron/preload.ts
  • src/hooks/useScreenRecorder.ts

📝 Walkthrough

Walkthrough

Adds renderer-exposed pause/resume cursor-capture APIs and implements pause-aware cursor telemetry: new IPC handlers, state fields, elapsed-time calculation that excludes paused intervals, sampling short-circuits when paused, and integration into recording pause/resume flows with tests.

Changes

Cohort / File(s) Summary
API & Preload
electron/electron-env.d.ts, electron/preload.ts
Adds pauseCursorCapture(boundaryMs?) and resumeCursorCapture(boundaryMs?) to window.electronAPI and forwards calls to IPC.
IPC Handlers & Registration
electron/ipc/register/recording.ts
Registers pause-cursor-capture and resume-cursor-capture handlers; takes boundary samples and resets cursor clock when recording starts/stops.
Telemetry Clock & Sampling
electron/ipc/cursor/telemetry.ts, electron/ipc/cursor/interaction.ts
Introduces pause-aware clock functions (resetCursorCaptureClock, pauseCursorCapture, resumeCursorCapture, isCursorCapturePaused, getCursorCaptureElapsedMs); sampling now skips when paused and uses adjusted elapsed timestamps. Interaction handlers use getCursorCaptureElapsedMs.
IPC State
electron/ipc/state.ts
Adds cursorCaptureAccumulatedPausedMs and cursorCapturePauseStartedAtMs with setters to track paused duration and pause start time.
Tests
electron/ipc/cursor/telemetry.test.ts
New Vitest suite validating pause/resume clock behavior, elapsed-time adjustments, and idempotence of repeated state transitions.
Hook Integration
src/hooks/useScreenRecorder.ts
Coordinates recording pause/resume with cursor capture via window.electronAPI.pauseCursorCapture/resumeCursorCapture(boundaryMs) and adds rollback/error handling on failure.

Sequence Diagram(s)

sequenceDiagram
    participant Renderer as Renderer (Hook/UI)
    participant Preload as Preload (contextBridge)
    participant Main as Main Process (ipcMain)
    participant Telemetry as Telemetry Module
    participant State as IPC State

    Renderer->>Preload: pauseCursorCapture(boundaryMs?)
    Preload->>Main: invoke('pause-cursor-capture', boundaryMs?)
    Main->>Telemetry: sampleCursorPoint(boundaryMs?)  %% sample at boundary
    Main->>Telemetry: pauseCursorCapture(pausedAtMs)
    Telemetry->>State: setCursorCapturePauseStartedAtMs(pausedAtMs)
    Telemetry->>State: setCursorCaptureAccumulatedPausedMs(updated)
    Telemetry-->>Main: { success: true }
    Main-->>Preload: result
    Preload-->>Renderer: Promise resolves

    Renderer->>Preload: resumeCursorCapture(boundaryMs?)
    Preload->>Main: invoke('resume-cursor-capture', boundaryMs?)
    Main->>Telemetry: resumeCursorCapture(resumedAtMs)
    Telemetry->>State: apply accumulated paused duration
    Main->>Telemetry: sampleCursorPoint(afterResumeMs)
    Telemetry-->>Main: { success: true }
    Main-->>Preload: result
    Preload-->>Renderer: Promise resolves
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

Checked

Poem

🐰
Paused the clock, I nibble time away,
Resumed with hops to carry on the day,
No double ticks, no sneaky drift in sight,
Cursor trails stay honest, neat, and light.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: fixing cursor synchronization after recording pause/resume.
Description check ✅ Passed The description covers all key sections: what changed, why it was needed, root cause, implementation details, impact, and validation steps. All required template sections are addressed with substantive content.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-cursor-pause-resume-sync

Review rate limit: 9/10 reviews remaining, refill in 6 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@webadderall webadderall marked this pull request as ready for review May 1, 2026 06:49
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/hooks/useScreenRecorder.ts (1)

1430-1466: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Keep the recorder clock and cursor clock on the same pause boundary.

These branches now record markRecordingPaused/Resumed(Date.now()) only after awaiting the cursor IPC, so getRecordingDurationMs() and the Windows pauseSegments pick up IPC latency instead of the actual recorder pause/resume edge. The catch blocks also still flip local pause state after a cursor IPC failure, which reintroduces cursor/video desync on the error path. Capture a single boundaryMs at the moment the recorder pauses/resumes, pass that through to cursor capture, and only commit local pause state if the cursor transition succeeds (or roll the recorder transition back).

Also applies to: 1473-1509

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useScreenRecorder.ts` around lines 1430 - 1466, When
pausing/resuming, capture a single boundaryMs (const boundaryMs = Date.now())
immediately at the moment you perform the recorder transition (e.g. right after
calling mediaRecorder.current.pause()/resume() and
webcamRecorder.current.pause()/resume(), or after successful
window.electronAPI.pauseNativeScreenRecording()/resumeNativeScreenRecording()),
then pass that boundaryMs into the cursor IPC
(window.electronAPI.pauseCursorCapture(boundaryMs) /
resumeCursorCapture(boundaryMs)). Only call
markRecordingPaused(boundaryMs)/markRecordingResumed(boundaryMs) and
setPaused(...) after the cursor IPC resolves successfully; if the cursor IPC
fails, roll the recorder/native transition back (call
mediaRecorder.current.resume()/pause() and
webcamRecorder.current.resume()/pause() as needed and, if you called
pauseNativeScreenRecording()/resumeNativeScreenRecording(), call the opposite
native API to revert) and log the cursor error. Ensure this change is applied in
the blocks that call pauseNativeScreenRecording, pauseCursorCapture,
mediaRecorder.current, webcamRecorder.current, markRecordingPaused,
markRecordingResumed, and setPaused.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/hooks/useScreenRecorder.ts`:
- Around line 1430-1466: When pausing/resuming, capture a single boundaryMs
(const boundaryMs = Date.now()) immediately at the moment you perform the
recorder transition (e.g. right after calling
mediaRecorder.current.pause()/resume() and
webcamRecorder.current.pause()/resume(), or after successful
window.electronAPI.pauseNativeScreenRecording()/resumeNativeScreenRecording()),
then pass that boundaryMs into the cursor IPC
(window.electronAPI.pauseCursorCapture(boundaryMs) /
resumeCursorCapture(boundaryMs)). Only call
markRecordingPaused(boundaryMs)/markRecordingResumed(boundaryMs) and
setPaused(...) after the cursor IPC resolves successfully; if the cursor IPC
fails, roll the recorder/native transition back (call
mediaRecorder.current.resume()/pause() and
webcamRecorder.current.resume()/pause() as needed and, if you called
pauseNativeScreenRecording()/resumeNativeScreenRecording(), call the opposite
native API to revert) and log the cursor error. Ensure this change is applied in
the blocks that call pauseNativeScreenRecording, pauseCursorCapture,
mediaRecorder.current, webcamRecorder.current, markRecordingPaused,
markRecordingResumed, and setPaused.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 87986be3-cc3b-4f80-a401-f5491ebe9633

📥 Commits

Reviewing files that changed from the base of the PR and between bebac5c and 667efdc.

📒 Files selected for processing (8)
  • electron/electron-env.d.ts
  • electron/ipc/cursor/interaction.ts
  • electron/ipc/cursor/telemetry.test.ts
  • electron/ipc/cursor/telemetry.ts
  • electron/ipc/register/recording.ts
  • electron/ipc/state.ts
  • electron/preload.ts
  • src/hooks/useScreenRecorder.ts

@webadderall webadderall merged commit 5570c11 into main May 1, 2026
1 of 2 checks passed
Sign up for free to 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