- Auto mode (per the safety question): wired to Claude's built-in --permission-mode
auto — its own classifier auto-approves safe actions and routes destructive ones
to our approval UI ("all but destructive"). Per-chat toggle (bolt) + a global
"start new chats in Auto" default. Session.auto + RunSpec/ResumeSpec.autoApprove +
DB v3 column; transcript still shows every auto-approved call.
- Model/effort: pickers list full SKUs (claude-opus-4-8, …[1m], sonnet, haiku) and
always show the effective concrete value (never "Default"); Settings adds default
model / default effort / default-auto; new chats inherit them.
- Favorite / archive / delete chats via right-click context menu AND swipe actions;
favorites sort first with a star, archived collapse into a per-project section,
archiving closes the open chat. Session.favorite/archived (DB v3).
- Summary reworked into sectioned, glanceable Markdown (Now / Done / Next) rendered
with MarkdownText; window title shows the project name (app name gone); padding
below project names.
Tests: newChatsInheritDefaults, favoriteArchiveDeleteChat. Full suite 92 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
62 lines
2.6 KiB
Swift
62 lines
2.6 KiB
Swift
import SwiftUI
|
|
import NucleicCore
|
|
|
|
/// App settings: defaults applied to new chats, plus the Apple Intelligence model
|
|
/// used for summaries and auto-generated names.
|
|
struct SettingsView: View {
|
|
@Environment(AppStore.self) private var store
|
|
|
|
@AppStorage(IntelligenceChoice.storageKey) private var choiceRaw = IntelligenceChoice.onDevice.rawValue
|
|
@AppStorage(ModelCatalog.defaultModelKey) private var defaultModel = ModelCatalog.fallbackModel
|
|
@AppStorage(ModelCatalog.defaultEffortKey) private var defaultEffort = ModelCatalog.fallbackEffort
|
|
@AppStorage(ModelCatalog.defaultAutoKey) private var defaultAuto = false
|
|
|
|
private var choice: IntelligenceChoice { IntelligenceChoice(rawValue: choiceRaw) ?? .onDevice }
|
|
|
|
var body: some View {
|
|
Form {
|
|
Section("New chat defaults") {
|
|
Picker("Default model", selection: $defaultModel) {
|
|
ForEach(ModelCatalog.models, id: \.self) { Text($0).tag($0) }
|
|
}
|
|
Picker("Default effort", selection: $defaultEffort) {
|
|
ForEach(ModelCatalog.efforts, id: \.self) { Text($0).tag($0) }
|
|
}
|
|
Toggle("Start new chats in Auto mode", isOn: $defaultAuto)
|
|
Text("Auto mode lets Claude auto-approve safe actions; destructive ones still ask.")
|
|
.font(.caption).foregroundStyle(.secondary)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
}
|
|
|
|
Section("Apple Intelligence") {
|
|
Picker("Model", selection: $choiceRaw) {
|
|
ForEach(IntelligenceChoice.allCases) { Text($0.label).tag($0.rawValue) }
|
|
}
|
|
.pickerStyle(.radioGroup)
|
|
Text(choice.detail)
|
|
.font(.caption).foregroundStyle(.secondary)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
}
|
|
|
|
Section("Status") {
|
|
LabeledContent("On-device model", value: IntelligenceAvailability.status(for: .onDevice))
|
|
LabeledContent("Private Cloud Compute", value: IntelligenceAvailability.status(for: .privateCloudCompute))
|
|
}
|
|
.font(.callout)
|
|
}
|
|
.formStyle(.grouped)
|
|
.frame(width: 480)
|
|
.padding()
|
|
.onAppear { pushDefaults() }
|
|
.onChange(of: defaultModel) { _, _ in pushDefaults() }
|
|
.onChange(of: defaultEffort) { _, _ in pushDefaults() }
|
|
.onChange(of: defaultAuto) { _, _ in pushDefaults() }
|
|
}
|
|
|
|
private func pushDefaults() {
|
|
store.defaultModel = defaultModel
|
|
store.defaultEffort = defaultEffort
|
|
store.defaultAuto = defaultAuto
|
|
}
|
|
}
|