Browse Source
feat: persistent voice commands execution log and robust ZWNJ-tolerant Antigravity matcher
main
feat: persistent voice commands execution log and robust ZWNJ-tolerant Antigravity matcher
main
4 changed files with 605 additions and 265 deletions
-
1src/AppDelegate.swift
-
32src/StatusBarController.swift
-
718src/VoiceCommandExecutor.swift
-
119src/VoiceCommandLogger.swift
718
src/VoiceCommandExecutor.swift
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,119 @@ |
|||||
|
import Foundation |
||||
|
import Cocoa |
||||
|
|
||||
|
public final class VoiceCommandLogger { |
||||
|
public static let shared = VoiceCommandLogger() |
||||
|
|
||||
|
public let logFileURL: URL |
||||
|
private let queue = DispatchQueue(label: "com.soniox.voicecommandlogger", qos: .utility) |
||||
|
private let dateFormatter: DateFormatter |
||||
|
|
||||
|
private init() { |
||||
|
let appSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first! |
||||
|
let dir = appSupport.appendingPathComponent("SonioxVoice") |
||||
|
try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) |
||||
|
self.logFileURL = dir.appendingPathComponent("voice_commands.log") |
||||
|
|
||||
|
self.dateFormatter = DateFormatter() |
||||
|
self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" |
||||
|
|
||||
|
ensureLogHeader() |
||||
|
} |
||||
|
|
||||
|
private func ensureLogHeader() { |
||||
|
if !FileManager.default.fileExists(atPath: logFileURL.path) { |
||||
|
let header = """ |
||||
|
================================================================================ |
||||
|
Soniox Voice Commands Execution & Debug Log |
||||
|
Log Location: \(logFileURL.path) |
||||
|
Started: \(dateFormatter.string(from: Date())) |
||||
|
================================================================================ |
||||
|
|
||||
|
|
||||
|
""" |
||||
|
try? header.write(to: logFileURL, atomically: true, encoding: .utf8) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public func logCommand( |
||||
|
rawSpeech: String, |
||||
|
body: String, |
||||
|
normalized: String, |
||||
|
actionCategory: String, |
||||
|
actionDetail: String, |
||||
|
status: String |
||||
|
) { |
||||
|
queue.async { |
||||
|
let timestamp = self.dateFormatter.string(from: Date()) |
||||
|
let statusIcon = status.contains("SUCCESS") ? "✅" : "❌" |
||||
|
let entry = """ |
||||
|
-------------------------------------------------------------------------------- |
||||
|
[\(timestamp)] ⚡️ VOICE SLASH COMMAND |
||||
|
🎤 Raw Soniox Speech: "\(rawSpeech)" |
||||
|
✂️ Extracted Body: "\(body)" |
||||
|
🔍 Normalized: "\(normalized)" |
||||
|
🏷️ Category: \(actionCategory) |
||||
|
🎯 Executed Action: \(actionDetail) |
||||
|
\(statusIcon) Status: \(status) |
||||
|
-------------------------------------------------------------------------------- |
||||
|
|
||||
|
""" |
||||
|
self.appendToFile(text: entry) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public func logDictation( |
||||
|
rawSpeech: String, |
||||
|
processed: String |
||||
|
) { |
||||
|
queue.async { |
||||
|
let timestamp = self.dateFormatter.string(from: Date()) |
||||
|
let entry = """ |
||||
|
-------------------------------------------------------------------------------- |
||||
|
[\(timestamp)] ✍️ VOICE DICTATION (Normal Speech) |
||||
|
🎤 Raw Soniox Speech: "\(rawSpeech)" |
||||
|
📝 Processed Text: "\(processed)" |
||||
|
📋 Destination: Pasted to Frontmost Application |
||||
|
✅ Status: SUCCESS |
||||
|
-------------------------------------------------------------------------------- |
||||
|
|
||||
|
""" |
||||
|
self.appendToFile(text: entry) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private func appendToFile(text: String) { |
||||
|
guard let data = text.data(using: .utf8) else { return } |
||||
|
if let fileHandle = try? FileHandle(forWritingTo: logFileURL) { |
||||
|
fileHandle.seekToEndOfFile() |
||||
|
fileHandle.write(data) |
||||
|
try? fileHandle.close() |
||||
|
} else { |
||||
|
try? data.write(to: logFileURL, options: .atomic) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public func clearLog() { |
||||
|
queue.async { |
||||
|
let timestamp = self.dateFormatter.string(from: Date()) |
||||
|
let header = """ |
||||
|
================================================================================ |
||||
|
Soniox Voice Commands Execution & Debug Log (Cleared) |
||||
|
Reset At: \(timestamp) |
||||
|
================================================================================ |
||||
|
|
||||
|
|
||||
|
""" |
||||
|
try? header.write(to: self.logFileURL, atomically: true, encoding: .utf8) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public func openLogInViewer() { |
||||
|
// Open log in default text editor or Console |
||||
|
NSWorkspace.shared.open(logFileURL) |
||||
|
} |
||||
|
|
||||
|
public func openLogFolder() { |
||||
|
NSWorkspace.shared.activateFileViewerSelecting([logFileURL]) |
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue