Skip to content

Commit 7e982b7

Browse files
committed
Merge branch 'feature/host-app-settings-for-openai' into develop
2 parents bb8bc40 + d403314 commit 7e982b7

22 files changed

Lines changed: 574 additions & 133 deletions

Copilot for Xcode.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
C882175A294187E100A22FD3 /* Client in Frameworks */ = {isa = PBXBuildFile; productRef = C8821759294187E100A22FD3 /* Client */; };
4747
C882175C294187EF00A22FD3 /* Client in Frameworks */ = {isa = PBXBuildFile; productRef = C882175B294187EF00A22FD3 /* Client */; };
4848
C8C8B60929AFA35F00034BEE /* CopilotForXcodeExtensionService.app in Embed XPCService */ = {isa = PBXBuildFile; fileRef = C861E60E2994F6070056CB02 /* CopilotForXcodeExtensionService.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
49+
C8EE079D29CC21300043B6D9 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EE079C29CC21300043B6D9 /* AccountView.swift */; };
50+
C8EE079F29CC25C20043B6D9 /* OpenAIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EE079E29CC25C20043B6D9 /* OpenAIView.swift */; };
4951
/* End PBXBuildFile section */
5052

5153
/* Begin PBXContainerItemProxy section */
@@ -188,6 +190,8 @@
188190
C87F3E61293DD004008523E8 /* Styles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Styles.swift; sourceTree = "<group>"; };
189191
C887BC832965D96000931567 /* DEVELOPMENT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = DEVELOPMENT.md; sourceTree = "<group>"; };
190192
C8CD828229B88006008D044D /* TestPlan.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = TestPlan.xctestplan; sourceTree = "<group>"; };
193+
C8EE079C29CC21300043B6D9 /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = "<group>"; };
194+
C8EE079E29CC25C20043B6D9 /* OpenAIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAIView.swift; sourceTree = "<group>"; };
191195
/* End PBXFileReference section */
192196

193197
/* Begin PBXFrameworksBuildPhase section */
@@ -298,6 +302,8 @@
298302
C83B2B81293DC38400C5ACCD /* LaunchAgentView.swift */,
299303
C83B2B7B293D9FB400C5ACCD /* CopilotView.swift */,
300304
C83B2B7D293DA0CA00C5ACCD /* AppInfoView.swift */,
305+
C8EE079C29CC21300043B6D9 /* AccountView.swift */,
306+
C8EE079E29CC25C20043B6D9 /* OpenAIView.swift */,
301307
C83B2B7F293DA1B600C5ACCD /* InstructionView.swift */,
302308
C841BB232994CAD400B0B336 /* SettingsView.swift */,
303309
C87F3E61293DD004008523E8 /* Styles.swift */,
@@ -531,7 +537,9 @@
531537
C87F3E60293DC600008523E8 /* Section.swift in Sources */,
532538
C87F3E62293DD004008523E8 /* Styles.swift in Sources */,
533539
C83B2B82293DC38400C5ACCD /* LaunchAgentView.swift in Sources */,
540+
C8EE079D29CC21300043B6D9 /* AccountView.swift in Sources */,
534541
C83B2B7E293DA0CA00C5ACCD /* AppInfoView.swift in Sources */,
542+
C8EE079F29CC25C20043B6D9 /* OpenAIView.swift in Sources */,
535543
C841BB242994CAD400B0B336 /* SettingsView.swift in Sources */,
536544
C83B2B80293DA1B600C5ACCD /* InstructionView.swift in Sources */,
537545
C83B2B7C293D9FB400C5ACCD /* CopilotView.swift in Sources */,
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import AppKit
2+
import Client
3+
import CopilotModel
4+
import SwiftUI
5+
6+
struct AccountView: View {
7+
enum Location {
8+
case account
9+
case gitHubCopilot
10+
case openAI
11+
}
12+
13+
@State var location: Location = .account
14+
15+
func navigate(to: Location) {
16+
withAnimation(.easeInOut(duration: 0.2)) {
17+
location = to
18+
}
19+
}
20+
21+
var body: some View {
22+
Section {
23+
switch location {
24+
case .account:
25+
VStack(alignment: .leading, spacing: 8) {
26+
Text("Accounts")
27+
.font(.title)
28+
.padding(.bottom, 12)
29+
30+
HStack {
31+
Button {
32+
navigate(to: .gitHubCopilot)
33+
} label: {
34+
Text("GitHub Copilot")
35+
}.buttonStyle(CopilotButtonStyle())
36+
37+
Button {
38+
navigate(to: .openAI)
39+
} label: {
40+
Text("OpenAI")
41+
}.buttonStyle(CopilotButtonStyle())
42+
43+
Spacer()
44+
}
45+
}
46+
case .gitHubCopilot:
47+
ChildPage(content: {
48+
CopilotView()
49+
}, onBackButtonClick: {
50+
navigate(to: .account)
51+
})
52+
case .openAI:
53+
ChildPage(content: {
54+
OpenAIView()
55+
}, onBackButtonClick: {
56+
navigate(to: .account)
57+
})
58+
}
59+
}
60+
}
61+
62+
struct ChildPage<V: View>: View {
63+
var content: () -> V
64+
var onBackButtonClick: () -> Void
65+
var body: some View {
66+
VStack(alignment: .leading) {
67+
Button(action: onBackButtonClick) {
68+
HStack {
69+
Image(systemName: "chevron.left")
70+
Text("Accounts")
71+
}
72+
.font(.title3)
73+
}
74+
.buttonStyle(.plain)
75+
content()
76+
}
77+
}
78+
}
79+
}
80+
81+
struct AccountView_Previews: PreviewProvider {
82+
static var previews: some View {
83+
VStack(alignment: .leading, spacing: 8) {
84+
AccountView()
85+
86+
AccountView(location: .gitHubCopilot)
87+
88+
AccountView(location: .openAI)
89+
}
90+
.frame(height: 800)
91+
.padding(.all, 8)
92+
.background(Color.black)
93+
}
94+
}

Copilot for Xcode/ContentView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ struct ContentView: View {
1414
VStack(alignment: .leading, spacing: 8) {
1515
AppInfoView()
1616
LaunchAgentView()
17-
CopilotView()
17+
AccountView()
1818
SettingsView()
1919
InstructionView()
2020
Spacer()

Copilot for Xcode/LaunchAgentView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import LaunchAgentManager
22
import SwiftUI
3-
import XPCShared
3+
import Preferences
44

55
struct LaunchAgentView: View {
66
@State var errorMessage: String?
77
@State var isDidRemoveLaunchAgentAlertPresented = false
88
@State var isDidSetupLaunchAgentAlertPresented = false
99
@State var isDidRestartLaunchAgentAlertPresented = false
10-
@AppStorage(SettingsKey.nodePath, store: .shared) var nodePath: String = ""
10+
@AppStorage(\.nodePath) var nodePath: String
1111

1212
var body: some View {
1313
Section {

Copilot for Xcode/OpenAIView.swift

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import AppKit
2+
import Client
3+
import CopilotModel
4+
import Preferences
5+
import SwiftUI
6+
7+
final class OpenAIViewSettings: ObservableObject {
8+
@AppStorage(\.openAIAPIKey) var openAIAPIKey: String
9+
@AppStorage(\.chatGPTModel) var chatGPTModel: String
10+
@AppStorage(\.chatGPTEndpoint) var chatGPTEndpoint: String
11+
init() {}
12+
}
13+
14+
struct OpenAIView: View {
15+
let apiKeyURL = URL(string: "https://platform.openai.com/account/api-keys")!
16+
let modelURL = URL(
17+
string: "https://platform.openai.com/docs/models/model-endpoint-compatibility"
18+
)!
19+
@Environment(\.openURL) var openURL
20+
@StateObject var settings = OpenAIViewSettings()
21+
22+
var body: some View {
23+
Section {
24+
VStack(alignment: .leading, spacing: 8) {
25+
Text("OpenAI")
26+
.font(.title)
27+
.padding(.bottom, 12)
28+
29+
Form {
30+
HStack {
31+
Text("OpenAI API Key")
32+
TextField(text: $settings.openAIAPIKey, prompt: Text("sk-*")) {
33+
EmptyView()
34+
}.textFieldStyle(.copilot)
35+
Button(action: {
36+
openURL(apiKeyURL)
37+
}) {
38+
Image(systemName: "questionmark.circle.fill")
39+
}
40+
.buttonStyle(.plain)
41+
}
42+
43+
HStack {
44+
Text("ChatGPT Model")
45+
TextField(text: $settings.chatGPTModel, prompt: Text("gpt-3.5-turbo")) {
46+
EmptyView()
47+
}.textFieldStyle(.copilot)
48+
49+
Button(action: {
50+
openURL(modelURL)
51+
}) {
52+
Image(systemName: "questionmark.circle.fill")
53+
}
54+
.buttonStyle(.plain)
55+
}
56+
57+
HStack {
58+
Text("ChatGPT Endpoint")
59+
TextField(
60+
text: $settings.chatGPTEndpoint,
61+
prompt: Text("https://api.openai.com/v1/chat/completions")
62+
) {
63+
EmptyView()
64+
}.textFieldStyle(.copilot)
65+
}
66+
}
67+
}
68+
}
69+
}
70+
}
71+
72+
struct OpenAIView_Previews: PreviewProvider {
73+
static var previews: some View {
74+
VStack(alignment: .leading, spacing: 8) {
75+
OpenAIView()
76+
}
77+
.frame(height: 800)
78+
.padding(.all, 8)
79+
.background(Color.black)
80+
}
81+
}

Copilot for Xcode/SettingsView.swift

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
import LaunchAgentManager
2+
import Preferences
23
import SwiftUI
3-
import XPCShared
44

55
final class Settings: ObservableObject {
6-
@AppStorage(SettingsKey.quitXPCServiceOnXcodeAndAppQuit, store: .shared)
7-
var quitXPCServiceOnXcodeAndAppQuit: Bool = false
8-
@AppStorage(SettingsKey.realtimeSuggestionToggle, store: .shared)
9-
var realtimeSuggestionToggle: Bool = false
10-
@AppStorage(SettingsKey.realtimeSuggestionDebounce, store: .shared)
11-
var realtimeSuggestionDebounce: Double = 0.7
12-
@AppStorage(SettingsKey.suggestionPresentationMode, store: .shared)
13-
var suggestionPresentationModeRawValue: Int = 0
14-
@AppStorage(SettingsKey.automaticallyCheckForUpdate, store: .shared)
15-
var automaticallyCheckForUpdate: Bool = false
16-
@AppStorage(SettingsKey.suggestionWidgetPositionMode, store: .shared)
17-
var suggestionWidgetPositionModeRawValue: Int = 0
18-
@AppStorage(SettingsKey.widgetColorScheme, store: .shared)
19-
var widgetColorScheme: Int = 0
6+
@AppStorage(\.quitXPCServiceOnXcodeAndAppQuit)
7+
var quitXPCServiceOnXcodeAndAppQuit: Bool
8+
@AppStorage(\.realtimeSuggestionToggle)
9+
var realtimeSuggestionToggle: Bool
10+
@AppStorage(\.realtimeSuggestionDebounce)
11+
var realtimeSuggestionDebounce: Double
12+
@AppStorage(\.suggestionPresentationMode)
13+
var suggestionPresentationMode: Preferences.PresentationMode
14+
@AppStorage(\.automaticallyCheckForUpdate)
15+
var automaticallyCheckForUpdate: Bool
16+
@AppStorage(\.suggestionWidgetPositionMode)
17+
var suggestionWidgetPositionMode: SuggestionWidgetPositionMode
18+
@AppStorage(\.widgetColorScheme)
19+
var widgetColorScheme: WidgetColorScheme
2020
init() {}
2121
}
2222

2323
struct SettingsView: View {
2424
@StateObject var settings = Settings()
2525
@State var editingRealtimeSuggestionDebounce: Double = UserDefaults.shared
26-
.value(forKey: SettingsKey.realtimeSuggestionDebounce) as? Double ?? 0.7
26+
.value(for: \.realtimeSuggestionDebounce)
2727

2828
var body: some View {
2929
Section {
@@ -38,44 +38,42 @@ struct SettingsView: View {
3838
}
3939
.toggleStyle(.switch)
4040

41-
Picker(selection: $settings.suggestionPresentationModeRawValue) {
41+
Picker(selection: $settings.suggestionPresentationMode) {
4242
ForEach(PresentationMode.allCases, id: \.rawValue) {
4343
switch $0 {
4444
case .comment:
45-
Text("Comment")
45+
Text("Comment").tag($0)
4646
case .floatingWidget:
47-
Text("Floating Widget")
47+
Text("Floating Widget").tag($0)
4848
}
4949
}
5050
} label: {
5151
Text("Present suggestions in")
5252
}
5353

54-
if settings.suggestionPresentationModeRawValue == PresentationMode.floatingWidget
55-
.rawValue
56-
{
57-
Picker(selection: $settings.suggestionWidgetPositionModeRawValue) {
54+
if settings.suggestionPresentationMode == PresentationMode.floatingWidget {
55+
Picker(selection: $settings.suggestionWidgetPositionMode) {
5856
ForEach(SuggestionWidgetPositionMode.allCases, id: \.rawValue) {
5957
switch $0 {
6058
case .fixedToBottom:
61-
Text("Fixed to Bottom")
59+
Text("Fixed to Bottom").tag($0)
6260
case .alignToTextCursor:
63-
Text("Follow Text Cursor")
61+
Text("Follow Text Cursor").tag($0)
6462
}
6563
}
6664
} label: {
6765
Text("Widget position")
6866
}
69-
67+
7068
Picker(selection: $settings.widgetColorScheme) {
7169
ForEach(WidgetColorScheme.allCases, id: \.rawValue) {
7270
switch $0 {
7371
case .system:
74-
Text("System")
72+
Text("System").tag($0)
7573
case .light:
76-
Text("Light")
74+
Text("Light").tag($0)
7775
case .dark:
78-
Text("Dark")
76+
Text("Dark").tag($0)
7977
}
8078
}
8179
} label: {

Core/Package.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ let package = Package(
2424
"CopilotModel",
2525
"Client",
2626
"XPCShared",
27+
"Preferences",
2728
"LaunchAgentManager",
2829
"Logger",
2930
]
@@ -40,7 +41,7 @@ let package = Package(
4041
.target(name: "CGEventObserver"),
4142
.target(
4243
name: "CopilotService",
43-
dependencies: ["LanguageClient", "CopilotModel", "XPCShared"]
44+
dependencies: ["LanguageClient", "CopilotModel", "XPCShared", "Preferences"]
4445
),
4546
.testTarget(
4647
name: "CopilotServiceTests",
@@ -64,13 +65,14 @@ let package = Package(
6465
),
6566
.target(
6667
name: "Client",
67-
dependencies: ["CopilotModel", "XPCShared", "Logger"]
68+
dependencies: ["CopilotModel", "Preferences", "XPCShared", "Logger"]
6869
),
6970
.target(
7071
name: "Service",
7172
dependencies: [
7273
"CopilotModel",
7374
"CopilotService",
75+
"Preferences",
7476
"XPCShared",
7577
"CGEventObserver",
7678
"DisplayLink",
@@ -94,6 +96,7 @@ let package = Package(
9496
"Client",
9597
"CopilotService",
9698
"SuggestionInjector",
99+
"Preferences",
97100
"XPCShared",
98101
"Environment",
99102
]
@@ -131,5 +134,6 @@ let package = Package(
131134
name: "OpenAIServiceTests",
132135
dependencies: ["OpenAIService"]
133136
),
137+
.target(name: "Preferences"),
134138
]
135139
)

0 commit comments

Comments
 (0)