refactor: redesign SettingsView layout for improved user experience
This commit is contained in:
@@ -6,49 +6,110 @@ struct SettingsView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
Form {
|
ScrollView {
|
||||||
Section("API Keys") {
|
VStack(spacing: 32) {
|
||||||
SecureField("Deepgram API Key", text: $viewModel.settings.deepgramKey)
|
// API Keys Section
|
||||||
.textFieldStyle(.roundedBorder)
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
Text("API Keys")
|
||||||
SecureField("OpenAI API Key", text: $viewModel.settings.openAIKey)
|
.font(.headline)
|
||||||
.textFieldStyle(.roundedBorder)
|
.fontWeight(.semibold)
|
||||||
}
|
|
||||||
|
VStack(spacing: 12) {
|
||||||
Section("Personal Information") {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
VStack(alignment: .leading) {
|
Text("Deepgram API Key")
|
||||||
Text("About Yourself")
|
.font(.subheadline)
|
||||||
.font(.caption)
|
.foregroundColor(.secondary)
|
||||||
.foregroundColor(.secondary)
|
SecureField("Enter your Deepgram API key", text: $viewModel.settings.deepgramKey)
|
||||||
TextEditor(text: $viewModel.settings.userBlurb)
|
.textFieldStyle(.roundedBorder)
|
||||||
.frame(minHeight: 80)
|
.frame(maxWidth: .infinity)
|
||||||
.scrollContentBackground(.hidden)
|
}
|
||||||
.background(Color.gray.opacity(0.05))
|
|
||||||
.cornerRadius(8)
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
|
Text("OpenAI API Key")
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
SecureField("Enter your OpenAI API key", text: $viewModel.settings.openAIKey)
|
||||||
|
.textFieldStyle(.roundedBorder)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
.padding(.horizontal, 24)
|
||||||
|
|
||||||
Section("AI Generation") {
|
Divider()
|
||||||
VStack(alignment: .leading) {
|
.padding(.horizontal, 24)
|
||||||
HStack {
|
|
||||||
Text("System Prompt")
|
// Personal Information Section
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
Text("Personal Information")
|
||||||
|
.font(.headline)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
Text("About Yourself")
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
Text("This information will be included in the AI context when generating meeting notes.")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
|
|
||||||
|
TextEditor(text: $viewModel.settings.userBlurb)
|
||||||
|
.frame(minHeight: 80, maxHeight: 120)
|
||||||
|
.scrollContentBackground(.hidden)
|
||||||
|
.background(Color(.controlBackgroundColor))
|
||||||
|
.cornerRadius(8)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.stroke(Color(.separatorColor), lineWidth: 1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 24)
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
.padding(.horizontal, 24)
|
||||||
|
|
||||||
|
// AI Generation Section
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
HStack {
|
||||||
|
Text("AI Generation")
|
||||||
|
.font(.headline)
|
||||||
|
.fontWeight(.semibold)
|
||||||
Spacer()
|
Spacer()
|
||||||
Button("Reset to Default") {
|
Button("Reset to Default") {
|
||||||
viewModel.resetToDefaults()
|
viewModel.resetToDefaults()
|
||||||
}
|
}
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
Text("System Prompt")
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
Text("Customize how the AI generates your meeting notes.")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
|
||||||
|
TextEditor(text: $viewModel.settings.systemPrompt)
|
||||||
|
.frame(minHeight: 100, maxHeight: 160)
|
||||||
|
.scrollContentBackground(.hidden)
|
||||||
|
.background(Color(.controlBackgroundColor))
|
||||||
|
.cornerRadius(8)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.stroke(Color(.separatorColor), lineWidth: 1)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
TextEditor(text: $viewModel.settings.systemPrompt)
|
|
||||||
.frame(minHeight: 120)
|
|
||||||
.scrollContentBackground(.hidden)
|
|
||||||
.background(Color.gray.opacity(0.05))
|
|
||||||
.cornerRadius(8)
|
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal, 24)
|
||||||
|
|
||||||
|
Spacer(minLength: 20)
|
||||||
}
|
}
|
||||||
|
.padding(.vertical, 24)
|
||||||
}
|
}
|
||||||
.navigationTitle("Settings")
|
.navigationTitle("Settings")
|
||||||
|
.frame(minWidth: 600, minHeight: 500)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .cancellationAction) {
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
Button("Cancel") {
|
Button("Cancel") {
|
||||||
@@ -61,23 +122,12 @@ struct SettingsView: View {
|
|||||||
viewModel.saveSettings()
|
viewModel.saveSettings()
|
||||||
}
|
}
|
||||||
.disabled(viewModel.isSaving)
|
.disabled(viewModel.isSaving)
|
||||||
|
.buttonStyle(.borderedProminent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.overlay {
|
.onChange(of: viewModel.saveSuccessful) { _, isSuccessful in
|
||||||
if viewModel.saveSuccessful {
|
if isSuccessful {
|
||||||
VStack {
|
dismiss()
|
||||||
Spacer()
|
|
||||||
HStack {
|
|
||||||
Image(systemName: "checkmark.circle.fill")
|
|
||||||
.foregroundColor(.green)
|
|
||||||
Text("Settings saved successfully")
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
.background(Color.green.opacity(0.1))
|
|
||||||
.cornerRadius(10)
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
.transition(.move(edge: .bottom).combined(with: .opacity))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.alert("Error", isPresented: .constant(viewModel.errorMessage != nil)) {
|
.alert("Error", isPresented: .constant(viewModel.errorMessage != nil)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user