diff --git a/Core/Sources/ChatService/ChatService.swift b/Core/Sources/ChatService/ChatService.swift index 0aa5a5a8..3f65d49b 100644 --- a/Core/Sources/ChatService/ChatService.swift +++ b/Core/Sources/ChatService/ChatService.swift @@ -229,15 +229,12 @@ public final class ChatService: ChatServiceType, ObservableObject { } public func updateToolCallStatus(toolCallId: String, status: AgentToolCall.ToolCallStatus, payload: Any? = nil) { - if status == .cancelled { - resetOngoingRequest(with: .cancelled) - return - } - // Send the tool call result back to the server - if let toolCallRequest = self.pendingToolCallRequests[toolCallId], status == .accepted { + if let toolCallRequest = self.pendingToolCallRequests[toolCallId], status == .accepted || status == .cancelled { self.pendingToolCallRequests.removeValue(forKey: toolCallId) - let toolResult = LanguageModelToolConfirmationResult(result: .Accept) + let toolResult = LanguageModelToolConfirmationResult( + result: status == .accepted ? .Accept : .Dismiss + ) let jsonResult = try? JSONEncoder().encode(toolResult) let jsonValue = (try? JSONDecoder().decode(JSONValue.self, from: jsonResult ?? Data())) ?? JSONValue.null toolCallRequest.completion( diff --git a/Core/Sources/ConversationTab/Chat.swift b/Core/Sources/ConversationTab/Chat.swift index eca31802..19e195be 100644 --- a/Core/Sources/ConversationTab/Chat.swift +++ b/Core/Sources/ConversationTab/Chat.swift @@ -784,9 +784,12 @@ struct Chat { let chatContext: ChatContext = .from(message, projectURL: projectURL) state.editor.setContext(chatContext, for: mode) state.editorMode = mode + let isReceivingMessage = service.isReceivingMessage return .run { send in - await send(.stopRespondingButtonTapped) + if isReceivingMessage { + await send(.stopRespondingButtonTapped) + } } } diff --git a/Core/Sources/ConversationTab/ChatPanel.swift b/Core/Sources/ConversationTab/ChatPanel.swift index d111fdfc..0cc83479 100644 --- a/Core/Sources/ConversationTab/ChatPanel.swift +++ b/Core/Sources/ConversationTab/ChatPanel.swift @@ -43,6 +43,7 @@ public struct ChatPanel: View { if let _ = chat.history.last?.followUp { ChatFollowUp(chat: chat) .scaledPadding(.vertical, 8) + .scaledPadding(.horizontal, 16) .dimWithExitEditMode(chat) } } @@ -50,11 +51,12 @@ public struct ChatPanel: View { if chat.fileEditMap.count > 0 { WorkingSetView(chat: chat) .dimWithExitEditMode(chat) + .scaledPadding(.horizontal, 16) } ChatPanelInputArea(chat: chat, r: r, editorMode: .input) - .chatPanelInputAreaPadding(.input) .dimWithExitEditMode(chat) + .scaledPadding(.horizontal, 16) } .scaledPadding(.vertical, 12) .background(Color.chatWindowBackgroundColor) @@ -183,7 +185,7 @@ struct ChatPanelMessages: View { } } .listStyle(.plain) - .padding(.horizontal, -8) + .scaledPadding(.leading, 8) .listRowBackground(EmptyView()) .modify { view in if #available(macOS 13.0, *) { @@ -406,6 +408,7 @@ struct ChatHistory: View { // check point CheckPoint(chat: chat, messageId: message.id) .padding(.vertical, 8) + .padding(.trailing, 8) } } @@ -413,6 +416,7 @@ struct ChatHistory: View { if message.id == pendingCheckpointMessageId { CheckPoint(chat: chat, messageId: message.id) .padding(.vertical, 8) + .padding(.trailing, 8) } } .dimWithExitEditMode( @@ -445,6 +449,7 @@ struct ChatHistoryItem: View { requestType: message.requestType ) .scaledPadding(.leading, chat.editorMode.isEditingUserMessage && chat.editorMode.editingUserMessageId == message.id ? 0 : 20) + .scaledPadding(.trailing, 8) case .assistant: BotMessage( message: message, diff --git a/Core/Sources/ConversationTab/ModelPicker/ModelPicker.swift b/Core/Sources/ConversationTab/ModelPicker/ModelPicker.swift index 228ad965..937bc5bd 100644 --- a/Core/Sources/ConversationTab/ModelPicker/ModelPicker.swift +++ b/Core/Sources/ConversationTab/ModelPicker/ModelPicker.swift @@ -302,9 +302,13 @@ struct ModelPicker: View { HStack(spacing: 0) { // Custom segmented control with color change ChatModePicker(chatMode: $chatMode, onScopeChange: switchModelsForScope) - .onAppear() { + .onAppear { updateAgentPicker() } + .onReceive( + NotificationCenter.default.publisher(for: .gitHubCopilotChatModeDidChange)) { _ in + updateAgentPicker() + } if chatMode == "Agent" { mcpButton diff --git a/Core/Sources/ConversationTab/TerminalViews/RunInTerminalToolView.swift b/Core/Sources/ConversationTab/TerminalViews/RunInTerminalToolView.swift index 5d57b03d..e2182eda 100644 --- a/Core/Sources/ConversationTab/TerminalViews/RunInTerminalToolView.swift +++ b/Core/Sources/ConversationTab/TerminalViews/RunInTerminalToolView.swift @@ -82,7 +82,7 @@ struct RunInTerminalToolView: View { toolView } - .padding(8) + .scaledPadding(8) .cornerRadius(8) .overlay( RoundedRectangle(cornerRadius: 8) @@ -123,7 +123,7 @@ struct RunInTerminalToolView: View { Text(command!) .textSelection(.enabled) .scaledFont(size: chatFontSize, design: .monospaced) - .padding(8) + .scaledPadding(8) .frame(maxWidth: .infinity, alignment: .leading) .foregroundStyle(codeForegroundColor) .background(codeBackgroundColor) @@ -147,19 +147,23 @@ struct RunInTerminalToolView: View { .frame(maxWidth: .infinity, alignment: .leading) HStack { - Button("Cancel") { + Button(action: { chat.send(.toolCallCancelled(tool.id)) + }) { + Text("Skip") + .scaledFont(.body) } - .scaledFont(.body) - - Button("Continue") { + + Button(action: { chat.send(.toolCallAccepted(tool.id)) + }) { + Text("Allow") + .scaledFont(.body) } - .scaledFont(.body) .buttonStyle(BorderedProminentButtonStyle()) } .frame(maxWidth: .infinity, alignment: .leading) - .padding(.top, 4) + .scaledPadding(.top, 4) } } } diff --git a/Core/Sources/ConversationTab/ViewExtension.swift b/Core/Sources/ConversationTab/ViewExtension.swift index 1b4bd992..181f3cbd 100644 --- a/Core/Sources/ConversationTab/ViewExtension.swift +++ b/Core/Sources/ConversationTab/ViewExtension.swift @@ -84,18 +84,6 @@ extension View { self.hoverForeground(isHovered: isHovered, defaultColor: .secondary) } - // MARK: - Chat Panel Input Area - func chatPanelInputAreaPadding(_ mode: Chat.EditorMode) -> some View { - var trailingPadding: CGFloat - switch mode { - case .input: - trailingPadding = 16 - case .editUserMessage: - trailingPadding = 8 - } - return self.padding(.trailing, trailingPadding) - } - // MARK: - Editor Mode /// Dims the view when in edit mode and provides tap/keyboard exit functionality diff --git a/Core/Sources/ConversationTab/Views/BotMessage.swift b/Core/Sources/ConversationTab/Views/BotMessage.swift index 97974f2e..d951f589 100644 --- a/Core/Sources/ConversationTab/Views/BotMessage.swift +++ b/Core/Sources/ConversationTab/Views/BotMessage.swift @@ -387,7 +387,7 @@ private struct TurnStatusView: View { } private var cancelStatus: some View { - statusView(icon: "slash.circle", iconColor: .secondary, text: "Cancelled") + statusView(icon: "slash.circle", iconColor: .secondary, text: "Stopped") } private var errorStatus: some View { diff --git a/Core/Sources/ConversationTab/Views/ChatPanelInputArea/InputAreaTextEditor.swift b/Core/Sources/ConversationTab/Views/ChatPanelInputArea/InputAreaTextEditor.swift index ae635e8d..c9addc8b 100644 --- a/Core/Sources/ConversationTab/Views/ChatPanelInputArea/InputAreaTextEditor.swift +++ b/Core/Sources/ConversationTab/Views/ChatPanelInputArea/InputAreaTextEditor.swift @@ -211,7 +211,7 @@ struct InputAreaTextEditor: View { } .overlay { RoundedRectangle(cornerRadius: 6) - .stroke(Color(nsColor: .controlColor), lineWidth: 1) + .stroke(.quaternary, lineWidth: 1) } .background { if isEditorActive { @@ -452,6 +452,14 @@ struct InputAreaTextEditor: View { @ViewBuilder func makeCurrentEditorView(_ ref: ConversationFileReference) -> some View { + let toggleTrailingPadding: CGFloat = { + if #available(macOS 26.0, *) { + return 8 + } else { + return 4 + } + }() + HStack(spacing: 0) { makeContextFileNameView(url: ref.url, isCurrentEditor: true, selection: ref.selection) @@ -459,7 +467,7 @@ struct InputAreaTextEditor: View { .toggleStyle(SwitchToggleStyle(tint: .blue)) .controlSize(.mini) .frame(width: 34) - .padding(.trailing, 4) + .padding(.trailing, toggleTrailingPadding) .onChange(of: isCurrentEditorContextEnabled) { newValue in enableCurrentEditorContext = newValue } diff --git a/Core/Sources/ConversationTab/Views/ConversationAgentProgressView.swift b/Core/Sources/ConversationTab/Views/ConversationAgentProgressView.swift index 27c3d590..39c8ccc4 100644 --- a/Core/Sources/ConversationTab/Views/ConversationAgentProgressView.swift +++ b/Core/Sources/ConversationTab/Views/ConversationAgentProgressView.swift @@ -39,7 +39,6 @@ struct ProgressToolCalls: View { RunInTerminalToolView(tool: tool, chat: chat) } else if tool.invokeParams != nil && tool.status == .waitForConfirmation { ToolConfirmationView(tool: tool, chat: chat) - .scaledPadding(8) } else { ToolStatusItemView(tool: tool) } @@ -64,20 +63,26 @@ struct ToolConfirmationView: View { .frame(maxWidth: .infinity, alignment: .leading) HStack { - Button("Cancel") { + Button(action: { chat.send(.toolCallCancelled(tool.id)) + }) { + Text("Skip") + .scaledFont(.body) } - .scaledFont(.body) - - Button("Continue") { + + Button(action: { chat.send(.toolCallAccepted(tool.id)) + }) { + Text("Allow") + .scaledFont(.body) } .buttonStyle(BorderedProminentButtonStyle()) - .scaledFont(.body) + } .frame(maxWidth: .infinity, alignment: .leading) .scaledPadding(.top, 4) } + .scaledPadding(8) .cornerRadius(8) .overlay( RoundedRectangle(cornerRadius: 8) diff --git a/Core/Sources/ConversationTab/Views/UserMessage.swift b/Core/Sources/ConversationTab/Views/UserMessage.swift index 7f32e179..4b8a22e3 100644 --- a/Core/Sources/ConversationTab/Views/UserMessage.swift +++ b/Core/Sources/ConversationTab/Views/UserMessage.swift @@ -112,7 +112,6 @@ private struct MessageInputArea: View { editorMode: editorMode ) .frame(maxWidth: .infinity) - .chatPanelInputAreaPadding(editorMode) } } diff --git a/Core/Sources/SuggestionWidget/ChatWindow/ChatHistoryView.swift b/Core/Sources/SuggestionWidget/ChatWindow/ChatHistoryView.swift index c5b6428e..bb3747c6 100644 --- a/Core/Sources/SuggestionWidget/ChatWindow/ChatHistoryView.swift +++ b/Core/Sources/SuggestionWidget/ChatWindow/ChatHistoryView.swift @@ -17,7 +17,7 @@ struct ChatHistoryView: View { var body: some View { WithPerceptionTracking { - VStack(alignment: .center, spacing: 8) { + VStack(alignment: .center, spacing: 0) { Header(isChatHistoryVisible: $isChatHistoryVisible) .scaledFrame(height: 32) .scaledPadding(.leading, 12) @@ -28,6 +28,7 @@ struct ChatHistoryView: View { ChatHistorySearchBarView(searchText: $searchText) .scaledPadding(.leading, 12) .scaledPadding(.trailing, 8) + .scaledPadding(.vertical, 8) ItemView(store: store, searchText: $searchText, isChatHistoryVisible: $isChatHistoryVisible) .scaledPadding(.leading, 12) @@ -222,6 +223,7 @@ struct ChatHistoryItemView: View { .padding(.horizontal, 12) } .frame(maxHeight: .infinity) + .contentShape(Rectangle()) .onHover(perform: { isHovered = $0 }) diff --git a/Core/Sources/SuggestionWidget/ChatWindowView.swift b/Core/Sources/SuggestionWidget/ChatWindowView.swift index e0fdda85..665ccd70 100644 --- a/Core/Sources/SuggestionWidget/ChatWindowView.swift +++ b/Core/Sources/SuggestionWidget/ChatWindowView.swift @@ -72,7 +72,6 @@ struct ChatView: View { Divider() ChatTabContainer(store: store) - .scaledPadding(.horizontal, 16) .frame(maxWidth: .infinity, maxHeight: .infinity) } } diff --git a/Docs/Images/document-folder-permission-request.png b/Docs/Images/document-folder-permission-request.png new file mode 100644 index 00000000..1d512ae4 Binary files /dev/null and b/Docs/Images/document-folder-permission-request.png differ diff --git a/Docs/Images/screen-record-permission-request.png b/Docs/Images/screen-record-permission-request.png new file mode 100644 index 00000000..0b3eeb25 Binary files /dev/null and b/Docs/Images/screen-record-permission-request.png differ diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index 68be3768..eee16478 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -12,6 +12,8 @@ common issues: - [Extension Permission](#extension-permission) - Allows GitHub Copilot to integrate with Xcode - [Accessibility Permission](#accessibility-permission) - Enables real-time code suggestions - [Background Permission](#background-permission) - Allows extension to connect with host app + - [Files & Folders Permission](#files--folders-permission) - Allows GitHub Copilot for Xcode to access files and folders + - [Screen & System Audio Recording Permission](#screen--system-audio-recording-permission-optional) (Optional) - Allow GitHub Copilot for Xcode to capture screen when using Copilot Vision Please note that GitHub Copilot for Xcode may not work properly if any necessary permissions are missing. @@ -30,7 +32,8 @@ Or you can navigate to the permission manually depending on your OS version: | macOS | Location | | :--- | :--- | -| 15 | System Settings > General > Login Items > Extensions > Xcode Source Editor | +| 26 | System Settings > General > Login Items & Extensions > Extensions > By Category > Xcode Source Editor | +| 15 | System Settings > General > Login Items & Extensions > Extensions > Xcode Source Editor | | 13 & 14 | System Settings > Privacy & Security > Extensions > Xcode Source Editor | | 12 | System Preferences > Extensions | @@ -74,15 +77,57 @@ Please ensure that this permission is enabled. You can manually navigate to the | macOS | Location | | :--- | :--- | +| 26 | System Settings > General > Login Items & Extensions > App Background Activity | | 15 | System Settings > General > Login Items & Extensions > Allow in the Background | | 13 & 14 | System Settings > General > Login Items > Allow in the Background | Ensure that "GitHub Copilot for Xcode" is enabled in the list of allowed background items. Without this permission, the extension may not be able to properly communicate with the host app, which can result in inconsistent behavior or reduced functionality. +## Files & Folders Permission + +GitHub Copilot for Xcode needs permission to read your project’s files so it can: + +- Use actual file contents as contextual grounding when you ask questions in Ask and Agent mode (instead of generic language-only answers) +- Safely apply or preview multi-file edits in Agent modes (e.g. refactors, adding tests, updating related types) +- Improve precision by leveraging nearby code, patterns, and naming conventions + +
+
+
+
+