Archived
Redesign Hope iOS UI, harden playback, and document project handoff
This commit is contained in:
1 parent
40aaf4fea7
commit
24f95639dc
24 files changed
+1470
-594
No files matched your search
@@ -26,7 +26,7 @@ struct ConnectionTestView: View {
|
||||
if isBusy {
|
||||
ProgressView()
|
||||
} else {
|
||||
Text("Get token")
|
||||
Text("Test connection")
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
@@ -44,7 +44,7 @@ struct ConnectionTestView: View {
|
||||
LabeledContent("User") {
|
||||
Text(response.user.displayName ?? response.user.id)
|
||||
}
|
||||
Label("Token endpoint and TLS are working.", systemImage: "checkmark.circle")
|
||||
Label("Connected to your server. Start a conversation to check Hope’s voice and replies.", systemImage: "checkmark.circle")
|
||||
.foregroundStyle(.green)
|
||||
}
|
||||
case .failure(let error):
|
||||
@@ -57,6 +57,7 @@ struct ConnectionTestView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Connection test")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
|
||||
private func runTest() {
|
||||
|
||||
@@ -30,18 +30,48 @@ struct SettingsView: View {
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section("Server") {
|
||||
Section {
|
||||
Button {
|
||||
showVoicePicker = true
|
||||
} label: {
|
||||
LabeledContent("Speaking voice", value: Voices.display(for: settings.voiceID) ?? settings.voiceID)
|
||||
}
|
||||
Picker("Conversation mode", selection: $settings.textMode) {
|
||||
Text("Voice").tag(false)
|
||||
Text("Type · Microphone off").tag(true)
|
||||
}
|
||||
} header: {
|
||||
Text("Make it yours")
|
||||
} footer: {
|
||||
Text("Voice and typing preferences are saved on this iPhone. You can switch modes from the Talk tab.")
|
||||
}
|
||||
Section {
|
||||
if session.isReady, !session.models.isEmpty {
|
||||
Button {
|
||||
showModelPicker = true
|
||||
} label: {
|
||||
LabeledContent("Model", value: session.selectedModel ?? settings.modelID ?? "Server default")
|
||||
}
|
||||
} else {
|
||||
LabeledContent("Model", value: settings.modelID ?? "Server default")
|
||||
Text("Connect from Talk to see the models available on your server.")
|
||||
.font(.footnote).foregroundStyle(.secondary)
|
||||
}
|
||||
} header: { Text("Intelligence") }
|
||||
Section {
|
||||
TextField(SettingsStore.defaultServerURL, text: $urlText)
|
||||
.keyboardType(.URL)
|
||||
.autocorrectionDisabled()
|
||||
.textInputAutocapitalization(.never)
|
||||
.accessibilityLabel("Server URL")
|
||||
.onAppear { urlText = settings.serverURL }
|
||||
if let urlError {
|
||||
Text(urlError)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.red)
|
||||
}
|
||||
Button("Apply server URL") { applyServerURL() }
|
||||
.disabled(!canApplyServerURL || urlText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
|
||||
Button("Save server address") { applyServerURL() }
|
||||
.disabled(!canApplyServerURL || urlText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || urlText == settings.serverURL)
|
||||
if !canApplyServerURL {
|
||||
Text(session.state == .tearingDown
|
||||
? "Waiting for failure cleanup to finish…"
|
||||
@@ -49,37 +79,17 @@ struct SettingsView: View {
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
Section("Voice") {
|
||||
Button {
|
||||
showVoicePicker = true
|
||||
} label: {
|
||||
LabeledContent("Voice", value: Voices.display(for: settings.voiceID) ?? settings.voiceID)
|
||||
}
|
||||
}
|
||||
Section("Model") {
|
||||
if session.isReady, !session.models.isEmpty {
|
||||
Button {
|
||||
showModelPicker = true
|
||||
} label: {
|
||||
LabeledContent("Model", value: session.selectedModel ?? settings.modelID ?? "—")
|
||||
}
|
||||
} else {
|
||||
Text("Start a conversation to load the model list.")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
Section("Interaction") {
|
||||
Toggle("Text mode", isOn: $settings.textMode)
|
||||
}
|
||||
Section {
|
||||
NavigationLink("Connection test") {
|
||||
ConnectionTestView(settings: settings)
|
||||
}
|
||||
} header: {
|
||||
Text("Connection")
|
||||
} footer: {
|
||||
Text("Hope runs on your own server. Your iPhone needs to be on the same network or a network that can reach it.")
|
||||
}
|
||||
}
|
||||
.navigationTitle("Settings")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.sheet(isPresented: $showVoicePicker) {
|
||||
VoicePickerView(settings: settings, session: session)
|
||||
}
|
||||
@@ -115,7 +125,7 @@ struct SettingsView: View {
|
||||
|
||||
struct VoicePickerView: View {
|
||||
@ObservedObject var settings: SettingsStore
|
||||
var session: VoiceSession
|
||||
@ObservedObject var session: VoiceSession
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
var body: some View {
|
||||
@@ -132,10 +142,11 @@ struct VoicePickerView: View {
|
||||
if settings.voiceID == voice.id {
|
||||
Spacer()
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundStyle(.blue)
|
||||
.foregroundStyle(HopeStyle.tint)
|
||||
}
|
||||
}
|
||||
}
|
||||
.accessibilityAddTraits(settings.voiceID == voice.id ? .isSelected : [])
|
||||
}
|
||||
}
|
||||
.navigationTitle("Voice")
|
||||
@@ -150,7 +161,7 @@ struct VoicePickerView: View {
|
||||
|
||||
struct ModelPickerView: View {
|
||||
@ObservedObject var settings: SettingsStore
|
||||
var session: VoiceSession
|
||||
@ObservedObject var session: VoiceSession
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
private var activeModelID: String? {
|
||||
@@ -176,10 +187,11 @@ struct ModelPickerView: View {
|
||||
if activeModelID == model.id {
|
||||
Spacer()
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundStyle(.blue)
|
||||
.foregroundStyle(HopeStyle.tint)
|
||||
}
|
||||
}
|
||||
}
|
||||
.accessibilityAddTraits(activeModelID == model.id ? .isSelected : [])
|
||||
}
|
||||
}
|
||||
.navigationTitle("Model")
|
||||
|
||||
Reference in new issue
Block a user