Skip to content

Commit 7301c75

Browse files
committed
Migrate suggestion feature settings to HostApp
1 parent 36b0c57 commit 7301c75

5 files changed

Lines changed: 329 additions & 22 deletions

File tree

Core/Sources/HostApp/AccountSettings/OpenAIView.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,14 @@ struct OpenAIView: View {
112112
Text(
113113
"\(settings.chatGPTTemperature.formatted(.number.precision(.fractionLength(1))))"
114114
)
115+
.font(.body)
115116
.monospacedDigit()
117+
.padding(.vertical, 2)
118+
.padding(.horizontal, 6)
119+
.background(
120+
RoundedRectangle(cornerRadius: 4, style: .continuous)
121+
.fill(Color.primary.opacity(0.1))
122+
)
116123
}
117124

118125
Picker(
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
import Preferences
2+
import SwiftUI
3+
4+
struct SuggestionSettingsView: View {
5+
final class Settings: ObservableObject {
6+
@AppStorage(\.realtimeSuggestionToggle)
7+
var realtimeSuggestionToggle
8+
@AppStorage(\.realtimeSuggestionDebounce)
9+
var realtimeSuggestionDebounce
10+
@AppStorage(\.suggestionPresentationMode)
11+
var suggestionPresentationMode
12+
@AppStorage(\.acceptSuggestionWithAccessibilityAPI)
13+
var acceptSuggestionWithAccessibilityAPI
14+
@AppStorage(\.disableSuggestionFeatureGlobally)
15+
var disableSuggestionFeatureGlobally
16+
@AppStorage(\.suggestionFeatureEnabledProjectList)
17+
var suggestionFeatureEnabledProjectList
18+
@AppStorage(\.hideCommonPrecedingSpacesInSuggestion)
19+
var hideCommonPrecedingSpacesInSuggestion
20+
@AppStorage(\.suggestionCodeFontSize)
21+
var suggestionCodeFontSize
22+
@AppStorage(\.suggestionFeatureProvider)
23+
var suggestionFeatureProvider
24+
init() {}
25+
}
26+
27+
@StateObject var settings = Settings()
28+
@State var isSuggestionFeatureEnabledListPickerOpen = false
29+
30+
var body: some View {
31+
Form {
32+
Group {
33+
Picker(selection: $settings.suggestionPresentationMode) {
34+
ForEach(PresentationMode.allCases, id: \.rawValue) {
35+
switch $0 {
36+
case .comment:
37+
Text("Comment (Deprecating Soon)").tag($0)
38+
case .floatingWidget:
39+
Text("Floating Widget").tag($0)
40+
}
41+
}
42+
} label: {
43+
Text("Presentation")
44+
}
45+
46+
Picker(selection: $settings.suggestionFeatureProvider) {
47+
ForEach(SuggestionFeatureProvider.allCases, id: \.rawValue) {
48+
switch $0 {
49+
case .gitHubCopilot:
50+
Text("GitHub Copilot").tag($0)
51+
case .codeium:
52+
Text("Codeium").tag($0)
53+
}
54+
}
55+
} label: {
56+
Text("Feature Provider")
57+
}
58+
59+
Toggle(isOn: $settings.realtimeSuggestionToggle) {
60+
Text("Real-time suggestion")
61+
}
62+
63+
HStack {
64+
Toggle(isOn: $settings.disableSuggestionFeatureGlobally) {
65+
Text("Disable Suggestion Feature Globally")
66+
}
67+
68+
Button("Exception List") {
69+
isSuggestionFeatureEnabledListPickerOpen = true
70+
}
71+
}.sheet(isPresented: $isSuggestionFeatureEnabledListPickerOpen) {
72+
SuggestionFeatureEnabledProjectListView(
73+
isOpen: $isSuggestionFeatureEnabledListPickerOpen
74+
)
75+
}
76+
77+
Toggle(isOn: $settings.hideCommonPrecedingSpacesInSuggestion) {
78+
Text("Hide Common Preceding Spaces")
79+
}
80+
HStack {
81+
Slider(value: $settings.realtimeSuggestionDebounce, in: 0...2, step: 0.1) {
82+
Text("Real-time Suggestion Debounce")
83+
}
84+
85+
Text(
86+
"\(settings.realtimeSuggestionDebounce.formatted(.number.precision(.fractionLength(2))))s"
87+
)
88+
.font(.body)
89+
.monospacedDigit()
90+
.padding(.vertical, 2)
91+
.padding(.horizontal, 6)
92+
.background(
93+
RoundedRectangle(cornerRadius: 4, style: .continuous)
94+
.fill(Color.primary.opacity(0.1))
95+
)
96+
}
97+
98+
Divider()
99+
}
100+
101+
Group {
102+
HStack {
103+
TextField(text: .init(get: {
104+
"\(Int(settings.suggestionCodeFontSize))"
105+
}, set: {
106+
settings.suggestionCodeFontSize = Double(Int($0) ?? 0)
107+
})) {
108+
Text("Font size of suggestion code")
109+
}
110+
.textFieldStyle(.roundedBorder)
111+
112+
Text("pt")
113+
}
114+
Divider()
115+
}
116+
117+
Group {
118+
Toggle(isOn: $settings.acceptSuggestionWithAccessibilityAPI) {
119+
Text("Use accessibility API to accept suggestion in widget")
120+
}
121+
122+
Text("You can turn it on if the accept button is not working for you.")
123+
.font(.caption)
124+
.foregroundStyle(.secondary)
125+
}
126+
}
127+
}
128+
}
129+
130+
struct SuggestionFeatureEnabledProjectListView: View {
131+
final class Settings: ObservableObject {
132+
@AppStorage(\.suggestionFeatureEnabledProjectList)
133+
var suggestionFeatureEnabledProjectList: [String]
134+
135+
init(suggestionFeatureEnabledProjectList: AppStorage<[String]>? = nil) {
136+
if let list = suggestionFeatureEnabledProjectList {
137+
_suggestionFeatureEnabledProjectList = list
138+
}
139+
}
140+
}
141+
142+
var isOpen: Binding<Bool>
143+
@State var isAddingNewProject = false
144+
@StateObject var settings = Settings()
145+
146+
var body: some View {
147+
VStack(spacing: 0) {
148+
HStack {
149+
Button(action: {
150+
self.isOpen.wrappedValue = false
151+
}) {
152+
Image(systemName: "xmark.circle.fill")
153+
.foregroundStyle(.secondary)
154+
.padding()
155+
}
156+
.buttonStyle(.plain)
157+
Text("Enabled Projects")
158+
Spacer()
159+
Button(action: {
160+
isAddingNewProject = true
161+
}) {
162+
Image(systemName: "plus.circle.fill")
163+
.foregroundStyle(.secondary)
164+
.padding()
165+
}
166+
.buttonStyle(.plain)
167+
}
168+
.background(Color(nsColor: .separatorColor))
169+
170+
List {
171+
ForEach(
172+
settings.suggestionFeatureEnabledProjectList,
173+
id: \.self
174+
) { project in
175+
HStack {
176+
Text(project)
177+
.contextMenu {
178+
Button("Remove") {
179+
settings.suggestionFeatureEnabledProjectList.removeAll(
180+
where: { $0 == project }
181+
)
182+
}
183+
}
184+
Spacer()
185+
186+
Button(action: {
187+
settings.suggestionFeatureEnabledProjectList.removeAll(
188+
where: { $0 == project }
189+
)
190+
}) {
191+
Image(systemName: "trash.fill")
192+
.foregroundStyle(.secondary)
193+
}
194+
.buttonStyle(.plain)
195+
}
196+
}
197+
}
198+
.removeBackground()
199+
.overlay {
200+
if settings.suggestionFeatureEnabledProjectList.isEmpty {
201+
Text("""
202+
Empty
203+
Add project with "+" button
204+
Or right clicking the circular widget
205+
""")
206+
.multilineTextAlignment(.center)
207+
}
208+
}
209+
}
210+
.frame(width: 300, height: 400)
211+
.sheet(isPresented: $isAddingNewProject) {
212+
SuggestionFeatureAddEnabledProjectView(isOpen: $isAddingNewProject, settings: settings)
213+
}
214+
}
215+
}
216+
217+
struct SuggestionFeatureAddEnabledProjectView: View {
218+
var isOpen: Binding<Bool>
219+
var settings: SuggestionFeatureEnabledProjectListView.Settings
220+
@State var rootPath = ""
221+
222+
var body: some View {
223+
VStack {
224+
Text(
225+
"Enter the root path of the project. Do not use `~` to replace /Users/yourUserName."
226+
)
227+
TextField("Root path", text: $rootPath)
228+
HStack {
229+
Spacer()
230+
Button("Cancel") {
231+
isOpen.wrappedValue = false
232+
}
233+
Button("Add") {
234+
settings.suggestionFeatureEnabledProjectList.append(rootPath)
235+
isOpen.wrappedValue = false
236+
}
237+
}
238+
}
239+
.padding()
240+
.frame(minWidth: 500)
241+
}
242+
}
243+
244+
struct SuggestionSettingsView_Previews: PreviewProvider {
245+
static var previews: some View {
246+
SuggestionSettingsView()
247+
}
248+
}
249+
250+
struct SuggestionFeatureEnabledProjectListView_Preview: PreviewProvider {
251+
static var previews: some View {
252+
SuggestionFeatureEnabledProjectListView(
253+
isOpen: .constant(true),
254+
settings: .init(suggestionFeatureEnabledProjectList: .init(wrappedValue: [
255+
"hello/2",
256+
"hello/3",
257+
"hello/4",
258+
], "SuggestionFeatureEnabledProjectListView_Preview"))
259+
)
260+
}
261+
}
262+
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import SwiftUI
2+
3+
struct FeatureSettingsView: View {
4+
@State var tag = 0
5+
6+
var body: some View {
7+
SidebarTabView(tag: $tag) { tag in
8+
ScrollView {
9+
SuggestionSettingsView()
10+
}
11+
.padding()
12+
.sidebarItem(
13+
tag: 0,
14+
currentTag: tag,
15+
title: "Suggestion",
16+
subtitle: "Generate suggestions for your code",
17+
image: "lightbulb.circle.fill"
18+
)
19+
20+
ScrollView {
21+
SuggestionSettingsView()
22+
}
23+
.padding()
24+
.sidebarItem(
25+
tag: 1,
26+
currentTag: tag,
27+
title: "Chat",
28+
subtitle: "Chat about your code",
29+
image: "bubble.right.circle.fill"
30+
)
31+
32+
ScrollView {
33+
SuggestionSettingsView()
34+
}
35+
.padding()
36+
.sidebarItem(
37+
tag: 2,
38+
currentTag: tag,
39+
title: "Prompt to Code",
40+
subtitle: "Write code with natural language",
41+
image: "square.and.pencil.circle.fill"
42+
)
43+
}
44+
}
45+
}
46+
47+
struct FeatureSettingsView_Previews: PreviewProvider {
48+
static var previews: some View {
49+
FeatureSettingsView()
50+
.frame(width: 800)
51+
}
52+
}
53+

Core/Sources/HostApp/SidebarTabView.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,18 @@ struct SidebarTabView<Content: View>: View {
5050
Image(systemName: image)
5151
.resizable()
5252
.scaledToFit()
53-
.frame(height: 20)
53+
.frame(width: 20, height: 20)
5454
}
55-
VStack(alignment: .leading) {
55+
VStack(alignment: .leading, spacing: 2) {
5656
Text(item.title)
5757
.foregroundStyle(.primary)
5858
if let subtitle = item.subtitle {
5959
Text(subtitle)
60+
.lineSpacing(0)
6061
.font(.caption)
6162
.foregroundStyle(.tertiary)
6263
.opacity(0.5)
64+
.multilineTextAlignment(.leading)
6365
}
6466
}
6567
}
@@ -99,7 +101,7 @@ struct SidebarTabView_Previews: PreviewProvider {
99101
tag: 0,
100102
currentTag: tag,
101103
title: "Hello",
102-
subtitle: "Meow",
104+
subtitle: "Meow\nMeow",
103105
image: "person.circle.fill"
104106
)
105107
Color.blue.sidebarItem(

0 commit comments

Comments
 (0)