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

Commite7fd64d

Browse files
committed
Pre-release 0.38.129
1 parentd1f7de3 commite7fd64d

File tree

28 files changed

+820
-334
lines changed

28 files changed

+820
-334
lines changed

‎Core/Sources/ChatService/ChatService.swift

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ public final class ChatService: ChatServiceType, ObservableObject {
9595

9696
subscribeToNotifications()
9797
subscribeToConversationContextRequest()
98-
subscribeToWatchedFilesHandler()
9998
subscribeToClientToolInvokeEvent()
10099
subscribeToClientToolConfirmationEvent()
101100
}
@@ -143,13 +142,6 @@ public final class ChatService: ChatServiceType, ObservableObject {
143142
}
144143
}).store(in:&cancellables)
145144
}
146-
147-
privatefunc subscribeToWatchedFilesHandler(){
148-
self.watchedFilesHandler.onWatchedFiles.sink(receiveValue:{[weak self](request, completion)in
149-
guardlet self, request.params!.workspaceFolder.uri!="/"else{return}
150-
self.startFileChangeWatcher()
151-
}).store(in:&cancellables)
152-
}
153145

154146
privatefunc subscribeToClientToolConfirmationEvent(){
155147
ClientToolHandlerImpl.shared.onClientToolConfirmationEvent.sink(receiveValue:{[weak self](request, completion)in
@@ -1042,26 +1034,6 @@ extension ChatService {
10421034
func fetchAllChatMessagesFromStorage()->[ChatMessage]{
10431035
returnChatMessageStore.getAll(by:self.chatTabInfo.id, metadata:.init(workspacePath:self.chatTabInfo.workspacePath, username:self.chatTabInfo.username))
10441036
}
1045-
1046-
/// for file change watcher
1047-
func startFileChangeWatcher(){
1048-
Task{[weak self]in
1049-
guardlet selfelse{return}
1050-
letworkspaceURL=URL(fileURLWithPath:self.chatTabInfo.workspacePath)
1051-
letprojectURL=WorkspaceXcodeWindowInspector.extractProjectURL(workspaceURL: workspaceURL, documentURL:nil)?? workspaceURL
1052-
awaitFileChangeWatcherServicePool.shared.watch(
1053-
for: workspaceURL
1054-
){ fileEventsin
1055-
Task{[weak self]in
1056-
guardlet selfelse{return}
1057-
try?awaitself.conversationProvider?.notifyDidChangeWatchedFiles(
1058-
.init(workspaceUri: projectURL.path, changes: fileEvents),
1059-
workspace:.init(workspaceURL: workspaceURL, projectURL: projectURL)
1060-
)
1061-
}
1062-
}
1063-
}
1064-
}
10651037
}
10661038

10671039
func replaceFirstWord(in content:String, from oldWord:String, to newWord:String)->String{

‎Core/Sources/ConversationTab/ChatPanel.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ struct ChatPanelInputArea: View {
499499
varfocusedField:FocusState<Chat.State.Field?>.Binding
500500
@Statevarcancellable=Set<AnyCancellable>()
501501
@StateprivatevarisFilePickerPresented=false
502-
@StateprivatevarallFiles:[FileReference]=[]
502+
@StateprivatevarallFiles:[FileReference]?=nil
503503
@StateprivatevarfilteredTemplates:[ChatTemplate]=[]
504504
@StateprivatevarfilteredAgent:[ChatAgent]=[]
505505
@StateprivatevarshowingTemplates=false
@@ -528,7 +528,7 @@ struct ChatPanelInputArea: View {
528528
}
529529
)
530530
.onAppear(){
531-
allFiles=ContextUtils.getFilesInActiveWorkspace(workspaceURL: chat.workspaceURL)
531+
allFiles=ContextUtils.getFilesFromWorkspaceIndex(workspaceURL: chat.workspaceURL)
532532
}
533533
}
534534

‎Core/Sources/ConversationTab/ContextUtils.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import SystemUtils
77

88
publicstructContextUtils{
99

10+
publicstaticfunc getFilesFromWorkspaceIndex(workspaceURL:URL?)->[FileReference]?{
11+
guardlet workspaceURL= workspaceURLelse{return[]}
12+
returnWorkspaceFileIndex.shared.getFiles(for: workspaceURL)
13+
}
14+
1015
publicstaticfunc getFilesInActiveWorkspace(workspaceURL:URL?)->[FileReference]{
1116
iflet workspaceURL= workspaceURL,let info=WorkspaceFile.getWorkspaceInfo(workspaceURL: workspaceURL){
1217
returnWorkspaceFile.getFilesInActiveWorkspace(workspaceURL: info.workspaceURL, workspaceRootURL: info.projectURL)

‎Core/Sources/ConversationTab/FilePicker.swift

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import SwiftUI
55
import SystemUtils
66

77
publicstructFilePicker:View{
8-
@BindingvarallFiles:[FileReference]
8+
@BindingvarallFiles:[FileReference]?
99
letworkspaceURL:URL?
1010
varonSubmit:(_ file:FileReference)->Void
1111
varonExit:()->Void
@@ -14,20 +14,21 @@ public struct FilePicker: View {
1414
@StateprivatevarselectedId:Int=0
1515
@StateprivatevarlocalMonitor:Any?=nil
1616

17-
privatevarfilteredFiles:[FileReference]{
17+
privatevarfilteredFiles:[FileReference]?{
1818
if searchText.isEmpty{
1919
return allFiles
2020
}
2121

22-
return allFiles.filter{ docin
22+
return allFiles?.filter{ docin
2323
(doc.fileName?? doc.url.lastPathComponent).localizedCaseInsensitiveContains(searchText)
2424
}
2525
}
2626

2727
privatestaticletdefaultEmptyStateText="No results found."
28+
privatestaticletisIndexingStateText="Indexing files, try later..."
2829

2930
privatevaremptyStateAttributedString:AttributedString?{
30-
varmessage=FilePicker.defaultEmptyStateText
31+
varmessage=allFiles==nil?FilePicker.isIndexingStateText:FilePicker.defaultEmptyStateText
3132
iflet workspaceURL= workspaceURL{
3233
letstatus=FileUtils.checkFileReadability(at: workspaceURL.path)
3334
iflet errorMessage= status.errorMessage(using:ContextUtils.workspaceReadabilityErrorMessageProvider){
@@ -89,25 +90,25 @@ public struct FilePicker: View {
8990
ScrollViewReader{ proxyin
9091
ScrollView{
9192
LazyVStack(alignment:.leading, spacing:4){
92-
ForEach(Array(filteredFiles.enumerated()), id: \.element){ index, docin
93-
FileRowView(doc: doc, id: index, selectedId: $selectedId)
94-
.contentShape(Rectangle())
95-
.onTapGesture{
96-
onSubmit(doc)
97-
selectedId= index
98-
isSearchBarFocused=true
99-
}
100-
.id(index)
101-
}
102-
103-
if filteredFiles.isEmpty{
93+
if allFiles==nil || filteredFiles?.isEmpty==true{
10494
emptyStateView
10595
.foregroundColor(.secondary)
10696
.padding(.leading,4)
10797
.padding(.vertical,4)
98+
}else{
99+
ForEach(Array((filteredFiles??[]).enumerated()), id: \.element){ index, docin
100+
FileRowView(doc: doc, id: index, selectedId: $selectedId)
101+
.contentShape(Rectangle())
102+
.onTapGesture{
103+
onSubmit(doc)
104+
selectedId= index
105+
isSearchBarFocused=true
106+
}
107+
.id(index)
108+
}
108109
}
109110
}
110-
.id(filteredFiles.hashValue)
111+
.id(filteredFiles?.hashValue)
111112
}
112113
.frame(maxHeight:200)
113114
.padding(.horizontal,4)
@@ -158,16 +159,14 @@ public struct FilePicker: View {
158159
}
159160

160161
privatefunc moveSelection(up:Bool, proxy:ScrollViewProxy){
161-
letfiles= filteredFiles
162-
guard !files.isEmptyelse{return}
162+
guardlet files= filteredFiles, !files.isEmptyelse{return}
163163
letnextId= selectedId+(up?-1:1)
164164
selectedId=max(0,min(nextId, files.count-1))
165165
proxy.scrollTo(selectedId, anchor:.bottom)
166166
}
167167

168168
privatefunc handleEnter(){
169-
letfiles= filteredFiles
170-
guard !files.isEmpty && selectedId< files.countelse{return}
169+
guardlet files= filteredFiles, !files.isEmpty && selectedId< files.countelse{return}
171170
onSubmit(files[selectedId])
172171
}
173172
}
@@ -192,9 +191,13 @@ struct FileRowView: View {
192191
Text(doc.fileName?? doc.url.lastPathComponent)
193192
.font(.body)
194193
.hoverPrimaryForeground(isHovered: selectedId== id)
194+
.lineLimit(1)
195+
.truncationMode(.middle)
195196
Text(doc.relativePath?? doc.url.path)
196197
.font(.caption)
197198
.foregroundColor(.secondary)
199+
.lineLimit(1)
200+
.truncationMode(.middle)
198201
}
199202

200203
Spacer()
@@ -206,7 +209,7 @@ struct FileRowView: View {
206209
.onHover(perform:{ hoveringin
207210
isHovered= hovering
208211
})
209-
.transition(.move(edge:.bottom))
212+
.help(doc.relativePath?? doc.url.path)
210213
}
211214
}
212215
}

‎Core/Sources/GitHubCopilotViewModel/GitHubCopilotViewModel.swift

Lines changed: 184 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,18 +163,198 @@ public class GitHubCopilotViewModel: ObservableObject {
163163
CopilotModelManager.updateLLMs(models)
164164
}
165165
}catchlet error asGitHubCopilotError{
166-
if case.languageServerError(.timeout)= error{
167-
// TODO figure out how to extend the default timeout on a Chime LSP request
168-
// Until then, reissue request
166+
switch error{
167+
case.languageServerError(.timeout):
169168
waitForSignIn()
170169
return
170+
case.languageServerError(
171+
.serverError(
172+
code:CLSErrorCode.deviceFlowFailed.rawValue,
173+
message: _,
174+
data: _
175+
)
176+
):
177+
awaitshowSignInFailedAlert(error: error)
178+
waitingForSignIn=false
179+
return
180+
default:
181+
throw error
171182
}
172-
throw error
173183
}catch{
174184
toast(error.localizedDescription,.error)
175185
}
176186
}
177187
}
188+
189+
privatefunc extractSigninErrorMessage(error:GitHubCopilotError)->String{
190+
leterrorDescription= error.localizedDescription
191+
192+
// Handle specific EACCES permission denied errors
193+
if errorDescription.contains("EACCES"){
194+
// Look for paths wrapped in single quotes
195+
letpattern="'([^']+)'"
196+
iflet regex=try?NSRegularExpression(pattern: pattern, options:[]){
197+
letrange=NSRange(location:0, length: errorDescription.utf16.count)
198+
iflet match= regex.firstMatch(in: errorDescription, options:[], range: range){
199+
letpathRange=Range(match.range(at:1), in: errorDescription)!
200+
letpath=String(errorDescription[pathRange])
201+
return path
202+
}
203+
}
204+
}
205+
206+
return errorDescription
207+
}
208+
209+
privatefunc getSigninErrorTitle(error:GitHubCopilotError)->String{
210+
leterrorDescription= error.localizedDescription
211+
212+
if errorDescription.contains("EACCES"){
213+
return"Can't sign you in. The app couldn't create or access files in"
214+
}
215+
216+
return"Error details:"
217+
}
218+
219+
privatevaraccessPermissionCommands:String{
220+
"""
221+
sudo mkdir -p ~/.config/github-copilot
222+
sudo chown -R $(whoami):staff ~/.config
223+
chmod -N ~/.config ~/.config/github-copilot
224+
"""
225+
}
226+
227+
privatevarcontainerBackgroundColor:CGColor{
228+
letisDarkMode=NSApp.effectiveAppearance.name==.darkAqua
229+
return isDarkMode
230+
?NSColor.black.withAlphaComponent(0.85).cgColor
231+
:NSColor.white.withAlphaComponent(0.85).cgColor
232+
}
233+
234+
// MARK: - Alert Building Functions
235+
236+
privatefunc showSignInFailedAlert(error:GitHubCopilotError)async{
237+
letalert=NSAlert()
238+
alert.messageText="GitHub Copilot Sign-in Failed"
239+
alert.alertStyle=.critical
240+
241+
letaccessoryView=createAlertAccessoryView(error: error)
242+
alert.accessoryView= accessoryView
243+
alert.addButton(withTitle:"Copy Commands")
244+
alert.addButton(withTitle:"Cancel")
245+
246+
letresponse=awaitMainActor.run{
247+
alert.runModal()
248+
}
249+
250+
if response==.alertFirstButtonReturn{
251+
copyCommandsToClipboard()
252+
}
253+
}
254+
255+
privatefunc createAlertAccessoryView(error:GitHubCopilotError)->NSView{
256+
letaccessoryView=NSView(frame:NSRect(x:0, y:0, width:400, height:142))
257+
258+
letdetailsHeader=createDetailsHeader(error: error)
259+
accessoryView.addSubview(detailsHeader)
260+
261+
leterrorContainer=createErrorContainer(error: error)
262+
accessoryView.addSubview(errorContainer)
263+
264+
letterminalHeader=createTerminalHeader()
265+
accessoryView.addSubview(terminalHeader)
266+
267+
letcommandsContainer=createCommandsContainer()
268+
accessoryView.addSubview(commandsContainer)
269+
270+
return accessoryView
271+
}
272+
273+
privatefunc createDetailsHeader(error:GitHubCopilotError)->NSView{
274+
letdetailsHeader=NSView(frame:NSRect(x:16, y:122, width:368, height:20))
275+
276+
letwarningIcon=NSImageView(frame:NSRect(x:0, y:4, width:16, height:16))
277+
warningIcon.image=NSImage(systemSymbolName:"exclamationmark.triangle.fill", accessibilityDescription:"Warning")
278+
warningIcon.contentTintColor=NSColor.systemOrange
279+
detailsHeader.addSubview(warningIcon)
280+
281+
letdetailsLabel=NSTextField(wrappingLabelWithString:getSigninErrorTitle(error: error))
282+
detailsLabel.frame=NSRect(x:20, y:0, width:346, height:20)
283+
detailsLabel.font=NSFont.systemFont(ofSize:12, weight:.regular)
284+
detailsLabel.textColor=NSColor.labelColor
285+
detailsHeader.addSubview(detailsLabel)
286+
287+
return detailsHeader
288+
}
289+
290+
privatefunc createErrorContainer(error:GitHubCopilotError)->NSView{
291+
leterrorContainer=NSView(frame:NSRect(x:16, y:96, width:368, height:22))
292+
errorContainer.wantsLayer=true
293+
errorContainer.layer?.backgroundColor= containerBackgroundColor
294+
errorContainer.layer?.borderColor=NSColor.separatorColor.cgColor
295+
errorContainer.layer?.borderWidth=1
296+
errorContainer.layer?.cornerRadius=6
297+
298+
leterrorMessage=NSTextField(wrappingLabelWithString:extractSigninErrorMessage(error: error))
299+
errorMessage.frame=NSRect(x:8, y:4, width:368, height:14)
300+
errorMessage.font=NSFont.monospacedSystemFont(ofSize:11, weight:.regular)
301+
errorMessage.textColor=NSColor.labelColor
302+
errorMessage.backgroundColor=.clear
303+
errorMessage.isBordered=false
304+
errorMessage.isEditable=false
305+
errorMessage.drawsBackground=false
306+
errorMessage.usesSingleLineMode=true
307+
errorContainer.addSubview(errorMessage)
308+
309+
return errorContainer
310+
}
311+
312+
privatefunc createTerminalHeader()->NSView{
313+
letterminalHeader=NSView(frame:NSRect(x:16, y:66, width:368, height:20))
314+
315+
lettoolIcon=NSImageView(frame:NSRect(x:0, y:4, width:16, height:16))
316+
toolIcon.image=NSImage(systemSymbolName:"terminal.fill", accessibilityDescription:"Terminal")
317+
toolIcon.contentTintColor=NSColor.secondaryLabelColor
318+
terminalHeader.addSubview(toolIcon)
319+
320+
letterminalLabel=NSTextField(wrappingLabelWithString:"Copy and run the commands below in Terminal, then retry.")
321+
terminalLabel.frame=NSRect(x:20, y:0, width:346, height:20)
322+
terminalLabel.font=NSFont.systemFont(ofSize:12, weight:.regular)
323+
terminalLabel.textColor=NSColor.labelColor
324+
terminalHeader.addSubview(terminalLabel)
325+
326+
return terminalHeader
327+
}
328+
329+
privatefunc createCommandsContainer()->NSView{
330+
letcommandsContainer=NSView(frame:NSRect(x:16, y:4, width:368, height:58))
331+
commandsContainer.wantsLayer=true
332+
commandsContainer.layer?.backgroundColor= containerBackgroundColor
333+
commandsContainer.layer?.borderColor=NSColor.separatorColor.cgColor
334+
commandsContainer.layer?.borderWidth=1
335+
commandsContainer.layer?.cornerRadius=6
336+
337+
letcommandsText=NSTextField(wrappingLabelWithString: accessPermissionCommands)
338+
commandsText.frame=NSRect(x:8, y:8, width:344, height:42)
339+
commandsText.font=NSFont.monospacedSystemFont(ofSize:11, weight:.regular)
340+
commandsText.textColor=NSColor.labelColor
341+
commandsText.backgroundColor=.clear
342+
commandsText.isBordered=false
343+
commandsText.isEditable=false
344+
commandsText.isSelectable=true
345+
commandsText.drawsBackground=false
346+
commandsContainer.addSubview(commandsText)
347+
348+
return commandsContainer
349+
}
350+
351+
privatefunc copyCommandsToClipboard(){
352+
NSPasteboard.general.clearContents()
353+
NSPasteboard.general.setString(
354+
self.accessPermissionCommands.replacingOccurrences(of:"\n", with:" &&"),
355+
forType:.string
356+
)
357+
}
178358

179359
publicfunc broadcastStatusChange(){
180360
DistributedNotificationCenter.default().post(

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp