Skip to content

fix(browser): silence a browser page once its tab goes away - #2721

Merged
loganj merged 3 commits into
mainfrom
loganj/browser-tab-close-audio
Nov 6, 2026
Merged

loganj merged 3 commits into
mainfrom
loganj/browser-tab-close-audio

Conversation

@loganj

@loganj loganj commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

🤖

Summary

  • Closing a Builderlab app tab, or switching to another channel, left the app's audio playing. There was no visible tab left to stop it with. Buzz keeps such a page alive but hidden for up to 10 minutes, so that coming back to it restores it as it was. The hidden page kept running, sound included.
  • Now the page's media is suspended as soon as its tab goes away. That covers audio and video elements and Web Audio. If the page comes back within the 10 minutes, its media resumes; otherwise the page closes as before.

Details

  • Suspension uses WebKit's setAllMediaPlaybackSuspended (macOS 12+, which matches the app's minimum). While suspended, the page cannot start playback again on its own. A new native command, browser_set_media_suspended, exposes it. It is registered in the command list, the main-window capability and the permission test.
  • The page is silenced at the same moment it is hidden, outside the browser's serialized open/close queue, so a slow sign-in for another app tab cannot delay it.
  • A page that cannot be silenced is closed instead of kept. A kept page that cannot resume is replaced by a fresh one. If its view goes away while it is resuming, the page is closed. Otherwise nothing would ever close it.
  • Not changed: a tab that is still open but not selected, or a page covered by a dialog, keeps playing, as a background browser tab does.
  • The embedded browser is available only on macOS. Elsewhere the command reports that it is unsupported, which makes a page close instead of being kept.

buzz-review-completed

🤖 Generated with Claude Code

loganj and others added 3 commits November 6, 2026 11:06
Closing a Builderlab app tab, or switching channels away from one, parks
its page hidden for up to 10 minutes so the same page can come back as it
was. The parked page kept running, so its audio kept playing with no
visible tab to stop it.

Detaching a view now suspends the page's media (WebKit
setAllMediaPlaybackSuspended, which also holds Web Audio) at the same time
as it hides the page, and a view that adopts the parked page resumes it.
A page that cannot be silenced closes instead of parking, and a parked
page that cannot resume is replaced by a fresh one.

Signed-off-by: Logan Johnson <[email protected]>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Resuming a parked page's media now waits on a native call. A view that
unmounted during it never received the page, so nothing would detach it
and the page kept playing. Check the view again after resuming and close
the page if it is gone. The view's own detach no longer leaves an
unhandled rejection when hiding or silencing fails.

Signed-off-by: Logan Johnson <[email protected]>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
… resume

Signed-off-by: Logan Johnson <[email protected]>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
@loganj
loganj force-pushed the loganj/browser-tab-close-audio branch from 7c93a67 to 8ac9fb0 Compare November 6, 2026 16:08
@loganj
loganj marked this pull request as ready for review November 6, 2026 16:54
@loganj
loganj requested a review from a team as a code owner November 6, 2026 16:54
wesbillman

This comment was marked as resolved.

@loganj

This comment has been minimized.

@kalvinnchau kalvinnchau left a comment

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.

🤖 Approved at 8ac9fb0. No actionable findings.

Reviewed immediate suspension, serialized re-adoption, suspend-failure cleanup, resume-failure replacement, disposal during resume, ten-minute retention, native command registration and permissions. The change stays within existing session/platform ownership. Background tabs and dialog occlusion intentionally preserve playback; removed views suspend it. Existing lifecycle tests protect meaningful failure and race boundaries and should be retained.

Validation includes 14 passing actual-app browser checks across Chromium and WebKit, covering removal/reopening, channel changes, failure recovery, blocked-queue suspension, disposal during resume and retention expiry. Same-commit CI passed, including JavaScript/Vitest and macOS Rust tests. The new command remains restricted to the main webview, with guest and remote-context denial represented in permission tests.

Validation limit: browser checks used modeled guest I/O and relay upstream, so they do not establish real WKWebView audio/video/Web Audio behavior or native completion ordering. The native API dispatches asynchronously without awaiting its completion callback. The author's reported native close/reopen audio test provides additional acceptance evidence; independent native playback and suspension persistence across navigation/process crashes remain unverified. No concrete defect was established in those areas.

@loganj
loganj merged commit 9f65767 into main Nov 6, 2026
46 checks passed
@loganj
loganj deleted the loganj/browser-tab-close-audio branch November 6, 2026 18:13
tlongwell-block added a commit that referenced this pull request Nov 6, 2026
* origin/main:
  fix(sidebar): rename Remove from Team to Close conversation (#2845)
  perf(channels): cut header and name-matching work in large channels (#2726)
  fix(browser): silence a browser page once its tab goes away (#2721)
  Community Mesh: shared compute plugin (including agent type) (#941)
  perf(relay): cut main-thread work from profile updates (#2730)
  fix(composer): lead the channel placeholder with its sidebar icon (#2829)
  test(activity): scope preview assertions to the request row (#2843)
  perf(search): fold people names once across palette keystrokes (#2570)
  fix(github): keep PR watches running when localStorage is full (#2699)
  Make drafts and sent part of Messages (#2728)
  fix(canvas): render every Canvas within the save limit (#2839)

Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: Tyler Longwell <[email protected]>
Signed-off-by: Tyler Longwell <[email protected]>
wpfleger96 added a commit that referenced this pull request Nov 6, 2026
…leanup

* origin/main:
  feat(agents): let app-hosted agents read their owner's channels and members (#2745)
  NEXUS-4271: Expose installed plugin status (#2754)
  fix(menu): stop clipping lifecycle actions behind a hidden scrollbar (#2716)
  feat(agents): give approved external apps the selected agent's full Buzz tools (#2707)
  AIDA-2549: Keep Claude settings drafts in sync (#2841)
  fix(channels): keep a channel's side panels open when returning to it (#2838)
  fix(builderlab): clarify remote-agent account availability (#2764)
  fix(sidebar): rename Remove from Team to Close conversation (#2845)
  perf(channels): cut header and name-matching work in large channels (#2726)
  fix(browser): silence a browser page once its tab goes away (#2721)
  Community Mesh: shared compute plugin (including agent type) (#941)
  perf(relay): cut main-thread work from profile updates (#2730)
  fix(composer): lead the channel placeholder with its sidebar icon (#2829)
  test(activity): scope preview assertions to the request row (#2843)
  perf(search): fold people names once across palette keystrokes (#2570)
  fix(github): keep PR watches running when localStorage is full (#2699)
  Make drafts and sent part of Messages (#2728)
  fix(canvas): render every Canvas within the save limit (#2839)

# Conflicts:
#	.github/workflows/release.yml
cheng-block added a commit to cheng-block/buzz-app that referenced this pull request Nov 6, 2026
…ime-hint

* upstream/main:
  ci: give the native job room for a cold Rust cache (block#2867)
  fix(pairing): refuse unknown communities natively and name the requesting process (block#2835)
  Add per-interest Attention pause and management controls (block#2847)
  feat(drafts): show where unsent drafts wait (block#2853)
  feat(agents): let app-hosted agents read their owner's channels and members (block#2745)
  NEXUS-4271: Expose installed plugin status (block#2754)
  fix(menu): stop clipping lifecycle actions behind a hidden scrollbar (block#2716)
  feat(agents): give approved external apps the selected agent's full Buzz tools (block#2707)
  AIDA-2549: Keep Claude settings drafts in sync (block#2841)
  fix(channels): keep a channel's side panels open when returning to it (block#2838)
  fix(builderlab): clarify remote-agent account availability (block#2764)
  fix(sidebar): rename Remove from Team to Close conversation (block#2845)
  perf(channels): cut header and name-matching work in large channels (block#2726)
  fix(browser): silence a browser page once its tab goes away (block#2721)
  Community Mesh: shared compute plugin (including agent type) (block#941)
  perf(relay): cut main-thread work from profile updates (block#2730)
  fix(composer): lead the channel placeholder with its sidebar icon (block#2829)
  test(activity): scope preview assertions to the request row (block#2843)
  perf(search): fold people names once across palette keystrokes (block#2570)
  fix(github): keep PR watches running when localStorage is full (block#2699)

Signed-off-by: Cheng Peng <[email protected]>
Co-authored-by: Cursor <[email protected]>
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.

3 participants