Complete Phase 4: Comprehensive preferences, localization, and UX polish
- Rename application from MenuWhisper to Tell me with new domain com.fmartingr.tellme - Implement comprehensive preferences window with 6 tabs (General, Models, Text Insertion, Interface, Advanced, Permissions) - Add full English/Spanish localization for all UI elements - Create functional onboarding flow with model download capability - Implement preview dialog for transcription editing - Add settings export/import functionality - Fix HUD content display issues and add comprehensive permission checking - Enhance build scripts and app bundle creation for proper localization support
This commit is contained in:
parent
7ba5895406
commit
54c3b65d4a
25 changed files with 3086 additions and 235 deletions
|
|
@ -28,6 +28,20 @@ public struct HotkeyConfig: Codable {
|
|||
public static let `default` = HotkeyConfig(keyCode: 9, modifiers: 768) // V key with Cmd+Shift
|
||||
}
|
||||
|
||||
public enum InsertionMethod: String, CaseIterable, Codable {
|
||||
case paste = "paste"
|
||||
case typing = "typing"
|
||||
|
||||
public var displayName: String {
|
||||
switch self {
|
||||
case .paste:
|
||||
return NSLocalizedString("preferences.insertion.method.paste", comment: "Paste method")
|
||||
case .typing:
|
||||
return NSLocalizedString("preferences.insertion.method.type", comment: "Type method")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Settings: ObservableObject {
|
||||
private let logger = Logger(category: "Settings")
|
||||
private let userDefaults = UserDefaults.standard
|
||||
|
|
@ -49,6 +63,15 @@ public class Settings: ObservableObject {
|
|||
didSet { userDefaults.set(dictationTimeLimit, forKey: "dictationTimeLimit") }
|
||||
}
|
||||
|
||||
// HUD Settings
|
||||
@Published public var hudOpacity: Double {
|
||||
didSet { userDefaults.set(hudOpacity, forKey: "hudOpacity") }
|
||||
}
|
||||
|
||||
@Published public var hudSize: Double {
|
||||
didSet { userDefaults.set(hudSize, forKey: "hudSize") }
|
||||
}
|
||||
|
||||
// Model Settings
|
||||
@Published public var activeModelName: String? {
|
||||
didSet { userDefaults.set(activeModelName, forKey: "activeModelName") }
|
||||
|
|
@ -59,25 +82,47 @@ public class Settings: ObservableObject {
|
|||
}
|
||||
|
||||
// Insertion Settings
|
||||
@Published public var insertionMethod: String {
|
||||
didSet { userDefaults.set(insertionMethod, forKey: "insertionMethod") }
|
||||
@Published public var insertionMethod: InsertionMethod {
|
||||
didSet { userDefaults.set(insertionMethod.rawValue, forKey: "insertionMethod") }
|
||||
}
|
||||
|
||||
@Published public var showPreview: Bool {
|
||||
didSet { userDefaults.set(showPreview, forKey: "showPreview") }
|
||||
}
|
||||
|
||||
// Advanced Settings
|
||||
@Published public var enableLogging: Bool {
|
||||
didSet { userDefaults.set(enableLogging, forKey: "enableLogging") }
|
||||
}
|
||||
|
||||
@Published public var processingThreads: Int {
|
||||
didSet { userDefaults.set(processingThreads, forKey: "processingThreads") }
|
||||
}
|
||||
|
||||
public init() {
|
||||
// Load settings from UserDefaults
|
||||
self.hotkey = Settings.loadHotkey()
|
||||
self.hotkeyMode = HotkeyMode(rawValue: userDefaults.string(forKey: "hotkeyMode") ?? "") ?? .pushToTalk
|
||||
self.playSounds = userDefaults.object(forKey: "playSounds") as? Bool ?? false
|
||||
self.dictationTimeLimit = userDefaults.object(forKey: "dictationTimeLimit") as? TimeInterval ?? 600 // 10 minutes
|
||||
|
||||
// HUD Settings
|
||||
self.hudOpacity = userDefaults.object(forKey: "hudOpacity") as? Double ?? 0.9
|
||||
self.hudSize = userDefaults.object(forKey: "hudSize") as? Double ?? 1.0
|
||||
|
||||
// Model Settings
|
||||
self.activeModelName = userDefaults.string(forKey: "activeModelName")
|
||||
self.forcedLanguage = userDefaults.string(forKey: "forcedLanguage")
|
||||
self.insertionMethod = userDefaults.string(forKey: "insertionMethod") ?? "paste"
|
||||
|
||||
// Insertion Settings
|
||||
let insertionMethodString = userDefaults.string(forKey: "insertionMethod") ?? "paste"
|
||||
self.insertionMethod = InsertionMethod(rawValue: insertionMethodString) ?? .paste
|
||||
self.showPreview = userDefaults.object(forKey: "showPreview") as? Bool ?? false
|
||||
|
||||
// Advanced Settings
|
||||
self.enableLogging = userDefaults.object(forKey: "enableLogging") as? Bool ?? false
|
||||
self.processingThreads = userDefaults.object(forKey: "processingThreads") as? Int ?? 4
|
||||
|
||||
logger.info("Settings initialized")
|
||||
}
|
||||
|
||||
|
|
@ -88,10 +133,14 @@ public class Settings: ObservableObject {
|
|||
"hotkeyMode": hotkeyMode.rawValue,
|
||||
"playSounds": playSounds,
|
||||
"dictationTimeLimit": dictationTimeLimit,
|
||||
"hudOpacity": hudOpacity,
|
||||
"hudSize": hudSize,
|
||||
"activeModelName": activeModelName as Any,
|
||||
"forcedLanguage": forcedLanguage as Any,
|
||||
"insertionMethod": insertionMethod,
|
||||
"showPreview": showPreview
|
||||
"insertionMethod": insertionMethod.rawValue,
|
||||
"showPreview": showPreview,
|
||||
"enableLogging": enableLogging,
|
||||
"processingThreads": processingThreads
|
||||
]
|
||||
|
||||
return try JSONSerialization.data(withJSONObject: settingsDict, options: .prettyPrinted)
|
||||
|
|
@ -118,10 +167,19 @@ public class Settings: ObservableObject {
|
|||
dictationTimeLimit = timeLimit
|
||||
}
|
||||
|
||||
if let opacity = settingsDict["hudOpacity"] as? Double {
|
||||
hudOpacity = opacity
|
||||
}
|
||||
|
||||
if let size = settingsDict["hudSize"] as? Double {
|
||||
hudSize = size
|
||||
}
|
||||
|
||||
activeModelName = settingsDict["activeModelName"] as? String
|
||||
forcedLanguage = settingsDict["forcedLanguage"] as? String
|
||||
|
||||
if let method = settingsDict["insertionMethod"] as? String {
|
||||
if let methodString = settingsDict["insertionMethod"] as? String,
|
||||
let method = InsertionMethod(rawValue: methodString) {
|
||||
insertionMethod = method
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +187,14 @@ public class Settings: ObservableObject {
|
|||
showPreview = preview
|
||||
}
|
||||
|
||||
if let logging = settingsDict["enableLogging"] as? Bool {
|
||||
enableLogging = logging
|
||||
}
|
||||
|
||||
if let threads = settingsDict["processingThreads"] as? Int {
|
||||
processingThreads = threads
|
||||
}
|
||||
|
||||
logger.info("Settings imported successfully")
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue