From baae439aae2285aef31f99410d2e33043bfa7264 Mon Sep 17 00:00:00 2001 From: Beingpax Date: Sun, 7 Dec 2025 15:24:10 +0545 Subject: [PATCH] Preserve transcription in clipboard by default, add restore option with configurable delay --- VoiceInk/CursorPaster.swift | 14 +++--- VoiceInk/Services/ImportExportService.swift | 12 ++++- VoiceInk/Views/Settings/SettingsView.swift | 54 ++++++++++++++++----- 3 files changed, 60 insertions(+), 20 deletions(-) diff --git a/VoiceInk/CursorPaster.swift b/VoiceInk/CursorPaster.swift index a222bc7..2ec7d87 100644 --- a/VoiceInk/CursorPaster.swift +++ b/VoiceInk/CursorPaster.swift @@ -5,12 +5,11 @@ class CursorPaster { static func pasteAtCursor(_ text: String) { let pasteboard = NSPasteboard.general - let preserveTranscript = UserDefaults.standard.bool(forKey: "preserveTranscriptInClipboard") + let shouldRestoreClipboard = UserDefaults.standard.bool(forKey: "restoreClipboardAfterPaste") var savedContents: [(NSPasteboard.PasteboardType, Data)] = [] - // Only save clipboard contents if we plan to restore them - if !preserveTranscript { + if shouldRestoreClipboard { let currentItems = pasteboard.pasteboardItems ?? [] for item in currentItems { @@ -22,7 +21,7 @@ class CursorPaster { } } - ClipboardManager.setClipboard(text, transient: !preserveTranscript) + ClipboardManager.setClipboard(text, transient: shouldRestoreClipboard) DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { if UserDefaults.standard.bool(forKey: "UseAppleScriptPaste") { @@ -32,8 +31,11 @@ class CursorPaster { } } - if !preserveTranscript { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) { + if shouldRestoreClipboard { + let restoreDelay = UserDefaults.standard.double(forKey: "clipboardRestoreDelay") + let delay = restoreDelay > 0 ? restoreDelay : 1.5 + + DispatchQueue.main.asyncAfter(deadline: .now() + delay) { if !savedContents.isEmpty { pasteboard.clearContents() for (type, data) in savedContents { diff --git a/VoiceInk/Services/ImportExportService.swift b/VoiceInk/Services/ImportExportService.swift index 91c2a85..81c4335 100644 --- a/VoiceInk/Services/ImportExportService.swift +++ b/VoiceInk/Services/ImportExportService.swift @@ -24,6 +24,8 @@ struct GeneralSettings: Codable { let isPauseMediaEnabled: Bool? let isTextFormattingEnabled: Bool? let isExperimentalFeaturesEnabled: Bool? + let restoreClipboardAfterPaste: Bool? + let clipboardRestoreDelay: Double? } struct VoiceInkExportedSettings: Codable { @@ -103,7 +105,9 @@ class ImportExportService { isSystemMuteEnabled: mediaController.isSystemMuteEnabled, isPauseMediaEnabled: playbackController.isPauseMediaEnabled, isTextFormattingEnabled: UserDefaults.standard.object(forKey: keyIsTextFormattingEnabled) as? Bool ?? true, - isExperimentalFeaturesEnabled: UserDefaults.standard.bool(forKey: "isExperimentalFeaturesEnabled") + isExperimentalFeaturesEnabled: UserDefaults.standard.bool(forKey: "isExperimentalFeaturesEnabled"), + restoreClipboardAfterPaste: UserDefaults.standard.bool(forKey: "restoreClipboardAfterPaste"), + clipboardRestoreDelay: UserDefaults.standard.double(forKey: "clipboardRestoreDelay") ) let exportedSettings = VoiceInkExportedSettings( @@ -275,6 +279,12 @@ class ImportExportService { if let textFormattingEnabled = general.isTextFormattingEnabled { UserDefaults.standard.set(textFormattingEnabled, forKey: self.keyIsTextFormattingEnabled) } + if let restoreClipboard = general.restoreClipboardAfterPaste { + UserDefaults.standard.set(restoreClipboard, forKey: "restoreClipboardAfterPaste") + } + if let clipboardDelay = general.clipboardRestoreDelay { + UserDefaults.standard.set(clipboardDelay, forKey: "clipboardRestoreDelay") + } } self.showRestartAlert(message: "Settings imported successfully from \(url.lastPathComponent). All settings (including general app settings) have been applied.") diff --git a/VoiceInk/Views/Settings/SettingsView.swift b/VoiceInk/Views/Settings/SettingsView.swift index 419286e..ec5ea58 100644 --- a/VoiceInk/Views/Settings/SettingsView.swift +++ b/VoiceInk/Views/Settings/SettingsView.swift @@ -17,6 +17,8 @@ struct SettingsView: View { @AppStorage("hasCompletedOnboarding") private var hasCompletedOnboarding = true @AppStorage("autoUpdateCheck") private var autoUpdateCheck = true @AppStorage("enableAnnouncements") private var enableAnnouncements = true + @AppStorage("restoreClipboardAfterPaste") private var restoreClipboardAfterPaste = false + @AppStorage("clipboardRestoreDelay") private var clipboardRestoreDelay = 1.5 @State private var showResetOnboardingAlert = false @State private var currentShortcut = KeyboardShortcuts.getShortcut(for: .toggleMiniRecorder) @State private var isCustomCancelEnabled = false @@ -174,9 +176,9 @@ struct SettingsView: View { // Middle-Click Toggle VStack(alignment: .leading, spacing: 12) { HStack(spacing: 8) { - Toggle("Enable Middle-Click Toggle", isOn: $hotkeyManager.isMiddleClickToggleEnabled.animation()) + Toggle("Enable Middle-Click Toggle", isOn: $hotkeyManager.isMiddleClickToggleEnabled) .toggleStyle(.switch) - + InfoTip( title: "Middle-Click Toggle", message: "Use middle mouse button to toggle VoiceInk recording." @@ -188,7 +190,7 @@ struct SettingsView: View { Text("Activation Delay") .font(.system(size: 13, weight: .medium)) .foregroundColor(.secondary) - + TextField("", value: $hotkeyManager.middleClickActivationDelay, formatter: { let formatter = NumberFormatter() formatter.numberStyle = .none @@ -200,14 +202,13 @@ struct SettingsView: View { .background(Color(NSColor.textBackgroundColor)) .cornerRadius(5) .frame(width: 70) - + Text("ms") .foregroundColor(.secondary) - + Spacer() } .padding(.leading, 16) - .transition(.opacity.combined(with: .move(edge: .top))) } } } @@ -258,14 +259,41 @@ struct SettingsView: View { .toggleStyle(.switch) .help("Automatically mute system audio when recording starts and restore when recording stops") - Toggle(isOn: Binding( - get: { UserDefaults.standard.bool(forKey: "preserveTranscriptInClipboard") }, - set: { UserDefaults.standard.set($0, forKey: "preserveTranscriptInClipboard") } - )) { - Text("Preserve transcript in clipboard") + VStack(alignment: .leading, spacing: 12) { + HStack(spacing: 8) { + Toggle("Restore clipboard after paste", isOn: $restoreClipboardAfterPaste) + .toggleStyle(.switch) + + InfoTip( + title: "Restore Clipboard", + message: "When enabled, VoiceInk will restore your original clipboard content after pasting the transcription." + ) + } + + if restoreClipboardAfterPaste { + HStack(spacing: 8) { + Text("Restore Delay") + .font(.system(size: 13, weight: .medium)) + .foregroundColor(.secondary) + + Picker("", selection: $clipboardRestoreDelay) { + Text("0.5s").tag(0.5) + Text("1.0s").tag(1.0) + Text("1.5s").tag(1.5) + Text("2.0s").tag(2.0) + Text("2.5s").tag(2.5) + Text("3.0s").tag(3.0) + Text("4.0s").tag(4.0) + Text("5.0s").tag(5.0) + } + .pickerStyle(.menu) + .frame(width: 90) + + Spacer() + } + .padding(.leading, 16) + } } - .toggleStyle(.switch) - .help("Keep the transcribed text in clipboard instead of restoring the original clipboard content") } }