cmux NEXT: Cmd-N's New Tab field keeps the keyboard; Cmd-L in a chat focuses its composer
Merged
AU
11d ago+164+164+012345678901234567890123456789-11-11-01234567890123456789
Summary
- Cmd-N focus. Cmd-N sometimes opened the new workspace with no focus in the New Tab field (chat box). This happened on the current NIGHTLY,
1.0.0-nightly.3968001295301(8cba1da), where the New Tab page and the agent chat render in Chromium.- Cause:
CEFTab.setFocused(true)refuses a page that is not shown yet and keeps the request inpendingFocus. Onlyattach, which runs when the browser is created, ever replayed it. - A Cmd-N New Tab page is a prewarmed spare, so its browser already exists. Its focus request can arrive just before its window shows, or while the content lifecycle still hides it (the paint hold). That request was dropped.
FocusEffectApplierhad already recorded the page as focused (focusedChildWindowPage), so it never asked again.- Fix: the request is now kept on the tab and granted when the page shows (
CEFPaneHost.presentandsetContentVisible(true)). AsetFocused(false)that arrives first cancels it. Theattachpath behaves as before.
- Cause:
- Cmd-L in an agent chat (Focus Location Bar) now focuses the chat's composer. Before, it opened a New Tab page next to the chat. Browser tabs, Markdown tabs and New Tab pages behave as before.
- Supporting changes.
debug.agent_panegets a read-onlycomposer_focusedverb.keyboard-parity-e2e.pynow expects Cmd-L in a chat to focus the composer.
Regression proof
CEFDeferredFocusTests drives CEFTab and CEFPaneHost against a spy Chromium shim:
- A page whose browser already exists asks for focus before its view is in a window: it is granted focus when the view enters one.
- A page asks while the content lifecycle hides it: it is granted focus when the lifecycle shows it.
- A request withdrawn before the page shows is never granted.
Command: cmux-ci run --class light --script scripts/ci/package-test-lane.sh --arg=suite --arg=Packages/macOS/CmuxNext --arg=<suites>
- Red, before the fix: test commit
8dbd55d5, stepad216704645bb49d3060b5f9. The test failed withExpectation failed: (FocusSpyShim.granted ā []) == [7]. That run also crashed, a defect in the test harness (a tab outlived itsunownedruntime), fixed in95e09618. - Green, at the head:
95e09618, stepfaa022b7f953df63f6fb447d, withCEFDeferredFocusTests,CEFQuitCreationTests,PopupOmnibarKeyTests,CodeEditorKeysTests. Result: all 4 suites passed, including the 3 new tests.
Review
An independent subagent correctness review returned SHIP. Follow-ups it raised, not done in this PR:
- Stale focus request. A request deferred through a direct
focusChat/focusComposercall (outsideFocusEffectApplier) is never withdrawn if the person clicks elsewhere before the page shows. The page could take the keys later when it shows.- I tried clearing the request when the view leaves its window (
conceal) and reverted it. A prewarmed spare fires that same event when it moves from its holding window into the pane, so clearing there could drop the request this PR keeps. - A better fix is to send those direct calls through the focus applier.
- I tried clearing the request when the view leaves its window (
- Popups. Popups waiting for a window move (
awaitsWindowMove) still never replay a deferred request. This predates this PR. - Key hold may replay too early. The New Tab key hold's
focusFieldreturns true right afterfocusChat(), even when Chromium deferred the grant. Held keys may then be replayed before the page has focus. This is unconfirmed. - Harness-setup mode. Cmd-L in an agent pane in harness-setup mode now does nothing instead of opening a New Tab page.
Observed / Not run
- Observed:
- The red and green fleet runs above.
scripts/cmux-next/prepush-gate.pyreported no new violations.
- Not run:
- No tagged dev build or GUI dogfood of Cmd-N or Cmd-L in the real app.
- The race was found by reading the code, not by catching it live. The nightly does not expose
debug.focus. keyboard-parity-e2e.pywas not run.
No capture: this change is to focus and keyboard behavior; nothing is drawn differently.
Changelog
- Fixed: Cmd-N reliably puts the cursor in the new workspace's New Tab field.
- Changed: Cmd-L in an agent chat focuses its message box.
š¤ Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes Cmd-N occasionally leaving the new workspace's New Tab field without the keyboard, and makes Cmd-L in an agent chat focus its composer instead of opening a New Tab page next to it.
Bug Fixes
- A Cmd-N New Tab page (a prewarmed spare whose browser already exists) could ask for focus before its window showed or while the content lifecycle still hid it; only browser creation used to replay the request, so it was dropped. The request now waits on the tab and is granted when the page shows; a prior
setFocused(false)cancels it. - Cmd-L in an agent chat focuses the composer now; browser tabs, Markdown tabs, and New Tab pages behave as before. In harness-setup mode it does nothing instead of opening a New Tab page.
Tests
- Added
CEFDeferredFocusTestscovering focus requested before the page shows, while the lifecycle hides it, and withdrawn before showing. debug.agent_paneadds a read-onlycomposer_focusedverb;keyboard-parity-e2e.pyexpects Cmd-L in a chat to focus the composer.