Skip to content

Commit 9788b5c

Browse files
committed
Pre-release 0.37.127
1 parent 81fc588 commit 9788b5c

25 files changed

+442
-293
lines changed

Core/Sources/HostApp/General.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ public struct General {
1212
@ObservableState
1313
public struct State: Equatable {
1414
var xpcServiceVersion: String?
15+
var xpcCLSVersion: String?
1516
var isAccessibilityPermissionGranted: ObservedAXStatus = .unknown
1617
var isExtensionPermissionGranted: ExtensionPermissionStatus = .unknown
18+
var xpcServiceAuthStatus: AuthStatus = .init(status: .unknown)
1719
var isReloading = false
1820
}
1921

@@ -24,8 +26,10 @@ public struct General {
2426
case reloadStatus
2527
case finishReloading(
2628
xpcServiceVersion: String,
29+
xpcCLSVersion: String?,
2730
axStatus: ObservedAXStatus,
28-
extensionStatus: ExtensionPermissionStatus
31+
extensionStatus: ExtensionPermissionStatus,
32+
authStatus: AuthStatus
2933
)
3034
case failedReloading
3135
case retryReloading
@@ -90,10 +94,14 @@ public struct General {
9094
let isAccessibilityPermissionGranted = try await service
9195
.getXPCServiceAccessibilityPermission()
9296
let isExtensionPermissionGranted = try await service.getXPCServiceExtensionPermission()
97+
let xpcServiceAuthStatus = try await service.getXPCServiceAuthStatus() ?? .init(status: .unknown)
98+
let xpcCLSVersion = try await service.getXPCCLSVersion()
9399
await send(.finishReloading(
94100
xpcServiceVersion: xpcServiceVersion,
101+
xpcCLSVersion: xpcCLSVersion,
95102
axStatus: isAccessibilityPermissionGranted,
96-
extensionStatus: isExtensionPermissionGranted
103+
extensionStatus: isExtensionPermissionGranted,
104+
authStatus: xpcServiceAuthStatus
97105
))
98106
} else {
99107
toast("Launching service app.", .info)
@@ -114,10 +122,12 @@ public struct General {
114122
}
115123
}.cancellable(id: ReloadStatusCancellableId(), cancelInFlight: true)
116124

117-
case let .finishReloading(version, axStatus, extensionStatus):
125+
case let .finishReloading(version, clsVersion, axStatus, extensionStatus, authStatus):
118126
state.xpcServiceVersion = version
119127
state.isAccessibilityPermissionGranted = axStatus
120128
state.isExtensionPermissionGranted = extensionStatus
129+
state.xpcServiceAuthStatus = authStatus
130+
state.xpcCLSVersion = clsVersion
121131
state.isReloading = false
122132
return .none
123133

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import ComposableArchitecture
22
import GitHubCopilotService
3-
import GitHubCopilotViewModel
43
import SwiftUI
54

65
struct AppInfoView: View {
@@ -15,61 +14,61 @@ struct AppInfoView: View {
1514
@Environment(\.toast) var toast
1615

1716
@StateObject var settings = Settings()
18-
@StateObject var viewModel: GitHubCopilotViewModel
1917

2018
@State var appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
2119
@State var automaticallyCheckForUpdates: Bool?
2220

2321
let store: StoreOf<General>
2422

2523
var body: some View {
26-
HStack(alignment: .center, spacing: 16) {
27-
let appImage = if let nsImage = NSImage(named: "AppIcon") {
28-
Image(nsImage: nsImage)
29-
} else {
30-
Image(systemName: "app")
31-
}
32-
appImage
33-
.resizable()
34-
.frame(width: 110, height: 110)
35-
VStack(alignment: .leading, spacing: 8) {
36-
HStack {
37-
Text(Bundle.main.object(forInfoDictionaryKey: "HOST_APP_NAME") as? String ?? "GitHub Copilot for Xcode")
38-
.font(.title)
39-
Text("(\(appVersion ?? ""))")
40-
.font(.title)
24+
WithPerceptionTracking {
25+
HStack(alignment: .center, spacing: 16) {
26+
let appImage = if let nsImage = NSImage(named: "AppIcon") {
27+
Image(nsImage: nsImage)
28+
} else {
29+
Image(systemName: "app")
4130
}
42-
Text("Language Server Version: \(viewModel.version ?? "Loading...")")
43-
Button(action: {
44-
updateChecker.checkForUpdates()
45-
}) {
46-
HStack(spacing: 2) {
47-
Text("Check for Updates")
31+
appImage
32+
.resizable()
33+
.frame(width: 110, height: 110)
34+
VStack(alignment: .leading, spacing: 8) {
35+
HStack {
36+
Text(Bundle.main.object(forInfoDictionaryKey: "HOST_APP_NAME") as? String ?? "GitHub Copilot for Xcode")
37+
.font(.title)
38+
Text("(\(appVersion ?? ""))")
39+
.font(.title)
4840
}
49-
}
50-
HStack {
51-
Toggle(isOn: .init(
52-
get: { automaticallyCheckForUpdates ?? updateChecker.getAutomaticallyChecksForUpdates() },
53-
set: { updateChecker.setAutomaticallyChecksForUpdates($0); automaticallyCheckForUpdates = $0 }
54-
)) {
55-
Text("Automatically Check for Updates")
41+
Text("Language Server Version: \(store.xpcCLSVersion ?? "Loading...")")
42+
Button(action: {
43+
updateChecker.checkForUpdates()
44+
}) {
45+
HStack(spacing: 2) {
46+
Text("Check for Updates")
47+
}
5648
}
57-
58-
Toggle(isOn: $settings.installPrereleases) {
59-
Text("Install pre-releases")
49+
HStack {
50+
Toggle(isOn: .init(
51+
get: { automaticallyCheckForUpdates ?? updateChecker.getAutomaticallyChecksForUpdates() },
52+
set: { updateChecker.setAutomaticallyChecksForUpdates($0); automaticallyCheckForUpdates = $0 }
53+
)) {
54+
Text("Automatically Check for Updates")
55+
}
56+
57+
Toggle(isOn: $settings.installPrereleases) {
58+
Text("Install pre-releases")
59+
}
6060
}
6161
}
62+
Spacer()
6263
}
63-
Spacer()
64+
.padding(.horizontal, 2)
65+
.padding(.vertical, 15)
6466
}
65-
.padding(.horizontal, 2)
66-
.padding(.vertical, 15)
6767
}
6868
}
6969

7070
#Preview {
7171
AppInfoView(
72-
viewModel: GitHubCopilotViewModel.shared,
7372
store: .init(initialState: .init(), reducer: { General() })
7473
)
7574
}

Core/Sources/HostApp/GeneralSettings/CopilotConnectionView.swift

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ComposableArchitecture
22
import GitHubCopilotViewModel
33
import SwiftUI
4+
import Client
45

56
struct CopilotConnectionView: View {
67
@AppStorage("username") var username: String = ""
@@ -18,23 +19,36 @@ struct CopilotConnectionView: View {
1819
}
1920
}
2021
}
22+
23+
var accountStatusString: String {
24+
switch store.xpcServiceAuthStatus.status {
25+
case .loggedIn:
26+
return "Active"
27+
case .notLoggedIn:
28+
return "Not Signed In"
29+
case .notAuthorized:
30+
return "No Subscription"
31+
case .unknown:
32+
return "Loading..."
33+
}
34+
}
2135

2236
var accountStatus: some View {
2337
SettingsButtonRow(
2438
title: "GitHub Account Status Permissions",
25-
subtitle: "GitHub Account: \(viewModel.status?.description ?? "Loading...")"
39+
subtitle: "GitHub Account: \(accountStatusString)"
2640
) {
2741
if viewModel.isRunningAction || viewModel.waitingForSignIn {
2842
ProgressView().controlSize(.small)
2943
}
3044
Button("Refresh Connection") {
31-
viewModel.checkStatus()
45+
store.send(.reloadStatus)
3246
}
3347
if viewModel.waitingForSignIn {
3448
Button("Cancel") {
3549
viewModel.cancelWaiting()
3650
}
37-
} else if viewModel.status == .notSignedIn {
51+
} else if store.xpcServiceAuthStatus.status == .notLoggedIn {
3852
Button("Log in to GitHub") {
3953
viewModel.signIn()
4054
}
@@ -54,21 +68,31 @@ struct CopilotConnectionView: View {
5468
""")
5569
}
5670
}
57-
if viewModel.status == .ok || viewModel.status == .alreadySignedIn ||
58-
viewModel.status == .notAuthorized
59-
{
60-
Button("Log Out from GitHub") { viewModel.signOut()
61-
viewModel.isSignInAlertPresented = false
71+
if store.xpcServiceAuthStatus.status == .loggedIn || store.xpcServiceAuthStatus.status == .notAuthorized {
72+
Button("Log Out from GitHub") {
73+
Task {
74+
viewModel.signOut()
75+
viewModel.isSignInAlertPresented = false
76+
let service = try getService()
77+
do {
78+
try await service.signOutAllGitHubCopilotService()
79+
} catch {
80+
toast(error.localizedDescription, .error)
81+
}
82+
}
6283
}
6384
}
6485
}
6586
}
6687

6788
var connection: some View {
68-
SettingsSection(title: "Account Settings", showWarning: viewModel.status == .notAuthorized) {
89+
SettingsSection(
90+
title: "Account Settings",
91+
showWarning: store.xpcServiceAuthStatus.status == .notAuthorized
92+
) {
6993
accountStatus
7094
Divider()
71-
if viewModel.status == .notAuthorized {
95+
if store.xpcServiceAuthStatus.status == .notAuthorized {
7296
SettingsLink(
7397
url: "https://github.com/features/copilot/plans",
7498
title: "Enable powerful AI features for free with the GitHub Copilot Free plan"
@@ -81,7 +105,7 @@ struct CopilotConnectionView: View {
81105
)
82106
}
83107
.onReceive(DistributedNotificationCenter.default().publisher(for: .authStatusDidChange)) { _ in
84-
viewModel.checkStatus()
108+
store.send(.reloadStatus)
85109
}
86110
}
87111

Core/Sources/HostApp/GeneralView.swift

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,25 @@ struct GeneralView: View {
77
@StateObject private var viewModel = GitHubCopilotViewModel.shared
88

99
var body: some View {
10-
ScrollView {
11-
VStack(alignment: .leading, spacing: 0) {
12-
generalView.padding(20)
13-
Divider()
14-
rightsView.padding(20)
10+
WithPerceptionTracking {
11+
ScrollView {
12+
VStack(alignment: .leading, spacing: 0) {
13+
generalView.padding(20)
14+
Divider()
15+
rightsView.padding(20)
16+
}
17+
.frame(maxWidth: .infinity)
18+
}
19+
.task {
20+
if isPreview { return }
21+
await store.send(.appear).finish()
1522
}
16-
.frame(maxWidth: .infinity)
17-
}
18-
.task {
19-
if isPreview { return }
20-
viewModel.checkStatus()
21-
await store.send(.appear).finish()
2223
}
2324
}
2425

2526
private var generalView: some View {
2627
VStack(alignment: .leading, spacing: 30) {
27-
AppInfoView(viewModel: viewModel, store: store)
28+
AppInfoView(store: store)
2829
GeneralSettingsView(store: store)
2930
CopilotConnectionView(viewModel: viewModel, store: store)
3031
}

Core/Sources/HostApp/MCPConfigView.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,6 @@ struct MCPConfigView: View {
161161
UserDefaults.shared.set(jsonString, for: \.gitHubCopilotMCPConfig)
162162
}
163163

164-
NotificationCenter.default.post(
165-
name: .gitHubCopilotShouldRefreshEditorInformation,
166-
object: nil
167-
)
168-
169164
Task {
170165
let service = try getService()
171166
do {

Core/Sources/HostApp/MCPSettings/CopilotMCPToolManagerObservable.swift

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import SwiftUI
22
import Combine
33
import Persist
44
import GitHubCopilotService
5+
import Client
6+
import Logger
57

68
class CopilotMCPToolManagerObservable: ObservableObject {
79
static let shared = CopilotMCPToolManagerObservable()
@@ -10,23 +12,42 @@ class CopilotMCPToolManagerObservable: ObservableObject {
1012
private var cancellables = Set<AnyCancellable>()
1113

1214
private init() {
13-
// Initial load
14-
availableMCPServerTools = CopilotMCPToolManager.getAvailableMCPServerToolsCollections()
15-
16-
// Setup notification to update when MCP server tools collections change
17-
NotificationCenter.default
15+
DistributedNotificationCenter.default()
1816
.publisher(for: .gitHubCopilotMCPToolsDidChange)
1917
.receive(on: DispatchQueue.main)
2018
.sink { [weak self] _ in
2119
guard let self = self else { return }
22-
self.refreshTools()
20+
Task {
21+
await self.refreshMCPServerTools()
22+
}
2323
}
2424
.store(in: &cancellables)
25+
26+
Task {
27+
// Initial load of MCP server tools collections from ExtensionService process
28+
await refreshMCPServerTools()
29+
}
30+
}
31+
32+
@MainActor
33+
private func refreshMCPServerTools() async {
34+
do {
35+
let service = try getService()
36+
let mcpTools = try await service.getAvailableMCPServerToolsCollections()
37+
refreshTools(tools: mcpTools)
38+
} catch {
39+
Logger.client.error("Failed to fetch MCP server tools: \(error)")
40+
}
2541
}
26-
27-
private func refreshTools() {
28-
self.availableMCPServerTools = CopilotMCPToolManager.getAvailableMCPServerToolsCollections()
29-
AppState.shared.cleanupMCPToolsStatus(availableTools: self.availableMCPServerTools)
30-
AppState.shared.createMCPToolsStatus(self.availableMCPServerTools)
42+
43+
private func refreshTools(tools: [MCPServerToolsCollection]?) {
44+
guard let tools = tools else {
45+
// nil means the tools data is ready, and skip it first.
46+
return
47+
}
48+
49+
AppState.shared.cleanupMCPToolsStatus(availableTools: tools)
50+
AppState.shared.createMCPToolsStatus(tools)
51+
self.availableMCPServerTools = tools
3152
}
3253
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy