Browse Source
feat: adaptive menu bar icon, fully English UI, double-tap Fn preset, and advanced settings
main
feat: adaptive menu bar icon, fully English UI, double-tap Fn preset, and advanced settings
main
5 changed files with 295 additions and 54 deletions
-
8src/AppDelegate.swift
-
70src/HotkeyManager.swift
-
14src/SonioxClient.swift
-
135src/SonioxSettings.swift
-
122src/StatusBarController.swift
@ -0,0 +1,135 @@ |
|||
import Foundation |
|||
import Cocoa |
|||
|
|||
public enum RecognitionLanguage: String, CaseIterable { |
|||
case multi = "multi" |
|||
case persian = "fa" |
|||
case english = "en" |
|||
case persianEnglish = "fa_en" |
|||
|
|||
public var displayName: String { |
|||
switch self { |
|||
case .multi: |
|||
return "Multilingual (Persian, English, Arabic)" |
|||
case .persian: |
|||
return "Persian Only (فارسی)" |
|||
case .english: |
|||
return "English Only" |
|||
case .persianEnglish: |
|||
return "Persian + English" |
|||
} |
|||
} |
|||
|
|||
public var hints: [String] { |
|||
switch self { |
|||
case .multi: |
|||
return ["fa", "en", "ar"] |
|||
case .persian: |
|||
return ["fa"] |
|||
case .english: |
|||
return ["en"] |
|||
case .persianEnglish: |
|||
return ["fa", "en"] |
|||
} |
|||
} |
|||
} |
|||
|
|||
public final class SonioxSettings { |
|||
public static let shared = SonioxSettings() |
|||
|
|||
private let defaults = UserDefaults.standard |
|||
|
|||
public var language: RecognitionLanguage { |
|||
get { |
|||
let val = defaults.string(forKey: "SonioxLanguage") ?? RecognitionLanguage.multi.rawValue |
|||
return RecognitionLanguage(rawValue: val) ?? .multi |
|||
} |
|||
set { |
|||
defaults.set(newValue.rawValue, forKey: "SonioxLanguage") |
|||
SonioxSessionPool.shared.reconnectAll() |
|||
} |
|||
} |
|||
|
|||
public var addTrailingSpace: Bool { |
|||
get { |
|||
if defaults.object(forKey: "SonioxAddTrailingSpace") == nil { return true } |
|||
return defaults.bool(forKey: "SonioxAddTrailingSpace") |
|||
} |
|||
set { |
|||
defaults.set(newValue, forKey: "SonioxAddTrailingSpace") |
|||
} |
|||
} |
|||
|
|||
public var normalizeText: Bool { |
|||
get { |
|||
if defaults.object(forKey: "SonioxNormalizeText") == nil { return true } |
|||
return defaults.bool(forKey: "SonioxNormalizeText") |
|||
} |
|||
set { |
|||
defaults.set(newValue, forKey: "SonioxNormalizeText") |
|||
} |
|||
} |
|||
|
|||
public var playSounds: Bool { |
|||
get { |
|||
if defaults.object(forKey: "SonioxPlaySounds") == nil { return true } |
|||
return defaults.bool(forKey: "SonioxPlaySounds") |
|||
} |
|||
set { |
|||
defaults.set(newValue, forKey: "SonioxPlaySounds") |
|||
} |
|||
} |
|||
|
|||
public var launchAtLogin: Bool { |
|||
get { |
|||
return defaults.bool(forKey: "SonioxLaunchAtLogin") |
|||
} |
|||
set { |
|||
defaults.set(newValue, forKey: "SonioxLaunchAtLogin") |
|||
updateLaunchAtLogin(enabled: newValue) |
|||
} |
|||
} |
|||
|
|||
public func resetToDefaults() { |
|||
defaults.removeObject(forKey: "SonioxHotkeyPreset") |
|||
defaults.removeObject(forKey: "SonioxDictationMode") |
|||
defaults.removeObject(forKey: "SonioxLanguage") |
|||
defaults.removeObject(forKey: "SonioxAddTrailingSpace") |
|||
defaults.removeObject(forKey: "SonioxNormalizeText") |
|||
defaults.removeObject(forKey: "SonioxPlaySounds") |
|||
defaults.removeObject(forKey: "SonioxLaunchAtLogin") |
|||
updateLaunchAtLogin(enabled: false) |
|||
HotkeyManager.shared.registerHotkeys() |
|||
SonioxSessionPool.shared.reconnectAll() |
|||
} |
|||
|
|||
private func updateLaunchAtLogin(enabled: Bool) { |
|||
let launchAgentDir = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("Library/LaunchAgents") |
|||
let plistURL = launchAgentDir.appendingPathComponent("com.soniox.voice.plist") |
|||
|
|||
if enabled { |
|||
try? FileManager.default.createDirectory(at: launchAgentDir, withIntermediateDirectories: true) |
|||
let plistContent = """ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|||
<plist version="1.0"> |
|||
<dict> |
|||
<key>Label</key> |
|||
<string>com.soniox.voice</string> |
|||
<key>ProgramArguments</key> |
|||
<array> |
|||
<string>/Applications/Soniox Voice.app/Contents/MacOS/SonioxVoice</string> |
|||
</array> |
|||
<key>RunAtLoad</key> |
|||
<true/> |
|||
<key>KeepAlive</key> |
|||
<false/> |
|||
</dict> |
|||
</plist> |
|||
""" |
|||
try? plistContent.write(to: plistURL, atomically: true, encoding: .utf8) |
|||
} else { |
|||
try? FileManager.default.removeItem(at: plistURL) |
|||
} |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue