Files
nucleic/Sources/NucleicApp/SettingsView.swift
T
abkslmandClaude Opus 4.8 99b5bdaa8e Auto mode, model SKUs + defaults, favorite/archive/delete, summary/title/padding
- 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]>
2026-06-12 14:38:06 -07:00

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
}
}