Skip to content

fix: camera light flashes and turns off when clicking webcam button (…#325

Open
dheerajmr01 wants to merge 5 commits intosiddharthvaddem:mainfrom
dheerajmr01:fix/camera-bugs
Open

fix: camera light flashes and turns off when clicking webcam button (…#325
dheerajmr01 wants to merge 5 commits intosiddharthvaddem:mainfrom
dheerajmr01:fix/camera-bugs

Conversation

@dheerajmr01
Copy link
Copy Markdown

@dheerajmr01 dheerajmr01 commented Apr 4, 2026

Description

This PR fixes these issues:

  1. When clicking the webcam button on the panel, the camera hardware light briefly flashes on for ~1 second then turns off, while the UI button remains enabled.
  2. When camera is physically disconnected while recording there is no indication on screen letting the user know the camera was disconnected.

Type of Change

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

Related Issue(s)

Fixes #308

Screenshot :

image

Testing

  1. Open OpenScreen on Windows, click the webcam button on the panel → camera light should turn on and stay on
  2. Enable webcam, disconnect camera physically (or through the Device Manager) at the start or while recording → button turns off, "Webcam disconnected" toast appears

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.

Summary by CodeRabbit

  • Bug Fixes

    • Webcam toggle is disabled while recording to prevent interruptions.
    • Improved webcam acquisition, cleanup, and recording startup with clearer handling for disconnected, not-found, blocked, or denied camera scenarios.
    • Desktop app camera access failures are now propagated so denials/failures are reported consistently.
  • Documentation

    • Added English, Spanish, and Chinese messages for camera disconnected and camera-not-found.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9e860183-98b0-4516-806a-400b7398aaa0

📥 Commits

Reviewing files that changed from the base of the PR and between 5ff6139 and 210baee.

📒 Files selected for processing (1)
  • src/hooks/useScreenRecorder.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/hooks/useScreenRecorder.ts

📝 Walkthrough

Walkthrough

Decouples webcam acquisition from recording start by moving getUserMedia into a useEffect that manages readiness, onended and cleanup; startRecording now waits for webcam readiness. Disables the webcam toggle while recording, adds camera-related i18n keys, relaxes Electron camera-access return filtering, and ignores .claude/ in git.

Changes

Cohort / File(s) Summary
Screen recorder hook
src/hooks/useScreenRecorder.ts
Moved webcam acquisition/cleanup into a useEffect; removed per-recording getUserMedia and per-recording webcam teardown; added webcamReady ref, acquire-id to ignore stale acquires, per-track onended handling, polling wait in startRecording, and toast/error mapping on acquisition failure.
UI toggle
src/components/launch/LaunchWindow.tsx
Added disabled={recording} to the webcam toggle button to prevent toggling while recording.
i18n (en / es / zh-CN)
src/i18n/locales/en/editor.json, src/i18n/locales/es/editor.json, src/i18n/locales/zh-CN/editor.json
Added recording.cameraDisconnected and recording.cameraNotFound localization keys and translations.
Electron camera access
src/lib/requestCameraAccess.ts
Electron branch now returns window.electronAPI.requestCameraAccess() result directly (removed prior success && granted gating), so denied/failure results pass through.
Misc
.gitignore
Added .claude/ entry to ignore list.

Sequence Diagram(s)

sequenceDiagram
  participant User as User
  participant LaunchWindow as LaunchWindow
  participant RecorderHook as useScreenRecorder
  participant MediaDevices as navigator.mediaDevices
  participant Toasts as Toasts

  User->>LaunchWindow: click "enable webcam"
  LaunchWindow->>RecorderHook: setWebcamEnabled(true)
  RecorderHook->>MediaDevices: getUserMedia(constraints)
  alt success
    MediaDevices-->>RecorderHook: MediaStream
    RecorderHook-->>LaunchWindow: store stream, webcamReady=true
  else failure
    MediaDevices-->>RecorderHook: DOMException
    RecorderHook->>Toasts: show cameraBlocked / cameraNotFound
    RecorderHook-->>LaunchWindow: setWebcamEnabled(false)
  end

  User->>LaunchWindow: click "start recording"
  LaunchWindow->>RecorderHook: startRecording()
  RecorderHook->>RecorderHook: poll webcamReady (~5s)
  alt webcamStream available
    RecorderHook->>RecorderHook: proceed with recording using webcam stream
  else no webcamStream
    RecorderHook-->>LaunchWindow: setWebcamEnabled(false)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

"I hopped to tweak the cam with care,
Streams now settle in my burrowed lair.
Toggle sleeps while recording's bright,
No sudden flicker, no brief light.
A joyful hop — the rabbit's night!" 🐰📹

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning One out-of-scope change detected: .gitignore was modified to add .claude/ directory, which is unrelated to the camera bug fixes specified in issue #308. Remove the .claude/ entry from .gitignore or move it to a separate PR, as it is not related to the camera functionality fixes.
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 (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the primary bug fix: preventing the camera light from flashing and turning off when clicking the webcam button.
Description check ✅ Passed The description covers the two bugs being fixed, includes a related issue link (#308), provides testing steps, and completes the required checklist sections.
Linked Issues check ✅ Passed The code changes fully address issue #308 requirements: proper media stream lifecycle management, preventing camera light from flashing, reflecting actual stream status in UI, and adding user notification for disconnection.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

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: 20b0899c05

ℹ️ 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".

Copy link
Copy Markdown

@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: 2

🤖 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/hooks/useScreenRecorder.ts`:
- Around line 467-470: The code incorrectly treats webcamStream.current === null
as a denial; change the check in the Record-click handler to wait for the
acquire flow to settle by adding or using an initializing/ready flag (e.g.,
webcamInitializing or webcamReady) instead of assuming null means denied: only
call setWebcamEnabledState(false) and toast.error(t("recording.cameraDenied"))
when acquisition has completed and been rejected (initializing is false and
webcamStream.current is null or an explicit denied status), or alternatively
gate the early return when webcamInitializing is true so the handler waits for
the effect that populates webcamStream.current to finish.
- Around line 220-225: The catch block in useScreenRecorder.ts currently treats
all webcam acquisition failures the same; update the cameraError handling inside
the catch (cameraError) branch to inspect the error type/name (e.g.,
cameraError.name or cameraError.message) and map permission errors
('NotAllowedError'/'PermissionDenied') to
toast.error(t("recording.cameraBlocked")) while mapping hardware/device-loss
errors
('NotFoundError'/'OverconstrainedError'/'NotReadableError'/'DevicesNotFoundError'
or messages indicating missing device) to
toast.error(t("recording.cameraDisconnected")); keep the existing
setWebcamEnabledState(false) behavior and ensure the logic references the
existing symbols cameraError, setWebcamEnabledState, toast.error, and t() so the
correct localized key is shown for each failure reason.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: cfe40ec9-97e6-4fd1-a3ce-e6af46933119

📥 Commits

Reviewing files that changed from the base of the PR and between 21893f0 and 20b0899.

📒 Files selected for processing (6)
  • src/components/launch/LaunchWindow.tsx
  • src/hooks/useScreenRecorder.ts
  • src/i18n/locales/en/editor.json
  • src/i18n/locales/es/editor.json
  • src/i18n/locales/zh-CN/editor.json
  • src/lib/requestCameraAccess.ts

Copy link
Copy Markdown

@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 (2)
src/hooks/useScreenRecorder.ts (2)

227-232: Error differentiation looks good, but consider adding AbortError.

The current error differentiation addresses the past review concern well. However, AbortError can also indicate hardware issues (e.g., camera in use by another application) and might warrant the same "not found" style message rather than a permission-blocked message.

💡 Optional enhancement
 const isDeviceError =
 	cameraError instanceof DOMException &&
-	["NotFoundError", "OverconstrainedError", "NotReadableError"].includes(
+	["NotFoundError", "OverconstrainedError", "NotReadableError", "AbortError"].includes(
 		cameraError.name,
 	);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useScreenRecorder.ts` around lines 227 - 232, The camera error
handling in useScreenRecorder.ts should also treat AbortError as a
device/hardware issue; update the isDeviceError check where cameraError is
evaluated (the instanceof DOMException && ["NotFoundError",
"OverconstrainedError", "NotReadableError"].includes(cameraError.name) logic) to
include "AbortError" so toast.error(t(...)) shows "recording.cameraNotFound" for
AbortError as well.

478-495: Polling pattern works but has an edge case when webcam is toggled off mid-wait.

If the user disables webcam while startRecording is waiting in the polling loop, the cleanup sets webcamReady.current = false, causing the loop to wait the full 5 seconds before timing out. This is a minor UX delay, not a bug.

Consider tracking the enabled state in the polling condition to exit early:

💡 Optional improvement for faster exit on disable
 if (webcamEnabled) {
 	if (!webcamReady.current) {
 		await new Promise<void>((resolve) => {
+			let resolved = false;
 			const interval = setInterval(() => {
-				if (webcamReady.current) {
+				if (webcamReady.current || !webcamEnabled) {
 					clearInterval(interval);
-					resolve();
+					if (!resolved) {
+						resolved = true;
+						resolve();
+					}
 				}
 			}, 50);
 			setTimeout(() => {
 				clearInterval(interval);
-				resolve();
+				if (!resolved) {
+					resolved = true;
+					resolve();
+				}
 			}, 5000);
 		});
 	}

Note: This requires passing webcamEnabled state into the closure properly or using a ref. The current implementation is acceptable as-is.

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

In `@src/hooks/useScreenRecorder.ts` around lines 478 - 495, Polling in
startRecording can hang up to the 5s timeout if the user disables the webcam
mid-wait because the loop only checks webcamReady.current; update the polling to
also observe the webcam enabled state so it can exit immediately when disabled.
Concretely, add or use an existing ref like webcamEnabledRef (or sync
webcamEnabled into a ref) and include a check inside the interval callback
(e.g., if (!webcamReady.current || !webcamEnabledRef.current) {
clearInterval(interval); resolve(); }) and ensure cleanup sets webcamEnabledRef
appropriately; keep the existing fallback timeout logic and the later
webcamStream.current check/notification.
🤖 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/hooks/useScreenRecorder.ts`:
- Around line 492-495: The camera acquisition failure currently may trigger a
duplicate toast: when webcamStream.current is null inside the startRecording
flow we call setWebcamEnabledState(false) and
toast.error(t("recording.cameraDenied")), but an earlier useEffect may already
have shown cameraNotFound or cameraBlocked and set state asynchronously. Update
the logic in useScreenRecorder (the block that checks webcamStream.current and
calls setWebcamEnabledState + toast.error) to avoid duplicating toasts — either
remove the toast.error here and rely on the useEffect messages, or add a guard
(e.g., a boolean flag or ref like hasShownCameraError) that the useEffect sets
when it displays cameraNotFound/cameraBlocked and that startRecording checks
before calling toast.error; ensure the flag is set/cleared appropriately so
setWebcamEnabledState(false) still runs but duplicate toasts are prevented.

---

Nitpick comments:
In `@src/hooks/useScreenRecorder.ts`:
- Around line 227-232: The camera error handling in useScreenRecorder.ts should
also treat AbortError as a device/hardware issue; update the isDeviceError check
where cameraError is evaluated (the instanceof DOMException && ["NotFoundError",
"OverconstrainedError", "NotReadableError"].includes(cameraError.name) logic) to
include "AbortError" so toast.error(t(...)) shows "recording.cameraNotFound" for
AbortError as well.
- Around line 478-495: Polling in startRecording can hang up to the 5s timeout
if the user disables the webcam mid-wait because the loop only checks
webcamReady.current; update the polling to also observe the webcam enabled state
so it can exit immediately when disabled. Concretely, add or use an existing ref
like webcamEnabledRef (or sync webcamEnabled into a ref) and include a check
inside the interval callback (e.g., if (!webcamReady.current ||
!webcamEnabledRef.current) { clearInterval(interval); resolve(); }) and ensure
cleanup sets webcamEnabledRef appropriately; keep the existing fallback timeout
logic and the later webcamStream.current check/notification.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8919a8dd-76f9-4150-8a2d-29d04aa5c67b

📥 Commits

Reviewing files that changed from the base of the PR and between 20b0899 and fdc8833.

📒 Files selected for processing (5)
  • .gitignore
  • src/hooks/useScreenRecorder.ts
  • src/i18n/locales/en/editor.json
  • src/i18n/locales/es/editor.json
  • src/i18n/locales/zh-CN/editor.json
✅ Files skipped from review due to trivial changes (4)
  • .gitignore
  • src/i18n/locales/es/editor.json
  • src/i18n/locales/en/editor.json
  • src/i18n/locales/zh-CN/editor.json

Copy link
Copy Markdown

@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)
src/hooks/useScreenRecorder.ts (1)

481-494: Extract the timeout value to a named constant.

The 5000ms timeout is a magic number. Consider extracting it for clarity and maintainability.

♻️ Suggested refactor

Add near the other constants at the top of the file:

const WEBCAM_READY_TIMEOUT_MS = 5000;
const WEBCAM_READY_POLL_INTERVAL_MS = 50;

Then update the polling code:

 				const interval = setInterval(() => {
 					if (webcamReady.current) {
 						clearInterval(interval);
 						resolve();
 					}
-				}, 50);
+				}, WEBCAM_READY_POLL_INTERVAL_MS);
 				setTimeout(() => {
 					clearInterval(interval);
 					resolve();
-				}, 5000);
+				}, WEBCAM_READY_TIMEOUT_MS);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useScreenRecorder.ts` around lines 481 - 494, Extract the
hard-coded timeouts into named constants and use them in the polling logic: add
constants (e.g. WEBCAM_READY_TIMEOUT_MS = 5000 and WEBCAM_READY_POLL_INTERVAL_MS
= 50) near the top of useScreenRecorder (with other constants), then replace the
literal 50 and 5000 in the Promise/polling block that checks webcamReady.current
with these constants so the interval and timeout values are clear and
maintainable.
🤖 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/hooks/useScreenRecorder.ts`:
- Around line 213-219: The onended handler for the media track (track.onended)
is firing when you explicitly stop the track, causing the spurious "Webcam
disconnected" toast; before you stop or replace tracks (e.g., in the cleanup
path that calls track.stop(), in the webcam toggle logic, or when switching
devices inside useScreenRecorder), clear the handler by setting track.onended =
null (or a no-op) first, then stop the track and update webcamStream.current;
this preserves the existing restarting.current check and
setWebcamEnabledState/toast.error behavior for genuine disconnects while
preventing notifications when you intentionally stop the camera.

---

Nitpick comments:
In `@src/hooks/useScreenRecorder.ts`:
- Around line 481-494: Extract the hard-coded timeouts into named constants and
use them in the polling logic: add constants (e.g. WEBCAM_READY_TIMEOUT_MS =
5000 and WEBCAM_READY_POLL_INTERVAL_MS = 50) near the top of useScreenRecorder
(with other constants), then replace the literal 50 and 5000 in the
Promise/polling block that checks webcamReady.current with these constants so
the interval and timeout values are clear and maintainable.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 32e26240-d5d7-4f5d-8490-56e5edb69b8a

📥 Commits

Reviewing files that changed from the base of the PR and between fdc8833 and b270aff.

📒 Files selected for processing (5)
  • .gitignore
  • src/hooks/useScreenRecorder.ts
  • src/i18n/locales/en/editor.json
  • src/i18n/locales/es/editor.json
  • src/i18n/locales/zh-CN/editor.json
✅ Files skipped from review due to trivial changes (4)
  • .gitignore
  • src/i18n/locales/en/editor.json
  • src/i18n/locales/zh-CN/editor.json
  • src/i18n/locales/es/editor.json

Copy link
Copy Markdown

@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

♻️ Duplicate comments (1)
src/hooks/useScreenRecorder.ts (1)

226-239: ⚠️ Potential issue | 🟡 Minor

Use the disconnect toast for unplugged-before-start failures.

This device-error branch still routes startup hardware-loss cases to recording.cameraNotFound, so unplugging the webcam before track.onended is attached will not show the same disconnect toast as the during-recording path. That misses the start-time disconnect case described in the PR.

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

In `@src/hooks/useScreenRecorder.ts` around lines 226 - 239, When a device-error
occurs at startup (cameraError with isDeviceError true) we should surface the
same disconnect toast as the during-recording path rather than the startup
"cameraNotFound" message; update the catch in useScreenRecorder (the block
handling cameraError, setWebcamEnabledState, webcamReady.current) so that when
isDeviceError is true it calls toast.error(t("recording.disconnected")) (or the
same key used by the track.onended disconnect path) instead of
t("recording.cameraNotFound"), ensuring the unplugged-before-start case shows
the disconnect toast.
🤖 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/hooks/useScreenRecorder.ts`:
- Line 106: The slow getUserMedia() calls need a cancellation/invalidation check
so late acquisitions can't repopulate webcamStream.current after the 5s fallback
toggles webcamEnabled off; add an AbortController or an acquireId flag used by
the webcam acquisition logic (the same place that uses webcamReady,
getUserMedia, and assigns webcamStream.current) and have the effect cleanup
increment/abort that id, and in the getUserMedia success callback verify the
controller is not aborted (or the acquireId still matches) and that
webcamEnabled is still true before assigning webcamStream.current or starting
recorder setup; if the result is stale, immediately stop all tracks on the
returned MediaStream and discard it. Ensure the same invalidation check is
applied to all acquisition sites mentioned (around useRef webcamReady, the
acquisition block that assigns webcamStream.current, and the 5s fallback/cleanup
logic).

---

Duplicate comments:
In `@src/hooks/useScreenRecorder.ts`:
- Around line 226-239: When a device-error occurs at startup (cameraError with
isDeviceError true) we should surface the same disconnect toast as the
during-recording path rather than the startup "cameraNotFound" message; update
the catch in useScreenRecorder (the block handling cameraError,
setWebcamEnabledState, webcamReady.current) so that when isDeviceError is true
it calls toast.error(t("recording.disconnected")) (or the same key used by the
track.onended disconnect path) instead of t("recording.cameraNotFound"),
ensuring the unplugged-before-start case shows the disconnect toast.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4df8e4b0-0cd3-4ebf-897a-5c1b6f86b168

📥 Commits

Reviewing files that changed from the base of the PR and between b270aff and 5ff6139.

📒 Files selected for processing (1)
  • src/hooks/useScreenRecorder.ts

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.

[Windows][Bug]: Camera briefly starts then stops when clicking video button on panel

1 participant