Skip to content

feat: implement pause and resume recording#509

Closed
Rodriq wants to merge 2 commits intosiddharthvaddem:mainfrom
Rodriq:feature/pause-and-resume-recording
Closed

feat: implement pause and resume recording#509
Rodriq wants to merge 2 commits intosiddharthvaddem:mainfrom
Rodriq:feature/pause-and-resume-recording

Conversation

@Rodriq
Copy link
Copy Markdown

@Rodriq Rodriq commented Apr 30, 2026

Pull Request Template

Description

Implemented the ability to pause and resume screen recordings. This update modifies the MediaRecorder stream handling and introduces seamless cursor telemetry synchronization by offsetting the capture time using accumulatedDurationMs passed from the Renderer to the Main process. It also adds a new global keyboard shortcut (Cmd/Ctrl+Alt+P) and dynamically updates the system tray menu to reflect the active recording state.

Motivation

Users need the flexibility to temporarily pause a recording to skip unwanted segments without having to stop the recording and stitch multiple video files together later. This feature ensures a smoother recording experience while maintaining perfect synchronization between the recorded video and the custom cursor overlay telemetry.

Type of Change

  • New Feature
  • Bug Fix
  • Refactor / Code Cleanup
  • Documentation Update
  • Other (please specify)

Related Issue(s)

Fixess #508

Screenshots / Video

Added pause resume functionality
image
image

Testing

To verify these changes:

  1. Launch the application locally (npm run dev or build).
  2. Start a new screen recording.
  3. Pause the recording using the system tray menu option or the global shortcut Cmd/Ctrl+Alt+P.
  4. Verify that the tray icon and menu update to reflect the "paused" state.
  5. Resume the recording using the tray menu or shortcut.
  6. Move the mouse around while recording, then stop the recording.
  7. Play back the recorded video to ensure the cursor overlay is perfectly synchronized with the video, even across the paused duration.

Checklist

  • I have performed a self-review of my code.
  • I have added any necessary screenshots or videos.
  • I have linked related issue(s) and updated the changelog if applicable.

Thank you for contributing!

Summary by CodeRabbit

  • New Features

    • Pause/resume now supported as explicit recording states (recording/paused/stopped)
    • Configurable global shortcut to toggle pause/resume (defaults to Ctrl+Alt+P)
    • Tray adds a toggle pause/resume action alongside stop
  • Bug Fixes / Improvements

    • More reliable telemetry handling across pause/resume so samples persist correctly
  • Tests

    • Added unit test covering resume-same-session telemetry behavior

@Rodriq Rodriq requested a review from siddharthvaddem as a code owner April 30, 2026 21:21
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

Recording control shifts from a boolean flag to a three-state enum ("recording" | "paused" | "stopped"), updating IPC, preload, main/tray UI, shortcuts, and cursor-telemetry session lifecycle; adds tray pause-toggle hook and a configurable global pause/resume shortcut.

Changes

Cohort / File(s) Summary
API Surface & Preload
electron/electron-env.d.ts, electron/preload.ts
setRecordingState signature changed from (recording: boolean, ...) to (state: "recording" | "paused" | "stopped", recordingId?: number, elapsedMs?: number); added onTogglePauseRecordingFromTray(callback): () => void.
IPC Handlers & Telemetry
electron/ipc/handlers.ts
Recording IPC handler switched to string state protocol; telemetry session lifecycle updated (start/sampling on "recording", pause sampling on "paused", end session on "stopped"). onRecordingStateChange callback type updated accordingly; removed save-shortcuts handler.
Main Process & Tray UI
electron/main.ts
Tray menu and tooltip logic refactored to derive from three-state model; added tray toggle-pause action (toggle-pause-recording-from-tray) and stop action; loads/registers configurable global shortcut from shortcuts.json and exposes save-shortcuts IPC endpoint.
Renderer Hook
src/hooks/useScreenRecorder.ts
Hook calls updated to use three-state API and pass elapsedMs; finalizeRecording reports "stopped"; tray pause-toggle handler wired with proper cleanup.
Shortcuts Config
src/lib/shortcuts.ts
Added togglePauseRecording action to SHORTCUT_ACTIONS, DEFAULT_SHORTCUTS, and SHORTCUT_LABELS (default Ctrl+Alt+P-like binding).
Cursor Telemetry Buffer & Tests
src/lib/cursorTelemetryBuffer.ts, src/lib/cursorTelemetryBuffer.test.ts
startSession(recordingId) becomes a no-op when same recordingId is already active to preserve samples; added test covering “resume same session” behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • siddharthvaddem
  • FabLrc

Poem

🎛️ a boolean was feeling too basic, so we leveled up the state,
pause, resume, stop — the tray now holds the gate.
global shortcut hums (ctrl+alt+p by default), lowkey epic,
telemetry keeps time, samples stay realistic.
nice and tidy — or at least, slightly less cursed.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% 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 concisely describes the main feature being implemented: pause and resume recording functionality.
Description check ✅ Passed The description covers all required template sections: description, motivation, type of change, related issue, screenshots, testing steps, and checklist items are all present and complete.
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.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 70cbd9027c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread electron/ipc/handlers.ts
// timestamp only if the renderer didn't supply one, so the
// buffer always has a stable key per session.
const id = typeof recordingId === "number" ? recordingId : Date.now();
cursorTelemetryBuffer.startSession(id);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve telemetry session when resuming recording

Avoid calling startSession() on every transition to "recording": this path now runs both at initial start and after pause, and startSession() clears the active sample buffer. In a pause→resume flow, all cursor telemetry captured before the pause is dropped, so the final overlay only contains post-resume movement. This breaks cursor/video sync for any recording that is paused at least once.

