Skip to content

Commit e5ed6fa

Browse files
committed
Adjust settings menu
1 parent 9e985ee commit e5ed6fa

10 files changed

+609
-493
lines changed
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
import Preferences
2+
import SharedUIComponents
3+
import SwiftUI
4+
5+
#if canImport(ProHostApp)
6+
import ProHostApp
7+
#endif
8+
9+
struct ChatSettingsGeneralSectionView: View {
10+
class Settings: ObservableObject {
11+
static let availableLocalizedLocales = Locale.availableLocalizedLocales
12+
@AppStorage(\.chatGPTLanguage) var chatGPTLanguage
13+
@AppStorage(\.chatGPTTemperature) var chatGPTTemperature
14+
@AppStorage(\.chatGPTMaxMessageCount) var chatGPTMaxMessageCount
15+
@AppStorage(\.chatFontSize) var chatFontSize
16+
@AppStorage(\.chatCodeFont) var chatCodeFont
17+
18+
@AppStorage(\.defaultChatFeatureChatModelId) var defaultChatFeatureChatModelId
19+
@AppStorage(\.defaultChatSystemPrompt) var defaultChatSystemPrompt
20+
@AppStorage(\.chatSearchPluginMaxIterations) var chatSearchPluginMaxIterations
21+
@AppStorage(\.defaultChatFeatureEmbeddingModelId) var defaultChatFeatureEmbeddingModelId
22+
@AppStorage(\.chatModels) var chatModels
23+
@AppStorage(\.embeddingModels) var embeddingModels
24+
@AppStorage(\.wrapCodeInChatCodeBlock) var wrapCodeInCodeBlock
25+
@AppStorage(
26+
\.keepFloatOnTopIfChatPanelAndXcodeOverlaps
27+
) var keepFloatOnTopIfChatPanelAndXcodeOverlaps
28+
@AppStorage(
29+
\.disableFloatOnTopWhenTheChatPanelIsDetached
30+
) var disableFloatOnTopWhenTheChatPanelIsDetached
31+
32+
init() {}
33+
}
34+
35+
@Environment(\.openURL) var openURL
36+
@Environment(\.toast) var toast
37+
@StateObject var settings = Settings()
38+
@State var maxTokenOverLimit = false
39+
40+
var body: some View {
41+
VStack {
42+
chatSettingsForm
43+
SettingsDivider("UI")
44+
uiForm
45+
SettingsDivider("Plugin")
46+
pluginForm
47+
}
48+
}
49+
50+
@ViewBuilder
51+
var chatSettingsForm: some View {
52+
Form {
53+
Picker(
54+
"Chat model",
55+
selection: $settings.defaultChatFeatureChatModelId
56+
) {
57+
if !settings.chatModels
58+
.contains(where: { $0.id == settings.defaultChatFeatureChatModelId })
59+
{
60+
Text(
61+
(settings.chatModels.first?.name).map { "\($0) (Default)" }
62+
?? "No model found"
63+
)
64+
.tag(settings.defaultChatFeatureChatModelId)
65+
}
66+
67+
ForEach(settings.chatModels, id: \.id) { chatModel in
68+
Text(chatModel.name).tag(chatModel.id)
69+
}
70+
}
71+
72+
Picker(
73+
"Embedding model",
74+
selection: $settings.defaultChatFeatureEmbeddingModelId
75+
) {
76+
if !settings.embeddingModels
77+
.contains(where: { $0.id == settings.defaultChatFeatureEmbeddingModelId })
78+
{
79+
Text(
80+
(settings.embeddingModels.first?.name).map { "\($0) (Default)" }
81+
?? "No model found"
82+
)
83+
.tag(settings.defaultChatFeatureEmbeddingModelId)
84+
}
85+
86+
ForEach(settings.embeddingModels, id: \.id) { embeddingModel in
87+
Text(embeddingModel.name).tag(embeddingModel.id)
88+
}
89+
}
90+
91+
if #available(macOS 13.0, *) {
92+
LabeledContent("Reply in language") {
93+
languagePicker
94+
}
95+
} else {
96+
HStack {
97+
Text("Reply in language")
98+
languagePicker
99+
}
100+
}
101+
102+
HStack {
103+
Slider(value: $settings.chatGPTTemperature, in: 0...2, step: 0.1) {
104+
Text("Temperature")
105+
}
106+
107+
Text(
108+
"\(settings.chatGPTTemperature.formatted(.number.precision(.fractionLength(1))))"
109+
)
110+
.font(.body)
111+
.foregroundColor(settings.chatGPTTemperature >= 1 ? .red : .secondary)
112+
.monospacedDigit()
113+
.padding(.vertical, 2)
114+
.padding(.horizontal, 6)
115+
.background(
116+
RoundedRectangle(cornerRadius: 4, style: .continuous)
117+
.fill(Color.primary.opacity(0.1))
118+
)
119+
}
120+
121+
Picker(
122+
"Memory",
123+
selection: $settings.chatGPTMaxMessageCount
124+
) {
125+
Text("No Limit").tag(0)
126+
Text("3 Messages").tag(3)
127+
Text("5 Messages").tag(5)
128+
Text("7 Messages").tag(7)
129+
Text("9 Messages").tag(9)
130+
Text("11 Messages").tag(11)
131+
}
132+
133+
VStack(alignment: .leading, spacing: 4) {
134+
Text("Default system prompt")
135+
EditableText(text: $settings.defaultChatSystemPrompt)
136+
.lineLimit(6)
137+
}
138+
.padding(.vertical, 4)
139+
}
140+
}
141+
142+
@ViewBuilder
143+
var uiForm: some View {
144+
Form {
145+
HStack {
146+
TextField(text: .init(get: {
147+
"\(Int(settings.chatFontSize))"
148+
}, set: {
149+
settings.chatFontSize = Double(Int($0) ?? 0)
150+
})) {
151+
Text("Font size of message")
152+
}
153+
.textFieldStyle(.roundedBorder)
154+
155+
Text("pt")
156+
}
157+
158+
FontPicker(font: $settings.chatCodeFont) {
159+
Text("Font of code")
160+
}
161+
162+
Toggle(isOn: $settings.wrapCodeInCodeBlock) {
163+
Text("Wrap text in code block")
164+
}
165+
166+
#if canImport(ProHostApp)
167+
168+
CodeHighlightThemePicker(scenario: .chat)
169+
170+
#endif
171+
172+
Toggle(isOn: $settings.disableFloatOnTopWhenTheChatPanelIsDetached) {
173+
Text("Disable always-on-top when the chat panel is detached")
174+
}
175+
176+
Toggle(isOn: $settings.keepFloatOnTopIfChatPanelAndXcodeOverlaps) {
177+
Text("Keep always-on-top if the chat panel and Xcode overlaps and Xcode is active")
178+
}.disabled(!settings.disableFloatOnTopWhenTheChatPanelIsDetached)
179+
}
180+
}
181+
182+
@ViewBuilder
183+
var pluginForm: some View {
184+
Form {
185+
TextField(text: .init(get: {
186+
"\(Int(settings.chatSearchPluginMaxIterations))"
187+
}, set: {
188+
settings.chatSearchPluginMaxIterations = Int($0) ?? 0
189+
})) {
190+
Text("Search plugin max iterations")
191+
}
192+
.textFieldStyle(.roundedBorder)
193+
}
194+
}
195+
196+
var languagePicker: some View {
197+
Menu {
198+
if !settings.chatGPTLanguage.isEmpty,
199+
!Settings.availableLocalizedLocales
200+
.contains(settings.chatGPTLanguage)
201+
{
202+
Button(
203+
settings.chatGPTLanguage,
204+
action: { self.settings.chatGPTLanguage = settings.chatGPTLanguage }
205+
)
206+
}
207+
Button(
208+
"Auto-detected by LLM",
209+
action: { self.settings.chatGPTLanguage = "" }
210+
)
211+
ForEach(
212+
Settings.availableLocalizedLocales,
213+
id: \.self
214+
) { localizedLocales in
215+
Button(
216+
localizedLocales,
217+
action: { self.settings.chatGPTLanguage = localizedLocales }
218+
)
219+
}
220+
} label: {
221+
Text(
222+
settings.chatGPTLanguage.isEmpty
223+
? "Auto-detected by LLM"
224+
: settings.chatGPTLanguage
225+
)
226+
}
227+
}
228+
}
229+
230+
// MARK: - Preview
231+
232+
//
233+
// #Preview {
234+
// ScrollView {
235+
// ChatSettingsView()
236+
// .padding()
237+
// }
238+
// .frame(height: 800)
239+
// .environment(\.overrideFeatureFlag, \.never)
240+
// }
241+
//
242+

0 commit comments

Comments
 (0)