Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
12e03b5
Ship local global search as a remappable menubar flow
austinywang May 12, 2026
7dad2d5
Sync global search branch with current main
austinywang May 12, 2026
bfe8fcd
Remove duplicate search refresh work
austinywang May 12, 2026
a8cc253
Merge remote-tracking branch 'origin/main' into issue-3865-menubar-gl…
austinywang May 12, 2026
7d30cfc
Fix global search stale index entries
austinywang May 12, 2026
7ba6d37
Address global search review feedback
austinywang May 12, 2026
e90362d
Fix search query token mapping compile error
austinywang May 12, 2026
79631a5
merge: sync global search with main
austinywang May 12, 2026
d930b0b
Fix global search CI failures
austinywang May 12, 2026
a5ee36b
Fix stale unavailable markdown search entries
austinywang May 12, 2026
84aa446
Cancel stale markdown search captures
austinywang May 12, 2026
0cbbf8d
Cancel global search refresh on dismiss
austinywang May 12, 2026
1c4aee9
Fix global search review followups
austinywang May 12, 2026
4470618
Address global search post-CI feedback
austinywang May 12, 2026
4bb5eba
Preserve markdown panel title search on read failure
austinywang May 12, 2026
be66aad
Address global search lifecycle feedback
austinywang May 12, 2026
0455429
Address global search review lifecycle feedback
austinywang May 13, 2026
ac4a685
Refine global search capture ownership
austinywang May 13, 2026
4881ca7
merge: resolve conflicts with main
austinywang May 13, 2026
7a21d6a
Merge remote-tracking branch 'origin/main' into issue-3865-menubar-gl…
austinywang May 13, 2026
cdcc4f0
Merge remote-tracking branch 'origin/main' into issue-3865-menubar-gl…
austinywang May 13, 2026
5c9ff7e
fix: address global search review blockers
austinywang May 13, 2026
4ba99e5
feat: show open panels in global search
austinywang May 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions GhosttyTabs.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
1A1B2C3D4E5F607180000003 /* WorkspacePromptSubmitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1B2C3D4E5F607180000004 /* WorkspacePromptSubmitTests.swift */; };
1F14445B9627DE9D3AF4FD2E /* BrowserPanelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C7B1B978620BE162CC057E /* BrowserPanelTests.swift */; };
2BB56A710BB1FC50367E5BCF /* TabManagerSessionSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10D684CFFB8CDEF89CE2D9E1 /* TabManagerSessionSnapshotTests.swift */; };
3865A0013865A0013865A001 /* SearchIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3865B0013865B0013865B001 /* SearchIndex.swift */; };
3865A0023865A0023865A002 /* GlobalSearchCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3865B0023865B0023865B002 /* GlobalSearchCoordinator.swift */; };
3865A0033865A0033865A003 /* MenubarSearchPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3865B0033865B0033865B003 /* MenubarSearchPopover.swift */; };
3865A0043865A0043865A004 /* SearchIndexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3865B0043865B0043865B004 /* SearchIndexTests.swift */; };
3865A0053865A0053865A005 /* GlobalSearchShortcutSettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3865B0053865B0053865B005 /* GlobalSearchShortcutSettingsTests.swift */; };
3865A0063865A0063865A006 /* AppDelegate+GlobalSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3865B0063865B0063865B006 /* AppDelegate+GlobalSearch.swift */; };
3865A0073865A0073865A007 /* GlobalSearchDocuments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3865B0073865B0073865B007 /* GlobalSearchDocuments.swift */; };
3865A0083865A0083865A008 /* GlobalSearchPanelCaptureManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3865B0083865B0083865B008 /* GlobalSearchPanelCaptureManager.swift */; };
C2B6A97D1F2E4C71A8B9D001 /* BrowserOmnibarPerformanceSupportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2B6A97D1F2E4C71A8B9D002 /* BrowserOmnibarPerformanceSupportTests.swift */; };
3023A1003023A1003023A100 /* ConfigSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3023B1003023B1003023B100 /* ConfigSource.swift */; };
3023A1013023A1013023A101 /* ConfigSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3023B1013023B1013023B101 /* ConfigSettingsView.swift */; };
Expand Down Expand Up @@ -528,6 +536,14 @@
2F0C05000000000000000001 /* MainWindowFocusController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindowFocusController.swift; sourceTree = "<group>"; };
3023B1003023B1003023B100 /* ConfigSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings/ConfigSource.swift; sourceTree = "<group>"; };
3023B1013023B1013023B101 /* ConfigSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings/ConfigSettingsView.swift; sourceTree = "<group>"; };
3865B0013865B0013865B001 /* SearchIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Search/SearchIndex.swift; sourceTree = "<group>"; };
3865B0023865B0023865B002 /* GlobalSearchCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Search/GlobalSearchCoordinator.swift; sourceTree = "<group>"; };
3865B0033865B0033865B003 /* MenubarSearchPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Search/MenubarSearchPopover.swift; sourceTree = "<group>"; };
3865B0043865B0043865B004 /* SearchIndexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchIndexTests.swift; sourceTree = "<group>"; };
3865B0053865B0053865B005 /* GlobalSearchShortcutSettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSearchShortcutSettingsTests.swift; sourceTree = "<group>"; };
3865B0063865B0063865B006 /* AppDelegate+GlobalSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Search/AppDelegate+GlobalSearch.swift"; sourceTree = "<group>"; };
3865B0073865B0073865B007 /* GlobalSearchDocuments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Search/GlobalSearchDocuments.swift; sourceTree = "<group>"; };
3865B0083865B0083865B008 /* GlobalSearchPanelCaptureManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Search/GlobalSearchPanelCaptureManager.swift; sourceTree = "<group>"; };
312DE7503B4658DD173121B8 /* AuthManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AuthManager.swift; sourceTree = "<group>"; };
FEED0000000000000000F001 /* FeedCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = FeedCoordinator.swift; sourceTree = "<group>"; };
FEED0000000000000000F004 /* FeedPanelView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = FeedPanelView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1239,6 +1255,7 @@
A5001620 /* AppleScriptSupport.swift */,
D1320AA0D1320AA0D1320AA4 /* AppIconDockTilePlugin.swift */,
A5001090 /* AppDelegate.swift */,
3865B0063865B0063865B006 /* AppDelegate+GlobalSearch.swift */,
C3873002C3873002C3873002 /* GhosttyCrashBreadcrumb.swift */,
C3677002000000000000002 /* CmuxSSHURLRequest.swift */,
C3677004000000000000002 /* AppDelegate+CmuxSSHURL.swift */,
Expand All @@ -1256,6 +1273,11 @@
A5001301 /* SurfaceSearchOverlay.swift */,
A5008370 /* BrowserSearchOverlay.swift */,
A5008372 /* BrowserFindJavaScript.swift */,
3865B0013865B0013865B001 /* SearchIndex.swift */,
3865B0073865B0073865B007 /* GlobalSearchDocuments.swift */,
3865B0083865B0083865B008 /* GlobalSearchPanelCaptureManager.swift */,
3865B0023865B0023865B002 /* GlobalSearchCoordinator.swift */,
3865B0033865B0033865B003 /* MenubarSearchPopover.swift */,
A5001410 /* Panel.swift */,
A5001411 /* TerminalPanel.swift */,
A5001412 /* BrowserPanel.swift */,
Expand Down Expand Up @@ -1402,6 +1424,7 @@
F5410005A1B2C3D4E5F60718 /* RestorableAgentHookProviderHermesTests.swift */,
F5410003A1B2C3D4E5F60718 /* RestorableAgentNonInteractiveTests.swift */,
F5300001A1B2C3D4E5F60718 /* RovoDevSessionIndexTests.swift */,
3865B0043865B0043865B004 /* SearchIndexTests.swift */,
F5300003A1B2C3D4E5F60718 /* RovoDevTranscriptPreviewTests.swift */,
F5310001A1B2C3D4E5F60718 /* RovoDevHookConfigTests.swift */,
FA100001A1B2C3D4E5F60718 /* BrowserImportMappingTests.swift */,
Expand All @@ -1410,6 +1433,7 @@
17FCD4CC61D54A2F8F2F463D /* AppDelegateBareSpaceShortcutRoutingTests.swift */,
C34670010000000000000002 /* AppDelegateRenameShortcutContextTests.swift */,
C34670040000000000000002 /* KeyboardShortcutTestSettingsIsolation.swift */,
3865B0053865B0053865B005 /* GlobalSearchShortcutSettingsTests.swift */,
E3309A0A /* AppDelegateEqualizeSplitsShortcutTests.swift */,
F6001001A1B2C3D4E5F60718 /* ShortcutUnbindingTests.swift */,
F6100001A1B2C3D4E5F60718 /* WorkspaceRemoteConnectionTests.swift */,
Expand Down Expand Up @@ -1923,6 +1947,12 @@
A5001303 /* SurfaceSearchOverlay.swift in Sources */,
A5008371 /* BrowserSearchOverlay.swift in Sources */,
A5008373 /* BrowserFindJavaScript.swift in Sources */,
3865A0013865A0013865A001 /* SearchIndex.swift in Sources */,
3865A0073865A0073865A007 /* GlobalSearchDocuments.swift in Sources */,
3865A0083865A0083865A008 /* GlobalSearchPanelCaptureManager.swift in Sources */,
3865A0023865A0023865A002 /* GlobalSearchCoordinator.swift in Sources */,
3865A0033865A0033865A003 /* MenubarSearchPopover.swift in Sources */,
3865A0063865A0063865A006 /* AppDelegate+GlobalSearch.swift in Sources */,
A5001400 /* Panel.swift in Sources */,
A5001401 /* TerminalPanel.swift in Sources */,
A5001402 /* BrowserPanel.swift in Sources */,
Expand Down Expand Up @@ -2109,6 +2139,7 @@
F5410004A1B2C3D4E5F60718 /* RestorableAgentHookProviderHermesTests.swift in Sources */,
F5410002A1B2C3D4E5F60718 /* RestorableAgentNonInteractiveTests.swift in Sources */,
F5300000A1B2C3D4E5F60718 /* RovoDevSessionIndexTests.swift in Sources */,
3865A0043865A0043865A004 /* SearchIndexTests.swift in Sources */,
F5300002A1B2C3D4E5F60718 /* RovoDevTranscriptPreviewTests.swift in Sources */,
F5310000A1B2C3D4E5F60718 /* RovoDevHookConfigTests.swift in Sources */,
FA100000A1B2C3D4E5F60718 /* BrowserImportMappingTests.swift in Sources */,
Expand All @@ -2117,6 +2148,7 @@
725746692D9647948561044D /* AppDelegateBareSpaceShortcutRoutingTests.swift in Sources */,
C34670010000000000000001 /* AppDelegateRenameShortcutContextTests.swift in Sources */,
C34670040000000000000001 /* KeyboardShortcutTestSettingsIsolation.swift in Sources */,
3865A0053865A0053865A005 /* GlobalSearchShortcutSettingsTests.swift in Sources */,
E3309A09 /* AppDelegateEqualizeSplitsShortcutTests.swift in Sources */,
F6001000A1B2C3D4E5F60718 /* ShortcutUnbindingTests.swift in Sources */,
F6100000A1B2C3D4E5F60718 /* WorkspaceRemoteConnectionTests.swift in Sources */,
Expand Down
70 changes: 70 additions & 0 deletions Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -111477,6 +111477,76 @@
"ja": { "stringUnit": { "state": "translated", "value": "プロセス" } }
}
},
"shortcut.globalSearch.label": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Global Search" } },
"ja": { "stringUnit": { "state": "translated", "value": "グローバル検索" } }
}
},
"statusMenu.searchAllWindows": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Search All Windows..." } },
"ja": { "stringUnit": { "state": "translated", "value": "すべてのウィンドウを検索..." } }
}
},
"globalSearch.palette.placeholder": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Search all windows, panels, browser tabs..." } },
"ja": { "stringUnit": { "state": "translated", "value": "すべてのウィンドウ、パネル、ブラウザタブを検索..." } }
}
},
"globalSearch.empty.prompt": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Type to search" } },
"ja": { "stringUnit": { "state": "translated", "value": "入力して検索" } }
}
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Localized string globalSearch.empty.prompt defined but never used

Low Severity

The localized string globalSearch.empty.prompt ("Type to search" / "入力して検索") is defined in the strings catalog but is never referenced anywhere in source code. This appears to be the intended empty state text for the search palette before the user types anything, but it was never wired into GlobalSearchPaletteView — which instead shows "No open panels" for the empty-query state.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4ba99e5. Configure here.

"globalSearch.empty.noResults": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "No results" } },
"ja": { "stringUnit": { "state": "translated", "value": "結果がありません" } }
}
},
"globalSearch.empty.noOpenPanels": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "No open panels" } },
"ja": { "stringUnit": { "state": "translated", "value": "開いているパネルがありません" } }
}
},
"globalSearch.kind.browser": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Browser" } },
"ja": { "stringUnit": { "state": "translated", "value": "ブラウザ" } }
}
},
"globalSearch.kind.markdown": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Markdown" } },
"ja": { "stringUnit": { "state": "translated", "value": "Markdown" } }
}
},
"globalSearch.kind.title": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Title" } },
"ja": { "stringUnit": { "state": "translated", "value": "タイトル" } }
}
},
"globalSearch.untitled": {
"extractionState": "manual",
"localizations": {
"en": { "stringUnit": { "state": "translated", "value": "Untitled" } },
"ja": { "stringUnit": { "state": "translated", "value": "無題" } }
}
},
"cli.rightSidebar.usage": {
"extractionState": "manual",
"localizations": {
Expand Down
39 changes: 38 additions & 1 deletion Sources/App/MenuBarExtraController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ final class MenuBarExtraController: NSObject, NSMenuDelegate {
private let statusItem: NSStatusItem
private let menu = NSMenu(title: "cmux")
private let notificationStore: TerminalNotificationStore
private let onShowGlobalSearch: (NSStatusBarButton, (() -> Void)?) -> Void
private let onShowMainWindow: () -> Void
private let onShowNotifications: () -> Void
private let onOpenNotification: (TerminalNotification) -> Void
Expand All @@ -20,6 +21,7 @@ final class MenuBarExtraController: NSObject, NSMenuDelegate {

private let stateHintItem = NSMenuItem(title: String(localized: "statusMenu.noUnread", defaultValue: "No unread notifications"), action: nil, keyEquivalent: "")
private let buildHintItem = NSMenuItem(title: "", action: nil, keyEquivalent: "")
private let globalSearchItem = NSMenuItem(title: String(localized: "statusMenu.searchAllWindows", defaultValue: "Search All Windows..."), action: nil, keyEquivalent: "")
private let showMainWindowItem = NSMenuItem(title: String(localized: "statusMenu.showCmux", defaultValue: "Show cmux"), action: nil, keyEquivalent: "")
private let taskManagerItem = NSMenuItem(title: String(localized: "statusMenu.taskManager", defaultValue: "Task Manager..."), action: nil, keyEquivalent: "")
private let notificationListSeparator = NSMenuItem.separator()
Expand All @@ -35,6 +37,7 @@ final class MenuBarExtraController: NSObject, NSMenuDelegate {
private var notificationItems: [NSMenuItem] = []
init(
notificationStore: TerminalNotificationStore,
onShowGlobalSearch: @escaping (NSStatusBarButton, (() -> Void)?) -> Void,
onShowMainWindow: @escaping () -> Void,
onShowNotifications: @escaping () -> Void,
onOpenNotification: @escaping (TerminalNotification) -> Void,
Expand All @@ -45,6 +48,7 @@ final class MenuBarExtraController: NSObject, NSMenuDelegate {
onQuitApp: @escaping () -> Void
) {
self.notificationStore = notificationStore
self.onShowGlobalSearch = onShowGlobalSearch
self.onShowMainWindow = onShowMainWindow
self.onShowNotifications = onShowNotifications
self.onOpenNotification = onOpenNotification
Expand All @@ -58,12 +62,14 @@ final class MenuBarExtraController: NSObject, NSMenuDelegate {
super.init()

buildMenu()
statusItem.menu = menu
if let button = statusItem.button {
button.imagePosition = .imageOnly
button.imageScaling = .scaleProportionallyDown
button.image = MenuBarIconRenderer.makeImage(unreadCount: 0)
button.toolTip = "cmux"
button.target = self
button.action = #selector(statusItemButtonAction(_:))
button.sendAction(on: [.leftMouseUp, .rightMouseUp])
}

notificationMenuSnapshotCancellable = notificationStore.$notificationMenuSnapshot
Expand All @@ -89,6 +95,10 @@ final class MenuBarExtraController: NSObject, NSMenuDelegate {

menu.addItem(.separator())

globalSearchItem.target = self
globalSearchItem.action = #selector(globalSearchAction)
menu.addItem(globalSearchItem)

showMainWindowItem.target = self
showMainWindowItem.action = #selector(showMainWindowAction)
menu.addItem(showMainWindowItem)
Expand Down Expand Up @@ -166,6 +176,7 @@ final class MenuBarExtraController: NSObject, NSMenuDelegate {
stateHintItem.title = snapshot.stateHintTitle
showMainWindowItem.isHidden = !MenuBarOnlySettings.shouldShowMainWindowMenuItem()

applyShortcut(KeyboardShortcutSettings.menuShortcut(for: .globalSearch), to: globalSearchItem)
applyShortcut(KeyboardShortcutSettings.menuShortcut(for: .showNotifications), to: showNotificationsItem)
applyShortcut(KeyboardShortcutSettings.menuShortcut(for: .jumpToUnread), to: jumpToUnreadItem)

Expand Down Expand Up @@ -230,6 +241,32 @@ final class MenuBarExtraController: NSObject, NSMenuDelegate {
onOpenNotification(payload.notification)
}

@objc private func statusItemButtonAction(_ sender: NSStatusBarButton) {
guard let event = NSApp.currentEvent else {
onShowGlobalSearch(sender, nil)
return
}

let flags = event.modifierFlags.intersection(.deviceIndependentFlagsMask)
if event.type == .rightMouseUp || flags.contains(.control) {
refreshUI()
menu.popUp(positioning: nil, at: NSPoint(x: 0, y: sender.bounds.height), in: sender)
} else {
onShowGlobalSearch(sender, nil)
}
}

@discardableResult
func toggleGlobalSearchPalette(onDismiss: (() -> Void)? = nil) -> Bool {
guard let button = statusItem.button else { return false }
onShowGlobalSearch(button, onDismiss)
return true
}

@objc private func globalSearchAction() {
toggleGlobalSearchPalette()
}

@objc private func showMainWindowAction() {
onShowMainWindow()
}
Expand Down
Loading
Loading