Skip to content

Keep native Browser pages when switching servers - #5490

Merged
brsbl merged 5 commits into
mainfrom
bb/preserve-browser-views-server-switch
Oct 14, 2026
Merged

brsbl merged 5 commits into
mainfrom
bb/preserve-browser-views-server-switch

Conversation

@brsbl

@brsbl brsbl commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Human comments

What was wrong

Switching BB servers destroyed every native Browser view but retained the window identity. Returning to the original server left saved tabs trying to attach missing views, producing a blank page and losing unsaved state.

What changed

  • Browser pages survive server switches with their navigation history and unsaved state.
  • Each server has an isolated view registry; inactive views are hidden and automation leases are revoked.
  • Window closure and app exit release retained views across every server.

How you verified

User QA passed. brsbl approved the native server-switch behavior at 633a598b0d780099270405c9d6ff7d50f2a9386f and reconfirmed all three PRs on October 14, 2026. Main integration preserved the feature patch; automated native verification passed at the final head above.

Before and after

Before: 37749a87bcdd41e8853c6fb487700e64ebbdfd3d · After: 69cf772c86f3a4d2e52b7a33f5477da2380f23b8.

Identical fixture, route, 1280 × 900 window, and 474 × 758 Browser viewport. Fresh Electron content captures at 2×. The blank before image is the Browser content region after returning to server A; after is the surviving native page.

Native Browser after switching back Before After
Page and unsaved note Before: missing native view After: native page and unsaved note retained

Context before the fix:

BB with the blank Browser tab after switching back

Final review, follow-ups, and scope

Fresh per-PR Slop Cop reviewed 37749a87bcdd41e8853c6fb487700e64ebbdfd3d..69cf772c86f3a4d2e52b7a33f5477da2380f23b8 on October 14, 2026. Security found no actionable issues. Architecture and code quality confirmed popup audio; performance confirmed inactive-page CPU use. No P0/P1 findings. E2E reused the completed exact-head Electron pass with two isolated servers and real input. The completed GitHub review added one independently validated P2 and duplicated the other two findings.

P2 — Broker retries can retain the old target after a local-server move. reconnect() captures the builtin URL before activateLocalServerMoveIfLocked() can select the committed destination. Scheduled retries reuse that URL, leaving native automation disconnected until another explicit reconnect. Follow-up: reconnect using the final resolved target while preserving generation guards. Done when a committed move discovered during target application connects to the destination without another user switch. Review comment.

P2 — Hidden popup audio can continue. Parking hides popup windows but does not mute their separate WebContents. Follow-up: mute/pause inactive popup contents without destroying them. Done when a playing popup becomes silent on A → B and restores correctly on return. Review comment.

P2 — Inactive automation pages remain unthrottled. Automation pages retain foreground timer/frame behavior across server switches. Follow-up: enable background throttling while parked and restore the active setting, including CDP teardown. Done when inactive animated pages reduce work while page state and active automation survive return. Review comment.

Two Slim PR passes retained the existing view manager, visibility/reload helpers, origin guards, and cleanup coverage. Before → after: production +88/−42 → +88/−42; tests +100/−2 → +100/−2; docs +7/−0 → +7/−0; generated 0 → 0. Local/enrolled cases protect separate descriptor paths; window-close/app-exit cases protect separate cleanup paths. No code changed during these passes; P2s remain follow-ups.

BB-Thread-ID: thr_8f2nidvuyn

AGENT GENERATED

@brsbl
brsbl marked this pull request as ready for review October 14, 2026 20:59
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-14T21:02:23.473303Z 69cf772 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

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

Copy link
Copy Markdown

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: 69cf772c86

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let stopped = false;
let generation = 0;
let registryServerOrigin: string | null = null;
let serverUrl = args.getServerUrl();

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 Re-read the target URL on each broker retry

When applyServerTarget() starts on the builtin target and then activateLocalServerMoveIfLocked() changes the target to the committed move destination, reconnect() has already captured the builtin URL. Descriptor lookup for that origin normally fails after the move, but every scheduled attempt now retries the same cached URL instead of consulting getServerUrl() as before, so native Browser automation remains disconnected from the moved server until another explicit reconnect occurs.

Useful? React with 👍 / 👎.

Comment on lines +1108 to +1109
for (const popup of entry.popupWindows) {
if (!popup.isDestroyed()) popup.hide();

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 Pause or mute popups when parking their server

If a native page has a popup playing audio when the user switches servers, popup.hide() only removes the window from view; it does not mute or pause the popup's separate WebContents. The parent entry is muted through applyEntryVisibility(), but its popups are not, so content from the inactive server can remain audible until the popup closes or the user switches back.

Useful? React with 👍 / 👎.

Comment on lines +1105 to +1107
for (const entry of entries.values()) {
entry.visible = false;
applyEntryVisibility(entry, entry.hostWindow);

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 Throttle automation views while their server is inactive

Automation-created views are initialized with backgroundThrottling: false, and parking a server only marks them invisible and mutes media. Consequently, every retained automation page from previously visited servers can continue running timers and scripts at full foreground frequency, causing CPU and battery usage to accumulate across server switches; restore throttling while these entries are parked and reinstate their automation setting when activated.

Useful? React with 👍 / 👎.

@brsbl
brsbl merged commit 1419e3d into main Oct 14, 2026
48 checks passed
@brsbl
brsbl deleted the bb/preserve-browser-views-server-switch branch October 14, 2026 23:10
brsbl added a commit that referenced this pull request Oct 15, 2026
…ar drawer (#5495)

## Human comments

## What was wrong

In macOS desktop windows narrower than 768px, New thread in the sidebar
drawer sits under the traffic lights, and the sidebar toggle draws on
top of it (reported on Discord after desktop-v0.46.0). #5229
(`ff9484883`) moved the navigation rail into the compact drawer and
dropped the macOS start inset from `SidebarTopReserveRow`. The drawer's
header row now starts right after the rail, at x=52. That is under the
lights (x=18–78), and the sidebar toggle, fixed at x=84–112, overlaps
it.

## What changed

- `SidebarTopReserveRow`
(`apps/app/src/components/sidebar/SidebarChrome.tsx`) pads the drawer's
header row so New thread starts at x=116, just after the sidebar toggle.
That is where it sat in 0.45.0.
- The padding applies only on macOS desktop, in the compact drawer (not
the framed sidebar), and outside full screen, where there are no traffic
lights.
- Wide framed windows, browsers and other platforms are unchanged. No
wire, CLI or contract changes.

## How you verified

- New `SidebarChrome.test.tsx`: the compact macOS drawer reserves the
space, while the framed window and a compact browser window don't. CI
failed with only the test ([run
38601538235](https://github.com/get-bb/bb/actions/runs/38601538235),
app-8: "expected false to be true") and passes with the fix.
- Real Mac (macOS 26.5.2), using the Electron desktop dev app built from
`main` (before) and from this branch (after), with the same seeded
thread. The window was resized natively to 700×800 with the sidebar
drawer open, and to 1100×800.
- **700px:** New thread moves from x=52–287, under the lights and the
toggle, to x=116–287. The hit test at its center returns New thread.
  - **1100px:** the framed layout is identical before and after.
- The colored dots are stand-ins drawn at the traffic lights' native
position (18, 18). The native buttons can't be captured without Screen
Recording permission.
- Not covered: a narrow window in native full screen, where the padding
is off by design because there are no lights.
- Remote CI runs the tests; nothing was run locally.
- CI: every job passes except the plugin SDK npm version guard in
Checks. Plugin Fork Check was skipped by change-based selection. The
guard fails because `@get-bb/plugin-sdk@0.6.43` was published to npm at
15:11 UTC today, and building the SDK from `main` now produces a
different `dist/provider-bridge-testing.js`. This PR doesn't touch the
SDK. Every PR based on current `main` without a version bump fails the
same way (for example #5490 and #5443), and #5492 passes because it
bumps to 0.6.44. It clears once a bump lands on `main` and this PR's CI
reruns.

| Screen / state | Viewport | Before | After |
| --- | --- | --- | --- |
| Thread · sidebar drawer open (zoom) | macOS desktop · 700×800 |
![Before 700
zoom](https://github.com/user-attachments/assets/1f708c7d-f2aa-42ee-aa2d-c5043d2468ed)
| ![After 700
zoom](https://github.com/user-attachments/assets/793b9443-c4e1-463f-a2e6-31c785b87891)
|
| Thread · sidebar drawer open | macOS desktop · 700×800 | ![Before
700](https://github.com/user-attachments/assets/379ba908-be84-4d51-aa75-654ff175ff77)
| ![After
700](https://github.com/user-attachments/assets/ffa9aebd-8e67-4d79-b9db-c46057e63433)
|
| Thread · framed sidebar (unchanged) | macOS desktop · 1100×800 |
![Before
1100](https://github.com/user-attachments/assets/a77833bb-81f7-4b5f-9546-fd5fb4a0c6b7)
| ![After
1100](https://github.com/user-attachments/assets/1327b504-d9f3-4e91-8706-a83257371042)
|

### Merge order with "Compact the macOS app frame and move headers into
the title bar" (#5529)

Resolved: the PRs can merge in either order. #5495 used to pad the
narrow-window drawer header with a literal `88px`, which assumed the
sidebar toggle sits at the 84px reserve; with #5529's wider reserve New
thread would have started under the toggle (measured at an earlier 88px
reserve: New thread at x=112 under the toggle at x=88–116). #5495 now
includes `5bd8c8b85`, which derives that padding from
`--bb-macos-traffic-light-reserve` (falling back to 84px), so New thread
starts 4px after the toggle under either geometry. Measured on a real
Mac at 700px:

| Build | Sidebar toggle | New thread |
| --- | --- | --- |
| #5495 head `5bd8c8b85` on main | x=84–112 | x=116 |
| #5529 head `96084fc15` + #5495 | x=87–115 | x=119 |

BB-Thread-ID: thr_eigbyjruhs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

> AGENT GENERATED

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
v-lebedev pushed a commit to vrata-tg/vrata-bb that referenced this pull request Oct 23, 2026
## Human comments

## What was wrong

Switching BB servers destroyed every native Browser view but retained
the window identity. Returning to the original server left saved tabs
trying to attach missing views, producing a blank page and losing
unsaved state.

## What changed

- Browser pages survive server switches with their navigation history
and unsaved state.
- Each server has an isolated view registry; inactive views are hidden
and automation leases are revoked.
- Window closure and app exit release retained views across every
server.

## How you verified

- The local/enrolled daemon regression [failed before the
fix](https://github.com/get-bb/bb/actions/runs/38599373329/job/115974641500).
- [Remote CI
passed](https://github.com/get-bb/bb/actions/runs/38635702642) at
`69cf772c86f3a4d2e52b7a33f5477da2380f23b8`, including desktop tests,
typechecking, SDK publishing, and Windows desktop smoke.
- Electron 44.3.0: switching servers and reloading BB preserved the same
native page, history, and unsaved input.

**User QA passed.** brsbl approved the native server-switch behavior at
`633a598b0d780099270405c9d6ff7d50f2a9386f` and reconfirmed all three PRs
on October 14, 2026. Main integration preserved the feature patch;
automated native verification passed at the final head above.

### Before and after

Before: `37749a87bcdd41e8853c6fb487700e64ebbdfd3d` · After:
`69cf772c86f3a4d2e52b7a33f5477da2380f23b8`.

Identical fixture, route, 1280 × 900 window, and 474 × 758 Browser
viewport. Fresh Electron content captures at 2×. The blank before image
is the Browser content region after returning to server A; after is the
surviving native page.

| Native Browser after switching back | Before | After |
| --- | --- | --- |
| Page and unsaved note | ![Before: missing native
view](https://github.com/user-attachments/assets/c972d681-43de-4b0b-b785-eefb273976c8)
| ![After: native page and unsaved note
retained](https://github.com/user-attachments/assets/6c17d849-852d-46da-a1f0-1d61eb38ae7f)
|

Context before the fix:

![BB with the blank Browser tab after switching
back](https://github.com/user-attachments/assets/4eeaede2-83cc-40ff-b0de-2fb114a9457f)

<details>
<summary>Final review, follow-ups, and scope</summary>

Fresh per-PR Slop Cop reviewed
`37749a87bcdd41e8853c6fb487700e64ebbdfd3d..69cf772c86f3a4d2e52b7a33f5477da2380f23b8`
on October 14, 2026. Security found no actionable issues. Architecture
and code quality confirmed popup audio; performance confirmed
inactive-page CPU use. No P0/P1 findings. E2E reused the completed
exact-head Electron pass with two isolated servers and real input. The
completed GitHub review added one independently validated P2 and
duplicated the other two findings.

**P2 — Broker retries can retain the old target after a local-server
move.** `reconnect()` captures the builtin URL before
`activateLocalServerMoveIfLocked()` can select the committed
destination. Scheduled retries reuse that URL, leaving native automation
disconnected until another explicit reconnect. Follow-up: reconnect
using the final resolved target while preserving generation guards. Done
when a committed move discovered during target application connects to
the destination without another user switch. [Review
comment](get-bb/bb#5490 (comment)).

**P2 — Hidden popup audio can continue.** Parking hides popup windows
but does not mute their separate WebContents. Follow-up: mute/pause
inactive popup contents without destroying them. Done when a playing
popup becomes silent on A → B and restores correctly on return. [Review
comment](get-bb/bb#5490 (comment)).

**P2 — Inactive automation pages remain unthrottled.** Automation pages
retain foreground timer/frame behavior across server switches.
Follow-up: enable background throttling while parked and restore the
active setting, including CDP teardown. Done when inactive animated
pages reduce work while page state and active automation survive return.
[Review
comment](get-bb/bb#5490 (comment)).

Two Slim PR passes retained the existing view manager, visibility/reload
helpers, origin guards, and cleanup coverage. Before → after: production
+88/−42 → +88/−42; tests +100/−2 → +100/−2; docs +7/−0 → +7/−0;
generated 0 → 0. Local/enrolled cases protect separate descriptor paths;
window-close/app-exit cases protect separate cleanup paths. No code
changed during these passes; P2s remain follow-ups.

</details>

BB-Thread-ID: thr_8f2nidvuyn

> AGENT GENERATED
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