Commit dac55bdb authored by Kourser's avatar Kourser
Browse files

fix(macos): reliable space-bar play/pause via a local key monitor

SwiftUI does not reliably fire a bare-space menu key equivalent (the
focused scroll view consumes the key first), so the space shortcut is
now handled by an NSEvent local monitor in the app delegate, like other
media apps: it toggles play/pause whenever an episode is loaded, passes
the key through while a text field is being edited (the field editor is
an NSTextView), and consumes the event so nothing double-fires. The
menu item keeps its ␣ label as the visual hint.

Co-Authored-By: Claude (RCA)
parent c2f15dac
Loading
Loading
Loading
Loading
+19 −1
Original line number Diff line number Diff line
@@ -2,11 +2,29 @@
import AppKit
import PlaybackKit

/// Provides the Dock menu: play/pause for the current episode, when any.
/// Provides the Dock menu (play/pause) and the space-bar shortcut. A bare
/// space key equivalent on a menu item is unreliable in SwiftUI (scroll views
/// consume the key first), so — like other media apps — we intercept the key
/// event directly, except while a text field is being edited.
@MainActor
final class MacAppDelegate: NSObject, NSApplicationDelegate {
    /// Wired by the app once the playback controller exists.
    static weak var playback: PlaybackController?
    private var spaceKeyMonitor: Any?

    func applicationDidFinishLaunching(_ notification: Notification) {
        spaceKeyMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in
            MainActor.assumeIsolated {
                guard event.charactersIgnoringModifiers == " ",
                      event.modifierFlags.intersection([.command, .option, .control, .shift]).isEmpty,
                      !(NSApp.keyWindow?.firstResponder is NSTextView), // typing a space
                      let playback = Self.playback, playback.currentEpisode != nil
                else { return event }
                playback.togglePlayPause()
                return nil // consumed
            }
        }
    }

    func applicationDockMenu(_ sender: NSApplication) -> NSMenu? {
        guard let playback = Self.playback, playback.currentEpisode != nil else { return nil }