Skip to content

Select find text on repeated Cmd+F - #3314

Merged
lawrencecchen merged 2 commits into
mainfrom
task-cmd-f-select-find-text
Apr 30, 2026
Merged

lawrencecchen merged 2 commits into
mainfrom
task-cmd-f-select-find-text

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Apr 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Select existing terminal find text when Cmd+F is pressed while terminal find is already open
  • Select existing browser find text when Cmd+F is pressed while browser find is already open
  • Add a UI regression test covering replacement behavior for both fields

Testing

  • ./scripts/reload.sh --tag task-cmd-f-select-find-text (pass)

Issues

  • Related: user-requested task: repeated Cmd+F should select the open terminal or browser find field text

Summary by cubic

Pressing Cmd+F again now selects the current find text in the terminal and in-app browser so you can replace it immediately. First open focuses the field; repeated Cmd+F selects all.

  • New Features
    • Terminal: Repeated Cmd+F sends a selectAll focus signal and selects all even if already focused; first open focuses without selecting. IME composition is respected.
    • Browser: Same behavior via focus notifications with selectAll; first open moves the caret to the end. IME-safe.
    • Tests: Added FindSelectionShortcutUITests to confirm repeated Cmd+F replaces text in both find fields.

Written for commit bd983f5. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • Improvements

    • Smarter search-field focusing: optional “select all” on focus, preserves IME composition, avoids unwanted caret moves, and keeps selection behavior consistent when switching between browser and terminal panes
    • More reliable search initiation so repeated activations target the intended pane and selection state
  • Tests

    • New UI tests validating repeated search activation, cross-pane focus/selection, and find/replace flows

@vercel

vercel Bot commented Apr 29, 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 Apr 29, 2026 11:45pm
cmux-staging Building Building Preview, Comment Apr 29, 2026 11:45pm

@coderabbitai

coderabbitai Bot commented Apr 29, 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2f5fd863-3fe3-476c-945a-5759683535ec

📥 Commits

Reviewing files that changed from the base of the PR and between 511ad54 and bd983f5.

📒 Files selected for processing (6)
  • GhosttyTabs.xcodeproj/project.pbxproj
  • Sources/Find/BrowserSearchOverlay.swift
  • Sources/Find/SurfaceSearchOverlay.swift
  • Sources/Panels/BrowserPanel.swift
  • Sources/TabManager.swift
  • cmuxUITests/FindSelectionShortcutUITests.swift
🚧 Files skipped from review as they are similar to previous changes (2)
  • GhosttyTabs.xcodeproj/project.pbxproj
  • Sources/Panels/BrowserPanel.swift

📝 Walkthrough

Walkthrough

Adds a selectAll-aware focus/selection flow for search fields, centralizes first-responder checks, avoids selection during IME composition, updates notification payloads and callers to include selectAll, and adds a UI test that exercises repeated Cmd+F across terminal and browser panes.

Changes

Cohort / File(s) Summary
Find overlays & focus helpers
Sources/Find/BrowserSearchOverlay.swift, Sources/Find/SurfaceSearchOverlay.swift
Add focusField(..., selectAll:), introduce cmuxTextFieldIsFirstResponder helper, read FindFocusNotificationKey.selectAll from notifications, avoid changing selection when editor.hasMarkedText(), and unify focus/selection behavior.
Focus notification senders & routing
Sources/Panels/BrowserPanel.swift, Sources/TabManager.swift
Include selectAll in .browserSearchFocus / .ghosttySearchFocus userInfo; postBrowserSearchFocusNotification signature updated; startFind / startSearch delegate terminal startup to startOrFocusTerminalSearch and pass selectAll via callback.
UI test and project registration
cmuxUITests/FindSelectionShortcutUITests.swift, GhosttyTabs.xcodeproj/project.pbxproj
Add FindSelectionShortcutUITests exercising repeated Cmd+F selection/replace across splits and register the test source in the cmuxUITests target.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant TM as TabManager
    participant NC as NotificationCenter
    participant OV as SearchOverlay
    participant ED as Editor

    User->>TM: Press Cmd+F (start/focus)
    TM->>NC: Post .browser/.ghosttySearchFocus (userInfo: selectAll)
    NC->>OV: Deliver focus notification (contains selectAll)
    OV->>OV: call cmuxTextFieldIsFirstResponder()
    alt selectAll == true
        OV->>ED: focusField(selectAll: true)
        ED->>ED: if hasMarkedText() then skip selection
        ED->>ED: else select all text
    else
        OV->>ED: focusField(selectAll: false)
        ED->>ED: if not already first responder move caret to end
    end
    ED->>User: Update caret/selection in UI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
