a789495a98
FreeBSD Smoke / FreeBSD Smoke (x86_64) (push) Has been cancelled
CI / Quality Guardrails (push) Has been cancelled
CI / Build & Test (macos-latest) (push) Has been cancelled
CI / Build & Test (ubuntu-latest) (push) Has been cancelled
CI / Build & Test (windows-latest) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / PowerShell Syntax (push) Has been cancelled
CI / Windows Cross-Target Check (Linux) (push) Has been cancelled
121 lines
4.5 KiB
Swift
121 lines
4.5 KiB
Swift
import JCodeKit
|
|
import SwiftUI
|
|
|
|
/// Settings sheet: model picker, reasoning effort, sessions, servers, info.
|
|
struct SettingsView: View {
|
|
@Environment(AppModel.self) private var model
|
|
@Environment(\.dismiss) private var dismiss
|
|
@State var renameDraft = ""
|
|
@State var showRename = false
|
|
@State var showPairNew = false
|
|
|
|
/// Reasoning effort levels offered when the provider exposes the knob.
|
|
static let reasoningEfforts = ["none", "low", "medium", "high", "xhigh"]
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
List {
|
|
modelSection
|
|
if model.session.reasoningEffort != nil {
|
|
reasoningSection
|
|
}
|
|
SettingsSessionsSection(renameDraft: $renameDraft, showRename: $showRename)
|
|
SettingsServersSection(showPairNew: $showPairNew)
|
|
SettingsInfoSection()
|
|
}
|
|
.scrollContentBackground(.hidden)
|
|
.background(Theme.background)
|
|
.dynamicTypeSize(.large ... .accessibility3)
|
|
.navigationTitle("Settings")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .confirmationAction) {
|
|
Button("Done") { dismiss() }
|
|
}
|
|
}
|
|
}
|
|
.preferredColorScheme(.dark)
|
|
.alert("Rename session", isPresented: $showRename) {
|
|
TextField("Title", text: $renameDraft)
|
|
Button("Rename") {
|
|
model.renameSession(renameDraft)
|
|
}
|
|
Button("Cancel", role: .cancel) {}
|
|
}
|
|
.sheet(isPresented: $showPairNew) {
|
|
NavigationStack {
|
|
PairingView()
|
|
.background(Theme.background)
|
|
.toolbar {
|
|
ToolbarItem(placement: .cancellationAction) {
|
|
Button("Cancel") { showPairNew = false }
|
|
}
|
|
}
|
|
}
|
|
.preferredColorScheme(.dark)
|
|
}
|
|
.onChange(of: model.activeServer?.id) {
|
|
showPairNew = false
|
|
}
|
|
}
|
|
|
|
private var modelSection: some View {
|
|
Section("Model") {
|
|
ForEach(model.session.availableModels, id: \.self) { name in
|
|
let isActive = name == model.session.modelName
|
|
Button {
|
|
model.setModel(name)
|
|
} label: {
|
|
HStack {
|
|
Text(name)
|
|
.font(Theme.mono(13))
|
|
.foregroundStyle(Theme.textPrimary)
|
|
.lineLimit(1)
|
|
Spacer()
|
|
if isActive {
|
|
Image(systemName: "checkmark")
|
|
.font(.caption)
|
|
.foregroundStyle(Theme.mint)
|
|
.accessibilityHidden(true)
|
|
}
|
|
}
|
|
}
|
|
.listRowBackground(Theme.surface)
|
|
.accessibilityLabel("Model \(name)")
|
|
.accessibilityValue(isActive ? "Selected" : "")
|
|
.accessibilityHint("Selects this model")
|
|
.accessibilityAddTraits(isActive ? [.isSelected] : [])
|
|
}
|
|
}
|
|
}
|
|
|
|
private var reasoningSection: some View {
|
|
Section("Reasoning effort") {
|
|
ForEach(Self.reasoningEfforts, id: \.self) { effort in
|
|
let isActive = effort == model.session.reasoningEffort
|
|
Button {
|
|
model.setReasoningEffort(effort)
|
|
} label: {
|
|
HStack {
|
|
Text(effort)
|
|
.font(Theme.mono(13))
|
|
.foregroundStyle(Theme.textPrimary)
|
|
Spacer()
|
|
if isActive {
|
|
Image(systemName: "checkmark")
|
|
.font(.caption)
|
|
.foregroundStyle(Theme.mint)
|
|
.accessibilityHidden(true)
|
|
}
|
|
}
|
|
}
|
|
.listRowBackground(Theme.surface)
|
|
.accessibilityLabel("Reasoning effort \(effort)")
|
|
.accessibilityValue(isActive ? "Selected" : "")
|
|
.accessibilityHint("Sets how much the model reasons before answering")
|
|
.accessibilityAddTraits(isActive ? [.isSelected] : [])
|
|
}
|
|
}
|
|
}
|
|
}
|