@@ -9,6 +9,7 @@ import AppKit
99import Cocoa
1010import Combine
1111import Foundation
12+ import Intents
1213import Network
1314import os. log
1415import ServiceManagement
@@ -21,7 +22,7 @@ private func mainAsyncAfter(_ duration: TimeInterval, _ action: @escaping () ->
2122 return workItem
2223}
2324
24- private func drawColoredTopLine( _ color: NSColor , hideAfter: TimeInterval = 5 ) {
25+ private func drawColoredTopLine( _ color: NSColor , hideAfter: TimeInterval = 5 , sound : NSSound ? = nil ) {
2526 DispatchQueue . main. async {
2627 lastColor = color
2728 lastHideAfter = hideAfter
@@ -54,6 +55,8 @@ private func drawColoredTopLine(_ color: NSColor, hideAfter: TimeInterval = 5) {
5455 window. fade ( to: 0.7 )
5556 }
5657
58+ sound? . playIfNotDND ( )
59+
5760 guard hideAfter > 0 else { return }
5861
5962 fader = mainAsyncAfter ( hideAfter) {
@@ -68,12 +71,71 @@ private func drawColoredTopLine(_ color: NSColor, hideAfter: TimeInterval = 5) {
6871 }
6972}
7073
74+ extension NSSound {
75+ func playIfNotDND( ) {
76+ if #available( macOS 12 . 0 , * ) , focused {
77+ return
78+ }
79+ play ( )
80+ }
81+ }
82+
83+ let SONOMA_TO_ORIGINAL_SOUND_NAMES = [
84+ " Mezzo " : " Basso " ,
85+ " Breeze " : " Blow " ,
86+ " Pebble " : " Bottle " ,
87+ " Jump " : " Frog " ,
88+ " Funky " : " Funk " ,
89+ " Crystal " : " Glass " ,
90+ " Heroine " : " Hero " ,
91+ " Pong " : " Morse " ,
92+ " Sonar " : " Ping " ,
93+ " Bubble " : " Pop " ,
94+ " Pluck " : " Purr " ,
95+ " Sonumi " : " Sosumi " ,
96+ " Submerge " : " Submarine " ,
97+ " Boop " : " Tink " ,
98+ ]
99+
100+ @available ( macOS 12 . 0 , * )
101+ var focused : Bool {
102+ guard INFocusStatusCenter . default. authorizationStatus == . authorized else {
103+ INFocusStatusCenter . default. requestAuthorization { status in
104+ log ( " Focus Status: \( status) " )
105+ }
106+ return false
107+ }
108+
109+ return INFocusStatusCenter . default. focusStatus. isFocused ?? false
110+ }
111+
71112private struct FadeSecondsConfig : Codable , Equatable {
72113 var connected : Double ? = 5.0
73114 var disconnected : Double ? = 0.0
74115 var slow : Double ? = 10.0
75116}
76117
118+ private struct SoundsConfig : Codable , Equatable {
119+ var connected : String ? = " " // e.g. "Funky"
120+ var disconnected : String ? = " " // e.g. "Mezzo"
121+ var slow : String ? = " " // e.g. "Submerge"
122+ var volume : Float ? = 0.7
123+
124+ var connectedSound : NSSound ? { connected != nil ? sound ( named: connected!) : nil }
125+ var disconnectedSound : NSSound ? { disconnected != nil ? sound ( named: disconnected!) : nil }
126+ var slowSound : NSSound ? { slow != nil ? sound ( named: slow!) : nil }
127+
128+ func sound( named name: String ) -> NSSound ? {
129+ guard let s = NSSound ( named: name) ?? NSSound ( named: SONOMA_TO_ORIGINAL_SOUND_NAMES [ name] ?? " " ) else {
130+ return nil
131+ }
132+
133+ s. volume = max ( min ( volume ?? 0.7 , 1.0 ) , 0.0 )
134+ return s
135+ }
136+
137+ }
138+
77139private enum PingStatus : Equatable {
78140 case reachable( Double )
79141 case timedOut
@@ -87,6 +149,14 @@ private enum PingStatus: Equatable {
87149 }
88150 }
89151
152+ var sound : NSSound ? {
153+ switch self {
154+ case . reachable: CONFIG . sounds? . connectedSound
155+ case . timedOut: CONFIG . sounds? . disconnectedSound
156+ case . slow: CONFIG . sounds? . slowSound
157+ }
158+ }
159+
90160 var hideAfter : TimeInterval {
91161 switch self {
92162 case . reachable: CONFIG . fadeSeconds? . connected ?? 5
@@ -122,7 +192,7 @@ private var lastPingStatus: PingStatus? {
122192 didSet {
123193 guard let lastPingStatus, lastPingStatus != oldValue else { return }
124194
125- drawColoredTopLine ( lastPingStatus. color, hideAfter: lastPingStatus. hideAfter)
195+ drawColoredTopLine ( lastPingStatus. color, hideAfter: lastPingStatus. hideAfter, sound : lastPingStatus . sound )
126196 log ( " Internet connection: \( lastPingStatus. message) " )
127197 }
128198}
@@ -198,7 +268,7 @@ func start() {
198268 process = nil
199269 pingRestartTask = nil
200270 }
201- drawColoredTopLine ( . systemRed, hideAfter: 0 )
271+ drawColoredTopLine ( . systemRed, hideAfter: CONFIG . fadeSeconds ? . disconnected ?? 0 , sound : CONFIG . sounds ? . disconnectedSound )
202272 @unknown default :
203273 log ( " Internet connection: \( path. status) " )
204274 }
@@ -344,6 +414,7 @@ private struct Config: Codable, Equatable {
344414 var pingSlowThresholdMilliseconds = 300.0
345415
346416 var fadeSeconds : FadeSecondsConfig ? = FadeSecondsConfig ( )
417+ var sounds : SoundsConfig ? = SoundsConfig ( )
347418}
348419
349420private var CONFIG_FS_WATCHER : FSEventStreamRef ?
0 commit comments