11import Preferences
2+ import SharedUIComponents
23import SwiftUI
34
5+ #if canImport(ProHostApp)
6+ import ProHostApp
7+ #endif
8+
49struct ChatSettingsView : View {
510 class Settings : ObservableObject {
611 static let availableLocalizedLocales = Locale . availableLocalizedLocales
@@ -11,8 +16,6 @@ struct ChatSettingsView: View {
1116 @AppStorage ( \. chatCodeFontSize) var chatCodeFontSize
1217 @AppStorage ( \. maxFocusedCodeLineCount)
1318 var maxFocusedCodeLineCount
14- @AppStorage ( \. useCodeScopeByDefaultInChatContext)
15- var useCodeScopeByDefaultInChatContext
1619 @AppStorage ( \. defaultChatFeatureChatModelId) var defaultChatFeatureChatModelId
1720 @AppStorage ( \. defaultChatSystemPrompt) var defaultChatSystemPrompt
1821 @AppStorage ( \. chatSearchPluginMaxIterations) var chatSearchPluginMaxIterations
@@ -32,12 +35,13 @@ struct ChatSettingsView: View {
3235 var body : some View {
3336 VStack {
3437 chatSettingsForm
35- Divider ( )
38+ SettingsDivider ( " UI " )
3639 uiForm
37- Divider ( )
40+ SettingsDivider ( " Context " )
3841 contextForm
39- Divider ( )
42+ SettingsDivider ( " Plugin " )
4043 pluginForm
44+ ScopeForm ( )
4145 }
4246 }
4347
@@ -160,7 +164,7 @@ struct ChatSettingsView: View {
160164
161165 Text ( " pt " )
162166 }
163-
167+
164168 Toggle ( isOn: $settings. wrapCodeInCodeBlock) {
165169 Text ( " Wrap code in code block " )
166170 }
@@ -170,10 +174,6 @@ struct ChatSettingsView: View {
170174 @ViewBuilder
171175 var contextForm : some View {
172176 Form {
173- Toggle ( isOn: $settings. useCodeScopeByDefaultInChatContext) {
174- Text ( " Use @code scope by default in chat context. " )
175- }
176-
177177 HStack {
178178 TextField ( text: . init( get: {
179179 " \( Int ( settings. maxFocusedCodeLineCount) ) "
@@ -235,13 +235,206 @@ struct ChatSettingsView: View {
235235 )
236236 }
237237 }
238+
239+ struct ScopeForm : View {
240+ class Settings : ObservableObject {
241+ @AppStorage ( \. enableFileScopeByDefaultInChatContext)
242+ var enableFileScopeByDefaultInChatContext : Bool
243+ @AppStorage ( \. enableCodeScopeByDefaultInChatContext)
244+ var enableCodeScopeByDefaultInChatContext : Bool
245+ @AppStorage ( \. enableSenseScopeByDefaultInChatContext)
246+ var enableSenseScopeByDefaultInChatContext : Bool
247+ @AppStorage ( \. enableProjectScopeByDefaultInChatContext)
248+ var enableProjectScopeByDefaultInChatContext : Bool
249+ @AppStorage ( \. enableWebScopeByDefaultInChatContext)
250+ var enableWebScopeByDefaultInChatContext : Bool
251+ @AppStorage ( \. preferredChatModelIdForSenseScope)
252+ var preferredChatModelIdForSenseScope : String
253+ @AppStorage ( \. preferredChatModelIdForProjectScope)
254+ var preferredChatModelIdForProjectScope : String
255+ @AppStorage ( \. preferredChatModelIdForWebScope)
256+ var preferredChatModelIdForWebScope : String
257+ @AppStorage ( \. chatModels) var chatModels
258+
259+ init ( ) { }
260+ }
261+
262+ @StateObject var settings = Settings ( )
263+
264+ var body : some View {
265+ VStack {
266+ Scope (
267+ title: Text ( " File Scope " ) ,
268+ description: " Enable the bot to read the metadata of the editing file. "
269+ ) {
270+ Form {
271+ Toggle ( isOn: $settings. enableFileScopeByDefaultInChatContext) {
272+ Text ( " Enable @file scope by default in chat context. " )
273+ }
274+ }
275+ }
276+
277+ Scope (
278+ title: Text ( " Code Scope " ) ,
279+ description: " Enable the bot to read the code and metadata in the editing file. "
280+ ) {
281+ Form {
282+ Toggle ( isOn: $settings. enableCodeScopeByDefaultInChatContext) {
283+ Text ( " Enable @code scope by default in chat context. " )
284+ }
285+ }
286+ }
287+
288+ #if canImport(ProHostApp)
289+
290+ Scope (
291+ title: WithFeatureEnabled ( \. projectScopeInChat) { Text ( " Sense Scope " ) } ,
292+ description: " Experimental. Enable the bot to read the relevant code of the editing file in the project, third party packages and the SDK. "
293+ ) {
294+ WithFeatureEnabled ( \. projectScopeInChat, alignment: . hidden) {
295+ Form {
296+ Toggle ( isOn: $settings. enableSenseScopeByDefaultInChatContext) {
297+ Text ( " Enable @sense scope by default in chat context. " )
298+ }
299+
300+ Picker (
301+ " Preferred Chat Model " ,
302+ selection: $settings. preferredChatModelIdForSenseScope
303+ ) {
304+ Text ( " None " ) . tag ( " " )
305+
306+ if !settings. chatModels
307+ . contains ( where: {
308+ $0. id == settings. preferredChatModelIdForSenseScope
309+ } ) ,
310+ !settings. preferredChatModelIdForSenseScope. isEmpty
311+ {
312+ Text (
313+ ( settings. chatModels. first? . name) . map { " \( $0) (Default) " }
314+ ?? " No Model Found "
315+ )
316+ . tag ( settings. preferredChatModelIdForSenseScope)
317+ }
318+
319+ ForEach ( settings. chatModels, id: \. id) { chatModel in
320+ Text ( chatModel. name) . tag ( chatModel. id)
321+ }
322+ }
323+ }
324+ }
325+ . padding ( . top, 20 )
326+ }
327+
328+ Scope (
329+ title: WithFeatureEnabled ( \. projectScopeInChat) { Text ( " Project Scope " ) } ,
330+ description: " Experimental. Enable the bot to search code symbols in the project, third party packages and the SDK. "
331+ ) {
332+ WithFeatureEnabled ( \. projectScopeInChat, alignment: . hidden) {
333+ Form {
334+ Toggle ( isOn: $settings. enableProjectScopeByDefaultInChatContext) {
335+ Text ( " Enable @project scope by default in chat context. " )
336+ }
337+
338+ Picker (
339+ " Preferred Chat Model " ,
340+ selection: $settings. preferredChatModelIdForProjectScope
341+ ) {
342+ Text ( " None " ) . tag ( " " )
343+
344+ if !settings. chatModels
345+ . contains ( where: {
346+ $0. id == settings. preferredChatModelIdForProjectScope
347+ } ) ,
348+ !settings. preferredChatModelIdForProjectScope. isEmpty
349+ {
350+ Text (
351+ ( settings. chatModels. first? . name) . map { " \( $0) (Default) " }
352+ ?? " No Model Found "
353+ )
354+ . tag ( settings. preferredChatModelIdForProjectScope)
355+ }
356+
357+ ForEach ( settings. chatModels, id: \. id) { chatModel in
358+ Text ( chatModel. name) . tag ( chatModel. id)
359+ }
360+ }
361+ }
362+ }
363+ }
364+
365+ #endif
366+
367+ Scope (
368+ title: Text ( " Web Scope " ) ,
369+ description: " Allow the bot to search on Bing or read a web page. "
370+ ) {
371+ Form {
372+ Toggle ( isOn: $settings. enableWebScopeByDefaultInChatContext) {
373+ Text ( " Enable @web scope by default in chat context. " )
374+ }
375+
376+ Picker (
377+ " Preferred Chat Model " ,
378+ selection: $settings. preferredChatModelIdForWebScope
379+ ) {
380+ Text ( " None " ) . tag ( " " )
381+
382+ if !settings. chatModels
383+ . contains ( where: {
384+ $0. id == settings. preferredChatModelIdForWebScope
385+ } ) ,
386+ !settings. preferredChatModelIdForWebScope. isEmpty
387+ {
388+ Text (
389+ ( settings. chatModels. first? . name) . map { " \( $0) (Default) " }
390+ ?? " No Model Found "
391+ )
392+ . tag ( settings. preferredChatModelIdForWebScope)
393+ }
394+
395+ ForEach ( settings. chatModels, id: \. id) { chatModel in
396+ Text ( chatModel. name) . tag ( chatModel. id)
397+ }
398+ }
399+ }
400+ }
401+ }
402+ }
403+
404+ struct Scope < Title: View , Content: View > : View {
405+ let title : Title
406+ let description : String
407+ let content : ( ) -> Content
408+
409+ var body : some View {
410+ SettingsDivider ( title)
411+ VStack {
412+ Text ( description)
413+ . multilineTextAlignment ( . center)
414+ . foregroundStyle ( . secondary)
415+ . padding ( 8 )
416+ . background {
417+ RoundedRectangle ( cornerRadius: 8 )
418+ . fill ( Color . secondary. opacity ( 0.1 ) )
419+ }
420+ . frame ( maxWidth: 500 )
421+ Form {
422+ content ( )
423+ }
424+ }
425+ }
426+ }
427+ }
238428}
239429
240430// MARK: - Preview
241431
242- struct ChatSettingsView_Previews : PreviewProvider {
243- static var previews : some View {
432+ #Preview {
433+ ScrollView {
244434 ChatSettingsView ( )
435+ . padding ( )
245436 }
437+ . frame ( height: 800 )
438+ . environment ( \. overrideFeatureFlag, \. never)
246439}
247440
0 commit comments