Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite445012

Browse files
committed
Pre-release 0.44.147
1 parent2a6a8d6 commite445012

File tree

10 files changed

+238
-48
lines changed

10 files changed

+238
-48
lines changed

‎Core/Sources/HostApp/SharedComponents/Badge.swift‎

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,30 @@ struct BadgeItem {
2222

2323
structBadge:View{
2424
lettext:String
25+
letattributedText:AttributedString?
2526
letlevel:BadgeItem.Level
2627
leticon:String?
2728
letisSelected:Bool
2829

2930
init(badgeItem:BadgeItem){
3031
text= badgeItem.text
32+
attributedText=nil
3133
level= badgeItem.level
3234
icon= badgeItem.icon
3335
isSelected= badgeItem.isSelected
3436
}
3537

3638
init(text:String, level:BadgeItem.Level, icon:String?=nil, isSelected:Bool=false){
3739
self.text= text
40+
self.attributedText=nil
41+
self.level= level
42+
self.icon= icon
43+
self.isSelected= isSelected
44+
}
45+
46+
init(attributedText:AttributedString, level:BadgeItem.Level, icon:String?=nil, isSelected:Bool=false){
47+
self.text=String(attributedText.characters)
48+
self.attributedText= attributedText
3849
self.level= level
3950
self.icon= icon
4051
self.isSelected= isSelected
@@ -47,10 +58,18 @@ struct Badge: View {
4758
.font(.caption2)
4859
.padding(.vertical,1)
4960
}
50-
Text(text)
51-
.fontWeight(.semibold)
52-
.font(.caption2)
53-
.lineLimit(1)
61+
iflet attributedText= attributedText{
62+
Text(attributedText)
63+
.fontWeight(.semibold)
64+
.font(.caption2)
65+
.lineLimit(1)
66+
.truncationMode(.middle)
67+
}else{
68+
Text(text)
69+
.fontWeight(.semibold)
70+
.font(.caption2)
71+
.lineLimit(1)
72+
}
5473
}
5574
.padding(.vertical,1)
5675
.padding(.horizontal,3)
@@ -77,5 +96,6 @@ struct Badge: View {
7796
lineWidth:1
7897
)
7998
)
99+
.help(text)
80100
}
81101
}

‎Core/Sources/HostApp/ToolsConfigView.swift‎

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ struct MCPConfigView: View {
2222
@Environment(\.colorScheme)varcolorScheme
2323

2424
privatestaticvarlastSyncTimestamp:Date?=nil
25+
@StateprivatevardebounceTimer:Timer?
26+
privatestaticletrefreshDebounceInterval:TimeInterval=1.0 // 1.0 second debounce
2527

2628
enumToolType:String,CaseIterable,Identifiable{
2729
case MCP, BuiltIn
@@ -241,16 +243,24 @@ struct MCPConfigView: View {
241243
UserDefaults.shared.set(jsonString, for: \.gitHubCopilotMCPConfig)
242244
}
243245

244-
Task{
245-
do{
246-
letservice=trygetService()
247-
tryawait service.postNotification(
248-
name:Notification.Name
249-
.gitHubCopilotShouldRefreshEditorInformation.rawValue
250-
)
251-
toast("MCP configuration updated",.info)
252-
}catch{
253-
toast(error.localizedDescription,.error)
246+
// Debounce the refresh notification to avoid sending too frequently
247+
debounceTimer?.invalidate()
248+
debounceTimer=Timer.scheduledTimer(withTimeInterval:MCPConfigView.refreshDebounceInterval, repeats:false){ _in
249+
Task{
250+
do{
251+
letservice=trygetService()
252+
tryawait service.postNotification(
253+
name:Notification.Name
254+
.gitHubCopilotShouldRefreshEditorInformation.rawValue
255+
)
256+
awaitMainActor.run{
257+
toast("Fetching MCP tools...",.info)
258+
}
259+
}catch{
260+
awaitMainActor.run{
261+
toast(error.localizedDescription,.error)
262+
}
263+
}
254264
}
255265
}
256266
}

‎Core/Sources/HostApp/ToolsSettings/MCPRegistry/MCPRegistryInstallation.swift‎

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -263,25 +263,8 @@ public class MCPRegistryService: ObservableObject {
263263
letjsonData=tryJSONSerialization.data(withJSONObject: config, options:[.prettyPrinted])
264264
try jsonData.write(to: configFileURL)
265265

266-
// Update UserDefaults and trigger refresh
267-
// Extract only the "servers" object to save to UserDefaults (consistent with ToolsConfigView)
268-
iflet serversDict=config["servers"]as?[String:Any]{
269-
letserversData=tryJSONSerialization.data(withJSONObject: serversDict, options:[.prettyPrinted])
270-
iflet jsonString=String(data: serversData, encoding:.utf8){
271-
UserDefaults.shared.set(jsonString, for: \.gitHubCopilotMCPConfig)
272-
}
273-
}
274-
275-
Task{
276-
do{
277-
letservice=trygetService()
278-
tryawait service.postNotification(
279-
name:Notification.Name.gitHubCopilotShouldRefreshEditorInformation.rawValue
280-
)
281-
}catch{
282-
Logger.client.error("Failed to post refresh notification:\(error)")
283-
}
284-
}
266+
// Note: UserDefaults update and notification will be handled by ToolsConfigView's file monitor
267+
// with debouncing to prevent duplicate notifications
285268
}
286269

287270
// MARK: - Server Installation Status

‎Core/Sources/HostApp/ToolsSettings/MCPRegistry/MCPRegistryURLInputField.swift‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ struct MCPRegistryURLInputField: View {
1313
letisSheet:Bool
1414
letmcpRegistryEntry:MCPRegistryEntry?
1515
letonValidationChange:((Bool)->Void)?
16+
letonCommit:(()->Void)?
1617

1718
privatevarisRegistryOnly:Bool{
1819
mcpRegistryEntry?.registryAccess==.registryOnly
@@ -23,13 +24,15 @@ struct MCPRegistryURLInputField: View {
2324
maxURLLength:Int=2048,
2425
isSheet:Bool=false,
2526
mcpRegistryEntry:MCPRegistryEntry?=nil,
26-
onValidationChange:((Bool)->Void)?=nil
27+
onValidationChange:((Bool)->Void)?=nil,
28+
onCommit:(()->Void)?=nil
2729
){
2830
self._urlText= urlText
2931
self.maxURLLength= maxURLLength
3032
self.isSheet= isSheet
3133
self.mcpRegistryEntry= mcpRegistryEntry
3234
self.onValidationChange= onValidationChange
35+
self.onCommit= onCommit
3336
}
3437

3538
varbody:someView{
@@ -43,6 +46,9 @@ struct MCPRegistryURLInputField: View {
4346
.onChange(of: urlText){ newValuein
4447
handleURLChange(newValue)
4548
}
49+
.onSubmit{
50+
onCommit?()
51+
}
4652
}
4753
}else{
4854
TextField("MCP Registry URL:", text: $urlText)
@@ -52,20 +58,25 @@ struct MCPRegistryURLInputField: View {
5258
.onChange(of: urlText){ newValuein
5359
handleURLChange(newValue)
5460
}
61+
.onSubmit{
62+
onCommit?()
63+
}
5564
}
5665

5766
Menu{
5867
ForEach(urlHistory, id: \.self){ urlin
5968
Button(url){
6069
urlText= url
6170
isFocused=false
71+
onCommit?()
6272
}
6373
}
6474

6575
Divider()
6676

6777
Button("Reset to Default"){
6878
urlText= defaultMCPRegistryURL
79+
onCommit?()
6980
}
7081

7182
if !urlHistory.isEmpty{

‎Core/Sources/HostApp/ToolsSettings/MCPRegistry/MCPRegistryURLSheet.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,6 @@ struct MCPRegistryURLSheet: View {
6666
}
6767

6868
privatefunc openHelpLink(){
69-
NSWorkspace.shared.open(URL(string:"https://registry.mcpservers.org")!)
69+
NSWorkspace.shared.open(URL(string:"https://docs.github.com/en/copilot/how-tos/provide-context/use-mcp/select-an-mcp-registry")!)
7070
}
7171
}

‎Core/Sources/HostApp/ToolsSettings/MCPRegistry/MCPRegistryURLView.swift‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,12 @@ struct MCPRegistryURLView: View {
7575
maxURLLength: maxURLLength,
7676
isSheet:false,
7777
mcpRegistryEntry: mcpRegistry?.first,
78-
onValidationChange:{ isValidin
79-
if isValid &&(!tempURLText.isEmpty || tempURLText.isEmpty){
78+
onValidationChange:{ _in
79+
// Only validate, don't update mcpRegistryURL here
80+
},
81+
onCommit:{
82+
// Update mcpRegistryURL when user presses Enter
83+
if tempURLText!= mcpRegistryURL{
8084
mcpRegistryURL= tempURLText
8185
}
8286
}
@@ -105,6 +109,7 @@ struct MCPRegistryURLView: View {
105109
)
106110
.animation(.easeInOut(duration:0.3), value: isExpanded)
107111
.onAppear{
112+
tempURLText= mcpRegistryURL
108113
Task{awaitgetMCPRegistryAllowlist()}
109114
}
110115
.onReceive(DistributedNotificationCenter.default().publisher(for:.authStatusDidChange)){ _in
@@ -122,6 +127,11 @@ struct MCPRegistryURLView: View {
122127
}
123128

124129
privatefunc loadMCPServers()async{
130+
// Update mcpRegistryURL with current tempURLText before loading
131+
if tempURLText!= mcpRegistryURL{
132+
mcpRegistryURL= tempURLText
133+
}
134+
125135
isLoading=true
126136
defer{ isLoading=false}
127137
do{
@@ -208,7 +218,17 @@ struct MCPRegistryURLView: View {
208218
defer{ isLoading=false}
209219

210220
// Let the view model handle the entire update flow including clearing and fetching
211-
awaitMCPServerGalleryWindow.refreshFromURL(mcpRegistryEntry: mcpRegistry?.first)
221+
iflet error=awaitMCPServerGalleryWindow.refreshFromURL(mcpRegistryEntry: mcpRegistry?.first){
222+
// Display error in the URL view
223+
iflet serviceError= erroras?XPCExtensionServiceError{
224+
errorMessage= serviceError.underlyingError?.localizedDescription?? serviceError.localizedDescription
225+
}else{
226+
errorMessage= error.localizedDescription
227+
}
228+
isExpanded=true
229+
}else{
230+
errorMessage=""
231+
}
212232
}
213233
}
214234

‎Core/Sources/HostApp/ToolsSettings/MCPRegistry/MCPServerGalleryView.swift‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import GitHubCopilotService
55
import Logger
66
import SharedUIComponents
77
import SwiftUI
8+
import XPCShared
89

910
enumMCPServerGalleryWindow{
1011
staticletidentifier="MCPServerGalleryWindow"
@@ -53,8 +54,8 @@ enum MCPServerGalleryWindow {
5354
currentViewModel?.updateData(serverList: serverList, mcpRegistryEntry: mcpRegistryEntry)
5455
}
5556

56-
@MainActorstaticfunc refreshFromURL(mcpRegistryEntry:MCPRegistryEntry?=nil)async{
57-
await currentViewModel?.refreshFromURL(mcpRegistryEntry: mcpRegistryEntry)
57+
@MainActorstaticfunc refreshFromURL(mcpRegistryEntry:MCPRegistryEntry?=nil)async->Error?{
58+
returnawait currentViewModel?.refreshFromURL(mcpRegistryEntry: mcpRegistryEntry)
5859
}
5960

6061
staticfunc isOpen()->Bool{
@@ -87,6 +88,14 @@ struct MCPServerGalleryView: View {
8788

8889
varbody:someView{
8990
VStack(spacing:0){
91+
iflet error= viewModel.lastError{
92+
iflet serviceError= erroras?XPCExtensionServiceError{
93+
Badge(text: serviceError.underlyingError?.localizedDescription?? serviceError.localizedDescription, level:.danger, icon:"xmark.circle.fill")
94+
}else{
95+
Badge(text: error.localizedDescription, level:.danger, icon:"xmark.circle.fill")
96+
}
97+
}
98+
9099
tableHeaderView
91100
serverListView
92101
}

‎Core/Sources/HostApp/ToolsSettings/MCPRegistry/MCPServerGalleryViewModel.swift‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ final class MCPServerGalleryViewModel: ObservableObject {
2828
@PublishedvarpendingServer:MCPRegistryServerDetail?
2929
@PublishedvarinfoSheetServer:MCPRegistryServerDetail?
3030
@PublishedvarmcpRegistryEntry:MCPRegistryEntry?
31+
@Publishedprivate(set)varlastError:Error?
3132

3233
@AppStorage(\.mcpRegistryURL)varmcpRegistryURL
3334

@@ -154,11 +155,12 @@ final class MCPServerGalleryViewModel: ObservableObject {
154155
searchText=""
155156

156157
// Load servers from the base URL
157-
awaitloadServerList(resetToFirstPage:true)
158+
_=awaitloadServerList(resetToFirstPage:true)
158159
}
159160
}
160161

161-
func refreshFromURL(mcpRegistryEntry:MCPRegistryEntry?=nil)async{
162+
// Called from Settings view to refresh with optional new registry entry
163+
func refreshFromURL(mcpRegistryEntry:MCPRegistryEntry?=nil)async->Error?{
162164
isRefreshing=true
163165
defer{ isRefreshing=false}
164166

@@ -170,10 +172,12 @@ final class MCPServerGalleryViewModel: ObservableObject {
170172
Logger.client.info("Cleared gallery view model data for refresh")
171173

172174
// Load servers from the base URL
173-
awaitloadServerList(resetToFirstPage:true)
175+
leterror=awaitloadServerList(resetToFirstPage:true)
174176

175177
// Reload installed servers after fetching new data
176178
loadInstalledServers()
179+
180+
return error
177181
}
178182

179183
func updateData(serverList:MCPRegistryServerList, mcpRegistryEntry:MCPRegistryEntry?=nil){
@@ -214,7 +218,7 @@ final class MCPServerGalleryViewModel: ObservableObject {
214218
}
215219
}
216220

217-
privatefunc loadServerList(resetToFirstPage:Bool)async{
221+
privatefunc loadServerList(resetToFirstPage:Bool)async->Error?{
218222
if resetToFirstPage{
219223
isInitialLoading=true
220224
}else{
@@ -225,6 +229,8 @@ final class MCPServerGalleryViewModel: ObservableObject {
225229
isInitialLoading=false
226230
isLoadingMore=false
227231
}
232+
233+
lastError=nil
228234

229235
do{
230236
letservice=trygetService()
@@ -247,8 +253,12 @@ final class MCPServerGalleryViewModel: ObservableObject {
247253
servers.append(contentsOf: serverList?.servers??[])
248254
registryMetadata= serverList?.metadata
249255
}
256+
257+
returnnil
250258
}catch{
251259
Logger.client.error("Failed to load MCP servers:\(error)")
260+
lastError= error
261+
return error
252262
}
253263
}
254264

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp