From 6b2d0323dea0a59d79da1f618fbf086d5d7f5283 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Thu, 1 Feb 2024 15:27:17 +0800 Subject: [PATCH 01/29] Tweak logs --- .../XcodeInspector/XcodeInspector+TriggerCommand.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tool/Sources/XcodeInspector/XcodeInspector+TriggerCommand.swift b/Tool/Sources/XcodeInspector/XcodeInspector+TriggerCommand.swift index 5672a9ea..e6ea06eb 100644 --- a/Tool/Sources/XcodeInspector/XcodeInspector+TriggerCommand.swift +++ b/Tool/Sources/XcodeInspector/XcodeInspector+TriggerCommand.swift @@ -32,14 +32,14 @@ public extension AppInstanceInspector { if activateApp { if !runningApplication.activate() { Logger.service.error(""" - Trigger menu item \(sourcePath) failed: \ + Trigger menu item \(sourcePath): \ Xcode not activated. """) } } else { if !runningApplication.isActive { Logger.service.error(""" - Trigger menu item \(sourcePath) failed: \ + Trigger menu item \(sourcePath): \ Xcode not activated. """) } From 3fbf8ae740b756491190180b1c8137b5da075769 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Sat, 3 Feb 2024 00:26:56 +0800 Subject: [PATCH 02/29] Disable the check by default --- Tool/Sources/Preferences/Keys.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tool/Sources/Preferences/Keys.swift b/Tool/Sources/Preferences/Keys.swift index 4cc88a08..88fad76c 100644 --- a/Tool/Sources/Preferences/Keys.swift +++ b/Tool/Sources/Preferences/Keys.swift @@ -572,7 +572,7 @@ public extension UserDefaultPreferenceKeys { var restartXcodeInspectorIfAccessibilityAPIIsMalfunctioning: FeatureFlag { .init( - defaultValue: true, + defaultValue: false, key: "FeatureFlag-RestartXcodeInspectorIfAccessibilityAPIIsMalfunctioning" ) } From f7098804e807220c71721a4d7de911a252273721 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Sat, 3 Feb 2024 00:36:31 +0800 Subject: [PATCH 03/29] Add a button to reactivate observations to Xcode in the menu --- ExtensionService/AppDelegate+Menu.swift | 13 +++++++++++++ Tool/Sources/XcodeInspector/XcodeInspector.swift | 12 +++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/ExtensionService/AppDelegate+Menu.swift b/ExtensionService/AppDelegate+Menu.swift index 1eea6ba9..bd11c5a2 100644 --- a/ExtensionService/AppDelegate+Menu.swift +++ b/ExtensionService/AppDelegate+Menu.swift @@ -82,6 +82,14 @@ extension AppDelegate { keyEquivalent: "" ) quitItem.target = self + + let reactivateObservationsItem = NSMenuItem( + title: "Reactivate Observations to Xcode", + action: #selector(reactivateObservationsToXcode), + keyEquivalent: "" + ) + + reactivateObservationsItem.target = self statusBarMenu.addItem(copilotName) statusBarMenu.addItem(openCopilotForXcode) @@ -91,6 +99,7 @@ extension AppDelegate { statusBarMenu.addItem(.separator()) statusBarMenu.addItem(xcodeInspectorDebug) statusBarMenu.addItem(accessibilityAPIPermission) + statusBarMenu.addItem(reactivateObservationsItem) statusBarMenu.addItem(quitItem) statusBarMenu.delegate = self @@ -211,6 +220,10 @@ private extension AppDelegate { @objc func restartXcodeInspector() { XcodeInspector.shared.restart(cleanUp: true) } + + @objc func reactivateObservationsToXcode() { + XcodeInspector.shared.reactivateObservationsToXcode() + } } private extension NSMenuItem { diff --git a/Tool/Sources/XcodeInspector/XcodeInspector.swift b/Tool/Sources/XcodeInspector/XcodeInspector.swift index c44c5143..7445f92e 100644 --- a/Tool/Sources/XcodeInspector/XcodeInspector.swift +++ b/Tool/Sources/XcodeInspector/XcodeInspector.swift @@ -227,6 +227,15 @@ public final class XcodeInspector: ObservableObject { appChangeObservations.insert(appChangeTask) } + public func reactivateObservationsToXcode() { + Task { @MainActor in + if let activeXcode { + setActiveXcode(activeXcode) + activeXcode.observeAXNotifications() + } + } + } + @MainActor private func setActiveXcode(_ xcode: XcodeAppInstanceInspector) { previousActiveApplication = activeApplication @@ -257,13 +266,11 @@ public final class XcodeInspector: ObservableObject { } else if let element = focusedElement, let editorElement = element.firstParent(where: \.isSourceEditor) { - Logger.service.debug("Focused on child of source editor.") focusedEditor = .init( runningApplication: xcode.runningApplication, element: editorElement ) } else { - Logger.service.debug("No source editor found.") focusedEditor = nil } } @@ -272,7 +279,6 @@ public final class XcodeInspector: ObservableObject { let focusedElementChanged = Task { @MainActor in for await notification in xcode.axNotifications { if notification.kind == .focusedUIElementChanged { - Logger.service.debug("Update focused element") try Task.checkCancellation() setFocusedElement() } From 4374f519f3386b8f786426aba6df33151d9e95af Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Sat, 3 Feb 2024 00:51:07 +0800 Subject: [PATCH 04/29] Add beta branch support --- Core/Package.swift | 1 + Core/Sources/UpdateChecker/UpdateChecker.swift | 13 ++++++++++++- Tool/Sources/Preferences/Keys.swift | 7 +++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Core/Package.swift b/Core/Package.swift index 6523b254..32d85d13 100644 --- a/Core/Package.swift +++ b/Core/Package.swift @@ -289,6 +289,7 @@ let package = Package( name: "UpdateChecker", dependencies: [ "Sparkle", + .product(name: "Preferences", package: "Tool"), .product(name: "Logger", package: "Tool"), ] ), diff --git a/Core/Sources/UpdateChecker/UpdateChecker.swift b/Core/Sources/UpdateChecker/UpdateChecker.swift index d1a261b4..702d4dbb 100644 --- a/Core/Sources/UpdateChecker/UpdateChecker.swift +++ b/Core/Sources/UpdateChecker/UpdateChecker.swift @@ -1,5 +1,6 @@ -import Sparkle import Logger +import Preferences +import Sparkle public final class UpdateChecker { let updater: SPUUpdater @@ -35,3 +36,13 @@ public final class UpdateChecker { } } +class UpdaterDelegate: NSObject, SPUUpdaterDelegate { + func allowedChannels(for updater: SPUUpdater) -> Set { + if UserDefaults.shared.value(for: \.installBetaBuilds) { + Set(["beta"]) + } else { + [] + } + } +} + diff --git a/Tool/Sources/Preferences/Keys.swift b/Tool/Sources/Preferences/Keys.swift index 88fad76c..475bf16f 100644 --- a/Tool/Sources/Preferences/Keys.swift +++ b/Tool/Sources/Preferences/Keys.swift @@ -93,6 +93,13 @@ public struct UserDefaultPreferenceKeys { defaultValue: false, key: "ShowHideWidgetShortcutGlobally" ) + + // MARK: Update Channel + + public let installBetaBuilds = PreferenceKey( + defaultValue: false, + key: "InstallBetaBuilds" + ) } // MARK: - OpenAI Account Settings From f91862fb591ca19c1f07f1d73c770e07ddbaf257 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Sat, 3 Feb 2024 00:59:24 +0800 Subject: [PATCH 05/29] Add toggle to enable beta builds --- Core/Sources/HostApp/GeneralView.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Core/Sources/HostApp/GeneralView.swift b/Core/Sources/HostApp/GeneralView.swift index 3c33d9a4..047ff149 100644 --- a/Core/Sources/HostApp/GeneralView.swift +++ b/Core/Sources/HostApp/GeneralView.swift @@ -238,6 +238,8 @@ struct GeneralSettingsView: View { var hideCircularWidget @AppStorage(\.showHideWidgetShortcutGlobally) var showHideWidgetShortcutGlobally + @AppStorage(\.installBetaBuilds) + var installBetaBuilds } @StateObject var settings = Settings() @@ -255,6 +257,10 @@ struct GeneralSettingsView: View { )) { Text("Automatically Check for Update") } + + Toggle(isOn: $settings.installBetaBuilds) { + Text("Install beta builds") + } Picker(selection: $settings.suggestionWidgetPositionMode) { ForEach(SuggestionWidgetPositionMode.allCases, id: \.rawValue) { From 458168995d1bc57074ebe5957814fd40089ea9e1 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Sat, 3 Feb 2024 01:03:35 +0800 Subject: [PATCH 06/29] Set delegate of updater --- Core/Sources/UpdateChecker/UpdateChecker.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/Sources/UpdateChecker/UpdateChecker.swift b/Core/Sources/UpdateChecker/UpdateChecker.swift index 702d4dbb..a9d7b88b 100644 --- a/Core/Sources/UpdateChecker/UpdateChecker.swift +++ b/Core/Sources/UpdateChecker/UpdateChecker.swift @@ -5,6 +5,7 @@ import Sparkle public final class UpdateChecker { let updater: SPUUpdater let hostBundleFound: Bool + let delegate = UpdaterDelegate() public init(hostBundle: Bundle?) { if hostBundle == nil { @@ -17,7 +18,7 @@ public final class UpdateChecker { hostBundle: hostBundle ?? Bundle.main, applicationBundle: Bundle.main, userDriver: SPUStandardUserDriver(hostBundle: hostBundle ?? Bundle.main, delegate: nil), - delegate: nil + delegate: delegate ) do { try updater.start() From fd549312d4898454da829ba28f71ca08b034c3c1 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Sun, 4 Feb 2024 23:38:27 +0800 Subject: [PATCH 07/29] Update --- Pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pro b/Pro index bdca40b9..e89ed3f0 160000 --- a/Pro +++ b/Pro @@ -1 +1 @@ -Subproject commit bdca40b959c4dfb69a41e9dabd044757a012d77d +Subproject commit e89ed3f065916050a83aa9205e09f82684e5bb92 From dd07194713d05f1177fc46ee83eabc4133e9f5dd Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Sun, 4 Feb 2024 23:38:58 +0800 Subject: [PATCH 08/29] Fix that variables with accessors are not used as focused code context --- .../FocusedCodeFinder/Swift/SwiftFocusedCodeFinder.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tool/Sources/FocusedCodeFinder/Swift/SwiftFocusedCodeFinder.swift b/Tool/Sources/FocusedCodeFinder/Swift/SwiftFocusedCodeFinder.swift index 368cb015..e8a452cb 100644 --- a/Tool/Sources/FocusedCodeFinder/Swift/SwiftFocusedCodeFinder.swift +++ b/Tool/Sources/FocusedCodeFinder/Swift/SwiftFocusedCodeFinder.swift @@ -187,7 +187,7 @@ public class SwiftFocusedCodeFinder: KnownLanguageFocusedCodeFinder< .split(omittingEmptySubsequences: false, whereSeparator: \.isNewline) .joined(separator: " "), name: name, - canBeUsedAsCodeRange: false + canBeUsedAsCodeRange: node.bindings.first?.accessorBlock != nil ) case let node as AccessorDeclSyntax: From cebf7bd51f29ca5ef1634065d345be1c0d274dbd Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Mon, 5 Feb 2024 00:31:42 +0800 Subject: [PATCH 09/29] Update chat feature settings --- .../FeatureSettings/ChatSettingsView.swift | 149 ++++++++++-------- .../HostApp/SharedComponents/SubSection.swift | 44 ++++-- Pro | 2 +- 3 files changed, 121 insertions(+), 74 deletions(-) diff --git a/Core/Sources/HostApp/FeatureSettings/ChatSettingsView.swift b/Core/Sources/HostApp/FeatureSettings/ChatSettingsView.swift index e2a70d16..633cc5ee 100644 --- a/Core/Sources/HostApp/FeatureSettings/ChatSettingsView.swift +++ b/Core/Sources/HostApp/FeatureSettings/ChatSettingsView.swift @@ -285,78 +285,102 @@ struct ChatSettingsView: View { #if canImport(ProHostApp) SubSection( - title: WithFeatureEnabled(\.projectScopeInChat, alignment: .trailing) { - Text("Sense Scope (Experimental)") - }, - description: "Experimental. Enable the bot to read the relevant code of the editing file in the project, third party packages and the SDK." + title: Text("Sense Scope (Experimental)"), + description: IfFeatureEnabled(\.senseScopeInChat) { + Text(""" + Enable the bot to access the relevant code \ + of the editing document in the project, third party packages and the SDK. + """) + } else: { + VStack(alignment: .leading) { + Text(""" + Enable the bot to read the relevant code \ + of the editing document in the SDK, and + """) + + WithFeatureEnabled(\.senseScopeInChat, alignment: .inlineLeading) { + Text("the project and third party packages.") + } + } + } ) { - WithFeatureEnabled(\.senseScopeInChat, alignment: .hidden) { - Form { - Toggle(isOn: $settings.enableSenseScopeByDefaultInChatContext) { - Text("Enable by default") + Form { + Toggle(isOn: $settings.enableSenseScopeByDefaultInChatContext) { + Text("Enable by default") + } + + Picker( + "Preferred Chat Model", + selection: $settings.preferredChatModelIdForSenseScope + ) { + Text("None").tag("") + + if !settings.chatModels + .contains(where: { + $0.id == settings.preferredChatModelIdForSenseScope + }), + !settings.preferredChatModelIdForSenseScope.isEmpty + { + Text( + (settings.chatModels.first?.name).map { "\($0) (Default)" } + ?? "No Model Found" + ) + .tag(settings.preferredChatModelIdForSenseScope) } - Picker( - "Preferred Chat Model", - selection: $settings.preferredChatModelIdForSenseScope - ) { - Text("None").tag("") - - if !settings.chatModels - .contains(where: { - $0.id == settings.preferredChatModelIdForSenseScope - }), - !settings.preferredChatModelIdForSenseScope.isEmpty - { - Text( - (settings.chatModels.first?.name).map { "\($0) (Default)" } - ?? "No Model Found" - ) - .tag(settings.preferredChatModelIdForSenseScope) - } - - ForEach(settings.chatModels, id: \.id) { chatModel in - Text(chatModel.name).tag(chatModel.id) - } + ForEach(settings.chatModels, id: \.id) { chatModel in + Text(chatModel.name).tag(chatModel.id) } } } } SubSection( - title: WithFeatureEnabled(\.projectScopeInChat, alignment: .trailing) { - Text("Project Scope (Experimental)") - }, - description: "Experimental. Enable the bot to search code symbols in the project, third party packages and the SDK." + title: Text("Project Scope (Experimental)"), + description: IfFeatureEnabled(\.projectScopeInChat) { + Text(""" + Enable the bot to search code and texts \ + in the project, third party packages and the SDK. + """) + } else: { + VStack(alignment: .leading) { + Text(""" + Enable the bot to search code and texts \ + in the neighboring files of the editing document, and + """) + + WithFeatureEnabled(\.senseScopeInChat, alignment: .inlineLeading) { + Text("the project, third party packages and the SDK.") + } + } + } ) { - WithFeatureEnabled(\.projectScopeInChat, alignment: .hidden) { - Form { - Toggle(isOn: $settings.enableProjectScopeByDefaultInChatContext) { - Text("Enable by default") + Form { + Toggle(isOn: $settings.enableProjectScopeByDefaultInChatContext) { + Text("Enable by default") + } + + Picker( + "Preferred Chat Model", + selection: $settings.preferredChatModelIdForProjectScope + ) { + Text("None").tag("") + + if !settings.chatModels + .contains(where: { + $0.id == settings.preferredChatModelIdForProjectScope + }), + !settings.preferredChatModelIdForProjectScope.isEmpty + { + Text( + (settings.chatModels.first?.name).map { "\($0) (Default)" } + ?? "No Model Found" + ) + .tag(settings.preferredChatModelIdForProjectScope) } - Picker( - "Preferred Chat Model", - selection: $settings.preferredChatModelIdForProjectScope - ) { - Text("None").tag("") - - if !settings.chatModels - .contains(where: { - $0.id == settings.preferredChatModelIdForProjectScope - }), - !settings.preferredChatModelIdForProjectScope.isEmpty - { - Text( - (settings.chatModels.first?.name).map { "\($0) (Default)" } - ?? "No Model Found" - ) - .tag(settings.preferredChatModelIdForProjectScope) - } - - ForEach(settings.chatModels, id: \.id) { chatModel in - Text(chatModel.name).tag(chatModel.id) - } + ForEach(settings.chatModels, id: \.id) { chatModel in + Text(chatModel.name).tag(chatModel.id) } } } @@ -404,14 +428,15 @@ struct ChatSettingsView: View { } // MARK: - Preview + // -//#Preview { +// #Preview { // ScrollView { // ChatSettingsView() // .padding() // } // .frame(height: 800) // .environment(\.overrideFeatureFlag, \.never) -//} +// } // diff --git a/Core/Sources/HostApp/SharedComponents/SubSection.swift b/Core/Sources/HostApp/SharedComponents/SubSection.swift index cfb58569..b294e3e4 100644 --- a/Core/Sources/HostApp/SharedComponents/SubSection.swift +++ b/Core/Sources/HostApp/SharedComponents/SubSection.swift @@ -1,11 +1,11 @@ import SwiftUI -struct SubSection: View { +struct SubSection: View { let title: Title - let description: String + let description: Description @ViewBuilder let content: () -> Content - init(title: Title, description: String = "", @ViewBuilder content: @escaping () -> Content) { + init(title: Title, description: Description, @ViewBuilder content: @escaping () -> Content) { self.title = title self.description = description self.content = content @@ -13,21 +13,19 @@ struct SubSection: View { var body: some View { VStack(alignment: .leading) { - if !(title is EmptyView && description.isEmpty) { + if !(title is EmptyView && description is EmptyView) { VStack(alignment: .leading, spacing: 8) { title .font(.system(size: 14).weight(.semibold)) - if !description.isEmpty { - Text(description) - .multilineTextAlignment(.leading) - .foregroundStyle(.secondary) - } + description + .multilineTextAlignment(.leading) + .foregroundStyle(.secondary) } .frame(maxWidth: .infinity, alignment: .leading) } - if !(title is EmptyView && description.isEmpty) { + if !(title is EmptyView && description is EmptyView) { Divider().padding(.bottom, 4) } @@ -45,8 +43,32 @@ struct SubSection: View { } } +extension SubSection where Description == Text { + init(title: Title, description: String, @ViewBuilder content: @escaping () -> Content) { + self.init(title: title, description: Text(description), content: content) + } +} + +extension SubSection where Description == EmptyView { + init(title: Title, @ViewBuilder content: @escaping () -> Content) { + self.init(title: title, description: EmptyView(), content: content) + } +} + extension SubSection where Title == EmptyView { - init(description: String = "", @ViewBuilder content: @escaping () -> Content) { + init(description: Description, @ViewBuilder content: @escaping () -> Content) { + self.init(title: EmptyView(), description: description, content: content) + } +} + +extension SubSection where Title == EmptyView, Description == EmptyView { + init(@ViewBuilder content: @escaping () -> Content) { + self.init(title: EmptyView(), description: EmptyView(), content: content) + } +} + +extension SubSection where Title == EmptyView, Description == Text { + init(description: String, @ViewBuilder content: @escaping () -> Content) { self.init(title: EmptyView(), description: description, content: content) } } diff --git a/Pro b/Pro index e89ed3f0..85be255e 160000 --- a/Pro +++ b/Pro @@ -1 +1 @@ -Subproject commit e89ed3f065916050a83aa9205e09f82684e5bb92 +Subproject commit 85be255e8a1ed5da8e19fcb29696dc2188720d4a From 31848ac7b63ad7064894ca316ea19002841c01ab Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Mon, 5 Feb 2024 00:34:42 +0800 Subject: [PATCH 10/29] Update prompt to code settings --- .../PromptToCodeSettingsView.swift | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/Core/Sources/HostApp/FeatureSettings/PromptToCodeSettingsView.swift b/Core/Sources/HostApp/FeatureSettings/PromptToCodeSettingsView.swift index b5ca5e9a..12808a59 100644 --- a/Core/Sources/HostApp/FeatureSettings/PromptToCodeSettingsView.swift +++ b/Core/Sources/HostApp/FeatureSettings/PromptToCodeSettingsView.swift @@ -104,11 +104,11 @@ struct PromptToCodeSettingsView: View { Text("pt") }.disabled(true) } - + ScopeForm() } } - + struct ScopeForm: View { class Settings: ObservableObject { @AppStorage(\.enableSenseScopeByDefaultInPromptToCode) @@ -125,18 +125,30 @@ struct PromptToCodeSettingsView: View { #if canImport(ProHostApp) SubSection( - title: WithFeatureEnabled(\.senseScopeInChat, alignment: .trailing) { - Text("Sense Scope (Experimental)") - }, - description: "Experimental. Enable the bot to read the relevant code of the editing file in the project, third party packages and the SDK." - ) { - WithFeatureEnabled(\.projectScopeInChat, alignment: .hidden) { - Form { - Toggle(isOn: $settings.enableSenseScopeByDefaultInPromptToCode) { - Text("Enable by default") + title: Text("Sense Scope (Experimental)"), + description: IfFeatureEnabled(\.senseScopeInChat) { + Text(""" + Enable the bot to access the relevant code \ + of the editing document in the project, third party packages and the SDK. + """) + } else: { + VStack(alignment: .leading) { + Text(""" + Enable the bot to read the relevant code \ + of the editing document in the SDK, and + """) + + WithFeatureEnabled(\.senseScopeInChat, alignment: .inlineLeading) { + Text("the project and third party packages.") } } } + ) { + Form { + Toggle(isOn: $settings.enableSenseScopeByDefaultInPromptToCode) { + Text("Enable by default") + } + } } #endif From 6f0f5fcebe68b06055f65c4292570acef2f707f1 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Mon, 5 Feb 2024 17:20:23 +0800 Subject: [PATCH 11/29] Update --- Pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pro b/Pro index 85be255e..552dcd2f 160000 --- a/Pro +++ b/Pro @@ -1 +1 @@ -Subproject commit 85be255e8a1ed5da8e19fcb29696dc2188720d4a +Subproject commit 552dcd2fbed05aedc5e23db5ba95e610b5c7a305 From 49ccb390dacea263544fb97c5272788707c8cdda Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Mon, 5 Feb 2024 17:25:30 +0800 Subject: [PATCH 12/29] Add new models --- Tool/Sources/Preferences/Types/ChatGPTModel.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Tool/Sources/Preferences/Types/ChatGPTModel.swift b/Tool/Sources/Preferences/Types/ChatGPTModel.swift index 9531b55d..1541f61c 100644 --- a/Tool/Sources/Preferences/Types/ChatGPTModel.swift +++ b/Tool/Sources/Preferences/Types/ChatGPTModel.swift @@ -5,6 +5,7 @@ public enum ChatGPTModel: String { case gpt35Turbo16k = "gpt-3.5-turbo-16k" case gpt4 = "gpt-4" case gpt432k = "gpt-4-32k" + case gpt4TurboPreview = "gpt-4-turbo-preview" case gpt40314 = "gpt-4-0314" case gpt40613 = "gpt-4-0613" case gpt41106Preview = "gpt-4-1106-preview" @@ -12,9 +13,11 @@ public enum ChatGPTModel: String { case gpt35Turbo0301 = "gpt-3.5-turbo-0301" case gpt35Turbo0613 = "gpt-3.5-turbo-0613" case gpt35Turbo1106 = "gpt-3.5-turbo-1106" + case gpt35Turbo0125 = "gpt-3.5-turbo-0125" case gpt35Turbo16k0613 = "gpt-3.5-turbo-16k-0613" case gpt432k0314 = "gpt-4-32k-0314" case gpt432k0613 = "gpt-4-32k-0613" + case gpt40125 = "gpt-4-0125-preview" } public extension ChatGPTModel { @@ -36,6 +39,8 @@ public extension ChatGPTModel { return 4096 case .gpt35Turbo1106: return 16385 + case .gpt35Turbo0125: + return 16385 case .gpt35Turbo16k: return 16385 case .gpt35Turbo16k0613: @@ -48,6 +53,10 @@ public extension ChatGPTModel { return 128000 case .gpt4VisionPreview: return 128000 + case .gpt4TurboPreview: + return 128000 + case .gpt40125: + return 128000 } } From 647ea5de343203a852c3e4726f62c67dd408551c Mon Sep 17 00:00:00 2001 From: Ge Will <531sunlight@gmail.com> Date: Mon, 5 Feb 2024 22:27:22 +0800 Subject: [PATCH 13/29] Modify baseURL to full path Add compatibility with Perplexity.ai API's distinct endpoint structure https://api.perplexity.ai/chat/completions --- Tool/Sources/AIModel/ChatModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tool/Sources/AIModel/ChatModel.swift b/Tool/Sources/AIModel/ChatModel.swift index 56126968..ca721192 100644 --- a/Tool/Sources/AIModel/ChatModel.swift +++ b/Tool/Sources/AIModel/ChatModel.swift @@ -63,7 +63,7 @@ public struct ChatModel: Codable, Equatable, Identifiable { case .openAI, .openAICompatible: let baseURL = info.baseURL if baseURL.isEmpty { return "https://api.openai.com/v1/chat/completions" } - return "\(baseURL)/v1/chat/completions" + return baseURL case .azureOpenAI: let baseURL = info.baseURL let deployment = info.azureOpenAIDeploymentName From 37ce0514dba9cd98bef2c7ed16c47fe0157eca10 Mon Sep 17 00:00:00 2001 From: Ge Will <531sunlight@gmail.com> Date: Tue, 6 Feb 2024 00:54:27 +0800 Subject: [PATCH 14/29] Feat Is base URL with full path toggle in ChatModelEditView --- .../ChatModelManagement/ChatModelEdit.swift | 5 +- .../ChatModelEditView.swift | 9 ++- .../EmbeddingModelEdit.swift | 5 +- .../EmbeddingModelEditView.swift | 8 ++- .../SharedModelManagement/BaseURLPicker.swift | 62 ++++++++++++------- .../BaseURLSelection.swift | 1 + Tool/Sources/AIModel/ChatModel.swift | 13 +++- Tool/Sources/AIModel/EmbeddingModel.swift | 11 +++- Tool/Sources/Preferences/Keys.swift | 1 + 9 files changed, 83 insertions(+), 32 deletions(-) diff --git a/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEdit.swift b/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEdit.swift index 2bbec86b..342ef862 100644 --- a/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEdit.swift +++ b/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEdit.swift @@ -16,6 +16,7 @@ struct ChatModelEdit: ReducerProtocol { @BindingState var modelName: String = "" var apiKeyName: String { apiKeySelection.apiKeyName } var baseURL: String { baseURLSelection.baseURL } + var isFullURL: Bool { baseURLSelection.isFullURL } var availableModelNames: [String] = [] var availableAPIKeys: [String] = [] var isTesting = false @@ -76,6 +77,7 @@ struct ChatModelEdit: ReducerProtocol { info: .init( apiKeyName: state.apiKeyName, baseURL: state.baseURL, + isFullURL: state.isFullURL, maxTokens: state.maxTokens, supportsFunctionCalling: state.supportsFunctionCalling, modelName: state.modelName @@ -171,7 +173,7 @@ extension ChatModelEdit.State { apiKeyName: model.info.apiKeyName, apiKeyManagement: .init(availableAPIKeyNames: [model.info.apiKeyName]) ), - baseURLSelection: .init(baseURL: model.info.baseURL) + baseURLSelection: .init(baseURL: model.info.baseURL, isFullURL: model.info.isFullURL) ) } } @@ -185,6 +187,7 @@ extension ChatModel { info: .init( apiKeyName: state.apiKeyName, baseURL: state.baseURL.trimmingCharacters(in: .whitespacesAndNewlines), + isFullURL: state.isFullURL, maxTokens: state.maxTokens, supportsFunctionCalling: { if case .googleAI = state.format { diff --git a/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift b/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift index 0f751c84..7be5cd23 100644 --- a/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift +++ b/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift @@ -101,9 +101,10 @@ struct ChatModelEditView: View { } } - func baseURLTextField(prompt: Text?) -> some View { + func baseURLTextField(prompt: Text?, showIsFullURL: Bool = false) -> some View { BaseURLPicker( prompt: prompt, + showIsFullURL: showIsFullURL, store: store.scope( state: \.baseURLSelection, action: ChatModelEdit.Action.baseURLSelection @@ -260,7 +261,10 @@ struct ChatModelEditView: View { @ViewBuilder var openAICompatible: some View { - baseURLTextField(prompt: Text("https://")) + baseURLTextField( + prompt: Text("https://"), + showIsFullURL: true + ) apiKeyNamePicker WithViewStore( @@ -334,6 +338,7 @@ struct ChatModelEditView: View { info: .init( apiKeyName: "key", baseURL: "apple.com", + isFullURL: false, maxTokens: 3000, supportsFunctionCalling: false, modelName: "gpt-3.5-turbo" diff --git a/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEdit.swift b/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEdit.swift index 6b0d772b..df8dbf22 100644 --- a/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEdit.swift +++ b/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEdit.swift @@ -15,6 +15,7 @@ struct EmbeddingModelEdit: ReducerProtocol { @BindingState var modelName: String = "" var apiKeyName: String { apiKeySelection.apiKeyName } var baseURL: String { baseURLSelection.baseURL } + var isFullURL: Bool { baseURLSelection.isFullURL } var availableModelNames: [String] = [] var availableAPIKeys: [String] = [] var isTesting = false @@ -75,6 +76,7 @@ struct EmbeddingModelEdit: ReducerProtocol { info: .init( apiKeyName: state.apiKeyName, baseURL: state.baseURL, + isFullURL: state.isFullURL, maxTokens: state.maxTokens, modelName: state.modelName ) @@ -157,7 +159,7 @@ extension EmbeddingModelEdit.State { apiKeyName: model.info.apiKeyName, apiKeyManagement: .init(availableAPIKeyNames: [model.info.apiKeyName]) ), - baseURLSelection: .init(baseURL: model.info.baseURL) + baseURLSelection: .init(baseURL: model.info.baseURL, isFullURL: model.info.isFullURL) ) } } @@ -171,6 +173,7 @@ extension EmbeddingModel { info: .init( apiKeyName: state.apiKeyName, baseURL: state.baseURL.trimmingCharacters(in: .whitespacesAndNewlines), + isFullURL: state.isFullURL, maxTokens: state.maxTokens, modelName: state.modelName.trimmingCharacters(in: .whitespacesAndNewlines) ) diff --git a/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEditView.swift b/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEditView.swift index c1162181..db996913 100644 --- a/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEditView.swift +++ b/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEditView.swift @@ -96,9 +96,10 @@ struct EmbeddingModelEditView: View { } } - func baseURLTextField(prompt: Text?) -> some View { + func baseURLTextField(prompt: Text?, showIsFullURL: Bool = false) -> some View { BaseURLPicker( prompt: prompt, + showIsFullURL: showIsFullURL, store: store.scope( state: \.baseURLSelection, action: EmbeddingModelEdit.Action.baseURLSelection @@ -223,7 +224,10 @@ struct EmbeddingModelEditView: View { @ViewBuilder var openAICompatible: some View { - baseURLTextField(prompt: Text("https://")) + baseURLTextField( + prompt: Text("https://"), + showIsFullURL: true + ) apiKeyNamePicker WithViewStore( diff --git a/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift b/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift index 47f5144a..d9957677 100644 --- a/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift +++ b/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift @@ -3,35 +3,51 @@ import SwiftUI struct BaseURLPicker: View { let prompt: Text? + let showIsFullURL: Bool let store: StoreOf - + var body: some View { WithViewStore(store) { viewStore in - TextField("Base URL", text: viewStore.$baseURL, prompt: prompt) - .overlay(alignment: .trailing) { - Picker( - "", - selection: viewStore.$baseURL, - content: { - if !viewStore.state.availableBaseURLs - .contains(viewStore.state.baseURL), - !viewStore.state.baseURL.isEmpty - { - Text("Custom Value").tag(viewStore.state.baseURL) - } - - Text("Empty (Default Value)").tag("") - - ForEach(viewStore.state.availableBaseURLs, id: \.self) { baseURL in - Text(baseURL).tag(baseURL) + Group { + TextField("Base URL", text: viewStore.$baseURL, prompt: prompt) + .overlay(alignment: .trailing) { + Picker( + "", + selection: viewStore.$baseURL, + content: { + if !viewStore.state.availableBaseURLs + .contains(viewStore.state.baseURL), + !viewStore.state.baseURL.isEmpty + { + Text("Custom Value").tag(viewStore.state.baseURL) + } + + Text("Empty (Default Value)").tag("") + + ForEach(viewStore.state.availableBaseURLs, id: \.self) { baseURL in + Text(baseURL).tag(baseURL) + } } - } + ) + .frame(width: 20) + } + if showIsFullURL { + Toggle( + "Is base URL with full path", + isOn: viewStore.$isFullURL ) - .frame(width: 20) - } - .onAppear { - viewStore.send(.appear) + + Text( + "Add compatibility API's distinct endpoint structure. For example Perplexity.ai API's URL is https://api.perplexity.ai/chat/completions" + ) + .foregroundColor(.secondary) + .font(.callout) + .dynamicHeightTextInFormWorkaround() } + } + .onAppear { + viewStore.send(.appear) + } } } } diff --git a/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLSelection.swift b/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLSelection.swift index c4cd4b96..daff8e21 100644 --- a/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLSelection.swift +++ b/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLSelection.swift @@ -6,6 +6,7 @@ import SwiftUI struct BaseURLSelection: ReducerProtocol { struct State: Equatable { @BindingState var baseURL: String = "" + @BindingState var isFullURL: Bool = false var availableBaseURLs: [String] = [] } diff --git a/Tool/Sources/AIModel/ChatModel.swift b/Tool/Sources/AIModel/ChatModel.swift index ca721192..14ce0a58 100644 --- a/Tool/Sources/AIModel/ChatModel.swift +++ b/Tool/Sources/AIModel/ChatModel.swift @@ -28,6 +28,8 @@ public struct ChatModel: Codable, Equatable, Identifiable { public var apiKeyName: String @FallbackDecoding public var baseURL: String + @FallbackDecoding + public var isFullURL: Bool @FallbackDecoding public var maxTokens: Int @FallbackDecoding @@ -44,6 +46,7 @@ public struct ChatModel: Codable, Equatable, Identifiable { public init( apiKeyName: String = "", baseURL: String = "", + isFullURL: Bool = false, maxTokens: Int = 4000, supportsFunctionCalling: Bool = true, supportsOpenAIAPI2023_11: Bool = false, @@ -51,6 +54,7 @@ public struct ChatModel: Codable, Equatable, Identifiable { ) { self.apiKeyName = apiKeyName self.baseURL = baseURL + self.isFullURL = isFullURL self.maxTokens = maxTokens self.supportsFunctionCalling = supportsFunctionCalling self.supportsOpenAIAPI2023_11 = supportsOpenAIAPI2023_11 @@ -60,10 +64,15 @@ public struct ChatModel: Codable, Equatable, Identifiable { public var endpoint: String { switch format { - case .openAI, .openAICompatible: + case .openAI: let baseURL = info.baseURL if baseURL.isEmpty { return "https://api.openai.com/v1/chat/completions" } - return baseURL + return "\(baseURL)/v1/chat/completions" + case .openAICompatible: + let baseURL = info.baseURL + if baseURL.isEmpty { return "https://api.openai.com/v1/chat/completions" } + if info.isFullURL { return baseURL } + return "\(baseURL)/v1/chat/completions" case .azureOpenAI: let baseURL = info.baseURL let deployment = info.azureOpenAIDeploymentName diff --git a/Tool/Sources/AIModel/EmbeddingModel.swift b/Tool/Sources/AIModel/EmbeddingModel.swift index f690980e..a87170be 100644 --- a/Tool/Sources/AIModel/EmbeddingModel.swift +++ b/Tool/Sources/AIModel/EmbeddingModel.swift @@ -27,6 +27,8 @@ public struct EmbeddingModel: Codable, Equatable, Identifiable { public var apiKeyName: String @FallbackDecoding public var baseURL: String + @FallbackDecoding + public var isFullURL: Bool @FallbackDecoding public var maxTokens: Int @FallbackDecoding @@ -41,12 +43,14 @@ public struct EmbeddingModel: Codable, Equatable, Identifiable { public init( apiKeyName: String = "", baseURL: String = "", + isFullURL: Bool = false, maxTokens: Int = 8192, dimensions: Int = 1536, modelName: String = "" ) { self.apiKeyName = apiKeyName self.baseURL = baseURL + self.isFullURL = isFullURL self.maxTokens = maxTokens self.dimensions = dimensions self.modelName = modelName @@ -55,10 +59,15 @@ public struct EmbeddingModel: Codable, Equatable, Identifiable { public var endpoint: String { switch format { - case .openAI, .openAICompatible: + case .openAI: let baseURL = info.baseURL if baseURL.isEmpty { return "https://api.openai.com/v1/embeddings" } return "\(baseURL)/v1/embeddings" + case .openAICompatible: + let baseURL = info.baseURL + if baseURL.isEmpty { return "https://api.openai.com/v1/embeddings" } + if info.isFullURL { return baseURL } + return "\(baseURL)/v1/embeddings" case .azureOpenAI: let baseURL = info.baseURL let deployment = info.azureOpenAIDeploymentName diff --git a/Tool/Sources/Preferences/Keys.swift b/Tool/Sources/Preferences/Keys.swift index 4cc88a08..373355fb 100644 --- a/Tool/Sources/Preferences/Keys.swift +++ b/Tool/Sources/Preferences/Keys.swift @@ -214,6 +214,7 @@ public extension UserDefaultPreferenceKeys { info: .init( apiKeyName: "", baseURL: "", + isFullURL: false, maxTokens: ChatGPTModel.gpt35Turbo.maxToken, supportsFunctionCalling: true, modelName: ChatGPTModel.gpt35Turbo.rawValue From 7ca2ebd7cf425a0ae087de9ace5202af9d1b4cf8 Mon Sep 17 00:00:00 2001 From: Ge Will <531sunlight@gmail.com> Date: Tue, 6 Feb 2024 01:09:34 +0800 Subject: [PATCH 15/29] Modify case spell --- .../AccountSettings/SharedModelManagement/BaseURLPicker.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift b/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift index d9957677..cad52175 100644 --- a/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift +++ b/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift @@ -33,7 +33,7 @@ struct BaseURLPicker: View { } if showIsFullURL { Toggle( - "Is base URL with full path", + "Is Base URL with Full Path", isOn: viewStore.$isFullURL ) From c720e8d9a8898b66d26ef97f4c1d5748943a3fad Mon Sep 17 00:00:00 2001 From: Ge Will <531sunlight@gmail.com> Date: Tue, 6 Feb 2024 01:13:44 +0800 Subject: [PATCH 16/29] Modify embeddingModels default value of ifFullURL --- Tool/Sources/Preferences/Keys.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Tool/Sources/Preferences/Keys.swift b/Tool/Sources/Preferences/Keys.swift index 373355fb..dce6aaa1 100644 --- a/Tool/Sources/Preferences/Keys.swift +++ b/Tool/Sources/Preferences/Keys.swift @@ -248,6 +248,7 @@ public extension UserDefaultPreferenceKeys { info: .init( apiKeyName: "", baseURL: "", + isFullURL: false, maxTokens: OpenAIEmbeddingModel.textEmbeddingAda002.maxToken, modelName: OpenAIEmbeddingModel.textEmbeddingAda002.rawValue ) From 3b000985a800c90b04e5081bc85249f717cfbfac Mon Sep 17 00:00:00 2001 From: Matt Overholt Date: Mon, 5 Feb 2024 19:58:47 -0600 Subject: [PATCH 17/29] Update CodeiumAuthService.swift Update Codeium enterprise register user path --- Tool/Sources/CodeiumService/CodeiumAuthService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tool/Sources/CodeiumService/CodeiumAuthService.swift b/Tool/Sources/CodeiumService/CodeiumAuthService.swift index 9141a572..79ed97ac 100644 --- a/Tool/Sources/CodeiumService/CodeiumAuthService.swift +++ b/Tool/Sources/CodeiumService/CodeiumAuthService.swift @@ -38,7 +38,7 @@ public final class CodeiumAuthService { let apiUrl = UserDefaults.shared.value(for: \.codeiumApiUrl) if UserDefaults.shared.value(for: \.codeiumEnterpriseMode), apiUrl != "" { registerUserUrl = - URL(string: apiUrl + "/exa.api_server_pb.ApiServerService/RegisterUser") + URL(string: apiUrl + "/exa.seat_management_pb.SeatManagementService/RegisterUser") } var request = URLRequest(url: registerUserUrl!) From 519f0aec472c9bc5652e46e8ba5513e26608758e Mon Sep 17 00:00:00 2001 From: Ge Will <531sunlight@gmail.com> Date: Wed, 7 Feb 2024 01:51:46 +0800 Subject: [PATCH 18/29] Modify Full URL UI to segment control --- .../SharedModelManagement/BaseURLPicker.swift | 70 +++++++++++-------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift b/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift index cad52175..9883e382 100644 --- a/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift +++ b/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift @@ -9,40 +9,48 @@ struct BaseURLPicker: View { var body: some View { WithViewStore(store) { viewStore in Group { - TextField("Base URL", text: viewStore.$baseURL, prompt: prompt) - .overlay(alignment: .trailing) { - Picker( - "", - selection: viewStore.$baseURL, - content: { - if !viewStore.state.availableBaseURLs - .contains(viewStore.state.baseURL), - !viewStore.state.baseURL.isEmpty - { - Text("Custom Value").tag(viewStore.state.baseURL) - } - - Text("Empty (Default Value)").tag("") - - ForEach(viewStore.state.availableBaseURLs, id: \.self) { baseURL in - Text(baseURL).tag(baseURL) - } - } - ) - .frame(width: 20) - } if showIsFullURL { - Toggle( - "Is Base URL with Full Path", - isOn: viewStore.$isFullURL + Picker( + selection: viewStore.$isFullURL, + content: { + Text("Base URL").tag(false) + Text("Full URL").tag(true) + }, + label: { Text("URL") } + ) + .pickerStyle(.segmented) + } + HStack { + TextField( + showIsFullURL ? "" : "Base URL", + text: viewStore.$baseURL, + prompt: prompt ) - - Text( - "Add compatibility API's distinct endpoint structure. For example Perplexity.ai API's URL is https://api.perplexity.ai/chat/completions" + if viewStore.isFullURL == false { + Text("/v1/chat/completions") + } + } + .padding(.trailing) + .overlay(alignment: .trailing) { + Picker( + "", + selection: viewStore.$baseURL, + content: { + if !viewStore.state.availableBaseURLs + .contains(viewStore.state.baseURL), + !viewStore.state.baseURL.isEmpty + { + Text("Custom Value").tag(viewStore.state.baseURL) + } + + Text("Empty (Default Value)").tag("") + + ForEach(viewStore.state.availableBaseURLs, id: \.self) { baseURL in + Text(baseURL).tag(baseURL) + } + } ) - .foregroundColor(.secondary) - .font(.callout) - .dynamicHeightTextInFormWorkaround() + .frame(width: 20) } } .onAppear { From 7709c0b878826542fb3c40257578b9a6f133f12a Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Wed, 7 Feb 2024 15:34:35 +0800 Subject: [PATCH 19/29] Adjust UI --- .../ChatModelEditView.swift | 58 ++++++++++--- .../EmbeddingModelEditView.swift | 54 ++++++++++-- .../SharedModelManagement/BaseURLPicker.swift | 87 +++++++++---------- 3 files changed, 136 insertions(+), 63 deletions(-) diff --git a/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift b/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift index 7be5cd23..a17ec7a2 100644 --- a/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift +++ b/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift @@ -101,17 +101,29 @@ struct ChatModelEditView: View { } } - func baseURLTextField(prompt: Text?, showIsFullURL: Bool = false) -> some View { + func baseURLTextField( + title: String = "Base URL", + prompt: Text?, + @ViewBuilder trailingContent: @escaping () -> V + ) -> some View { BaseURLPicker( + title: title, prompt: prompt, - showIsFullURL: showIsFullURL, store: store.scope( state: \.baseURLSelection, action: ChatModelEdit.Action.baseURLSelection - ) + ), + trailingContent: trailingContent ) } + func baseURLTextField( + title: String = "Base URL", + prompt: Text? + ) -> some View { + baseURLTextField(title: title, prompt: prompt, trailingContent: { EmptyView() }) + } + var supportsFunctionCallingToggle: some View { WithViewStore( store, @@ -203,7 +215,9 @@ struct ChatModelEditView: View { @ViewBuilder var openAI: some View { - baseURLTextField(prompt: Text("https://api.openai.com")) + baseURLTextField(prompt: Text("https://api.openai.com")) { + Text("/v1/chat/completion") + } apiKeyNamePicker WithViewStore( @@ -261,10 +275,34 @@ struct ChatModelEditView: View { @ViewBuilder var openAICompatible: some View { - baseURLTextField( - prompt: Text("https://"), - showIsFullURL: true - ) + WithViewStore(store.scope( + state: \.baseURLSelection, + action: ChatModelEdit.Action.baseURLSelection + ), removeDuplicates: { $0.isFullURL != $1.isFullURL }) { viewStore in + Picker( + selection: viewStore.$isFullURL, + content: { + Text("Base URL").tag(false) + Text("Full URL").tag(true) + }, + label: { Text("URL") } + ) + .pickerStyle(.segmented) + } + + WithViewStore(store, observe: \.isFullURL) { viewStore in + baseURLTextField( + title: "", + prompt: viewStore.state + ? Text("https://api.openai.com/v1/chat/completion") + : Text("https://api.openai.com") + ) { + if !viewStore.state { + Text("/v1/chat/completion") + } + } + } + apiKeyNamePicker WithViewStore( @@ -277,11 +315,11 @@ struct ChatModelEditView: View { maxTokensTextField supportsFunctionCallingToggle } - + @ViewBuilder var googleAI: some View { apiKeyNamePicker - + WithViewStore( store, removeDuplicates: { $0.modelName == $1.modelName } diff --git a/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEditView.swift b/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEditView.swift index db996913..df186ed0 100644 --- a/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEditView.swift +++ b/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEditView.swift @@ -96,17 +96,29 @@ struct EmbeddingModelEditView: View { } } - func baseURLTextField(prompt: Text?, showIsFullURL: Bool = false) -> some View { + func baseURLTextField( + title: String = "Base URL", + prompt: Text?, + @ViewBuilder trailingContent: @escaping () -> V + ) -> some View { BaseURLPicker( + title: title, prompt: prompt, - showIsFullURL: showIsFullURL, store: store.scope( state: \.baseURLSelection, action: EmbeddingModelEdit.Action.baseURLSelection - ) + ), + trailingContent: trailingContent ) } + func baseURLTextField( + title: String = "Base URL", + prompt: Text? + ) -> some View { + baseURLTextField(title: title, prompt: prompt, trailingContent: { EmptyView() }) + } + struct MaxTokensTextField: Equatable { @BindingViewState var maxTokens: Int var suggestedMaxTokens: Int? @@ -179,7 +191,9 @@ struct EmbeddingModelEditView: View { @ViewBuilder var openAI: some View { - baseURLTextField(prompt: Text("https://api.openai.com")) + baseURLTextField(prompt: Text("https://api.openai.com")) { + Text("/v1/embeddings") + } apiKeyNamePicker WithViewStore( @@ -224,10 +238,34 @@ struct EmbeddingModelEditView: View { @ViewBuilder var openAICompatible: some View { - baseURLTextField( - prompt: Text("https://"), - showIsFullURL: true - ) + WithViewStore(store.scope( + state: \.baseURLSelection, + action: EmbeddingModelEdit.Action.baseURLSelection + ), removeDuplicates: { $0.isFullURL != $1.isFullURL }) { viewStore in + Picker( + selection: viewStore.$isFullURL, + content: { + Text("Base URL").tag(false) + Text("Full URL").tag(true) + }, + label: { Text("URL") } + ) + .pickerStyle(.segmented) + } + + WithViewStore(store, observe: \.isFullURL) { viewStore in + baseURLTextField( + title: "", + prompt: viewStore.state + ? Text("https://api.openai.com/v1/embeddings") + : Text("https://api.openai.com") + ) { + if !viewStore.state { + Text("/v1/embeddings") + } + } + } + apiKeyNamePicker WithViewStore( diff --git a/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift b/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift index 9883e382..066983e7 100644 --- a/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift +++ b/Core/Sources/HostApp/AccountSettings/SharedModelManagement/BaseURLPicker.swift @@ -1,57 +1,40 @@ import ComposableArchitecture import SwiftUI -struct BaseURLPicker: View { +struct BaseURLPicker: View { + let title: String let prompt: Text? - let showIsFullURL: Bool let store: StoreOf + @ViewBuilder let trailingContent: () -> TrailingContent var body: some View { WithViewStore(store) { viewStore in - Group { - if showIsFullURL { - Picker( - selection: viewStore.$isFullURL, - content: { - Text("Base URL").tag(false) - Text("Full URL").tag(true) - }, - label: { Text("URL") } - ) - .pickerStyle(.segmented) - } - HStack { - TextField( - showIsFullURL ? "" : "Base URL", - text: viewStore.$baseURL, - prompt: prompt - ) - if viewStore.isFullURL == false { - Text("/v1/chat/completions") - } - } - .padding(.trailing) - .overlay(alignment: .trailing) { - Picker( - "", - selection: viewStore.$baseURL, - content: { - if !viewStore.state.availableBaseURLs - .contains(viewStore.state.baseURL), - !viewStore.state.baseURL.isEmpty - { - Text("Custom Value").tag(viewStore.state.baseURL) - } - - Text("Empty (Default Value)").tag("") - - ForEach(viewStore.state.availableBaseURLs, id: \.self) { baseURL in - Text(baseURL).tag(baseURL) + HStack { + TextField(title, text: viewStore.$baseURL, prompt: prompt) + .overlay(alignment: .trailing) { + Picker( + "", + selection: viewStore.$baseURL, + content: { + if !viewStore.state.availableBaseURLs + .contains(viewStore.state.baseURL), + !viewStore.state.baseURL.isEmpty + { + Text("Custom Value").tag(viewStore.state.baseURL) + } + + Text("Empty (Default Value)").tag("") + + ForEach(viewStore.state.availableBaseURLs, id: \.self) { baseURL in + Text(baseURL).tag(baseURL) + } } - } - ) - .frame(width: 20) - } + ) + .frame(width: 20) + } + + trailingContent() + .foregroundStyle(.secondary) } .onAppear { viewStore.send(.appear) @@ -60,3 +43,17 @@ struct BaseURLPicker: View { } } +extension BaseURLPicker where TrailingContent == EmptyView { + init( + title: String, + prompt: Text? = nil, + store: StoreOf + ) { + self.init( + title: title, + prompt: prompt, + store: store, + trailingContent: { EmptyView() } + ) + } +} From 3be73a89249ab2892c46423002232cb351b30d72 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Wed, 7 Feb 2024 15:40:31 +0800 Subject: [PATCH 20/29] Fix window size of embedding model edit view --- .../EmbeddingModelEditView.swift | 12 ++++++++++++ .../EmbeddingModelManagementView.swift | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEditView.swift b/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEditView.swift index df186ed0..c9c7b452 100644 --- a/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEditView.swift +++ b/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelEditView.swift @@ -63,6 +63,7 @@ struct EmbeddingModelEditView: View { .onAppear { store.send(.appear) } + .fixedSize(horizontal: false, vertical: true) } var nameTextField: some View { @@ -219,6 +220,17 @@ struct EmbeddingModelEditView: View { } maxTokensTextField + + VStack(alignment: .leading, spacing: 8) { + Text(Image(systemName: "exclamationmark.triangle.fill")) + Text( + " To get an API key, please visit [https://platform.openai.com/api-keys](https://platform.openai.com/api-keys)" + ) + + Text(Image(systemName: "exclamationmark.triangle.fill")) + Text( + " If you don't have access to GPT-4, you may need to visit [https://platform.openai.com/account/billing/overview](https://platform.openai.com/account/billing/overview) to buy some credits. A ChatGPT Plus subscription is not enough to access GPT-4 through API." + ) + } + .padding(.vertical) } @ViewBuilder diff --git a/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelManagementView.swift b/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelManagementView.swift index a71b356d..a3bfa16c 100644 --- a/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelManagementView.swift +++ b/Core/Sources/HostApp/AccountSettings/EmbeddingModelManagement/EmbeddingModelManagementView.swift @@ -12,7 +12,7 @@ struct EmbeddingModelManagementView: View { action: EmbeddingModelManagement.Action.embeddingModelItem )) { store in EmbeddingModelEditView(store: store) - .frame(minWidth: 400) + .frame(width: 800) } } } From aabd7658f4a368e2c3e166e8cdf2529629be96d1 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Wed, 7 Feb 2024 15:42:52 +0800 Subject: [PATCH 21/29] Make buttons default action --- Core/Sources/HostApp/AccountSettings/CodeiumView.swift | 4 +++- Pro | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Core/Sources/HostApp/AccountSettings/CodeiumView.swift b/Core/Sources/HostApp/AccountSettings/CodeiumView.swift index dc483fb5..f6e8ee26 100644 --- a/Core/Sources/HostApp/AccountSettings/CodeiumView.swift +++ b/Core/Sources/HostApp/AccountSettings/CodeiumView.swift @@ -267,7 +267,9 @@ struct CodeiumSignInView: View { } }) { Text(isGeneratingKey ? "Signing In.." : "Sign In") - }.disabled(isGeneratingKey) + } + .disabled(isGeneratingKey) + .keyboardShortcut(.defaultAction) } } .padding() diff --git a/Pro b/Pro index 552dcd2f..576da602 160000 --- a/Pro +++ b/Pro @@ -1 +1 @@ -Subproject commit 552dcd2fbed05aedc5e23db5ba95e610b5c7a305 +Subproject commit 576da602b454daddda5c88910305c95fd6cfe91c From 84fc894db7ad75de4528c2e8846646d02a1a871b Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Tue, 6 Feb 2024 14:44:53 +0800 Subject: [PATCH 22/29] Make runningApplication private --- .../FeatureReducers/WidgetFeature.swift | 8 ++-- ExtensionService/AppDelegate+Menu.swift | 2 +- Tool/Sources/AppActivator/AppActivator.swift | 4 +- .../XcodeInspector/AppInstanceInspector.swift | 37 ++++++++++++++++--- .../XcodeInspector/XcodeInspector.swift | 6 +-- 5 files changed, 42 insertions(+), 15 deletions(-) diff --git a/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift b/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift index b55bdfea..503ee8db 100644 --- a/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift +++ b/Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift @@ -238,10 +238,10 @@ public struct WidgetFeature: ReducerProtocol { case .observeActiveApplicationChange: return .run { send in - let stream = AsyncStream { continuation in + let stream = AsyncStream { continuation in let cancellable = xcodeInspector.$activeApplication.sink { newValue in guard let newValue else { return } - continuation.yield(newValue.runningApplication) + continuation.yield(newValue) } continuation.onTermination = { _ in cancellable.cancel() @@ -293,7 +293,7 @@ public struct WidgetFeature: ReducerProtocol { guard let activeXcode = xcodeInspector.activeXcode else { continue } guard await windows.fullscreenDetector.isOnActiveSpace else { continue } let app = AXUIElementCreateApplication( - activeXcode.runningApplication.processIdentifier + activeXcode.processIdentifier ) if let _ = app.focusedWindow { await windows.orderFront() @@ -538,7 +538,7 @@ public struct WidgetFeature: ReducerProtocol { let task = Task { @MainActor in if let activeApp, activeApp.isXcode { let application = AXUIElementCreateApplication( - activeApp.runningApplication.processIdentifier + activeApp.processIdentifier ) /// We need this to hide the windows when Xcode is minimized. let noFocus = application.focusedWindow == nil diff --git a/ExtensionService/AppDelegate+Menu.swift b/ExtensionService/AppDelegate+Menu.swift index bd11c5a2..6ccd371a 100644 --- a/ExtensionService/AppDelegate+Menu.swift +++ b/ExtensionService/AppDelegate+Menu.swift @@ -164,7 +164,7 @@ extension AppDelegate: NSMenuDelegate { for xcode in inspector.xcodes { let item = NSMenuItem( - title: "Xcode \(xcode.runningApplication.processIdentifier)", + title: "Xcode \(xcode.processIdentifier)", action: nil, keyEquivalent: "" ) diff --git a/Tool/Sources/AppActivator/AppActivator.swift b/Tool/Sources/AppActivator/AppActivator.swift index ed83287a..0afbefd5 100644 --- a/Tool/Sources/AppActivator/AppActivator.swift +++ b/Tool/Sources/AppActivator/AppActivator.swift @@ -31,7 +31,7 @@ public extension NSWorkspace { Task { @MainActor in guard let app = XcodeInspector.shared.previousActiveApplication else { return } try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000)) - app.runningApplication.activate() + app.activate() } } @@ -39,7 +39,7 @@ public extension NSWorkspace { Task { @MainActor in guard let app = XcodeInspector.shared.latestActiveXcode else { return } try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000)) - app.runningApplication.activate() + app.activate() } } } diff --git a/Tool/Sources/XcodeInspector/AppInstanceInspector.swift b/Tool/Sources/XcodeInspector/AppInstanceInspector.swift index 1613a8b7..12192abe 100644 --- a/Tool/Sources/XcodeInspector/AppInstanceInspector.swift +++ b/Tool/Sources/XcodeInspector/AppInstanceInspector.swift @@ -2,16 +2,43 @@ import AppKit import Foundation public class AppInstanceInspector: ObservableObject { + let runningApplication: NSRunningApplication + public let processIdentifier: pid_t + public let bundleURL: URL? + public let bundleIdentifier: String? + public var appElement: AXUIElement { - AXUIElementCreateApplication(runningApplication.processIdentifier) + return AXUIElementCreateApplication(runningApplication.processIdentifier) + } + + public var isTerminated: Bool { + return runningApplication.isTerminated + } + + public var isActive: Bool { + guard !runningApplication.isTerminated else { return false } + return runningApplication.isActive + } + + public var isXcode: Bool { + guard !runningApplication.isTerminated else { return false } + return runningApplication.isXcode + } + + public var isExtensionService: Bool { + guard !runningApplication.isTerminated else { return false } + return runningApplication.isCopilotForXcodeExtensionService + } + + public func activate() -> Bool { + return runningApplication.activate() } - public let runningApplication: NSRunningApplication - public var isActive: Bool { runningApplication.isActive } - public var isXcode: Bool { runningApplication.isXcode } - public var isExtensionService: Bool { runningApplication.isCopilotForXcodeExtensionService } init(runningApplication: NSRunningApplication) { self.runningApplication = runningApplication + processIdentifier = runningApplication.processIdentifier + bundleURL = runningApplication.bundleURL + bundleIdentifier = runningApplication.bundleIdentifier } } diff --git a/Tool/Sources/XcodeInspector/XcodeInspector.swift b/Tool/Sources/XcodeInspector/XcodeInspector.swift index 7445f92e..ddb5ac84 100644 --- a/Tool/Sources/XcodeInspector/XcodeInspector.swift +++ b/Tool/Sources/XcodeInspector/XcodeInspector.swift @@ -122,7 +122,7 @@ public final class XcodeInspector: ObservableObject { appChangeObservations.forEach { $0.cancel() } appChangeObservations.removeAll() - let appChangeTask = Task { [weak self] in + let appChangeTask = Task(priority: .utility) { [weak self] in guard let self else { return } if let activeXcode { await setActiveXcode(activeXcode) @@ -140,7 +140,7 @@ public final class XcodeInspector: ObservableObject { else { continue } if app.isXcode { if let existed = xcodes.first(where: { - $0.runningApplication.processIdentifier == app.processIdentifier + $0.processIdentifier == app.processIdentifier && !$0.isTerminated }) { await MainActor.run { self.setActiveXcode(existed) @@ -175,7 +175,7 @@ public final class XcodeInspector: ObservableObject { let processIdentifier = app.processIdentifier await MainActor.run { self.xcodes.removeAll { - $0.runningApplication.processIdentifier == processIdentifier + $0.processIdentifier == processIdentifier || $0.isTerminated } if self.latestActiveXcode?.runningApplication .processIdentifier == processIdentifier From 7bd52846feb819279000c5699b6d3ea4299b0a66 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Tue, 6 Feb 2024 14:45:32 +0800 Subject: [PATCH 23/29] Remove a useless Task --- .../Apps/XcodeAppInstanceInspector.swift | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/Tool/Sources/XcodeInspector/Apps/XcodeAppInstanceInspector.swift b/Tool/Sources/XcodeInspector/Apps/XcodeAppInstanceInspector.swift index 823c3f69..d5cfe80f 100644 --- a/Tool/Sources/XcodeInspector/Apps/XcodeAppInstanceInspector.swift +++ b/Tool/Sources/XcodeInspector/Apps/XcodeAppInstanceInspector.swift @@ -162,34 +162,32 @@ public final class XcodeAppInstanceInspector: AppInstanceInspector { ) focusedWindow = window - // should find a better solution to do this thread safe - Task { @MainActor in - focusedWindowObservations.forEach { $0.cancel() } - focusedWindowObservations.removeAll() - - documentURL = window.documentURL - workspaceURL = window.workspaceURL - projectRootURL = window.projectRootURL - - window.$documentURL - .filter { $0 != .init(fileURLWithPath: "/") } - .receive(on: DispatchQueue.main) - .sink { [weak self] url in - self?.documentURL = url - }.store(in: &focusedWindowObservations) - window.$workspaceURL - .filter { $0 != .init(fileURLWithPath: "/") } - .receive(on: DispatchQueue.main) - .sink { [weak self] url in - self?.workspaceURL = url - }.store(in: &focusedWindowObservations) - window.$projectRootURL - .filter { $0 != .init(fileURLWithPath: "/") } - .receive(on: DispatchQueue.main) - .sink { [weak self] url in - self?.projectRootURL = url - }.store(in: &focusedWindowObservations) - } + focusedWindowObservations.forEach { $0.cancel() } + focusedWindowObservations.removeAll() + + documentURL = window.documentURL + workspaceURL = window.workspaceURL + projectRootURL = window.projectRootURL + + window.$documentURL + .filter { $0 != .init(fileURLWithPath: "/") } + .receive(on: DispatchQueue.main) + .sink { [weak self] url in + self?.documentURL = url + }.store(in: &focusedWindowObservations) + window.$workspaceURL + .filter { $0 != .init(fileURLWithPath: "/") } + .receive(on: DispatchQueue.main) + .sink { [weak self] url in + self?.workspaceURL = url + }.store(in: &focusedWindowObservations) + window.$projectRootURL + .filter { $0 != .init(fileURLWithPath: "/") } + .receive(on: DispatchQueue.main) + .sink { [weak self] url in + self?.projectRootURL = url + }.store(in: &focusedWindowObservations) + } else { let window = XcodeWindowInspector(uiElement: window) focusedWindow = window From 454750d28cfd91c1206627419c01e4e22397f2c7 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Tue, 6 Feb 2024 14:48:34 +0800 Subject: [PATCH 24/29] Generate app group related keys dynamically --- Tool/Sources/Configs/Configurations.swift | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Tool/Sources/Configs/Configurations.swift b/Tool/Sources/Configs/Configurations.swift index 387bd238..1f49ba81 100644 --- a/Tool/Sources/Configs/Configurations.swift +++ b/Tool/Sources/Configs/Configurations.swift @@ -1,22 +1,22 @@ import Foundation +private var teamIDPrefix: String { + Bundle.main.infoDictionary?["TEAM_ID_PREFIX"] as? String ?? "" +} + +private var bundleIdentifierBase: String { + Bundle.main.infoDictionary?["BUNDLE_IDENTIFIER_BASE"] as? String ?? "" +} + public var userDefaultSuiteName: String { - "5YKZ4Y3DAW.group.com.intii.CopilotForXcode" + "\(teamIDPrefix)group.\(bundleIdentifierBase)" } public var keychainAccessGroup: String { - #if DEBUG - return "5YKZ4Y3DAW.dev.com.intii.CopilotForXcode.Shared" - #else - return "5YKZ4Y3DAW.com.intii.CopilotForXcode.Shared" - #endif + return "\(teamIDPrefix)\(bundleIdentifierBase).Shared" } public var keychainService: String { - #if DEBUG - return "dev.com.intii.CopilotForXcode" - #else - return "com.intii.CopilotForXcode" - #endif + return bundleIdentifierBase } From 5b58927cd9db171a5d0b62ac394488c785db60b6 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Tue, 6 Feb 2024 14:49:10 +0800 Subject: [PATCH 25/29] Bump version to 0.30.3 --- Version.xcconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Version.xcconfig b/Version.xcconfig index d2934015..60866af4 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -1,3 +1,3 @@ -APP_VERSION = 0.30.2 -APP_BUILD = 314 +APP_VERSION = 0.30.3 +APP_BUILD = 315 From 93e133aa331038cb7c3f59e310f713099225bb68 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Wed, 7 Feb 2024 21:50:33 +0800 Subject: [PATCH 26/29] Fix typo --- .../ChatModelManagement/ChatModelEditView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift b/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift index a17ec7a2..b46a0baf 100644 --- a/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift +++ b/Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift @@ -216,7 +216,7 @@ struct ChatModelEditView: View { @ViewBuilder var openAI: some View { baseURLTextField(prompt: Text("https://api.openai.com")) { - Text("/v1/chat/completion") + Text("/v1/chat/completions") } apiKeyNamePicker @@ -294,11 +294,11 @@ struct ChatModelEditView: View { baseURLTextField( title: "", prompt: viewStore.state - ? Text("https://api.openai.com/v1/chat/completion") + ? Text("https://api.openai.com/v1/chat/completions") : Text("https://api.openai.com") ) { if !viewStore.state { - Text("/v1/chat/completion") + Text("/v1/chat/completions") } } } From 2e7f611cb41e4265f2dda496ec5753a972c74215 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Wed, 7 Feb 2024 21:51:56 +0800 Subject: [PATCH 27/29] Bump build number --- Version.xcconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Version.xcconfig b/Version.xcconfig index 60866af4..ea6385ef 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -1,3 +1,3 @@ APP_VERSION = 0.30.3 -APP_BUILD = 315 +APP_BUILD = 316 From 70712b79a98f7f9f93a5a7dd06d91b0cd44c2c50 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Wed, 7 Feb 2024 22:15:36 +0800 Subject: [PATCH 28/29] Update --- Pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pro b/Pro index 576da602..4059899c 160000 --- a/Pro +++ b/Pro @@ -1 +1 @@ -Subproject commit 576da602b454daddda5c88910305c95fd6cfe91c +Subproject commit 4059899cf333c828dad9668ae66760e3a7372cb5 From 973f3b38f1e104e128e79567412ffbef2efb55a4 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Wed, 7 Feb 2024 22:15:48 +0800 Subject: [PATCH 29/29] Update appcast.xml --- appcast.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/appcast.xml b/appcast.xml index ebedb1ef..69297368 100644 --- a/appcast.xml +++ b/appcast.xml @@ -3,6 +3,18 @@ Copilot for Xcode + + 0.30.3 + Wed, 07 Feb 2024 22:13:53 +0800 + 316 + 0.30.3 + 12.0 + + https://github.com/intitni/CopilotForXcode/releases/tag/0.30.3 + + + + 0.30.2 Thu, 01 Feb 2024 15:09:51 +0800