From f0df362fac4eebfe75b350d254841315e9514d7e Mon Sep 17 00:00:00 2001 From: Beingpax Date: Sat, 3 Jan 2026 09:47:07 +0545 Subject: [PATCH] Refactor Power Mode hotkeys into dedicated PowerModeShortcutManager for cleaner architecture and better observer lifecycle management --- VoiceInk/HotkeyManager.swift | 71 +--------------- .../PowerMode/PowerModeShortcutManager.swift | 85 +++++++++++++++++++ 2 files changed, 88 insertions(+), 68 deletions(-) create mode 100644 VoiceInk/PowerMode/PowerModeShortcutManager.swift diff --git a/VoiceInk/HotkeyManager.swift b/VoiceInk/HotkeyManager.swift index 081dd95..93e1a92 100644 --- a/VoiceInk/HotkeyManager.swift +++ b/VoiceInk/HotkeyManager.swift @@ -43,6 +43,7 @@ class HotkeyManager: ObservableObject { private var whisperState: WhisperState private var miniRecorderShortcutManager: MiniRecorderShortcutManager + private var powerModeShortcutManager: PowerModeShortcutManager // MARK: - Helper Properties private var canProcessHotkeyAction: Bool { @@ -74,8 +75,6 @@ class HotkeyManager: ObservableObject { private var lastShortcutTriggerTime: Date? private let shortcutCooldownInterval: TimeInterval = 0.5 - private var registeredPowerModeIds: Set = [] - enum HotkeyOption: String, CaseIterable { case none = "none" case rightOption = "rightOption" @@ -129,6 +128,7 @@ class HotkeyManager: ObservableObject { self.whisperState = whisperState self.miniRecorderShortcutManager = MiniRecorderShortcutManager(whisperState: whisperState) + self.powerModeShortcutManager = PowerModeShortcutManager(whisperState: whisperState) KeyboardShortcuts.onKeyUp(for: .pasteLastTranscription) { [weak self] in guard let self = self else { return } @@ -164,21 +164,6 @@ class HotkeyManager: ObservableObject { Task { @MainActor in try? await Task.sleep(nanoseconds: 100_000_000) self.setupHotkeyMonitoring() - self.setupPowerModeHotkeys() - } - - // Observe PowerMode configuration changes - NotificationCenter.default.addObserver( - self, - selector: #selector(powerModeConfigurationsDidChange), - name: NSNotification.Name("PowerModeConfigurationsDidChange"), - object: nil - ) - } - - @objc private func powerModeConfigurationsDidChange() { - Task { @MainActor in - setupPowerModeHotkeys() } } @@ -437,58 +422,8 @@ class HotkeyManager: ObservableObject { } deinit { - NotificationCenter.default.removeObserver(self) Task { @MainActor in removeAllMonitoring() } } -} - -// MARK: - PowerMode Hotkey Management -extension HotkeyManager { - func setupPowerModeHotkeys() { - let powerModesWithShortcuts = Set(PowerModeManager.shared.configurations - .filter { $0.hotkeyShortcut != nil } - .map { $0.id }) - - let idsToRemove = registeredPowerModeIds.subtracting(powerModesWithShortcuts) - - idsToRemove.forEach { id in - KeyboardShortcuts.setShortcut(nil, for: .powerMode(id: id)) - KeyboardShortcuts.disable(.powerMode(id: id)) - registeredPowerModeIds.remove(id) - } - - PowerModeManager.shared.configurations.forEach { config in - guard config.hotkeyShortcut != nil else { return } - guard !registeredPowerModeIds.contains(config.id) else { return } - - KeyboardShortcuts.onKeyUp(for: .powerMode(id: config.id)) { [weak self] in - guard let self = self else { return } - Task { @MainActor in - await self.handlePowerModeHotkey(powerModeId: config.id) - } - } - - registeredPowerModeIds.insert(config.id) - } - } - - private func handlePowerModeHotkey(powerModeId: UUID) async { - guard canProcessHotkeyAction else { return } - - guard let config = PowerModeManager.shared.getConfiguration(with: powerModeId), - config.hotkeyShortcut != nil else { - return - } - - await whisperState.toggleMiniRecorder(powerModeId: powerModeId) - } -} - -// MARK: - PowerMode Keyboard Shortcut Names -extension KeyboardShortcuts.Name { - static func powerMode(id: UUID) -> Self { - Self("powerMode_\(id.uuidString)") - } -} +} \ No newline at end of file diff --git a/VoiceInk/PowerMode/PowerModeShortcutManager.swift b/VoiceInk/PowerMode/PowerModeShortcutManager.swift new file mode 100644 index 0000000..1037347 --- /dev/null +++ b/VoiceInk/PowerMode/PowerModeShortcutManager.swift @@ -0,0 +1,85 @@ +import Foundation +import KeyboardShortcuts + +@MainActor +class PowerModeShortcutManager { + private weak var whisperState: WhisperState? + private var registeredPowerModeIds: Set = [] + + init(whisperState: WhisperState) { + self.whisperState = whisperState + + setupPowerModeHotkeys() + + NotificationCenter.default.addObserver( + self, + selector: #selector(powerModeConfigurationsDidChange), + name: NSNotification.Name("PowerModeConfigurationsDidChange"), + object: nil + ) + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + @objc private func powerModeConfigurationsDidChange() { + Task { @MainActor in + setupPowerModeHotkeys() + } + } + + private func setupPowerModeHotkeys() { + let powerModesWithShortcuts = Set(PowerModeManager.shared.configurations + .filter { $0.hotkeyShortcut != nil } + .map { $0.id }) + + // Remove shortcuts for deleted or updated configs + let idsToRemove = registeredPowerModeIds.subtracting(powerModesWithShortcuts) + idsToRemove.forEach { id in + KeyboardShortcuts.setShortcut(nil, for: .powerMode(id: id)) + KeyboardShortcuts.disable(.powerMode(id: id)) + registeredPowerModeIds.remove(id) + } + + // Add new shortcuts + PowerModeManager.shared.configurations.forEach { config in + guard config.hotkeyShortcut != nil else { return } + guard !registeredPowerModeIds.contains(config.id) else { return } + + KeyboardShortcuts.onKeyUp(for: .powerMode(id: config.id)) { [weak self] in + guard let self = self else { return } + Task { @MainActor in + await self.handlePowerModeHotkey(powerModeId: config.id) + } + } + + registeredPowerModeIds.insert(config.id) + } + } + + private func handlePowerModeHotkey(powerModeId: UUID) async { + guard let whisperState = whisperState, + canProcessHotkeyAction(whisperState: whisperState) else { return } + + guard let config = PowerModeManager.shared.getConfiguration(with: powerModeId), + config.hotkeyShortcut != nil else { + return + } + + await whisperState.toggleMiniRecorder(powerModeId: powerModeId) + } + + private func canProcessHotkeyAction(whisperState: WhisperState) -> Bool { + whisperState.recordingState != .transcribing && + whisperState.recordingState != .enhancing && + whisperState.recordingState != .busy + } +} + +// MARK: - PowerMode Keyboard Shortcut Names +extension KeyboardShortcuts.Name { + static func powerMode(id: UUID) -> Self { + Self("powerMode_\(id.uuidString)") + } +}