import Cocoa import SwiftUI class AppDelegate: NSObject, NSApplicationDelegate { private var statusItem: NSStatusItem! private var volumeMonitor: VolumeMonitor! private var mediaKeyMonitor: MediaKeyMonitor! private var hudPanel: HUDPanel! private var prefsWindow: NSWindow? func applicationDidFinishLaunching(_ notification: Notification) { NSApp.setActivationPolicy(.accessory) setupStatusBar() setupHUD() setupVolumeMonitor() setupMediaKeyMonitor() } private func updateStatusIcon(volume: Float, isMuted: Bool) { guard let button = statusItem?.button else { return } let iconName: String if isMuted || volume == 0 { iconName = "speaker.slash.fill" } else if volume < 0.33 { iconName = "speaker.wave.1.fill" } else if volume < 0.66 { iconName = "speaker.wave.2.fill" } else { iconName = "speaker.wave.3.fill" } button.image = NSImage(systemSymbolName: iconName, accessibilityDescription: "HUDini") button.image?.size = NSSize(width: 18, height: 18) } private func setupStatusBar() { statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) // Set initial icon based on current volume if let button = statusItem.button { button.image = NSImage(systemSymbolName: "speaker.wave.2.fill", accessibilityDescription: "HUDini") button.image?.size = NSSize(width: 18, height: 18) } let menu = NSMenu() let titleItem = NSMenuItem(title: "HUDini", action: nil, keyEquivalent: "") menu.addItem(titleItem) menu.addItem(NSMenuItem.separator()) let hudItem = NSMenuItem(title: "Volume HUD", action: #selector(toggleHUD(_:)), keyEquivalent: "") hudItem.state = .on hudItem.target = self hudItem.tag = 100 menu.addItem(hudItem) let mediaItem = NSMenuItem(title: "Play Key Launch", action: #selector(toggleMediaKeys(_:)), keyEquivalent: "") mediaItem.state = .on mediaItem.target = self mediaItem.tag = 200 menu.addItem(mediaItem) menu.addItem(NSMenuItem.separator()) let prefsItem = NSMenuItem(title: "Preferences...", action: #selector(openPreferences), keyEquivalent: ",") prefsItem.target = self menu.addItem(prefsItem) menu.addItem(NSMenuItem.separator()) let quitItem = NSMenuItem(title: "Quit", action: #selector(quit), keyEquivalent: "q") quitItem.target = self menu.addItem(quitItem) statusItem.menu = menu } private func setupHUD() { hudPanel = HUDPanel() } private func setupVolumeMonitor() { volumeMonitor = VolumeMonitor { [weak self] volume, isMuted in DispatchQueue.main.async { self?.hudPanel.show(volume: volume, isMuted: isMuted) self?.updateStatusIcon(volume: volume, isMuted: isMuted) } } volumeMonitor.start() // Set initial icon from current volume let vol = volumeMonitor.getVolume() let muted = volumeMonitor.isMuted() updateStatusIcon(volume: vol, isMuted: muted) } private func setupMediaKeyMonitor() { mediaKeyMonitor = MediaKeyMonitor() let ok = mediaKeyMonitor.start() if !ok { DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in _ = self?.mediaKeyMonitor.start() } } } // MARK: - Actions @objc private func toggleHUD(_ sender: NSMenuItem) { if sender.state == .on { sender.state = .off volumeMonitor.stop() } else { sender.state = .on volumeMonitor.start() } } @objc private func toggleMediaKeys(_ sender: NSMenuItem) { if sender.state == .on { sender.state = .off mediaKeyMonitor.isEnabled = false } else { sender.state = .on mediaKeyMonitor.isEnabled = true } } @objc private func openPreferences() { if let window = prefsWindow { window.makeKeyAndOrderFront(nil) NSApp.activate(ignoringOtherApps: true) return } let prefsView = PreferencesView( currentBundleID: mediaKeyMonitor.preferredBundleID, startAtLogin: LoginItemManager.isEnabled, onSave: { [weak self] bundleID in self?.mediaKeyMonitor.preferredBundleID = bundleID }, onStartAtLoginChanged: { enabled in // Use the .app bundle path from current process let appPath = Bundle.main.bundlePath // If running from .build, try to find the .app bundle let finalPath: String if appPath.contains(".build") { let projectDir = (appPath as NSString) .components(separatedBy: ".build").first ?? "" let bundlePath = projectDir + "HUDini.app" finalPath = FileManager.default.fileExists(atPath: bundlePath) ? bundlePath : "/Applications/HUDini.app" } else { finalPath = appPath } LoginItemManager.setEnabled(enabled, appPath: finalPath) } ) let window = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 320, height: 420), styleMask: [.titled, .closable], backing: .buffered, defer: false ) window.title = "HUDini" window.contentView = NSHostingView(rootView: prefsView) window.center() window.isReleasedWhenClosed = false window.makeKeyAndOrderFront(nil) NSApp.activate(ignoringOtherApps: true) prefsWindow = window } @objc private func quit() { volumeMonitor.stop() mediaKeyMonitor.stop() NSApp.terminate(nil) } }