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

Commitf51663a

Browse files
committed
Pre-release 0.32.112
1 parentc32cc7c commitf51663a

File tree

16 files changed

+170
-83
lines changed

16 files changed

+170
-83
lines changed

‎Core/Sources/ChatService/ChatService.swift

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -88,20 +88,8 @@ public final class ChatService: ChatServiceType, ObservableObject {
8888

8989
privatefunc subscribeToWatchedFilesHandler(){
9090
self.watchedFilesHandler.onWatchedFiles.sink(receiveValue:{[weak self](request, completion)in
91-
guardlet self,
92-
request.params!.workspaceUri!="/",
93-
!ProjectContextSkill.isWorkspaceResolved(self.chatTabInfo.workspacePath)
94-
else{return}
95-
96-
ProjectContextSkill.resolveSkill(
97-
request: request,
98-
workspacePath:self.chatTabInfo.workspacePath,
99-
completion: completion
100-
)
101-
102-
/// after sync complete files to CLS, start file watcher
91+
guardlet self, request.params!.workspaceUri!="/"else{return}
10392
self.startFileChangeWatcher()
104-
10593
}).store(in:&cancellables)
10694
}
10795

@@ -463,20 +451,17 @@ public final class ChatService: ChatServiceType, ObservableObject {
463451

464452
Task{
465453
// mark running steps to cancelled
466-
ifvar message=await memory.history.last,
467-
message.role==.assistant{
468-
message.steps= message.steps.map{ stepin
469-
return.init(
470-
id: step.id,
471-
title: step.title,
472-
description: step.description,
473-
status: step.status==.running?.cancelled: step.status,
474-
error: step.error
475-
)
476-
}
454+
awaitmutateHistory({ historyin
455+
guard !history.isEmpty,
456+
let lastIndex= history.indices.last,
457+
history[lastIndex].role==.assistantelse{return}
477458

478-
await memory.appendMessage(message)
479-
}
459+
foriin0..<history[lastIndex].steps.count{
460+
ifhistory[lastIndex].steps[i].status==.running{
461+
history[lastIndex].steps[i].status=.cancelled
462+
}
463+
}
464+
})
480465

481466
// The message of progress report could change rapidly
482467
// Directly upsert the last chat message of history here

‎Core/Sources/ConversationTab/ModelPicker.swift

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,11 @@ extension AppState {
3131
extensionCopilotModelManager{
3232
staticfunc getAvailableChatLLMs()->[LLMModel]{
3333
letLLMs=CopilotModelManager.getAvailableLLMs()
34-
letavailableModels=LLMs.filter(
34+
returnLLMs.filter(
3535
{ $0.scopes.contains(.chatPanel)}
3636
).map{
3737
LLMModel(modelName: $0.modelName, modelFamily: $0.modelFamily)
3838
}
39-
return availableModels.isEmpty?[defaultModel]: availableModels
4039
}
4140
}
4241

@@ -50,6 +49,7 @@ struct ModelPicker: View {
5049
@StateprivatevarselectedModel= defaultModel.modelName
5150
@StateprivatevarisHovered=false
5251
@StateprivatevarisPressed=false
52+
staticvarlastRefreshModelsTime:Date=.init(timeIntervalSince1970:0)
5353

5454
init(){
5555
self.updateCurrentModel()
@@ -66,15 +66,23 @@ struct ModelPicker: View {
6666
varbody:someView{
6767
WithPerceptionTracking{
6868
Menu(selectedModel){
69-
ForEach(models, id: \.self){ optionin
69+
if models.isEmpty{
7070
Button{
71-
selectedModel= option.modelName
72-
AppState.shared.setSelectedModel(option)
71+
// No action needed
7372
} label:{
74-
if selectedModel== option.modelName{
75-
Text("\(option.modelName)")
76-
}else{
77-
Text("\(option.modelName)")
73+
Text("Loading...")
74+
}
75+
}else{
76+
ForEach(models, id: \.self){ optionin
77+
Button{
78+
selectedModel= option.modelName
79+
AppState.shared.setSelectedModel(option)
80+
} label:{
81+
if selectedModel== option.modelName{
82+
Text("\(option.modelName)")
83+
}else{
84+
Text("\(option.modelName)")
85+
}
7886
}
7987
}
8088
}
@@ -108,6 +116,12 @@ struct ModelPicker: View {
108116

109117
@MainActor
110118
func refreshModels()async{
119+
letnow=Date()
120+
if now.timeIntervalSince(Self.lastRefreshModelsTime)<60{
121+
return
122+
}
123+
124+
Self.lastRefreshModelsTime= now
111125
letcopilotModels=awaitSharedChatService.shared.copilotModels()
112126
if !copilotModels.isEmpty{
113127
CopilotModelManager.updateLLMs(copilotModels)

‎Core/Sources/ConversationTab/Views/ConversationProgressStepView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct StatusItemView: View {
2929
case.running:
3030
ProgressView()
3131
.controlSize(.small)
32+
.frame(width:16, height:16)
3233
.scaleEffect(0.7)
3334
case.completed:
3435
Image(systemName:"checkmark")

‎Core/Sources/GitHubCopilotViewModel/GitHubCopilotViewModel.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,12 @@ public class GitHubCopilotViewModel: ObservableObject {
155155
waitingForSignIn=false
156156
self.username= username
157157
self.status= status
158+
awaitStatus.shared.updateAuthStatus(.loggedIn, username: username)
159+
broadcastStatusChange()
158160
letmodels=try?await service.models()
159161
iflet models= models, !models.isEmpty{
160162
CopilotModelManager.updateLLMs(models)
161163
}
162-
awaitStatus.shared.updateAuthStatus(.loggedIn, username: username)
163-
broadcastStatusChange()
164164
}catchlet error asGitHubCopilotError{
165165
if case.languageServerError(.timeout)= error{
166166
// TODO figure out how to extend the default timeout on a Chime LSP request

‎Core/Sources/PersistMiddleware/Extensions/ChatMessage+Storage.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,29 @@ extension ChatMessage {
1414
varsuggestedTitle:String?
1515
varerrorMessage:String?
1616
varsteps:[ConversationProgressStep]
17+
18+
// Custom decoder to provide default value for steps
19+
init(from decoder:Decoder)throws{
20+
letcontainer=try decoder.container(keyedBy:CodingKeys.self)
21+
content=try container.decode(String.self, forKey:.content)
22+
rating=try container.decode(ConversationRating.self, forKey:.rating)
23+
references=try container.decode([ConversationReference].self, forKey:.references)
24+
followUp=try container.decodeIfPresent(ConversationFollowUp.self, forKey:.followUp)
25+
suggestedTitle=try container.decodeIfPresent(String.self, forKey:.suggestedTitle)
26+
errorMessage=try container.decodeIfPresent(String.self, forKey:.errorMessage)
27+
steps=try container.decodeIfPresent([ConversationProgressStep].self, forKey:.steps)??[]
28+
}
29+
30+
// Default memberwise init for encoding
31+
init(content:String, rating:ConversationRating, references:[ConversationReference], followUp:ConversationFollowUp?, suggestedTitle:String?, errorMessage:String?, steps:[ConversationProgressStep]?){
32+
self.content= content
33+
self.rating= rating
34+
self.references= references
35+
self.followUp= followUp
36+
self.suggestedTitle= suggestedTitle
37+
self.errorMessage= errorMessage
38+
self.steps= steps??[]
39+
}
1740
}
1841

1942
func toTurnItem()->TurnItem{

‎Core/Sources/Service/Service.swift

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -94,24 +94,32 @@ public final class Service {
9494
keyBindingManager.start()
9595

9696
Task{
97-
awaitXcodeInspector.shared.safe.$activeDocumentURL
98-
.removeDuplicates()
99-
.filter{ $0!=.init(fileURLWithPath:"/")}
100-
.compactMap{ $0}
101-
.sink{[weak self] fileURLin
102-
Task{
103-
do{
104-
let _=tryawaitself?.workspacePool
105-
.fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL)
106-
}catchlet error asWorkspace.WorkspaceFileError{
107-
Logger.workspacePool
108-
.info(error.localizedDescription)
109-
}
110-
catch{
111-
Logger.workspacePool.error(error)
112-
}
97+
awaitPublishers.CombineLatest(
98+
XcodeInspector.shared.safe.$activeDocumentURL
99+
.removeDuplicates(),
100+
XcodeInspector.shared.safe.$latestActiveXcode
101+
)
102+
.receive(on:DispatchQueue.main)
103+
.sink{[weak self] documentURL, latestXcodein
104+
Task{
105+
letfileURL= documentURL?? latestXcode?.realtimeDocumentURL
106+
guard fileURL!=nil, fileURL!=.init(fileURLWithPath:"/")else{
107+
return
108+
}
109+
do{
110+
let _=tryawaitself?.workspacePool
111+
.fetchOrCreateWorkspaceAndFilespace(
112+
fileURL: fileURL!
113+
)
114+
}catchlet error asWorkspace.WorkspaceFileError{
115+
Logger.workspacePool
116+
.info(error.localizedDescription)
117+
}
118+
catch{
119+
Logger.workspacePool.error(error)
113120
}
114-
}.store(in:&cancellable)
121+
}
122+
}.store(in:&cancellable)
115123

116124
// Combine both workspace and auth status changes into a single stream
117125
awaitPublishers.CombineLatest3(
@@ -202,13 +210,11 @@ extension Service {
202210
letname=self.getDisplayNameOfXcodeWorkspace(url: workspaceURL)
203211
letpath= workspaceURL.path
204212

205-
// switch workspace and username
206-
self.guiController.store.send(.switchWorkspace(path: path, name: name, username: username))
207-
213+
// switch workspace and username and wait for it to complete
214+
awaitself.guiController.store.send(.switchWorkspace(path: path, name: name, username: username)).finish()
208215
// restore if needed
209216
awaitself.guiController.restore(path: path, name: name, username: username)
210-
211-
// init chat tab if no history tab
212-
self.guiController.store.send(.initWorkspaceChatTabIfNeeded(path: path, username: username))
217+
// init chat tab if no history tab (only after workspace is fully switched and restored)
218+
awaitself.guiController.store.send(.initWorkspaceChatTabIfNeeded(path: path, username: username)).finish()
213219
}
214220
}

‎Core/Sources/SuggestionWidget/FeatureReducers/ChatPanelFeature.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,10 +513,13 @@ public struct ChatPanelFeature {
513513
ifvar existChatWorkspace= state.chatHistory.workspaces[id: chatWorkspace.id]{
514514

515515
ifvar selectedChatTabInfo= chatWorkspace.tabInfo.first(where:{ $0.id== chatWorkspace.selectedTabId}){
516-
//cancel selectedChatTabInfo in chat workspace
517-
selectedChatTabInfo.isSelected=false
516+
//Keep the selection state when restoring
517+
selectedChatTabInfo.isSelected=true
518518
chatWorkspace.tabInfo[id: selectedChatTabInfo.id]= selectedChatTabInfo
519519

520+
// Update the existing workspace's selected tab to match
521+
existChatWorkspace.selectedTabId= selectedChatTabInfo.id
522+
520523
// merge tab info
521524
existChatWorkspace.tabInfo.append(contentsOf: chatWorkspace.tabInfo)
522525
state.chatHistory.updateHistory(existChatWorkspace)

‎ExtensionService/AppDelegate.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
180180
.userInfo?[NSWorkspace.applicationUserInfoKey]as?NSRunningApplication,
181181
app.isUserOfService
182182
else{continue}
183-
ifNSWorkspace.shared.runningApplications.contains(where: \.isUserOfService){
184-
continue
183+
184+
// Check if Xcode is running
185+
letisXcodeRunning=NSWorkspace.shared.runningApplications.contains{
186+
$0.bundleIdentifier=="com.apple.dt.Xcode"
187+
}
188+
189+
if !isXcodeRunning{
190+
Logger.client.info("No Xcode instances running, preparing to quit")
191+
quit()
185192
}
186-
quit()
187193
}
188194
}
189195
}

‎Tool/Sources/ConversationServiceProvider/ConversationServiceProvider.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ public struct ConversationProgressStep: Codable, Equatable, Identifiable {
170170
publicletid:String
171171
publiclettitle:String
172172
publicletdescription:String?
173-
publicletstatus:StepStatus
173+
publicvarstatus:StepStatus
174174
publicleterror:StepError?
175175

176176
publicinit(id:String, title:String, description:String?, status:StepStatus, error:StepError?){
Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,55 @@
11
import JSONRPC
22
import Combine
3+
import Workspace
4+
import XcodeInspector
5+
import Foundation
36

47
publicprotocolWatchedFilesHandler{
58
varonWatchedFiles:PassthroughSubject<(WatchedFilesRequest,(AnyJSONRPCResponse)->Void),Never>{get}
6-
func handleWatchedFiles(_ request:WatchedFilesRequest, completion:@escaping(AnyJSONRPCResponse)->Void)
9+
func handleWatchedFiles(_ request:WatchedFilesRequest,workspaceURL:URL,completion:@escaping(AnyJSONRPCResponse)->Void, service:GitHubCopilotService?)
710
}
811

912
publicfinalclassWatchedFilesHandlerImpl:WatchedFilesHandler{
1013
publicstaticletshared=WatchedFilesHandlerImpl()
1114

1215
publicletonWatchedFiles:PassthroughSubject<(WatchedFilesRequest,(AnyJSONRPCResponse)->Void),Never>=.init()
16+
17+
publicfunc handleWatchedFiles(_ request:WatchedFilesRequest, workspaceURL:URL, completion:@escaping(AnyJSONRPCResponse)->Void, service:GitHubCopilotService?){
18+
guardlet params= request.params, params.workspaceUri!="/"else{return}
1319

14-
publicfunc handleWatchedFiles(_ request:WatchedFilesRequest, completion:@escaping(AnyJSONRPCResponse)->Void){
20+
letprojectURL=WorkspaceXcodeWindowInspector.extractProjectURL(workspaceURL: workspaceURL, documentURL:nil)?? workspaceURL
21+
22+
letfiles=WorkspaceFile.getWatchedFiles(
23+
workspaceURL: workspaceURL,
24+
projectURL: projectURL,
25+
excludeGitIgnoredFiles: params.excludeGitignoredFiles,
26+
excludeIDEIgnoredFiles: params.excludeIDEIgnoredFiles
27+
)
28+
29+
letbatchSize=BatchingFileChangeWatcher.maxEventPublishSize
30+
/// only `batchSize`(100) files to complete this event for setup watching workspace in CLS side
31+
letjsonResult:JSONValue=.array(files.prefix(batchSize).map{.string($0)})
32+
letjsonValue:JSONValue=.hash(["files": jsonResult])
33+
34+
completion(AnyJSONRPCResponse(id: request.id, result: jsonValue))
35+
36+
Task{
37+
if files.count> batchSize{
38+
forstartIndexinstride(from: batchSize, to: files.count, by: batchSize){
39+
letendIndex=min(startIndex+ batchSize, files.count)
40+
letbatch=Array(files[startIndex..<endIndex])
41+
try?await service?.notifyDidChangeWatchedFiles(.init(
42+
workspaceUri: params.workspaceUri,
43+
changes: batch.map{.init(uri: $0, type:.created)}
44+
))
45+
46+
try?awaitTask.sleep(nanoseconds:100_000_000) // 100ms
47+
}
48+
}
49+
}
50+
51+
/// publish event for watching workspace file changes
1552
onWatchedFiles.send((request, completion))
1653
}
1754
}
55+

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp