Skip to content

Add failing regression test for browser find focus - #1891

Merged
lawrencecchen merged 9 commits into
mainfrom
task-find-input-focus-after-location-navigation
Mar 21, 2026
Merged

lawrencecchen merged 9 commits into
mainfrom
task-find-input-focus-after-location-navigation

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Mar 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add a narrow XCUITest for the Cmd+D, Cmd+L, navigate to example.com, Cmd+F repro.
  • Assert typing after Cmd+F lands in BrowserFindSearchTextField, not the omnibar.

Testing

Task

  • Plain-text task: Cmd+D, Cmd+L, navigate to example.com, Cmd+F should focus the browser find input.

Summary by CodeRabbit

  • Bug Fixes

    • Make in-page Find reliably receive focus when invoked, preventing the address bar from stealing input and improving Escape/Return/IME handling.
  • Tests

    • Added a UI regression that verifies split creation, browser navigation, and Cmd+F focus behavior preserves the omnibar content.
  • Chores

    • CI updated to run the new UI test, clarify test-job separation, adjust UI-regressions cache keys, and increase test time allowance.

@vercel

vercel Bot commented Mar 21, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Mar 21, 2026 5:06am

@coderabbitai

coderabbitai Bot commented Mar 21, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a UI test verifying Cmd+F focuses the in-page find field after creating a split and navigating via the omnibar; changes find-overlay focus handling (AppDelegate, BrowserPanel, BrowserPanelView, BrowserSearchOverlay) to avoid treating the overlay as web content and to manage programmatic focus via an AppKit-backed native text field; updates CI/workflow and guard script to run the new test.

Changes

Cohort / File(s) Summary
Browser Pane Navigation Tests
cmuxUITests/BrowserPaneNavigationKeybindUITests.swift
Adds testCmdFFocusesBrowserFindFieldAfterCmdDCmdLNavigation() exercising split creation (Cmd+D), omnibar navigation (Cmd+L → example.com), then Cmd+F to assert the in-page find field appears, is focused, and receives typed input while the omnibar remains unchanged.
Browser search/find overlay UI
Sources/Find/BrowserSearchOverlay.swift
Replaces SwiftUI TextField + @FocusState with an AppKit-backed BrowserSearchNativeTextField wrapped by BrowserSearchTextFieldRepresentable; moves focus/command handling into NSTextFieldDelegate, synchronizes text safely, and implements explicit programmatic focus via notifications and a pending-focus flag.
BrowserPanel focus startup
Sources/Panels/BrowserPanel.swift
When starting find, clears pendingAddressBarFocusRequestId and posts .browserDidBlurAddressBar with the panel id before initiating search focus request.
Browser panel view exit-handoff logic
Sources/Panels/BrowserPanelView.swift
Narrows omnibar “exit handoff” reclaiming of WebKit first responder to require panel.searchState == nil in addition to existing checks.
Responder / owning WebView lookup
Sources/AppDelegate.swift
Short-circuits responder-chain WebView association for the browser find overlay: if overlay panel id exists, returns nil so overlay editors are not treated as web content.
CI — GitHub Actions workflow
.github/workflows/ci.yml
Renames job ui-display-resolution-regression → ui-regressions, updates SwiftPM cache prefix, clarifies comments for tests-build-and-lag, and adds an xcodebuild step to run the new UI test with -disableAutomaticPackageResolution and increased test timeout (120s).
Self-hosted CI guard script
tests/test_ci_self_hosted_guard.sh
Updates awk selector and messages to validate the ui-regressions workflow block name instead of the previous job name.

Sequence Diagram(s)

sequenceDiagram
  participant User as User
  participant App as App/UI
  participant Window as NSWindow
  participant Panel as BrowserPanel
  participant Overlay as BrowserSearchOverlay
  participant WebView as CmuxWebView

  rect rgba(200,200,255,0.5)
  User->>App: Cmd+D (split)
  App->>Panel: create right split
  end

  rect rgba(200,255,200,0.5)
  User->>App: Cmd+L (omnibar)
  App->>Panel: focus omnibar / navigate to example.com
  Panel->>WebView: load URL
  end

  rect rgba(255,200,200,0.5)
  User->>App: Cmd+F
  App->>Panel: startFind()
  Panel->>Panel: clear pendingAddressBarFocusRequestId
  Panel->>App: post .browserDidBlurAddressBar(panel.id)
  Panel->>Overlay: beginSearchFocusRequest
  Overlay->>Window: request window.makeFirstResponder(nativeTextField) if allowed
  Window->>Overlay: native field becomes first responder
  Overlay->>User: accepts typed input into find field
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
I hopped through splits with a confident tap,
Cmd+L sent me to example.com's map,
Cmd+F nudged the find box to blink and prepare,
Keys landed on "needle", focused and fair,
The omnibar stayed silent as I nibbled there.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main objective: adding a regression test to verify browser find focus behavior after navigation.
Description check ✅ Passed The description covers the required Summary and Testing sections with specific details about what was tested and expected behavior; however, the Demo Video and Review Trigger sections are missing.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task-find-input-focus-after-location-navigation

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 and usage tips.

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 1 file

@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: 396aeb99db

ℹ️ 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".

Comment thread cmuxUITests/BrowserPaneNavigationKeybindUITests.swift Outdated
@greptile-apps

greptile-apps Bot commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a single failing XCUITest (testCmdFFocusesBrowserFindFieldAfterCmdDCmdLNavigation) that reproduces a focus regression: after the sequence Cmd+D (split right) → Cmd+L (open omnibar) → navigate to example.com → Cmd+F, focus incorrectly lands in the browser omnibar rather than the browser find field. The test is intentionally written to go red on CI before any fix is applied, following the project's two-commit regression-test policy documented in CLAUDE.md.

  • The test correctly uses CMUX_UI_TEST_GOTO_SPLIT_RECORD_ONLY (matching the pattern used by the existing runFindFocusPersistenceScenario helper) to enable split telemetry recording without triggering the full setup mode.
  • Two assertions provide overlapping coverage of the bug: findField.value == "needle" (typing landed in find) and omnibar.value == omnibarValueBeforeFindTyping (omnibar was not inadvertently focused).
  • A window.waitForExistence guard present in the analogous runFindFocusPersistenceScenario is absent here, creating a minor risk of flaky failures on slow CI runners if Cmd+D fires before the window hierarchy is ready.
  • The XCTAssertEqual failure message omits the actual omnibar value, which would be the most relevant debugging signal if the assertion fires.

Confidence Score: 4/5

  • Safe to merge — it is a test-only addition and is explicitly intended to be a failing test before the accompanying fix lands.
  • The change adds one test method to an existing XCUITest class with no production code changes. The test logic is sound, follows the established patterns in the file, and correctly captures the described bug scenario. Two minor style suggestions were identified (missing window guard, incomplete failure message), neither of which affects correctness.
  • No files require special attention beyond the minor style suggestions on cmuxUITests/BrowserPaneNavigationKeybindUITests.swift.

Important Files Changed

Filename Overview
cmuxUITests/BrowserPaneNavigationKeybindUITests.swift Adds a focused regression test for the Cmd+D → Cmd+L → navigate → Cmd+F focus bug. The test logic is sound and follows established patterns; two minor style improvements suggested (window existence guard, failure message completeness).

Sequence Diagram

sequenceDiagram
    participant T as XCUITest
    participant A as cmux App
    participant DB as Data File (JSON)

    T->>A: launch (RECORD_ONLY=1, SOCKET_PATH, DATA_PATH)
    A-->>T: runningForeground
    T->>A: typeKey("d", .command)  [Cmd+D → split right]
    T->>DB: waitForDataMatch(timeout:6s) lastSplitDirection=="right" && paneCount>=2
    DB-->>T: split confirmed

    T->>A: typeKey("l", .command)  [Cmd+L → open omnibar]
    T->>A: waitForExistence("BrowserOmnibarTextField", timeout:8s)
    A-->>T: omnibar visible

    T->>A: typeKey("a", .command)  [select all]
    T->>A: typeKey(delete)
    T->>A: typeText("example.com")
    T->>A: typeKey(return)

    T->>A: waitForCondition(timeout:8s) omnibar contains "example.com"
    A-->>T: navigation confirmed

    T->>A: typeKey("f", .command)  [Cmd+F → open find]
    T->>A: waitForExistence("BrowserFindSearchTextField", timeout:6s)
    A-->>T: find field visible

    Note over T: Capture omnibarValueBeforeFindTyping
    T->>A: typeText("needle")

    T->>A: waitForCondition(timeout:4s) findField.value == "needle"
    Note over T,A: BUG: focus lands in omnibar → findField stays empty → test FAILS
    T->>T: XCTAssertEqual(omnibar.value, omnibarValueBeforeFindTyping)
    Note over T,A: BUG: omnibar receives "needle" → value changed → test FAILS
Loading

Last reviewed commit: "test: add browser fi..."

Comment thread cmuxUITests/BrowserPaneNavigationKeybindUITests.swift
Comment thread cmuxUITests/BrowserPaneNavigationKeybindUITests.swift Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

551-554: Harden virtual-display lifecycle handling.

The helper is started in the background but this step doesn’t verify it stayed alive or clean it up. Add a quick liveness check and an if: always() cleanup step to reduce flakiness.

Suggested hardening
       - name: Create virtual display
         run: |
           set -euo pipefail
@@
           /tmp/create-virtual-display &
           VDISPLAY_PID=$!
           echo "VDISPLAY_PID=$VDISPLAY_PID" >> "$GITHUB_ENV"
           sleep 3
+          kill -0 "$VDISPLAY_PID"
           echo "=== Display after ==="
           system_profiler SPDisplaysDataType 2>/dev/null || echo "(none)"
+
+      - name: Cleanup virtual display
+        if: always()
+        run: |
+          set -euo pipefail
+          if [ -n "${VDISPLAY_PID:-}" ]; then
+            kill "$VDISPLAY_PID" >/dev/null 2>&1 || true
+          fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 551 - 554, Start the background
virtual-display helper (/tmp/create-virtual-display) and immediately verify it
stayed alive by checking the process id stored in VDISPLAY_PID (ensure
VDISPLAY_PID is exported to GITHUB_ENV as shown), e.g., test the process with
kill -0 or wait -n to detect early failure and fail the job if it died; then add
a separate cleanup job/step that runs with if: always() which checks for
VDISPLAY_PID and kills/cleans the process (and removes any temp files) to
guarantee teardown. Ensure the liveness check references VDISPLAY_PID and the
cleanup step references the same VDISPLAY_PID variable so the lifecycle is
robust.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci.yml:
- Line 497: The workflow uses a custom runner label "warp-macos-15-arm64-6x"
which actionlint doesn't know about; add an actionlint config file
(.actionlintrc.yaml or .actionlint.yaml) at the repository root and declare that
custom self-hosted runner label so actionlint validation succeeds (ensure the
config lists the label used in the runs-on: warp-macos-15-arm64-6x entry).

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 551-554: Start the background virtual-display helper
(/tmp/create-virtual-display) and immediately verify it stayed alive by checking
the process id stored in VDISPLAY_PID (ensure VDISPLAY_PID is exported to
GITHUB_ENV as shown), e.g., test the process with kill -0 or wait -n to detect
early failure and fail the job if it died; then add a separate cleanup job/step
that runs with if: always() which checks for VDISPLAY_PID and kills/cleans the
process (and removes any temp files) to guarantee teardown. Ensure the liveness
check references VDISPLAY_PID and the cleanup step references the same
VDISPLAY_PID variable so the lifecycle is robust.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b71149f9-92b3-4e1d-b962-00c996fea985

📥 Commits

Reviewing files that changed from the base of the PR and between 396aeb9 and a30e698.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

Comment thread .github/workflows/ci.yml Outdated

ui-browser-find-focus-regression:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: warp-macos-15-arm64-6x

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locating actionlint configuration..."
CONFIGS="$(fd -a 'actionlint\\.ya?ml$' || true)"
if [ -z "$CONFIGS" ]; then
  echo "No actionlint config found. Add one and declare custom self-hosted labels."
  exit 0
fi

echo "$CONFIGS"
CFG="$(echo "$CONFIGS" | head -n1)"
echo "Inspecting: $CFG"
rg -n 'self-hosted-runner|labels|warp-macos-15-arm64-6x|runner' "$CFG" || true

Repository: manaflow-ai/cmux

Length of output: 171


Add actionlint configuration to declare custom Warp runner labels.

The custom runner label warp-macos-15-arm64-6x is not registered with actionlint. Create an .actionlintrc.yaml or .actionlint.yaml file at the repository root with the self-hosted runner labels to prevent workflow validation failures.

🧰 Tools
🪛 actionlint (1.7.11)

[error] 497-497: label "warp-macos-15-arm64-6x" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2025-vs2026", "windows-2022", "windows-11-arm", "ubuntu-slim", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26-large", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xlarge", "macos-14-large", "macos-14", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml at line 497, The workflow uses a custom runner
label "warp-macos-15-arm64-6x" which actionlint doesn't know about; add an
actionlint config file (.actionlintrc.yaml or .actionlint.yaml) at the
repository root and declare that custom self-hosted runner label so actionlint
validation succeeds (ensure the config lists the label used in the runs-on:
warp-macos-15-arm64-6x entry).

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

493-494: Minor: Add blank line between steps for consistency.

The rest of the workflow has blank lines between steps for readability.

📝 Suggested formatting
             -only-testing:cmuxUITests/DisplayResolutionRegressionUITests \
             test
+
       - name: Run browser find focus UI regression
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 493 - 494, Add a blank line to match
the workflow's step separation style by inserting an empty line before the step
with name "Run browser find focus UI regression" so there is a blank line
between the preceding "test" step and this step; update the CI YAML so each
top-level job step maintains the same blank-line spacing for readability.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 493-494: Add a blank line to match the workflow's step separation
style by inserting an empty line before the step with name "Run browser find
focus UI regression" so there is a blank line between the preceding "test" step
and this step; update the CI YAML so each top-level job step maintains the same
blank-line spacing for readability.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8d1137bc-f48d-4080-b385-ae4b28275311

📥 Commits

Reviewing files that changed from the base of the PR and between a30e698 and af1175a.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

@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: af1175ac81

ℹ️ 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".

Comment thread cmuxUITests/BrowserPaneNavigationKeybindUITests.swift

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
Sources/Find/BrowserSearchOverlay.swift (1)

105-110: Duplicate notification handling for .browserSearchFocus.

This .onReceive handler sets isSearchFieldFocused = true, which triggers updateNSView to call makeFirstResponder. However, BrowserSearchTextFieldRepresentable also observes the same notification at lines 288-304 and directly calls window.makeFirstResponder(field).

