From 147daa5f5f6ecf3509cd7db7415755d7fb2b10d2 Mon Sep 17 00:00:00 2001 From: Beingpax Date: Tue, 18 Mar 2025 10:47:08 +0545 Subject: [PATCH] Revamp word replacement with direct regex implementation --- .../Services/AudioTranscriptionManager.swift | 5 +++ .../Services/AudioTranscriptionService.swift | 6 ++++ .../Services/WordReplacementService.swift | 33 +++++++++++++++++ .../Dictionary/WordReplacementView.swift | 36 +++++++++++++------ VoiceInk/Whisper/WhisperState.swift | 14 ++++---- 5 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 VoiceInk/Services/WordReplacementService.swift diff --git a/VoiceInk/Services/AudioTranscriptionManager.swift b/VoiceInk/Services/AudioTranscriptionManager.swift index 876527c..c9a308a 100644 --- a/VoiceInk/Services/AudioTranscriptionManager.swift +++ b/VoiceInk/Services/AudioTranscriptionManager.swift @@ -98,6 +98,11 @@ class AudioTranscriptionManager: ObservableObject { var text = await whisperContext?.getTranscription() ?? "" text = text.trimmingCharacters(in: .whitespacesAndNewlines) + // Apply word replacements if enabled + if UserDefaults.standard.bool(forKey: "IsWordReplacementEnabled") { + text = WordReplacementService.shared.applyReplacements(to: text) + } + // Handle enhancement if enabled if let enhancementService = whisperState.enhancementService, enhancementService.isEnhancementEnabled, diff --git a/VoiceInk/Services/AudioTranscriptionService.swift b/VoiceInk/Services/AudioTranscriptionService.swift index 7a42c04..b92d9dc 100644 --- a/VoiceInk/Services/AudioTranscriptionService.swift +++ b/VoiceInk/Services/AudioTranscriptionService.swift @@ -103,6 +103,12 @@ class AudioTranscriptionService: ObservableObject { text = text.trimmingCharacters(in: .whitespacesAndNewlines) logger.notice("✅ Retranscription completed successfully, length: \(text.count) characters") + // Apply word replacements if enabled + if UserDefaults.standard.bool(forKey: "IsWordReplacementEnabled") { + text = WordReplacementService.shared.applyReplacements(to: text) + logger.notice("✅ Word replacements applied") + } + // Apply AI enhancement if enabled - using the same enhancement service as WhisperState if let enhancementService = enhancementService, enhancementService.isEnhancementEnabled, diff --git a/VoiceInk/Services/WordReplacementService.swift b/VoiceInk/Services/WordReplacementService.swift new file mode 100644 index 0000000..eb74277 --- /dev/null +++ b/VoiceInk/Services/WordReplacementService.swift @@ -0,0 +1,33 @@ +import Foundation + +class WordReplacementService { + static let shared = WordReplacementService() + + private init() {} + + func applyReplacements(to text: String) -> String { + guard let replacements = UserDefaults.standard.dictionary(forKey: "wordReplacements") as? [String: String], + !replacements.isEmpty else { + return text // No replacements to apply + } + + var modifiedText = text + + // Apply each replacement (case-insensitive, whole word) + for (original, replacement) in replacements { + // Create a regular expression that matches the word boundaries + let pattern = "\\b\(NSRegularExpression.escapedPattern(for: original))\\b" + if let regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive) { + let range = NSRange(modifiedText.startIndex..., in: modifiedText) + modifiedText = regex.stringByReplacingMatches( + in: modifiedText, + options: [], + range: range, + withTemplate: replacement + ) + } + } + + return modifiedText + } +} diff --git a/VoiceInk/Views/Dictionary/WordReplacementView.swift b/VoiceInk/Views/Dictionary/WordReplacementView.swift index e3e5c48..703170a 100644 --- a/VoiceInk/Views/Dictionary/WordReplacementView.swift +++ b/VoiceInk/Views/Dictionary/WordReplacementView.swift @@ -7,8 +7,15 @@ class WordReplacementManager: ObservableObject { } } + @Published var isEnabled: Bool { + didSet { + UserDefaults.standard.set(isEnabled, forKey: "IsWordReplacementEnabled") + } + } + init() { self.replacements = UserDefaults.standard.dictionary(forKey: "wordReplacements") as? [String: String] ?? [:] + self.isEnabled = UserDefaults.standard.bool(forKey: "IsWordReplacementEnabled") } func addReplacement(original: String, replacement: String) { @@ -28,17 +35,26 @@ struct WordReplacementView: View { var body: some View { VStack(alignment: .leading, spacing: 20) { - // Info Section + // Info Section with Toggle GroupBox { - Label { - Text("Define word replacements to automatically replace specific words or phrases during AI enhancement") - .font(.system(size: 12)) - .foregroundColor(.secondary) - .fixedSize(horizontal: false, vertical: true) - .frame(alignment: .leading) - } icon: { - Image(systemName: "info.circle.fill") - .foregroundColor(.blue) + HStack { + Label { + Text("Define word replacements to automatically replace specific words or phrases") + .font(.system(size: 12)) + .foregroundColor(.secondary) + .fixedSize(horizontal: false, vertical: true) + .frame(alignment: .leading) + } icon: { + Image(systemName: "info.circle.fill") + .foregroundColor(.blue) + } + + Spacer() + + Toggle("Enable", isOn: $manager.isEnabled) + .toggleStyle(.switch) + .labelsHidden() + .help("Enable automatic word replacement after transcription") } } diff --git a/VoiceInk/Whisper/WhisperState.swift b/VoiceInk/Whisper/WhisperState.swift index 0ec6c85..e229ecf 100644 --- a/VoiceInk/Whisper/WhisperState.swift +++ b/VoiceInk/Whisper/WhisperState.swift @@ -401,6 +401,12 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate { text = text.trimmingCharacters(in: .whitespacesAndNewlines) logger.notice("✅ Transcription completed successfully, length: \(text.count) characters") + // Apply word replacements if enabled + if UserDefaults.standard.bool(forKey: "IsWordReplacementEnabled") { + text = WordReplacementService.shared.applyReplacements(to: text) + logger.notice("✅ Word replacements applied") + } + if let enhancementService = enhancementService, enhancementService.isEnhancementEnabled, enhancementService.isConfigured { @@ -444,14 +450,6 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate { try? modelContext.save() } - if case .trialExpired = licenseViewModel.licenseState { - text = """ - Your trial has expired. Upgrade to VoiceInk Pro at tryvoiceink.com/buy - - \(text) - """ - } - messageLog += "Done: \(text)\n" SoundManager.shared.playStopSound()