Skip to content

fix(chrome-extension): keep camera preview alive across tab hand-offs and retry failed connects#2022

Open
ManthanNimodiya wants to merge 1 commit into
CapSoftware:mainfrom
ManthanNimodiya:fix/extension-camera-preview-handoff
Open

fix(chrome-extension): keep camera preview alive across tab hand-offs and retry failed connects#2022
ManthanNimodiya wants to merge 1 commit into
CapSoftware:mainfrom
ManthanNimodiya:fix/extension-camera-preview-handoff

Conversation

@ManthanNimodiya

@ManthanNimodiya ManthanNimodiya commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Fixes the "front camera never loads again after switching tabs" report from Discord.

Two problems combined: switching tabs stopped and reopened the physical camera with no synchronization, and the preview iframe had no retry, one failure left the tile dead until camera settings changed, which is why deselect/reselect fixed it.

  • Offscreen doc keeps the shared camera stream warm for 2s across hand-offs, registers new sessions before the stream await, and dedups concurrent getUserMedia calls
  • Preview iframe retries failed connects with capped backoff (skipping permission errors) and reconnects when the remote track ends, stays muted, or the peer fails

Repro: with the preview live, seize the camera with another app and switch tabs, previously the tile stayed dead after freeing the camera; now it recovers on its own.

Greptile Summary

This PR keeps camera previews alive during tab hand-offs and reconnects failed previews. The main changes are:

  • Deduplicate concurrent camera acquisitions by device.
  • Delay camera release for two seconds after the last session disconnects.
  • Register replacement sessions before awaiting camera access.
  • Retry recoverable connection failures with capped backoff.
  • Reconnect when remote tracks end, remain muted, or peers fail.

Confidence Score: 4/5

Transient peer disconnections can unnecessarily restart an otherwise recoverable preview.

  • Shared camera ownership and stale-session handling cover the main hand-off races.
  • Retry timers are cleared when the effect is disposed.
  • The disconnected state currently triggers immediate teardown instead of allowing recovery.

apps/chrome-extension/src/preview/camera-preview.tsx

Important Files Changed

Filename Overview
apps/chrome-extension/src/offscreen/recorder.ts Adds delayed stream release, acquisition deduplication, and session registration before camera acquisition.
apps/chrome-extension/src/preview/camera-preview.tsx Adds retry and liveness handling, but treats a transient peer disconnection as terminal.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
apps/chrome-extension/src/preview/camera-preview.tsx:505-509
**Transient Disconnect Tears Down Preview**

A peer can enter `disconnected` briefly and recover to `connected`, but this branch immediately closes it and starts another camera hand-off. A temporary connection interruption therefore causes an avoidable black frame and camera restart instead of allowing the active peer to recover.

```suggestion
				if (
					peer.connectionState === "failed" ||
					peer.connectionState === "closed"
				) {
```

Reviews (1): Last reviewed commit: "fix(chrome-extension): keep camera previ..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Context used:

  • Context used - CLAUDE.md (source)
  • Context used - AGENTS.md (source)

Comment on lines +505 to +509
if (
peer.connectionState === "failed" ||
peer.connectionState === "disconnected" ||
peer.connectionState === "closed"
) {

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.

P1 Transient Disconnect Tears Down Preview

A peer can enter disconnected briefly and recover to connected, but this branch immediately closes it and starts another camera hand-off. A temporary connection interruption therefore causes an avoidable black frame and camera restart instead of allowing the active peer to recover.

Suggested change
if (
peer.connectionState === "failed" ||
peer.connectionState === "disconnected" ||
peer.connectionState === "closed"
) {
if (
peer.connectionState === "failed" ||
peer.connectionState === "closed"
) {
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/chrome-extension/src/preview/camera-preview.tsx
Line: 505-509

Comment:
**Transient Disconnect Tears Down Preview**

A peer can enter `disconnected` briefly and recover to `connected`, but this branch immediately closes it and starts another camera hand-off. A temporary connection interruption therefore causes an avoidable black frame and camera restart instead of allowing the active peer to recover.

```suggestion
				if (
					peer.connectionState === "failed" ||
					peer.connectionState === "closed"
				) {
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +487 to +491
const reconnect = () => {
if (disposed || peerRef.current !== peer) return;
stopPreview();
void startPreview(0);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Minor: when reconnect() runs, it might leave retryTimer / muteTimer pending (they’ll no-op due to peerRef guard, but still keep closures alive). Clearing them here avoids stray timers and makes the reconnect path a bit tighter.

Suggested change
const reconnect = () => {
if (disposed || peerRef.current !== peer) return;
stopPreview();
void startPreview(0);
};
const reconnect = () => {
if (disposed || peerRef.current !== peer) return;
clearRetryTimer();
clearMuteTimer();
stopPreview();
void startPreview(0);
};

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