menubar.swift

  1import Cocoa
  2
  3// Compilation: swiftc menubar.swift -o menubar
  4// Usage: ./menubar <matchaPath>
  5
  6class MenubarController: NSObject {
  7    private var statusItem: NSStatusItem
  8    private var matchaPath: String
  9    private var unreadCount: Int = 0
 10    
 11    init(matchaPath: String) {
 12        self.matchaPath = matchaPath
 13        self.statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
 14        super.init()
 15        
 16        setupMenu()
 17        updateTitle()
 18        setupNotifications()
 19    }
 20    
 21    private func setupMenu() {
 22        let menu = NSMenu()
 23        
 24        let openItem = NSMenuItem(title: "Open Matcha", action: #selector(openMatcha), keyEquivalent: "o")
 25        openItem.target = self
 26        menu.addItem(openItem)
 27        
 28        let composeItem = NSMenuItem(title: "Compose Message", action: #selector(openCompose), keyEquivalent: "n")
 29        composeItem.target = self
 30        menu.addItem(composeItem)
 31        
 32        menu.addItem(NSMenuItem.separator())
 33        
 34        let refreshItem = NSMenuItem(title: "Check for Mail", action: #selector(refreshMail), keyEquivalent: "r")
 35        refreshItem.target = self
 36        menu.addItem(refreshItem)
 37        
 38        menu.addItem(NSMenuItem.separator())
 39        
 40        let quitItem = NSMenuItem(title: "Quit Menubar Helper", action: #selector(terminate), keyEquivalent: "q")
 41        quitItem.target = self
 42        menu.addItem(quitItem)
 43        
 44        statusItem.menu = menu
 45    }
 46    
 47    private func updateTitle() {
 48        if let button = statusItem.button {
 49            // Using a system symbol or a custom string
 50            let icon = unreadCount > 0 ? "✉️ " : "📩 "
 51            button.title = icon + (unreadCount > 0 ? "\(unreadCount)" : "")
 52        }
 53    }
 54    
 55    private func setupNotifications() {
 56        // Listen for updates from the main Matcha Go process
 57        DistributedNotificationCenter.default().addObserver(
 58            self,
 59            selector: #selector(handleUpdateNotification(_:)),
 60            name: NSNotification.Name("com.floatpane.matcha.UpdateUnread"),
 61            object: nil
 62        )
 63    }
 64    
 65    @objc private func handleUpdateNotification(_ notification: Notification) {
 66        if let userInfo = notification.userInfo,
 67           let countString = userInfo["count"] as? String,
 68           let count = Int(countString) {
 69            self.unreadCount = count
 70            updateTitle()
 71        }
 72    }
 73    
 74    @objc private func openMatcha() {
 75        runTerminalCommand(command: "'\(matchaPath)'")
 76    }
 77    
 78    @objc private func openCompose() {
 79        runTerminalCommand(command: "'\(matchaPath)' send")
 80    }
 81    
 82    @objc private func refreshMail() {
 83        // Trigger a notification that the Go app can listen for, or just run a command
 84        DistributedNotificationCenter.default().postNotificationName(
 85            NSNotification.Name("com.floatpane.matcha.RefreshRequest"),
 86            object: nil,
 87            userInfo: nil,
 88            deliverImmediately: true
 89        )
 90    }
 91    
 92    private func runTerminalCommand(command: String) {
 93        let script = """
 94        tell application "Terminal"
 95            activate
 96            if (count of windows) is 0 then
 97                do script "\(command)"
 98            else
 99                do script "\(command)" in window 1
100            end if
101        end tell
102        """
103        
104        if let appleScript = NSAppleScript(source: script) {
105            var error: NSDictionary?
106            appleScript.executeAndReturnError(&error)
107        }
108    }
109    
110    @objc private func terminate() {
111        NSApplication.shared.terminate(nil)
112    }
113}
114
115let args = ProcessInfo.processInfo.arguments
116guard args.count > 1 else {
117    print("Usage: ./menubar <matchaPath>")
118    exit(1)
119}
120
121let matchaPath = args[1]
122
123let app = NSApplication.shared
124let controller = MenubarController(matchaPath: matchaPath)
125app.setActivationPolicy(.prohibited) // Run as a background agent
126app.run()