I hopped to the search field bright,
Cmd+F gave focus just right,
IME tiptoed — I paused my paw,
Select‑all danced when called by law,
A tiny hop, a tidy find — hooray!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Select find text on repeated Cmd+F' clearly and concisely summarizes the main change: enabling text selection in find fields when Cmd+F is pressed repeatedly while the field is already focused.
Description check ✅ Passed The PR description covers all key template sections: a detailed Summary explaining the feature for both terminal and browser, a Testing section with verification steps, and references to related issues. The structure aligns well with the template requirements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task-cmd-f-select-find-text

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
Review rate limit: 6/8 reviews remaining, refill in 13 minutes and 20 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmuxUITests/BrowserPaneNavigationKeybindUITests.swift`:
- Around line 964-966: After sending the Cmd+D split command (app.typeKey("d",
modifierFlags: [.command])) wait for the split to finish before calling
focusRightPaneForFindScenario(...); add a short synchronization step that polls
for the new pane/split UI (for example using an XCTAssertTrue(...
.waitForExistence(timeout:)) or a small helper like
waitForSplitCreation()/waitForSecondEditorSplit(timeout:)) targeting the right
pane or split divider, then proceed to call focusRightPaneForFindScenario(app,
route: .cmdOptionArrows).

In `@Sources/Find/BrowserSearchOverlay.swift`:
- Around line 225-230: The checks that detect whether a text field is already
focused (found in focusField(_:in:selectAll:), the notification handler around
line ~315, focusNativeTextField(_:in:), and the computed property
alreadyFocused) currently use unsafe casts like ((fr as? NSTextView)?.delegate
as? NSTextField) === field; replace those with the safe field-editor ownership
helper cmuxFieldEditorOwnerView(_:) by resolving the firstResponder as (fr as?
NSTextView).flatMap { cmuxFieldEditorOwnerView($0) } === field so you traverse
the responder chain safely and avoid dereferencing NSTextView.delegate; update
each occurrence accordingly to use cmuxFieldEditorOwnerView.

In `@Sources/TabManager.swift`:
- Around line 1926-1933: When selectAllExistingText is false (first-open path)
the code posts NotificationCenter.default.post(name: .ghosttySearchFocus,
object: panel.surface, userInfo: [FindFocusNotificationKey.selectAll:
selectAllExistingText]) before running
panel.performBindingAction("start_search"), which can cause the overlay to drop
the focus event; move or defer the .ghosttySearchFocus notification so it is
posted only after panel.performBindingAction("start_search") completes in the
branch where selectAllExistingText == false, while preserving the original
behavior when selectAllExistingText == true (i.e., still post immediately with
the same userInfo and object values).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7cda5193-81c8-409f-98ac-ed75410dc24b

📥 Commits

Reviewing files that changed from the base of the PR and between e181f99 and 9368d56.

📒 Files selected for processing (6)
  • Sources/Find/BrowserSearchOverlay.swift
  • Sources/Find/SurfaceSearchOverlay.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/Panels/BrowserPanel.swift
  • Sources/TabManager.swift
  • cmuxUITests/BrowserPaneNavigationKeybindUITests.swift

Comment on lines +964 to +966
app.typeKey("d", modifierFlags: [.command])
focusRightPaneForFindScenario(app, route: .cmdOptionArrows)

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 | ⚡ Quick win

Stabilize split creation before the first pane-focus hop.

focusRightPaneForFindScenario(...) runs immediately after Cmd+D; adding a short split-complete wait here will reduce CI flake.

Suggested test hardening
         app.typeKey("d", modifierFlags: [.command])
+        XCTAssertTrue(
+            waitForDataMatch(timeout: 6.0) { data in
+                guard data["lastSplitDirection"] == "right" else { return false }
+                guard let paneCountAfterSplit = Int(data["paneCountAfterSplit"] ?? "") else { return false }
+                return paneCountAfterSplit >= 2
+            },
+            "Expected Cmd+D split to complete before focusing the right pane. data=\(String(describing: loadData()))"
+        )
         focusRightPaneForFindScenario(app, route: .cmdOptionArrows)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmuxUITests/BrowserPaneNavigationKeybindUITests.swift` around lines 964 -
966, After sending the Cmd+D split command (app.typeKey("d", modifierFlags:
[.command])) wait for the split to finish before calling
focusRightPaneForFindScenario(...); add a short synchronization step that polls
for the new pane/split UI (for example using an XCTAssertTrue(...
.waitForExistence(timeout:)) or a small helper like
waitForSplitCreation()/waitForSecondEditorSplit(timeout:)) targeting the right
pane or split divider, then proceed to call focusRightPaneForFindScenario(app,
route: .cmdOptionArrows).

Comment thread Sources/Find/BrowserSearchOverlay.swift
Comment thread Sources/TabManager.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.

♻️ Duplicate comments (1)
Sources/TabManager.swift (1)

1921-1938: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep the first-open focus notification after start_search.

The initial Cmd+F path still posts .ghosttySearchFocus before the field is mounted, so the focus event can be dropped. This is the same race that was already called out on the previous revision.

Suggested fix
-            NotificationCenter.default.post(
-                name: .ghosttySearchFocus,
-                object: panel.surface,
-                userInfo: [FindFocusNotificationKey.selectAll: selectAllExistingText]
-            )
             if !selectAllExistingText {
                 _ = panel.performBindingAction("start_search")
             }
+            NotificationCenter.default.post(
+                name: .ghosttySearchFocus,
+                object: panel.surface,
+                userInfo: [FindFocusNotificationKey.selectAll: selectAllExistingText]
+            )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/TabManager.swift` around lines 1921 - 1938, The focus notification is
being posted before the search field is mounted, so ensure the first-open focus
notification is sent after starting the search; when selectAllExistingText is
false call panel.performBindingAction("start_search") (or mount the search
field) before posting NotificationCenter.default.post(name: .ghosttySearchFocus,
...), and retain the existing selectAllExistingText flag/logic around
panel.searchState and TerminalSurface.SearchState so the notification still
carries FindFocusNotificationKey.selectAll for the initial open.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@Sources/TabManager.swift`:
- Around line 1921-1938: The focus notification is being posted before the
search field is mounted, so ensure the first-open focus notification is sent
after starting the search; when selectAllExistingText is false call
panel.performBindingAction("start_search") (or mount the search field) before
posting NotificationCenter.default.post(name: .ghosttySearchFocus, ...), and
retain the existing selectAllExistingText flag/logic around panel.searchState
and TerminalSurface.SearchState so the notification still carries
FindFocusNotificationKey.selectAll for the initial open.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3b6046e1-fa50-40b0-ac7c-eda1dc2e6302

📥 Commits

Reviewing files that changed from the base of the PR and between 9368d56 and 3e2cd3c.

📒 Files selected for processing (6)
  • GhosttyTabs.xcodeproj/project.pbxproj
  • Sources/Find/BrowserSearchOverlay.swift
  • Sources/Find/SurfaceSearchOverlay.swift
  • Sources/Panels/BrowserPanel.swift
  • Sources/TabManager.swift
  • cmuxUITests/FindSelectionShortcutUITests.swift
