diff options
Diffstat (limited to 'menubar/main.swift')
| -rw-r--r-- | menubar/main.swift | 57 |
1 files changed, 52 insertions, 5 deletions
diff --git a/menubar/main.swift b/menubar/main.swift index 183c5db..a00288c 100644 --- a/menubar/main.swift +++ b/menubar/main.swift @@ -1,6 +1,7 @@ // dingger menu bar: herdr's agent panel as a macOS status item, plus a -// floating always-on-top panel (panel.swift) toggled by a global shortcut -// (hotkey.swift). Rows and colors come from model.swift. +// floating always-on-top panel (panel.swift) and agent search (search.swift), +// each on a global shortcut (hotkey.swift). Rows and colors come from +// model.swift. // // Reads the pane options the dingger daemon writes and renders herdr's // Symbols style with herdr's palette: @@ -19,8 +20,12 @@ // --snapshot-panel <png> [--full] // render the panel offscreen (after a different render, to // catch stale content) over white and black, then exit +// --print-search <query> +// print what agent search would list for <query>, then exit +// --snapshot-search <png> <query> [<down>] +// render agent search offscreen, then exit // --click <pane> perform a row click for <pane>, then exit -// --selftest check the panel visibility rules, then exit +// --selftest check the panel and search rules, then exit import AppKit @@ -32,6 +37,7 @@ final class Menubar: NSObject, NSApplicationDelegate, NSMenuDelegate { private let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) private let menu = NSMenu() private var panel: PanelController! + private var searchWindow: SearchController! private var visibility = Menubar.loadVisibility() private var settings = Settings() private var settingsModified: Date? @@ -39,6 +45,8 @@ final class Menubar: NSObject, NSApplicationDelegate, NSMenuDelegate { private var hotkeyRegistered = false private var jumpHotkey: Hotkey? private var jumpHotkeyRegistered = false + private var searchHotkey: Hotkey? + private var searchHotkeyRegistered = false private var prefersFull = UserDefaults.standard.bool(forKey: "panelFull") private var rows: [Row] = [] @@ -64,6 +72,7 @@ final class Menubar: NSObject, NSApplicationDelegate, NSMenuDelegate { panel = PanelController( onClick: { [weak self] paneId in self?.jump(to: paneId) }, onToggleFull: { [weak self] in self?.toggleFull() }) + searchWindow = SearchController(onJump: { [weak self] paneId in self?.jump(to: paneId) }) Settings.ensureFile() reloadSettings() refresh() @@ -82,6 +91,9 @@ final class Menubar: NSObject, NSApplicationDelegate, NSMenuDelegate { (jumpHotkey, jumpHotkeyRegistered) = registerShortcut(.jump, settings.jumpHotkey, "jump") { [weak self] in self?.jumpToTopWaiting() } + (searchHotkey, searchHotkeyRegistered) = registerShortcut(.search, settings.searchHotkey, "search") { + [weak self] in self?.toggleSearch() + } } private func registerShortcut(_ id: GlobalHotkey.Id, _ spec: String, _ name: String, @@ -113,6 +125,17 @@ final class Menubar: NSObject, NSApplicationDelegate, NSMenuDelegate { visibility.anyWaiting = rows.contains(where: \.waiting) applyPanel() + searchWindow.update(rows: rows) + } + + /// The search shortcut and menu item: open agent search, or close it. + @objc private func toggleSearch() { + if searchWindow.isOpen { + searchWindow.close("shortcut") + } else { + rows = fetchRows() + searchWindow.open(rows: rows) + } } private var panelContent: PanelContent { @@ -229,6 +252,15 @@ final class Menubar: NSObject, NSApplicationDelegate, NSMenuDelegate { } menu.addItem(jumpItem) + let searchItem = NSMenuItem(title: "Search Agents…", action: #selector(toggleSearch), keyEquivalent: "") + searchItem.target = self + searchItem.isEnabled = !rows.isEmpty + if let searchHotkey, searchHotkeyRegistered { + searchItem.keyEquivalent = searchHotkey.key.count == 1 ? searchHotkey.key : "" + searchItem.keyEquivalentModifierMask = searchHotkey.modifierFlags + } + menu.addItem(searchItem) + let policyMenu = NSMenu() for policy in PanelPolicy.allCases { let option = NSMenuItem(title: policy.label, action: #selector(selectPolicy(_:)), keyEquivalent: "") @@ -242,7 +274,8 @@ final class Menubar: NSObject, NSApplicationDelegate, NSMenuDelegate { menu.addItem(policyItem) for (shortcut, registered, spec) in [(hotkey, hotkeyRegistered, settings.hotkey), - (jumpHotkey, jumpHotkeyRegistered, settings.jumpHotkey)] { + (jumpHotkey, jumpHotkeyRegistered, settings.jumpHotkey), + (searchHotkey, searchHotkeyRegistered, settings.searchHotkey)] { guard !registered else { continue } let problem = shortcut.map { "Shortcut \($0.display) is used by another app" } ?? "Shortcut \"\(spec)\" is not valid" @@ -267,12 +300,13 @@ final class Menubar: NSObject, NSApplicationDelegate, NSMenuDelegate { let arguments = CommandLine.arguments if arguments.contains("--selftest") { - var failures = panelSelftest() + var failures = panelSelftest() + searchSelftest() if Hotkey.parse("ctrl+opt+cmd+e")?.display != "⌃⌥⌘E" { failures.append("hotkey: parse ctrl+opt+cmd+e") } if Hotkey.parse("e") != nil { failures.append("hotkey: a key without modifiers is rejected") } if Hotkey.parse("ctrl+opt+bogus") != nil { failures.append("hotkey: unknown keys are rejected") } if Hotkey.parse("cmd+shift+f12")?.display != "⇧⌘F12" { failures.append("hotkey: parse cmd+shift+f12") } if Hotkey.parse(Settings().jumpHotkey)?.display != "⌃⌥⌘U" { failures.append("hotkey: default jump is ⌃⌥⌘U") } + if Hotkey.parse(Settings().searchHotkey)?.display != "⌃⌥⇧⌘U" { failures.append("hotkey: default search is ⌃⌥⇧⌘U") } failures.forEach { print("FAIL \($0)") } print(failures.isEmpty ? "selftest ok" : "selftest: \(failures.count) failed") exit(failures.isEmpty ? 0 : 1) @@ -301,6 +335,19 @@ if let index = arguments.firstIndex(of: "--snapshot-panel"), index + 1 < argumen controller.render(PanelContent(rows: fetchRows(), full: full, canToggle: full)) exit(controller.snapshot(to: arguments[index + 1]) ? 0 : 1) } +if let index = arguments.firstIndex(of: "--print-search") { + var state = SearchState(rows: fetchRows()) + state.setQuery(index + 1 < arguments.count ? arguments[index + 1] : "") + print(state.plainText) + exit(0) +} +if let index = arguments.firstIndex(of: "--snapshot-search"), index + 2 < arguments.count { + _ = NSApplication.shared + let controller = SearchController(onJump: { _ in }) + let down = index + 3 < arguments.count ? Int(arguments[index + 3]) ?? 0 : 0 + exit(controller.snapshot(rows: fetchRows(), query: arguments[index + 2], down: down, + to: arguments[index + 1]) ? 0 : 1) +} if let index = arguments.firstIndex(of: "--click"), index + 1 < arguments.count { focus(arguments[index + 1]) exit(0) |
