Skip to content

fix(browser): prevent macOS PiP black window - #2105

Merged
yyhhyyyyyy merged 4 commits into
devfrom
codex/fix-macos-pip-black-window
Aug 8, 2026
Merged

fix(browser): prevent macOS PiP black window#2105
yyhhyyyyyy merged 4 commits into
devfrom
codex/fix-macos-pip-black-window

Conversation

@zerob13

@zerob13 zerob13 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fix the macOS black fullscreen window caused by the Browser PiP off-screen host, and reduce the host's background resource cost.

Root cause

Browser PiP keeps the existing WebContentsView renderable by attaching it to a separate transparent, off-screen BaseWindow. The host was created with show: true. On macOS, that independent native window could participate in fullscreen window handling despite being transparent and positioned off-screen, surfacing as a black fullscreen window.

Changes

  • Keep the preview BaseWindow hidden and non-fullscreenable on macOS.
  • Release the hidden preview host when an Agent run becomes idle or error:
    • stop the capture loop
    • destroy the host window
    • restore Electron background throttling
    • release the preview ownership claim
  • Guard terminal cleanup by Agent run ID so cleanup from an old run cannot destroy a newly started run's host.
  • Destroy the session browser during session deletion, including best-effort cleanup when another deletion stage fails.
  • Reduce preview capture cadence:
    • active: 250 ms -> 500 ms (4 FPS -> 2 FPS)
    • idle: 1000 ms -> 2000 ms (1 FPS -> 0.5 FPS)
  • Reduce the encoded PiP frame from 480x300 to 400x250 (about 31% fewer output pixels).
  • Keep the browser viewport at 1280x800 so page layout, CDP coordinates, and automation behavior do not change.

The smaller output frame reduces resize, JPEG encoding, IPC, and renderer drawing work. It does not reduce the source viewport captured by Electron.

Lifecycle boundary

The hidden host still exists while an Agent is actively using a background browser because PiP needs a renderable surface. It is now destroyed at the terminal session state. Expanding the Browser side panel continues to detach the view from the hidden host through the existing attach path.

Behavior

BEFORE
macOS fullscreen -> transparent BaseWindow joins fullscreen handling -> black window

AFTER
macOS fullscreen -> hidden, non-fullscreenable preview host -> normal app fullscreen

Agent running -> hidden preview host -> reduced-rate capture
Agent idle/error -> stop capture -> destroy host -> background throttling restored

Testing

  • pnpm exec vitest run --config vitest.config.ts test/main/desktop/browser/YoBrowserPresenter.test.ts test/main/session/deletion.test.ts (24 passed)
  • pnpm exec vitest run --config vitest.config.renderer.ts test/renderer/components/AgentBrowserPiP.test.ts (11 passed)
  • pnpm run format:check
  • pnpm run i18n
  • pnpm run lint
  • pnpm run typecheck

The application demo was not run.

Summary by CodeRabbit

  • Performance Improvements

    • Reduced browser preview size and adjusted capture intervals for smoother, more efficient operation.
    • Inactive previews are now released when sessions become idle or encounter errors.
  • Bug Fixes

    • Browser resources are cleaned up when sessions are deleted, even if earlier cleanup steps fail.
    • macOS previews remain hidden and cannot be switched to fullscreen unnecessarily.
  • UI Improvements

    • Updated the message copy control for consistent button sizing and behavior.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 59afeee5-6edd-4916-b385-752fa4b0c0c0

📥 Commits

Reviewing files that changed from the base of the PR and between 258b867 and f1f9775.

📒 Files selected for processing (2)
  • src/main/desktop/browser/YoBrowserPresenter.ts
  • test/main/desktop/browser/YoBrowserPresenter.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/desktop/browser/YoBrowserPresenter.ts

📝 Walkthrough

Walkthrough

YoBrowser previews now use smaller frames and longer capture intervals. Idle or errored sessions release inactive previews. Session deletion destroys the associated browser and records cleanup failures. The message copy control now uses DcButton.

Changes

YoBrowser lifecycle

Layer / File(s) Summary
Preview release and capture behavior
src/shared/types/desktop.ts, src/main/desktop/browser/YoBrowserPresenter.ts, test/main/desktop/browser/YoBrowserPresenter.test.ts, test/renderer/components/AgentBrowserPiP.test.ts
Preview frames use 400×250 dimensions. Active and idle capture intervals use 500 ms and 2 seconds. Inactive previews hide, stop, dispose, and release their claims. macOS hosts remain visible and become non-fullscreenable. Tests cover cleanup, capture timing, and recovery.
Session-state preview integration
src/main/app/composition.ts
Idle and errored session updates release inactive previews and log failures.
Session deletion browser cleanup
src/main/session/contracts.ts, src/main/session/deletion.ts, src/main/app/composition.ts, test/main/session/deletion.test.ts, test/main/session/sessionFixture.ts
Session deletion destroys the browser after backend cleanup. Browser cleanup failures are recorded while deletion continues.

Message toolbar control

Layer / File(s) Summary
Copy button replacement and validation
src/renderer/src/components/message/MessageToolbar.vue, test/renderer/components/message/MessageToolbar.trace.test.ts
The message copy control uses DcButton with the existing handler and feedback. The trace test verifies matching button sizes.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SessionRuntime
  participant YoBrowserPresenter
  participant PreviewHost
  SessionRuntime->>YoBrowserPresenter: Release preview for idle or errored session
  YoBrowserPresenter->>PreviewHost: Hide and stop capture
  YoBrowserPresenter->>PreviewHost: Dispose inactive preview host
  YoBrowserPresenter-->>SessionRuntime: Release preview claim or report failure
Loading

Possibly related PRs

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: preventing the macOS PiP black window.
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.
✨ Finishing Touches 💡 1
📝 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-macos-pip-black-window

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.

@zerob13
zerob13 marked this pull request as ready for review August 8, 2026 06:15
@zerob13
zerob13 requested a review from yyhhyyyyyy August 8, 2026 06:15

@yyhhyyyyyy yyhhyyyyyy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I found two issues that should be addressed before this is merged:

  1. The hidden macOS host is not a valid capture target.

    With the repository's Electron 41.10.4 on macOS 27.0 arm64, a minimal
    BaseWindow/WebContentsView using the same show: false configuration
    loads successfully, but capturePage(rect, { stayHidden: true }) rejects
    with Current display surface not available for capture.

    Electron checks IsSurfaceAvailableForCopy() before incrementing the
    capturer count, so stayHidden cannot create a compositor surface for a
    window that never had one:
    https://github.com/electron/electron/blob/v41.10.4/shell/browser/api/electron_api_web_contents.cc#L3793-L3806

    In the current loop this means no PiP frames on macOS, plus repeated capture
    warnings while the Agent remains active. The unit test cannot catch this
    because capturePage is mocked. Please preserve a renderable surface, for
    example by keeping the off-screen host shown while setting
    fullscreenable: false, or validate another approach with a real Electron
    smoke test on supported macOS versions.

  2. A timed-out capture can permanently block preview capture for the next run.

    stopPreviewCapture() returns after 500 ms but leaves the old
    state.previewCapture promise installed when the timeout wins.
    resumeClaimedPreview() will not schedule another frame while that field is
    non-null. I reproduced this with a first capture that never settles:
    terminal cleanup returns, run 2 claims and recreates the host, and
    setPreviewMode('capturing') succeeds, but no second capturePage call is
    ever made.

    Please detach the stale capture slot when the timeout wins and add a
    regression test covering a new run after a non-settling capture. The
    existing epoch checks can still fence any late completion from the old run.

@zerob13
zerob13 requested a review from yyhhyyyyyy August 8, 2026 09:16

@yyhhyyyyyy yyhhyyyyyy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@yyhhyyyyyy
yyhhyyyyyy merged commit 8311bd2 into dev Aug 8, 2026
12 checks passed
@yyhhyyyyyy
yyhhyyyyyy deleted the codex/fix-macos-pip-black-window branch August 8, 2026 12:27
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