fix(mac): stabilize voice wake visuals

This commit is contained in:
Peter Steinberger
2025-12-09 00:12:43 +01:00
parent 5674c9f4c2
commit c77fa12bda
4 changed files with 1784 additions and 1758 deletions

View File

@@ -107,6 +107,7 @@ final class AppState: ObservableObject {
@Published var isWorking: Bool = false @Published var isWorking: Bool = false
@Published var earBoostActive: Bool = false @Published var earBoostActive: Bool = false
@Published var blinkTick: Int = 0 @Published var blinkTick: Int = 0
@Published var sendCelebrationTick: Int = 0
@Published var heartbeatsEnabled: Bool { @Published var heartbeatsEnabled: Bool {
didSet { didSet {
UserDefaults.standard.set(self.heartbeatsEnabled, forKey: heartbeatsEnabledKey) UserDefaults.standard.set(self.heartbeatsEnabled, forKey: heartbeatsEnabledKey)
@@ -233,6 +234,10 @@ final class AppState: ObservableObject {
self.blinkTick &+= 1 self.blinkTick &+= 1
} }
func celebrateSend() {
self.sendCelebrationTick &+= 1
}
func setVoiceWakeEnabled(_ enabled: Bool) async { func setVoiceWakeEnabled(_ enabled: Bool) async {
guard voiceWakeSupported else { guard voiceWakeSupported else {
self.swabbleEnabled = false self.swabbleEnabled = false

View File

@@ -27,6 +27,7 @@ struct ClawdisApp: App {
isWorking: self.state.isWorking, isWorking: self.state.isWorking,
earBoostActive: self.state.earBoostActive, earBoostActive: self.state.earBoostActive,
blinkTick: self.state.blinkTick, blinkTick: self.state.blinkTick,
sendCelebrationTick: self.state.sendCelebrationTick,
relayStatus: self.relayManager.status, relayStatus: self.relayManager.status,
animationsEnabled: self.state.iconAnimationsEnabled) animationsEnabled: self.state.iconAnimationsEnabled)
} }
@@ -346,6 +347,7 @@ private struct CritterStatusLabel: View {
var isWorking: Bool var isWorking: Bool
var earBoostActive: Bool var earBoostActive: Bool
var blinkTick: Int var blinkTick: Int
var sendCelebrationTick: Int
var relayStatus: RelayProcessManager.Status var relayStatus: RelayProcessManager.Status
var animationsEnabled: Bool var animationsEnabled: Bool
@@ -377,7 +379,7 @@ private struct CritterStatusLabel: View {
.rotationEffect(.degrees(self.wiggleAngle), anchor: .center) .rotationEffect(.degrees(self.wiggleAngle), anchor: .center)
.offset(x: self.wiggleOffset) .offset(x: self.wiggleOffset)
.onReceive(self.ticker) { now in .onReceive(self.ticker) { now in
guard self.animationsEnabled else { guard self.animationsEnabled, !self.earBoostActive else {
self.resetMotion() self.resetMotion()
return return
} }
@@ -402,17 +404,31 @@ private struct CritterStatusLabel: View {
self.nextEarWiggle = now.addingTimeInterval(Double.random(in: 7.0...14.0)) self.nextEarWiggle = now.addingTimeInterval(Double.random(in: 7.0...14.0))
} }
if self.isWorking { if self.isWorking {
self.scurry() self.scurry()
}
} }
} .onChange(of: self.isPaused) { _, _ in self.resetMotion() }
.onChange(of: self.isPaused) { _, _ in self.resetMotion() } .onChange(of: self.blinkTick) { _, _ in
.onChange(of: self.blinkTick) { _, _ in self.blink() } guard !self.earBoostActive else { return }
.onChange(of: self.animationsEnabled) { _, enabled in self.blink()
if enabled { }
self.scheduleRandomTimers(from: Date()) .onChange(of: self.sendCelebrationTick) { _, _ in
} else { guard !self.earBoostActive else { return }
self.resetMotion() self.wiggleLegs()
}
.onChange(of: self.animationsEnabled) { _, enabled in
if enabled {
self.scheduleRandomTimers(from: Date())
} else {
self.resetMotion()
}
}
.onChange(of: self.earBoostActive) { _, active in
if active {
self.resetMotion()
} else if self.animationsEnabled {
self.scheduleRandomTimers(from: Date())
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -142,6 +142,8 @@ final class VoiceWakeOverlayController: ObservableObject {
self.model.level = 0 self.model.level = 0
if outcome == .empty { if outcome == .empty {
AppStateStore.shared.blinkOnce() AppStateStore.shared.blinkOnce()
} else if outcome == .sent {
AppStateStore.shared.celebrateSend()
} }
AppStateStore.shared.stopVoiceEars() AppStateStore.shared.stopVoiceEars()
} }