Health: clean degraded message; PTT hotkey monitors
This commit is contained in:
@@ -164,7 +164,7 @@ final class HealthStore: ObservableObject {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private func describeFailure(from snap: HealthSnapshot, fallback: String?) -> String {
|
func describeFailure(from snap: HealthSnapshot, fallback: String?) -> String {
|
||||||
if !snap.web.linked {
|
if !snap.web.linked {
|
||||||
return "Not linked — run clawdis login"
|
return "Not linked — run clawdis login"
|
||||||
}
|
}
|
||||||
@@ -185,6 +185,16 @@ final class HealthStore: ObservableObject {
|
|||||||
}
|
}
|
||||||
return "health probe failed"
|
return "health probe failed"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var degradedSummary: String? {
|
||||||
|
guard case let .degraded(reason) = self.state else { return nil }
|
||||||
|
if reason == "[object Object]" || reason.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
|
||||||
|
let snap = self.snapshot
|
||||||
|
{
|
||||||
|
return self.describeFailure(from: snap, fallback: reason)
|
||||||
|
}
|
||||||
|
return reason
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func msToAge(_ ms: Double) -> String {
|
func msToAge(_ ms: Double) -> String {
|
||||||
|
|||||||
@@ -98,8 +98,9 @@ struct MenuContent: View {
|
|||||||
label = "Health: login required"
|
label = "Health: login required"
|
||||||
color = .red
|
color = .red
|
||||||
case let .degraded(reason):
|
case let .degraded(reason):
|
||||||
|
let detail = HealthStore.shared.degradedSummary ?? reason
|
||||||
let ageText = lastAge.map { " · checked \($0)" } ?? ""
|
let ageText = lastAge.map { " · checked \($0)" } ?? ""
|
||||||
label = "Health degraded: \(reason)\(ageText)"
|
label = "Health degraded: \(detail)\(ageText)"
|
||||||
color = .orange
|
color = .orange
|
||||||
case .unknown:
|
case .unknown:
|
||||||
label = "Health pending"
|
label = "Health pending"
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import Speech
|
|||||||
final class VoicePushToTalkHotkey {
|
final class VoicePushToTalkHotkey {
|
||||||
static let shared = VoicePushToTalkHotkey()
|
static let shared = VoicePushToTalkHotkey()
|
||||||
|
|
||||||
private var monitor: Any?
|
private var globalMonitor: Any?
|
||||||
|
private var localMonitor: Any?
|
||||||
private var optionDown = false // right option only
|
private var optionDown = false // right option only
|
||||||
private var active = false
|
private var active = false
|
||||||
|
|
||||||
@@ -21,18 +22,27 @@ final class VoicePushToTalkHotkey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func startMonitoring() {
|
private func startMonitoring() {
|
||||||
guard self.monitor == nil else { return }
|
guard self.globalMonitor == nil, self.localMonitor == nil else { return }
|
||||||
// Listen-only global monitor; we rely on Input Monitoring permission to receive events.
|
// Listen-only global monitor; we rely on Input Monitoring permission to receive events.
|
||||||
self.monitor = NSEvent.addGlobalMonitorForEvents(matching: .flagsChanged) { [weak self] event in
|
self.globalMonitor = NSEvent.addGlobalMonitorForEvents(matching: .flagsChanged) { [weak self] event in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
self.updateModifierState(from: event)
|
self.updateModifierState(from: event)
|
||||||
}
|
}
|
||||||
|
// Also listen locally so we still catch events when the app is active/focused.
|
||||||
|
self.localMonitor = NSEvent.addLocalMonitorForEvents(matching: .flagsChanged) { [weak self] event in
|
||||||
|
self?.updateModifierState(from: event)
|
||||||
|
return event
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func stopMonitoring() {
|
private func stopMonitoring() {
|
||||||
if let monitor {
|
if let globalMonitor {
|
||||||
NSEvent.removeMonitor(monitor)
|
NSEvent.removeMonitor(globalMonitor)
|
||||||
self.monitor = nil
|
self.globalMonitor = nil
|
||||||
|
}
|
||||||
|
if let localMonitor {
|
||||||
|
NSEvent.removeMonitor(localMonitor)
|
||||||
|
self.localMonitor = nil
|
||||||
}
|
}
|
||||||
self.optionDown = false
|
self.optionDown = false
|
||||||
self.active = false
|
self.active = false
|
||||||
@@ -48,11 +58,15 @@ final class VoicePushToTalkHotkey {
|
|||||||
if chordActive && !self.active {
|
if chordActive && !self.active {
|
||||||
self.active = true
|
self.active = true
|
||||||
Task {
|
Task {
|
||||||
|
Logger(subsystem: "com.steipete.clawdis", category: "voicewake.ptt")
|
||||||
|
.info("ptt hotkey down")
|
||||||
await VoicePushToTalk.shared.begin()
|
await VoicePushToTalk.shared.begin()
|
||||||
}
|
}
|
||||||
} else if !chordActive && self.active {
|
} else if !chordActive && self.active {
|
||||||
self.active = false
|
self.active = false
|
||||||
Task {
|
Task {
|
||||||
|
Logger(subsystem: "com.steipete.clawdis", category: "voicewake.ptt")
|
||||||
|
.info("ptt hotkey up")
|
||||||
await VoicePushToTalk.shared.end()
|
await VoicePushToTalk.shared.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user