Redesign Hope iOS UI, harden playback, and document project handoff

This commit is contained in:
snowspeeder committed 2026-09-09 20:23:44 -04:00
1 parent 40aaf4fea7
commit 24f95639dc
24 files changed
+1470 -594

No files matched your search

+29 -6
View File
@@ -3,19 +3,19 @@ import Combine
final class SettingsStore: ObservableObject {
@Published var serverURL: String {
didSet { UserDefaults.standard.set(serverURL, forKey: Keys.serverURL) }
didSet { AppPreferences.store.set(serverURL, forKey: Keys.serverURL) }
}
@Published var voiceID: String {
didSet { UserDefaults.standard.set(voiceID, forKey: Keys.voiceID) }
didSet { AppPreferences.store.set(voiceID, forKey: Keys.voiceID) }
}
@Published var modelID: String? {
didSet { UserDefaults.standard.set(modelID, forKey: Keys.modelID) }
didSet { AppPreferences.store.set(modelID, forKey: Keys.modelID) }
}
@Published var textMode: Bool {
didSet { UserDefaults.standard.set(textMode, forKey: Keys.textMode) }
didSet { AppPreferences.store.set(textMode, forKey: Keys.textMode) }
}
@Published var volume: Double {
didSet { UserDefaults.standard.set(volume, forKey: Keys.volume) }
didSet { AppPreferences.store.set(volume, forKey: Keys.volume) }
}
private enum Keys {
@@ -29,7 +29,7 @@ final class SettingsStore: ObservableObject {
static let defaultServerURL = "https://192.168.86.2:9178"
init() {
let d = UserDefaults.standard
let d = AppPreferences.store
// Sanitize legacy values: earlier versions wrote the field to storage on
// every keystroke, so an invalid half-typed URL could be persisted and
// shipped to the network layer. Fall back to the documented default and
@@ -54,3 +54,26 @@ final class SettingsStore: ObservableObject {
serverURL.trimmingCharacters(in: .whitespacesAndNewlines)
}
}
enum AppPreferences {
static let store: UserDefaults = {
#if DEBUG
if ProcessInfo.processInfo.arguments.contains("--ui-testing") {
let suite = "dev.shane.voice.ui-testing"
let defaults = UserDefaults(suiteName: suite)!
if ProcessInfo.processInfo.arguments.contains("--reset-ui-testing") {
defaults.removePersistentDomain(forName: suite)
// Deterministic offline errors; UI tests must not contact the NAS.
defaults.set("https://127.0.0.1:1", forKey: "serverURL")
}
if ProcessInfo.processInfo.arguments.contains("--live-testing") {
defaults.set(SettingsStore.defaultServerURL, forKey: "serverURL")
defaults.set(true, forKey: "textMode")
defaults.set("qwen2.5-vl-3b", forKey: "modelID")
}
return defaults
}
#endif
return .standard
}()
}
+9 -1
View File
@@ -12,7 +12,15 @@ struct VoiceApp: App {
var body: some Scene {
WindowGroup {
ConversationView(settings: settings)
.preferredColorScheme(.dark)
.tint(HopeStyle.tint)
}
}
}
enum HopeStyle {
static let tint = Color(uiColor: UIColor { traits in
traits.userInterfaceStyle == .dark
? UIColor(red: 0.43, green: 0.68, blue: 1, alpha: 1)
: UIColor(red: 0.12, green: 0.32, blue: 0.68, alpha: 1)
})
}