Both paths guard against already-focused state, so this won't cause functional issues, but it's redundant. Consider removing one of the handlers—the Coordinator's observer (lines 288-304) is more direct and doesn't rely on SwiftUI state propagation timing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Find/BrowserSearchOverlay.swift` around lines 105 - 110, Remove the
duplicate `.onReceive` handler that listens for `.browserSearchFocus` and sets
`isSearchFieldFocused = true`; instead rely on the existing direct observer in
`BrowserSearchTextFieldRepresentable.Coordinator` (lines handling
`window.makeFirstResponder(field)`). Specifically, delete the
`.onReceive(NotificationCenter.default.publisher(for: .browserSearchFocus)) {
... }` block in `BrowserSearchOverlay` (the code that checks `notifiedPanelId ==
panelId`, `canApplyFocusRequest(focusRequestGeneration)`, and sets
`isSearchFieldFocused = true`) so focus is handled only by the Coordinator's
observer which uses `makeFirstResponder` directly. Ensure no other logic depends
on `isSearchFieldFocused` being toggled by this notification before removing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Sources/Find/BrowserSearchOverlay.swift`:
- Around line 105-110: Remove the duplicate `.onReceive` handler that listens
for `.browserSearchFocus` and sets `isSearchFieldFocused = true`; instead rely
on the existing direct observer in
`BrowserSearchTextFieldRepresentable.Coordinator` (lines handling
`window.makeFirstResponder(field)`). Specifically, delete the
`.onReceive(NotificationCenter.default.publisher(for: .browserSearchFocus)) {
... }` block in `BrowserSearchOverlay` (the code that checks `notifiedPanelId ==
panelId`, `canApplyFocusRequest(focusRequestGeneration)`, and sets
`isSearchFieldFocused = true`) so focus is handled only by the Coordinator's
observer which uses `makeFirstResponder` directly. Ensure no other logic depends
on `isSearchFieldFocused` being toggled by this notification before removing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7b272686-1ae3-4e6e-ac9e-11ff7719ade5

📥 Commits

Reviewing files that changed from the base of the PR and between af93cc2 and 717ac6d.

📒 Files selected for processing (3)
  • Sources/AppDelegate.swift
  • Sources/Find/BrowserSearchOverlay.swift
  • Sources/Panels/BrowserPanelView.swift

@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: 717ac6df29

ℹ️ 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".

Comment thread Sources/AppDelegate.swift

@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: 97b2202b0b

ℹ️ 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".

Comment on lines +293 to +295
let alreadyFocused = fr === field ||
field.currentEditor() != nil ||
((fr as? NSTextView)?.delegate as? NSTextField) === field

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Replace unsafe delegate probe in responder focus check

The alreadyFocused check dereferences NSTextView.delegate (((fr as? NSTextView)?.delegate as? NSTextField)) to detect field-editor ownership, but AppKit stores that delegate as unsafe-unretained; if responder teardown has already released the backing text field, this access can trap with an unowned-reference crash. This path runs in both the .browserSearchFocus observer and async focus retries, so closing/churning panes while Cmd+F focus handoff is in flight can crash the app; use a safe ownership check (e.g., currentEditor()/responder-chain walk) instead of touching delegate directly.

Useful? React with 👍 / 👎.

Comment thread Sources/AppDelegate.swift
Comment on lines +12469 to +12470
if BrowserWindowPortalRegistry.searchOverlayPanelId(for: responder, in: window) != nil {
return nil

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 Gate search-overlay lookup to field-editor responders

This new early check runs before any responder-type filtering, so every cmuxOwningWebView(for:in:event:) call now performs a portal-wide searchOverlayPanelId scan even when the first responder is unrelated (e.g., terminal/web view). Because this helper is called from cmux_performKeyEquivalent on key events, the change adds avoidable O(number of browser slots) work to a typing hot path and can regress input latency in sessions with many panes; limit the lookup to field-editor responders (the only case that needs special-casing).

Useful? React with 👍 / 👎.

@lawrencecchen
lawrencecchen merged commit a592ed1 into main Mar 21, 2026
14 checks passed
@lawrencecchen
lawrencecchen deleted the task-find-input-focus-after-location-navigation branch March 21, 2026 05:12
bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
* test: add browser find focus regression

* ci: run browser find focus regression on prs

* ci: merge pr ui regressions into one job

* fix: focus browser find after navigation

* test: rename ui regressions workflow guard

* Fix browser find focus after address bar navigation

* Fix test-e2e workflow manifest script

* Address PR review feedback

* Stabilize browser focus regression on CI

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
Preview — b35dfc4b Deployed Mar 21, 2026 by vercel[bot]
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