From 37407e420a756eb6d3889b9721c2ff4219edaddb Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 10 Mar 2025 02:45:41 +0000 Subject: [PATCH] Pre-release 0.31.106 --- .../Sources/ConversationTab/ModelPicker.swift | 36 ++++++++++++++----- .../GitHubCopilotViewModel.swift | 4 +++ .../PseudoCommandHandler.swift | 4 +-- .../WidgetWindowsController.swift | 2 +- .../LanguageServer/CopilotModelManager.swift | 21 +++++++++++ .../LanguageServer/GitHubCopilotService.swift | 8 +++++ Tool/Sources/Toast/NotificationView.swift | 10 +++--- Tool/Sources/Toast/Toast.swift | 23 +++++++----- 8 files changed, 83 insertions(+), 25 deletions(-) create mode 100644 Tool/Sources/GitHubCopilotService/LanguageServer/CopilotModelManager.swift diff --git a/Core/Sources/ConversationTab/ModelPicker.swift b/Core/Sources/ConversationTab/ModelPicker.swift index effd622..f761b4f 100644 --- a/Core/Sources/ConversationTab/ModelPicker.swift +++ b/Core/Sources/ConversationTab/ModelPicker.swift @@ -2,6 +2,7 @@ import SwiftUI import ChatService import Persist import ComposableArchitecture +import GitHubCopilotService public let SELECTED_LLM_KEY = "selectedLLM" @@ -27,6 +28,18 @@ extension AppState { } } +extension CopilotModelManager { + static func getAvailableChatLLMs() -> [LLMModel] { + let LLMs = CopilotModelManager.getAvailableLLMs() + let availableModels = LLMs.filter( + { $0.scopes.contains(.chatPanel) } + ).map { + LLMModel(modelName: $0.modelName, modelFamily: $0.modelFamily) + } + return availableModels.isEmpty ? [defaultModel] : availableModels + } +} + struct LLMModel: Codable, Hashable { let modelName: String let modelFamily: String @@ -35,12 +48,15 @@ struct LLMModel: Codable, Hashable { let defaultModel = LLMModel(modelName: "GPT-4o", modelFamily: "gpt-4o") struct ModelPicker: View { @State private var selectedModel = defaultModel.modelName - @State private var models: [LLMModel] = [ defaultModel ] @State private var isHovered = false @State private var isPressed = false init() { - self.updateCurrentModel() + self.updateCurrentModel() + } + + var models: [LLMModel] { + CopilotModelManager.getAvailableChatLLMs() } func updateCurrentModel() { @@ -74,13 +90,9 @@ struct ModelPicker: View { isHovered = hovering } .onAppear() { + updateCurrentModel() Task { - updateCurrentModel() - self.models = await ChatService.shared.copilotModels().filter( - { $0.scopes.contains(.chatPanel) } - ).map { - LLMModel(modelName: $0.modelName, modelFamily: $0.modelFamily) - } + await refreshModels() } } .help("Pick Model") @@ -93,6 +105,14 @@ struct ModelPicker: View { let width = selectedModel.size(withAttributes: attributes).width return CGFloat(width + 20) } + + @MainActor + func refreshModels() async { + let copilotModels = await ChatService.shared.copilotModels() + if !copilotModels.isEmpty { + CopilotModelManager.updateLLMs(copilotModels) + } + } } struct ModelPicker_Previews: PreviewProvider { diff --git a/Core/Sources/GitHubCopilotViewModel/GitHubCopilotViewModel.swift b/Core/Sources/GitHubCopilotViewModel/GitHubCopilotViewModel.swift index 7c369ed..5a2aad1 100644 --- a/Core/Sources/GitHubCopilotViewModel/GitHubCopilotViewModel.swift +++ b/Core/Sources/GitHubCopilotViewModel/GitHubCopilotViewModel.swift @@ -155,6 +155,10 @@ public class GitHubCopilotViewModel: ObservableObject { waitingForSignIn = false self.username = username self.status = status + let models = try? await service.models() + if let models = models, !models.isEmpty { + CopilotModelManager.updateLLMs(models) + } await Status.shared.updateAuthStatus(.loggedIn, username: username) broadcastStatusChange() } catch let error as GitHubCopilotError { diff --git a/Core/Sources/Service/SuggestionCommandHandler/PseudoCommandHandler.swift b/Core/Sources/Service/SuggestionCommandHandler/PseudoCommandHandler.swift index 91df57a..2ad3e76 100644 --- a/Core/Sources/Service/SuggestionCommandHandler/PseudoCommandHandler.swift +++ b/Core/Sources/Service/SuggestionCommandHandler/PseudoCommandHandler.swift @@ -266,7 +266,7 @@ struct PseudoCommandHandler { if now.timeIntervalSince(lastBundleNotFoundTime) > 60 * 60 { Self.lastBundleNotFoundTime = now toast.toast( - title: "Extension Permission Not Granted", + title: "GitHub Copilot Extension Permission Not Granted", content: """ Enable Extensions → Xcode Source Editor → GitHub Copilot \ for Xcode for faster and full-featured code completion. \ @@ -287,7 +287,7 @@ struct PseudoCommandHandler { content: "Quit and restart Xcode to enable extension.", level: .warning, button: .init( - title: "Restart", + title: "Restart Xcode", action: { NSWorkspace.restartXcode() } ) ) diff --git a/Core/Sources/SuggestionWidget/WidgetWindowsController.swift b/Core/Sources/SuggestionWidget/WidgetWindowsController.swift index c661c82..f1eb94b 100644 --- a/Core/Sources/SuggestionWidget/WidgetWindowsController.swift +++ b/Core/Sources/SuggestionWidget/WidgetWindowsController.swift @@ -744,7 +744,7 @@ public final class WidgetWindows { it.isReleasedWhenClosed = false it.isOpaque = false it.backgroundColor = .clear - it.level = widgetLevel(0) + it.level = widgetLevel(2) it.collectionBehavior = [.fullScreenAuxiliary, .transient, .canJoinAllSpaces] it.hasShadow = false it.contentView = NSHostingView( diff --git a/Tool/Sources/GitHubCopilotService/LanguageServer/CopilotModelManager.swift b/Tool/Sources/GitHubCopilotService/LanguageServer/CopilotModelManager.swift new file mode 100644 index 0000000..c031421 --- /dev/null +++ b/Tool/Sources/GitHubCopilotService/LanguageServer/CopilotModelManager.swift @@ -0,0 +1,21 @@ +import ConversationServiceProvider + +public class CopilotModelManager { + private static var availableLLMs: [CopilotModel] = [] + + public static func updateLLMs(_ models: [CopilotModel]) { + availableLLMs = models + } + + public static func getAvailableLLMs() -> [CopilotModel] { + return availableLLMs + } + + public static func hasLLMs() -> Bool { + return !availableLLMs.isEmpty + } + + public static func clearLLMs() { + availableLLMs = [] + } +} diff --git a/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotService.swift b/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotService.swift index 73d26e5..d4fcffd 100644 --- a/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotService.swift +++ b/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotService.swift @@ -683,6 +683,12 @@ public final class GitHubCopilotService: private func updateServiceAuthStatus(_ status: GitHubCopilotRequest.CheckStatus.Response) async { Logger.gitHubCopilot.info("check status response: \(status)") if status.status == .ok || status.status == .maybeOk { + if !CopilotModelManager.hasLLMs() { + let models = try? await models() + if let models = models, !models.isEmpty { + CopilotModelManager.updateLLMs(models) + } + } await Status.shared.updateAuthStatus(.loggedIn, username: status.user) await unwatchAuthStatus() } else if status.status == .notAuthorized { @@ -874,6 +880,8 @@ public final class GitHubCopilotService: if let signoutError { throw signoutError + } else { + CopilotModelManager.clearLLMs() } } } diff --git a/Tool/Sources/Toast/NotificationView.swift b/Tool/Sources/Toast/NotificationView.swift index 693eb1e..f29c9a8 100644 --- a/Tool/Sources/Toast/NotificationView.swift +++ b/Tool/Sources/Toast/NotificationView.swift @@ -15,6 +15,7 @@ struct AutoDismissMessage: View { message.level.color as Color, in: RoundedRectangle(cornerRadius: 8) ) + .frame(minWidth: 300) } } @@ -53,7 +54,10 @@ public struct NotificationView: View { Spacer() if let button = message.button { - Button(action: button.action) { + Button(action: { + button.action() + onDismiss() + }) { Text(button.title) .padding(.horizontal, 7) .padding(.vertical, 3) @@ -76,10 +80,6 @@ public struct NotificationView: View { } else { AutoDismissMessage(message: message) .frame(maxWidth: .infinity) - .overlay { - RoundedRectangle(cornerRadius: 8) - .stroke(Color.black.opacity(0.3), lineWidth: 1) - } } } } diff --git a/Tool/Sources/Toast/Toast.swift b/Tool/Sources/Toast/Toast.swift index 57f835c..d6132e8 100644 --- a/Tool/Sources/Toast/Toast.swift +++ b/Tool/Sources/Toast/Toast.swift @@ -271,15 +271,20 @@ public extension NSWorkspace { } NSWorkspace.shared.open(URL(string: urlString)!) } else { - let script = NSAppleScript( - source: """ - tell application "System Preferences" - activate - set the current pane to pane id "com.apple.preferences.extensions" - end tell - """ - ) - script?.executeAndReturnError(nil) + let process = Process() + process.executableURL = URL(fileURLWithPath: "/usr/bin/open") + process.arguments = [ + "-b", + "com.apple.systempreferences", + "/System/Library/PreferencePanes/Extensions.prefPane" + ] + + do { + try process.run() + } catch { + // Handle error silently + return + } } }