Add BH_NO_ACTIVATE flag to suppress focus-steal (switch_tab + new_tab + daemon)#402
Add BH_NO_ACTIVATE flag to suppress focus-steal (switch_tab + new_tab + daemon)#402undeemed wants to merge 3 commits into
Conversation
|
Closing — opened prematurely, will revisit on my own terms. Apologies for the noise. |
|
Wait this seems super useful. Can we reopen this so it can be merged? |
…itch
When driving a visible browser, every new_tab()/switch_tab() raises the
Chrome window to the OS foreground and steals keyboard focus from whatever
the user is typing in. Two CDP calls cause this on macOS:
- switch_tab(): Target.activateTarget raises + focuses the window.
- new_tab(): Target.createTarget opens the tab in the foreground
(foreground is the Mac default; createTarget takes a Mac-only
`background` flag).
Gate both behind an opt-in BH_NO_ACTIVATE=1 env flag (default off, so the
existing watch-along behavior is unchanged):
- switch_tab skips Target.activateTarget.
- new_tab / the daemon's no-real-pages bootstrap pass background:true.
The flag is read client-side, so callers just prefix runs with
BH_NO_ACTIVATE=1 -- no daemon restart needed. Routing is unaffected:
set_session still makes cdp()/js() default to the controlled tab, and the
horse-emoji title marker still shows which tab the agent drives.
Note: background:true only keeps the window backgrounded when the new tab is
not the window's only tab. With an empty window the first new_tab() still
foregrounds once; keep one real loaded sentinel tab open for fully
focus-free behavior.
Completes PR browser-use#402, which gated only the switch_tab activateTarget call;
verified empirically that createTarget alone still raises the window
(frontmost: Finder -> Chrome for Testing) unless background:true is set.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…_tab new_tab -> switch_tab -> Target.activateTarget raises the tab/window to the OS foreground on every call. When driving a *visible* browser that's the focus-steal users notice on each action. The 🐴 title marker already shows which tab is controlled, and set_session still routes cdp()/js() to the tab without activating it — so activation is purely cosmetic OS focus. Gate it behind an opt-in env flag (BH_NO_ACTIVATE=1) so automated/background drivers can suppress the focus-steal. Default behavior is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…daemon) switch_tab's activateTarget gate alone doesn't stop focus-steal: new_tab() and the daemon's no-real-pages bootstrap call Target.createTarget, which raises the OS window on macOS before switch_tab runs. Pass background:true under BH_NO_ACTIVATE=1 to cover the new_tab path too. Default unchanged. Adds unit tests for the background flag in new_tab and attach_first_page. Co-authored-by: Aaron Iba <ai@aaroniba.net>
|
Reopening — closed this early in June, complete now. |
64b7ed0 to
02f17a6
Compare
✅ Skill review passedReviewed 1 file(s) — no findings. |
02f17a6 to
1b9be4c
Compare
|
Only verified the focus-steal fix on macOS. The |
… check - new_tab(url, background=True|False) overrides BH_NO_ACTIVATE per call; switch_tab gains an `activate` override so backgrounding also skips the activateTarget raise (backgrounding createTarget alone doesn't stop it). - daemon bootstrap anchors a real data: page (not about:blank, which Chrome treats as internal) when no real pages exist, so new_tab stays backgrounded without a user-managed sentinel (follow-up suggested in browser-use#469). - centralize the env check in _no_activate(). - document BH_NO_ACTIVATE in SKILL.md + install.md. Tests for the per-call override (incl. the activate-skip regression) and the data: anchor; verified end-to-end on Chrome for Testing.
1b9be4c to
929b4dc
Compare
Problem
new_tab()→switch_tab()→cdp("Target.activateTarget", ...)raises the tab/window to the OS foreground on every call. When driving a visible browser (e.g. an isolated debug Chrome the user is watching), this steals focus on every single action — the window keeps popping to the front.Change
Gate the
Target.activateTargetcall behind an opt-in env flag:BH_NO_ACTIVATE=1is set.set_session(right below) is what makescdp()/js()default to the tab; activation is purely cosmetic OS focus, and the 🐴 title marker already shows which tab is controlled.switch_tab, so callers just prefix runs withBH_NO_ACTIVATE=1— no daemon restart needed.Use case
Automated/background drivers attached to a visible isolated browser, where focus-stealing on each
new_tabis disruptive.Summary by cubic
Added
BH_NO_ACTIVATEto suppress focus-steal:switch_tab()skipsTarget.activateTarget, andnew_tab()and the daemon passbackground:truetoTarget.createTargetso tabs open without raising the window (verified on macOS). Defaults are unchanged; when the flag is set the daemon anchors a realdata:tab to keep backgrounding reliable, and you can override per call vianew_tab(background=...)orswitch_tab(activate=...).Written for commit 929b4dc. Summary will update on new commits.