✅ Files skipped from review due to trivial changes (1)
  • GhosttyTabs.xcodeproj/project.pbxproj
🚧 Files skipped from review as they are similar to previous changes (2)
  • Sources/Find/SurfaceSearchOverlay.swift
  • Sources/Find/BrowserSearchOverlay.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: a2e4a1231d

ℹ️ 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 5040 to +5041
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self] in
self?.postBrowserSearchFocusNotification(reason: "async50ms", generation: generation)
self?.postBrowserSearchFocusNotification(reason: "async50ms", generation: generation, selectAll: false)

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 Preserve select-all intent on final browser focus retry

The async50ms retry is explicitly there to handle cases where earlier focus notifications race with find-bar mount, but this call now hard-codes selectAll: false. In that race path, a repeated Cmd+F (where created == false) will focus the field without selecting existing text, so typing appends instead of replacing. This makes the new behavior flaky on slower/layout-churn paths where only the delayed retry is effective.

Useful? React with 👍 / 👎.

@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)
cmuxUITests/FindSelectionShortcutUITests.swift (1)

8-15: 💤 Low value

Consider adding tearDown for temp file cleanup.

The test creates temp files but doesn't clean them up. While /tmp files are eventually cleared by the OS, explicit cleanup in tearDown is better practice and helps when running tests repeatedly during development.

