From c3bce16df2c4178f1e414470d906b9497085a751 Mon Sep 17 00:00:00 2001 From: Ali Alavi Date: Sun, 4 Oct 2026 18:50:46 +0000 Subject: [PATCH] fix: stream ONLY finalized (is_final == true) tokens live as user speaks, eliminating raw acoustic noise and double-pasting --- src/AppDelegate.swift | 10 ++++------ src/SonioxClient.swift | 30 ++++++++++++------------------ 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/AppDelegate.swift b/src/AppDelegate.swift index 9d203b7..ff54476 100644 --- a/src/AppDelegate.swift +++ b/src/AppDelegate.swift @@ -228,13 +228,11 @@ public final class AppDelegate: NSObject, NSApplicationDelegate { self.hasSpokenInCurrentSession = true self.lastVoiceDetectedTime = Date() - if SonioxSettings.shared.liveStreamingInput { - // If it's an executive slash command, do NOT type it into the input box - if SonioxSettings.shared.voiceCommands && VoiceCommandExecutor.shared.isSlashCommand(text: delta) { - return - } - InputBoxManager.shared.streamDelta(text: delta) + // If it's an executive slash command, do NOT type it into the input box + if SonioxSettings.shared.voiceCommands && VoiceCommandExecutor.shared.isSlashCommand(text: delta) { + return } + InputBoxManager.shared.streamDelta(text: delta) } session.onEndTriggerDetected = { [weak self] in diff --git a/src/SonioxClient.swift b/src/SonioxClient.swift index f357d14..c99aa2e 100644 --- a/src/SonioxClient.swift +++ b/src/SonioxClient.swift @@ -316,25 +316,19 @@ public final class SonioxLiveSession { } } - // Ultra-responsive real-time word-by-word streaming: - // A word in 'combined' is completed as soon as a whitespace character follows it. + // Emit live streaming deltas STRICTLY from committed final tokens (is_final == true): var deltaToEmit: String? = nil - if !triggerDetected { - if let lastSpaceRange = combined.rangeOfCharacter(from: .whitespacesAndNewlines, options: .backwards) { - let stablePortion = String(combined[.. streamedPrefix.count { - if stablePortion.hasPrefix(streamedPrefix) { - let delta = String(stablePortion.dropFirst(streamedPrefix.count)) - streamedPrefix = stablePortion - deltaToEmit = delta - } else { - let common = stablePortion.commonPrefix(with: streamedPrefix) - if stablePortion.count > common.count { - let delta = String(stablePortion.dropFirst(common.count)) - streamedPrefix = stablePortion - deltaToEmit = delta - } - } + if !triggerDetected && fullCommitted.count > streamedPrefix.count { + if fullCommitted.hasPrefix(streamedPrefix) { + let delta = String(fullCommitted.dropFirst(streamedPrefix.count)) + streamedPrefix = fullCommitted + deltaToEmit = delta + } else { + let common = fullCommitted.commonPrefix(with: streamedPrefix) + if fullCommitted.count > common.count { + let delta = String(fullCommitted.dropFirst(common.count)) + streamedPrefix = fullCommitted + deltaToEmit = delta } } }