From 4fb8bf361bdb78f147cf95edafb7981d4bc3d94e Mon Sep 17 00:00:00 2001 From: Beingpax Date: Mon, 27 Oct 2025 10:59:19 +0545 Subject: [PATCH] Add option to disable Enhancemnent Shortcut Cmd + E --- VoiceInk/MiniRecorderShortcutManager.swift | 21 ++++- .../EnhancementShortcutSettings.swift | 17 ++++ .../Settings/EnhancementShortcutsView.swift | 85 +++++++++++-------- 3 files changed, 88 insertions(+), 35 deletions(-) create mode 100644 VoiceInk/Services/EnhancementShortcutSettings.swift diff --git a/VoiceInk/MiniRecorderShortcutManager.swift b/VoiceInk/MiniRecorderShortcutManager.swift index 06f97f0..ca8af2c 100644 --- a/VoiceInk/MiniRecorderShortcutManager.swift +++ b/VoiceInk/MiniRecorderShortcutManager.swift @@ -49,15 +49,33 @@ class MiniRecorderShortcutManager: ObservableObject { setupEnhancementShortcut() setupEscapeHandlerOnce() setupCancelHandlerOnce() + + NotificationCenter.default.addObserver(self, selector: #selector(settingsDidChange), name: .AppSettingsDidChange, object: nil) } + @objc private func settingsDidChange() { + Task { + if await whisperState.isMiniRecorderVisible { + if EnhancementShortcutSettings.shared.isToggleEnhancementShortcutEnabled { + KeyboardShortcuts.setShortcut(.init(.e, modifiers: .command), for: .toggleEnhancement) + } else { + removeEnhancementShortcut() + } + } + } + } + private func setupVisibilityObserver() { visibilityTask = Task { @MainActor in for await isVisible in whisperState.$isMiniRecorderVisible.values { if isVisible { activateEscapeShortcut() activateCancelShortcut() - KeyboardShortcuts.setShortcut(.init(.e, modifiers: .command), for: .toggleEnhancement) + if EnhancementShortcutSettings.shared.isToggleEnhancementShortcutEnabled { + KeyboardShortcuts.setShortcut(.init(.e, modifiers: .command), for: .toggleEnhancement) + } else { + removeEnhancementShortcut() + } setupPromptShortcuts() setupPowerModeShortcuts() } else { @@ -278,6 +296,7 @@ class MiniRecorderShortcutManager: ObservableObject { deinit { visibilityTask?.cancel() + NotificationCenter.default.removeObserver(self) Task { @MainActor in deactivateEscapeShortcut() deactivateCancelShortcut() diff --git a/VoiceInk/Services/EnhancementShortcutSettings.swift b/VoiceInk/Services/EnhancementShortcutSettings.swift new file mode 100644 index 0000000..301d93f --- /dev/null +++ b/VoiceInk/Services/EnhancementShortcutSettings.swift @@ -0,0 +1,17 @@ +import Foundation +import SwiftUI + +class EnhancementShortcutSettings: ObservableObject { + static let shared = EnhancementShortcutSettings() + + @Published var isToggleEnhancementShortcutEnabled: Bool { + didSet { + UserDefaults.standard.set(isToggleEnhancementShortcutEnabled, forKey: "isToggleEnhancementShortcutEnabled") + NotificationCenter.default.post(name: .AppSettingsDidChange, object: nil) + } + } + + private init() { + self.isToggleEnhancementShortcutEnabled = UserDefaults.standard.object(forKey: "isToggleEnhancementShortcutEnabled") as? Bool ?? true + } +} diff --git a/VoiceInk/Views/Settings/EnhancementShortcutsView.swift b/VoiceInk/Views/Settings/EnhancementShortcutsView.swift index e52636a..6a1775b 100644 --- a/VoiceInk/Views/Settings/EnhancementShortcutsView.swift +++ b/VoiceInk/Views/Settings/EnhancementShortcutsView.swift @@ -1,16 +1,22 @@ import SwiftUI +import KeyboardShortcuts struct EnhancementShortcutsView: View { - private let shortcuts: [ShortcutRowData] = [ - .toggleEnhancement, - .switchPrompt - ] + @ObservedObject private var shortcutSettings = EnhancementShortcutSettings.shared var body: some View { VStack(spacing: 12) { - ForEach(shortcuts) { shortcut in - ShortcutRow(data: shortcut) - } + ShortcutRow( + title: "Toggle AI Enhancement", + description: "Quickly enable or disable enhancement while recording.", + keyDisplay: ["⌘", "E"], + isOn: $shortcutSettings.isToggleEnhancementShortcutEnabled + ) + ShortcutRow( + title: "Switch Enhancement Prompt", + description: "Switch between your saved prompts without touching the UI. Use ⌘1–⌘0 to activate the corresponding prompt in the order they are saved.", + keyDisplay: ["⌘", "1 – 0"] + ) } .background(Color.clear) } @@ -80,54 +86,63 @@ struct EnhancementShortcutsSection: View { } // MARK: - Supporting Views -private struct ShortcutRowData: Identifiable { - let id = UUID() +private struct ShortcutRow: View { let title: String let description: String let keyDisplay: [String] - - static let toggleEnhancement = ShortcutRowData( - title: "Toggle AI Enhancement", - description: "Quickly enable or disable enhancement while recording.", - keyDisplay: ["⌘", "E"] - ) - - static let switchPrompt = ShortcutRowData( - title: "Switch Enhancement Prompt", - description: "Switch between your saved prompts without touching the UI. Use ⌘1–⌘0 to activate the corresponding prompt in the order they are saved.", - keyDisplay: ["⌘", "1 – 0"] - ) -} + private var isOn: Binding? + + init(title: String, description: String, keyDisplay: [String], isOn: Binding? = nil) { + self.title = title + self.description = description + self.keyDisplay = keyDisplay + self.isOn = isOn + } -private struct ShortcutRow: View { - let data: ShortcutRowData - var body: some View { HStack(alignment: .center, spacing: 16) { VStack(alignment: .leading, spacing: 4) { HStack(spacing: 8) { - Text(data.title) + Text(title) .font(.system(size: 14, weight: .semibold)) - InfoTip(title: data.title, message: data.description, learnMoreURL: "https://tryvoiceink.com/docs/switching-enhancement-prompts") + InfoTip(title: title, message: description, learnMoreURL: "https://tryvoiceink.com/docs/switching-enhancement-prompts") } } Spacer(minLength: 0) - HStack(spacing: 8) { - ForEach(data.keyDisplay, id: \.self) { key in - KeyChip(label: key) - } + if let isOn = isOn { + keyDisplayView(isActive: isOn.wrappedValue) + .onTapGesture { + withAnimation(.bouncy) { + isOn.wrappedValue.toggle() + } + } + .contentShape(Rectangle()) + } else { + keyDisplayView() } } .padding(.horizontal, 8) } + + @ViewBuilder + private func keyDisplayView(isActive: Bool? = nil) -> some View { + HStack(spacing: 8) { + ForEach(keyDisplay, id: \.self) { key in + KeyChip(label: key, isActive: isActive) + } + } + } } private struct KeyChip: View { let label: String - + var isActive: Bool? = nil + var body: some View { + let active = isActive ?? true + Text(label) .font(.system(size: 13, weight: .semibold, design: .monospaced)) .padding(.horizontal, 12) @@ -137,9 +152,11 @@ private struct KeyChip: View { .fill(Color(NSColor.controlBackgroundColor)) .overlay( RoundedRectangle(cornerRadius: 10, style: .continuous) - .stroke(Color(NSColor.separatorColor).opacity(0.7), lineWidth: 0.8) + .stroke(Color(NSColor.separatorColor).opacity(active ? 0.7 : 0.3), lineWidth: 1) ) ) - .shadow(color: Color(NSColor.shadowColor).opacity(0.1), radius: 4, x: 0, y: 2) + .foregroundColor(active ? .primary : .secondary) + .shadow(color: Color(NSColor.shadowColor).opacity(active ? 0.1 : 0), radius: 4, x: 0, y: 2) + .opacity(active ? 1.0 : 0.6) } }