♻️ Suggested addition
override func tearDown() {
    try? FileManager.default.removeItem(atPath: dataPath)
    try? FileManager.default.removeItem(atPath: socketPath)
    super.tearDown()
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmuxUITests/FindSelectionShortcutUITests.swift` around lines 8 - 15, Add a
tearDown implementation to remove the temporary files created in setUp: in the
test class implement override func tearDown() that calls
FileManager.default.removeItem for both dataPath and socketPath (using try? to
ignore errors) before calling super.tearDown(), ensuring the temporary JSON and
socket files created in setUp are cleaned up after each test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmuxUITests/FindSelectionShortcutUITests.swift`:
- Around line 41-48: The test's Pane.needleKey values are wrong and must match
the app's keys; update the computed property needleKey in the Pane enum to
return "focusedTerminalFindNeedle" for .terminal and "focusedBrowserFindNeedle"
for .browser so the UI test reads the same JSON keys the app writes (refer to
Pane and its needleKey property).

---

Nitpick comments:
In `@cmuxUITests/FindSelectionShortcutUITests.swift`:
- Around line 8-15: Add a tearDown implementation to remove the temporary files
created in setUp: in the test class implement override func tearDown() that
calls FileManager.default.removeItem for both dataPath and socketPath (using
try? to ignore errors) before calling super.tearDown(), ensuring the temporary
JSON and socket files created in setUp are cleaned up after each test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 355ab725-eadf-4a0d-af07-ca4a64d71182

📥 Commits

Reviewing files that changed from the base of the PR and between 3e2cd3c and a2e4a12.

📒 Files selected for processing (6)
  • GhosttyTabs.xcodeproj/project.pbxproj
  • Sources/Find/BrowserSearchOverlay.swift
  • Sources/Find/SurfaceSearchOverlay.swift
  • Sources/Panels/BrowserPanel.swift
  • Sources/TabManager.swift
  • cmuxUITests/FindSelectionShortcutUITests.swift
✅ Files skipped from review due to trivial changes (1)
  • GhosttyTabs.xcodeproj/project.pbxproj
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/Find/BrowserSearchOverlay.swift

Comment on lines +41 to +48
private enum Pane {
case terminal
case browser

var focusKey: String { self == .terminal ? "terminal" : "browser" }
var needleKey: String { self == .terminal ? "terminalFindNeedle" : "browserFindNeedle" }
var replacementMessage: String { self == .terminal ? "terminal find text" : "browser find text" }
}

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 | 🔴 Critical | ⚡ Quick win

Incorrect JSON keys will cause test to always fail.

The needleKey values don't match the keys written by the app. Per the context snippet from Sources/AppDelegate.swift:8471-8492, the app writes "focusedTerminalFindNeedle" and "focusedBrowserFindNeedle", but the test looks for "terminalFindNeedle" and "browserFindNeedle".

🐛 Proposed fix
     private enum Pane {
         case terminal
         case browser

         var focusKey: String { self == .terminal ? "terminal" : "browser" }
-        var needleKey: String { self == .terminal ? "terminalFindNeedle" : "browserFindNeedle" }
+        var needleKey: String { self == .terminal ? "focusedTerminalFindNeedle" : "focusedBrowserFindNeedle" }
         var replacementMessage: String { self == .terminal ? "terminal find text" : "browser find text" }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private enum Pane {
case terminal
case browser
var focusKey: String { self == .terminal ? "terminal" : "browser" }
var needleKey: String { self == .terminal ? "terminalFindNeedle" : "browserFindNeedle" }
var replacementMessage: String { self == .terminal ? "terminal find text" : "browser find text" }
}
private enum Pane {
case terminal
case browser
var focusKey: String { self == .terminal ? "terminal" : "browser" }
var needleKey: String { self == .terminal ? "focusedTerminalFindNeedle" : "focusedBrowserFindNeedle" }
var replacementMessage: String { self == .terminal ? "terminal find text" : "browser find text" }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmuxUITests/FindSelectionShortcutUITests.swift` around lines 41 - 48, The
test's Pane.needleKey values are wrong and must match the app's keys; update the
computed property needleKey in the Pane enum to return
"focusedTerminalFindNeedle" for .terminal and "focusedBrowserFindNeedle" for
.browser so the UI test reads the same JSON keys the app writes (refer to Pane
and its needleKey property).

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

♻️ Duplicate comments (2)
Sources/TabManager.swift (1)

1923-1929: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

First-open Cmd+F can still lose focus through startOrFocusTerminalSearch.

This now delegates the initial-open path to startOrFocusTerminalSearch, but that helper only invokes searchFocusNotifier after start_search if terminalSurface.searchState is still nil on the next runloop. If start_search eagerly creates the search state, the notifier never runs, so the first Cmd+F can open the find UI without focusing the field.

Suggested fix in Sources/App/ShortcutRoutingSupport.swift
 if terminalSurface.performBindingAction("start_search") {
     DispatchQueue.main.async { [weak terminalSurface] in
-        guard let terminalSurface, terminalSurface.searchState == nil else { return }
-        terminalSurface.searchState = TerminalSurface.SearchState()
+        guard let terminalSurface else { return }
+        if terminalSurface.searchState == nil {
+            terminalSurface.searchState = TerminalSurface.SearchState()
+        }
         searchFocusNotifier(terminalSurface)
     }
     return true
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/TabManager.swift` around lines 1923 - 1929,
startOrFocusTerminalSearch can miss calling the search-focus notifier when
start_search eagerly creates terminalSurface.searchState; update
startOrFocusTerminalSearch so that when it triggers the initial-open path (i.e.,
start_search returns/creates a new search state or terminalSurface.searchState
becomes non-nil immediately) it posts the .ghosttySearchFocus notification
(using the same userInfo [FindFocusNotificationKey.selectAll:
hadExistingSearch]) immediately instead of relying on a subsequent runloop
check. Reference startOrFocusTerminalSearch, start_search,
terminalSurface.searchState, .ghosttySearchFocus and
FindFocusNotificationKey.selectAll when making the change.
cmuxUITests/FindSelectionShortcutUITests.swift (1)

40-47: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Incorrect JSON keys will cause test to always fail.

Per the context snippet from Sources/AppDelegate.swift:8471-8520, the app writes both key sets:

  • "focusedTerminalFindNeedle" / "focusedBrowserFindNeedle" — for the currently focused panel
  • "terminalFindNeedle" / "browserFindNeedle" — for any panel with find state (first found, regardless of focus)

Since assertFindReplacement first verifies focusedPanelKind == pane.focusKey (line 65), the test is checking the focused panel's find state. The needleKey should therefore use the "focused*" variants.

🐛 Proposed fix
     private enum Pane {
         case terminal
         case browser

         var focusKey: String { self == .terminal ? "terminal" : "browser" }
-        var needleKey: String { self == .terminal ? "terminalFindNeedle" : "browserFindNeedle" }
+        var needleKey: String { self == .terminal ? "focusedTerminalFindNeedle" : "focusedBrowserFindNeedle" }
         var replacementMessage: String { self == .terminal ? "terminal find text" : "browser find text" }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmuxUITests/FindSelectionShortcutUITests.swift` around lines 40 - 47, The
Pane.enum's needleKey currently returns "terminalFindNeedle"/"browserFindNeedle"
causing tests to read the non-focused keys; update Pane.needleKey to return the
focused variants ("focusedTerminalFindNeedle" for .terminal and
"focusedBrowserFindNeedle" for .browser) so assertFindReplacement (which checks
focusedPanelKind == pane.focusKey) reads the correct focused find state; locate
Pane in FindSelectionShortcutUITests.swift and change the needleKey string
values accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@cmuxUITests/FindSelectionShortcutUITests.swift`:
- Around line 40-47: The Pane.enum's needleKey currently returns
"terminalFindNeedle"/"browserFindNeedle" causing tests to read the non-focused
keys; update Pane.needleKey to return the focused variants
("focusedTerminalFindNeedle" for .terminal and "focusedBrowserFindNeedle" for
.browser) so assertFindReplacement (which checks focusedPanelKind ==
pane.focusKey) reads the correct focused find state; locate Pane in
FindSelectionShortcutUITests.swift and change the needleKey string values
accordingly.

In `@Sources/TabManager.swift`:
- Around line 1923-1929: startOrFocusTerminalSearch can miss calling the
search-focus notifier when start_search eagerly creates
terminalSurface.searchState; update startOrFocusTerminalSearch so that when it
triggers the initial-open path (i.e., start_search returns/creates a new search
state or terminalSurface.searchState becomes non-nil immediately) it posts the
.ghosttySearchFocus notification (using the same userInfo
[FindFocusNotificationKey.selectAll: hadExistingSearch]) immediately instead of
relying on a subsequent runloop check. Reference startOrFocusTerminalSearch,
start_search, terminalSurface.searchState, .ghosttySearchFocus and
FindFocusNotificationKey.selectAll when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 68b5b140-38bd-46d4-8835-bbf20e0b647f

📥 Commits

Reviewing files that changed from the base of the PR and between a2e4a12 and 511ad54.

📒 Files selected for processing (6)
  • GhosttyTabs.xcodeproj/project.pbxproj
  • Sources/Find/BrowserSearchOverlay.swift
  • Sources/Find/SurfaceSearchOverlay.swift
  • Sources/Panels/BrowserPanel.swift
  • Sources/TabManager.swift
  • cmuxUITests/FindSelectionShortcutUITests.swift
✅ Files skipped from review due to trivial changes (1)
  • GhosttyTabs.xcodeproj/project.pbxproj
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/Find/BrowserSearchOverlay.swift

@lawrencecchen
lawrencecchen force-pushed the task-cmd-f-select-find-text branch from 511ad54 to bd983f5 Compare April 29, 2026 23:44
@lawrencecchen
lawrencecchen merged commit 14060f5 into main Apr 30, 2026
24 of 26 checks passed
@lawrencecchen
lawrencecchen deleted the task-cmd-f-select-find-text branch April 30, 2026 00:31
ShubhamPatilsd pushed a commit to emergent-inc/mosaic that referenced this pull request Jul 9, 2026

This branch was successfully deployed

1 active deployment
Preview – cmux — bd983f57 Deployed Apr 29, 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