Useful? React with 👍 / 👎.

Comment thread electron/main.ts
parts.push(key);
const accelerator = parts.join("+");
try {
globalShortcut.register(accelerator, () => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Check global shortcut registration result

globalShortcut.register() returns false when an accelerator is unavailable (it does not throw), but this code ignores the return value and only handles exceptions. If the chosen binding is already taken by another app/OS, the UI will appear to save successfully while pause/resume never triggers globally, and because unregisterAll() runs first this can also leave users with no working global pause shortcut.

Useful? React with 👍 / 👎.

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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
electron/main.ts (1)

438-465: 💤 Low value

works but a couple nits

  1. config: any is kinda loose — consider typing it as Partial<ShortcutsConfig> or at least Record<string, unknown> for a bit more safety
  2. the single-char check on line 451 (binding.key.length === 1) means special keys like "Space" or "F1" won't register properly. currently fine since togglePauseRecording uses "p", but might bite you if someone customizes it to a function key later

lowkey the silent catch on line 444 is fine for "file doesn't exist" scenarios — fallback to defaults is reasonable.

🔧 optional: slightly tighter typing
-		let config: any = {};
+		let config: Partial<Record<string, { key?: string; ctrl?: boolean; alt?: boolean; shift?: boolean }>> = {};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@electron/main.ts` around lines 438 - 465, In updateGlobalShortcuts, replace
the loose any on config with a tighter type (e.g., Partial<ShortcutsConfig> or
Record<string, unknown>) and update the binding.key handling so special keys
like "Space" or "F1" are accepted: only uppercase single-character keys
(binding.key.length === 1 ? binding.key.toUpperCase() : binding.key) and
otherwise use the provided name verbatim; ensure you read binding from the typed
config (e.g., const binding = (config.togglePauseRecording as
Partial<ShortcutBinding>) || {...}) and keep the existing catch behavior for
missing files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@electron/main.ts`:
- Around line 438-465: In updateGlobalShortcuts, replace the loose any on config
with a tighter type (e.g., Partial<ShortcutsConfig> or Record<string, unknown>)
and update the binding.key handling so special keys like "Space" or "F1" are
accepted: only uppercase single-character keys (binding.key.length === 1 ?
binding.key.toUpperCase() : binding.key) and otherwise use the provided name
verbatim; ensure you read binding from the typed config (e.g., const binding =
(config.togglePauseRecording as Partial<ShortcutBinding>) || {...}) and keep the
existing catch behavior for missing files.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cbb42301-9d32-48f5-94d8-96002101f847

📥 Commits

Reviewing files that changed from the base of the PR and between 884021c and 70cbd90.

📒 Files selected for processing (6)
  • electron/electron-env.d.ts
  • electron/ipc/handlers.ts
  • electron/main.ts
  • electron/preload.ts
  • src/hooks/useScreenRecorder.ts
  • src/lib/shortcuts.ts

Comment thread electron/ipc/handlers.ts
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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lib/cursorTelemetryBuffer.ts`:
- Around line 151-153: The implementation of startSession now treats repeated
calls with the same recordingId as a no-op via the activeRecordingId check, but
the startSession documentation still states that repeated calls clear active
samples; update the contract to match behavior or change the code to match docs.
Specifically, either adjust the comment/docs for startSession to state that if
recordingId === activeRecordingId the call is a no-op and does not clear
samples, or modify the startSession logic (remove or adjust the
activeRecordingId === recordingId early return in the startSession function) so
repeated calls will clear active samples as documented; reference startSession,
activeRecordingId and recordingId when making the change so the behavior and
docs remain consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2ea3d65c-2152-401d-b9f7-5ace34185672

📥 Commits

Reviewing files that changed from the base of the PR and between 70cbd90 and a5ec219.

📒 Files selected for processing (2)
  • src/lib/cursorTelemetryBuffer.test.ts
  • src/lib/cursorTelemetryBuffer.ts

Comment on lines +151 to +153
if (activeRecordingId === recordingId) {
return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

startSession behavior changed, but contract docs still say the old thing

Line 151 makes same-id calls a no-op (nice), but the startSession docs still describe repeated calls as clearing active samples. lowkey risky for future callers relying on comments over code.

nit: cleaner doc patch
- * Begin a new recording session under the given `recordingId`. Clears
- * any in-progress active samples (without touching already-completed
- * pending batches). Safe to call repeatedly — e.g. a rapid Stop →
- * Record sequence — and the most recent id wins.
+ * Begin a new recording session under the given `recordingId`.
+ * If called with the same `recordingId` as the current active session,
+ * this is a no-op (used by pause/resume flows). If called with a different
+ * id, any in-progress active samples are cleared (without touching
+ * already-completed pending batches).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/cursorTelemetryBuffer.ts` around lines 151 - 153, The implementation
of startSession now treats repeated calls with the same recordingId as a no-op
via the activeRecordingId check, but the startSession documentation still states
that repeated calls clear active samples; update the contract to match behavior
or change the code to match docs. Specifically, either adjust the comment/docs
for startSession to state that if recordingId === activeRecordingId the call is
a no-op and does not clear samples, or modify the startSession logic (remove or
adjust the activeRecordingId === recordingId early return in the startSession
function) so repeated calls will clear active samples as documented; reference
startSession, activeRecordingId and recordingId when making the change so the
behavior and docs remain consistent.

@siddharthvaddem
Copy link
Copy Markdown
Owner

@Rodriq this feature already exists. Will be closing this PR

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.

2 participants