From 1068dea78f9cd9bb6e07beb9f2084489f7966b85 Mon Sep 17 00:00:00 2001 From: Beingpax Date: Thu, 17 Jul 2025 21:09:38 +0545 Subject: [PATCH] feat: Added Auto Enter option in Power Mode --- VoiceInk/CursorPaster.swift | 10 +++++++ VoiceInk/PowerMode/PowerModeConfig.swift | 9 ++++-- VoiceInk/PowerMode/PowerModeConfigView.swift | 29 +++++++++++++++++++- VoiceInk/Whisper/WhisperState.swift | 9 ++++++ 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/VoiceInk/CursorPaster.swift b/VoiceInk/CursorPaster.swift index 4062f2b..9ce08bb 100644 --- a/VoiceInk/CursorPaster.swift +++ b/VoiceInk/CursorPaster.swift @@ -80,4 +80,14 @@ class CursorPaster { vUp?.post(tap: .cghidEventTap) cmdUp?.post(tap: .cghidEventTap) } + + // Simulate pressing the Return / Enter key + static func pressEnter() { + guard AXIsProcessTrusted() else { return } + let source = CGEventSource(stateID: .hidSystemState) + let enterDown = CGEvent(keyboardEventSource: source, virtualKey: 0x24, keyDown: true) + let enterUp = CGEvent(keyboardEventSource: source, virtualKey: 0x24, keyDown: false) + enterDown?.post(tap: .cghidEventTap) + enterUp?.post(tap: .cghidEventTap) + } } diff --git a/VoiceInk/PowerMode/PowerModeConfig.swift b/VoiceInk/PowerMode/PowerModeConfig.swift index 827e19e..6a554b4 100644 --- a/VoiceInk/PowerMode/PowerModeConfig.swift +++ b/VoiceInk/PowerMode/PowerModeConfig.swift @@ -13,10 +13,12 @@ struct PowerModeConfig: Codable, Identifiable, Equatable { var useScreenCapture: Bool var selectedAIProvider: String? var selectedAIModel: String? + // NEW: Automatically press the Return key after pasting + var isAutoSendEnabled: Bool = false // Custom coding keys to handle migration from selectedWhisperModel enum CodingKeys: String, CodingKey { - case id, name, emoji, appConfigs, urlConfigs, isAIEnhancementEnabled, selectedPrompt, selectedLanguage, useScreenCapture, selectedAIProvider, selectedAIModel + case id, name, emoji, appConfigs, urlConfigs, isAIEnhancementEnabled, selectedPrompt, selectedLanguage, useScreenCapture, selectedAIProvider, selectedAIModel, isAutoSendEnabled case selectedWhisperModel // Old key case selectedTranscriptionModelName // New key } @@ -24,7 +26,7 @@ struct PowerModeConfig: Codable, Identifiable, Equatable { init(id: UUID = UUID(), name: String, emoji: String, appConfigs: [AppConfig]? = nil, urlConfigs: [URLConfig]? = nil, isAIEnhancementEnabled: Bool, selectedPrompt: String? = nil, selectedTranscriptionModelName: String? = nil, selectedLanguage: String? = nil, useScreenCapture: Bool = false, - selectedAIProvider: String? = nil, selectedAIModel: String? = nil) { + selectedAIProvider: String? = nil, selectedAIModel: String? = nil, isAutoSendEnabled: Bool = false) { self.id = id self.name = name self.emoji = emoji @@ -33,6 +35,7 @@ struct PowerModeConfig: Codable, Identifiable, Equatable { self.isAIEnhancementEnabled = isAIEnhancementEnabled self.selectedPrompt = selectedPrompt self.useScreenCapture = useScreenCapture + self.isAutoSendEnabled = isAutoSendEnabled self.selectedAIProvider = selectedAIProvider ?? UserDefaults.standard.string(forKey: "selectedAIProvider") self.selectedAIModel = selectedAIModel self.selectedTranscriptionModelName = selectedTranscriptionModelName ?? UserDefaults.standard.string(forKey: "CurrentTranscriptionModel") @@ -52,6 +55,7 @@ struct PowerModeConfig: Codable, Identifiable, Equatable { useScreenCapture = try container.decode(Bool.self, forKey: .useScreenCapture) selectedAIProvider = try container.decodeIfPresent(String.self, forKey: .selectedAIProvider) selectedAIModel = try container.decodeIfPresent(String.self, forKey: .selectedAIModel) + isAutoSendEnabled = try container.decodeIfPresent(Bool.self, forKey: .isAutoSendEnabled) ?? false if let newModelName = try container.decodeIfPresent(String.self, forKey: .selectedTranscriptionModelName) { selectedTranscriptionModelName = newModelName @@ -75,6 +79,7 @@ struct PowerModeConfig: Codable, Identifiable, Equatable { try container.encode(useScreenCapture, forKey: .useScreenCapture) try container.encodeIfPresent(selectedAIProvider, forKey: .selectedAIProvider) try container.encodeIfPresent(selectedAIModel, forKey: .selectedAIModel) + try container.encode(isAutoSendEnabled, forKey: .isAutoSendEnabled) try container.encodeIfPresent(selectedTranscriptionModelName, forKey: .selectedTranscriptionModelName) } diff --git a/VoiceInk/PowerMode/PowerModeConfigView.swift b/VoiceInk/PowerMode/PowerModeConfigView.swift index 55ca34f..36c90b2 100644 --- a/VoiceInk/PowerMode/PowerModeConfigView.swift +++ b/VoiceInk/PowerMode/PowerModeConfigView.swift @@ -35,6 +35,8 @@ struct ConfigurationView: View { // New state for screen capture toggle @State private var useScreenCapture = false + // NEW: Auto-send toggle state + @State private var isAutoSendEnabled = false // State for prompt editing (similar to EnhancementSettingsView) @State private var isEditingPrompt = false @@ -75,6 +77,7 @@ struct ConfigurationView: View { _configName = State(initialValue: "") _selectedEmoji = State(initialValue: "✏️") _useScreenCapture = State(initialValue: false) + _isAutoSendEnabled = State(initialValue: false) // Default to current global AI provider/model for new configurations - use UserDefaults only _selectedAIProvider = State(initialValue: UserDefaults.standard.string(forKey: "selectedAIProvider")) _selectedAIModel = State(initialValue: nil) // Initialize to nil and set it after view appears @@ -90,6 +93,7 @@ struct ConfigurationView: View { _selectedAppConfigs = State(initialValue: latestConfig.appConfigs ?? []) _websiteConfigs = State(initialValue: latestConfig.urlConfigs ?? []) _useScreenCapture = State(initialValue: latestConfig.useScreenCapture) + _isAutoSendEnabled = State(initialValue: latestConfig.isAutoSendEnabled) _selectedAIProvider = State(initialValue: latestConfig.selectedAIProvider) _selectedAIModel = State(initialValue: latestConfig.selectedAIModel) case .editDefault(let config): @@ -102,6 +106,7 @@ struct ConfigurationView: View { _configName = State(initialValue: latestConfig.name) _selectedEmoji = State(initialValue: latestConfig.emoji) _useScreenCapture = State(initialValue: latestConfig.useScreenCapture) + _isAutoSendEnabled = State(initialValue: latestConfig.isAutoSendEnabled) _selectedAIProvider = State(initialValue: latestConfig.selectedAIProvider) _selectedAIModel = State(initialValue: latestConfig.selectedAIModel) } @@ -588,6 +593,25 @@ struct ConfigurationView: View { .background(CardBackground(isSelected: false)) .padding(.horizontal) + // SECTION 4: ADVANCED + VStack(spacing: 16) { + SectionHeader(title: "Advanced") + + HStack { + Toggle("Auto Send", isOn: $isAutoSendEnabled) + + InfoTip( + title: "Auto Send", + message: "Automatically presses the Return/Enter key after pasting text. This is useful for chat applications or forms where its not necessary to to make changes to the transcribed text" + ) + + Spacer() + } + } + .padding() + .background(CardBackground(isSelected: false)) + .padding(.horizontal) + // Save Button VoiceInkButton( title: mode.isAdding ? "Add New Power Mode" : "Save Changes", @@ -671,7 +695,8 @@ struct ConfigurationView: View { selectedLanguage: selectedLanguage, useScreenCapture: useScreenCapture, selectedAIProvider: selectedAIProvider, - selectedAIModel: selectedAIModel + selectedAIModel: selectedAIModel, + isAutoSendEnabled: isAutoSendEnabled ) case .edit(let config): var updatedConfig = config @@ -684,6 +709,7 @@ struct ConfigurationView: View { updatedConfig.appConfigs = selectedAppConfigs.isEmpty ? nil : selectedAppConfigs updatedConfig.urlConfigs = websiteConfigs.isEmpty ? nil : websiteConfigs updatedConfig.useScreenCapture = useScreenCapture + updatedConfig.isAutoSendEnabled = isAutoSendEnabled updatedConfig.selectedAIProvider = selectedAIProvider updatedConfig.selectedAIModel = selectedAIModel return updatedConfig @@ -697,6 +723,7 @@ struct ConfigurationView: View { updatedConfig.selectedTranscriptionModelName = selectedTranscriptionModelName updatedConfig.selectedLanguage = selectedLanguage updatedConfig.useScreenCapture = useScreenCapture + updatedConfig.isAutoSendEnabled = isAutoSendEnabled updatedConfig.selectedAIProvider = selectedAIProvider updatedConfig.selectedAIModel = selectedAIModel return updatedConfig diff --git a/VoiceInk/Whisper/WhisperState.swift b/VoiceInk/Whisper/WhisperState.swift index c7a2c6c..74d7ee9 100644 --- a/VoiceInk/Whisper/WhisperState.swift +++ b/VoiceInk/Whisper/WhisperState.swift @@ -340,6 +340,15 @@ class WhisperState: NSObject, ObservableObject { if self.isAutoCopyEnabled { ClipboardManager.copyToClipboard(text) } + + // Automatically press Enter if the active Power Mode configuration allows it. + let powerMode = PowerModeManager.shared + if powerMode.isPowerModeEnabled && powerMode.currentActiveConfiguration.isAutoSendEnabled { + // Slight delay to ensure the paste operation completes + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + CursorPaster.pressEnter() + } + } } if let result = promptDetectionResult,