Skip to content

Commit d1f7de3

Browse files
committed
Release 0.38.0
1 parent 9788b5c commit d1f7de3

File tree

6 files changed

+66
-50
lines changed

6 files changed

+66
-50
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## 0.38.0 - June 30, 2025
9+
### Added
10+
- Support for Claude 4 in Chat.
11+
- Support for Copilot Vision (image attachments).
12+
- Support for remote MCP servers.
13+
14+
### Changed
15+
- Automatically suggests a title for conversations created in agent mode.
16+
- Improved restoration of MCP tool status after Copilot restarts.
17+
- Reduced duplication of MCP server instances.
18+
19+
### Fixed
20+
- Switching accounts now correctly refreshes the auth token and models.
21+
- Fixed file create/edit issues in agent mode.
22+
823
## 0.37.0 - June 18, 2025
924
### Added
1025
- **Advanced** settings: Added option to configure **Custom Instructions** for GitHub Copilot during chat sessions.

Core/Sources/ChatService/ToolCalls/CreateFileTool.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,26 @@ public class CreateFileTool: ICopilotTool {
2525

2626
guard !FileManager.default.fileExists(atPath: filePath)
2727
else {
28+
Logger.client.info("CreateFileTool: File already exists at \(filePath)")
2829
completeResponse(request, status: .error, response: "File already exists at \(filePath)", completion: completion)
2930
return true
3031
}
3132

3233
do {
34+
// Create intermediate directories if they don't exist
35+
let parentDirectory = fileURL.deletingLastPathComponent()
36+
try FileManager.default.createDirectory(at: parentDirectory, withIntermediateDirectories: true, attributes: nil)
3337
try content.write(to: fileURL, atomically: true, encoding: .utf8)
3438
} catch {
39+
Logger.client.error("CreateFileTool: Failed to write content to file at \(filePath): \(error)")
3540
completeResponse(request, status: .error, response: "Failed to write content to file: \(error)", completion: completion)
3641
return true
3742
}
3843

3944
guard FileManager.default.fileExists(atPath: filePath),
4045
let writtenContent = try? String(contentsOf: fileURL, encoding: .utf8)
4146
else {
47+
Logger.client.info("CreateFileTool: Failed to verify file creation at \(filePath)")
4248
completeResponse(request, status: .error, response: "Failed to verify file creation.", completion: completion)
4349
return true
4450
}

Core/Sources/SuggestionWidget/ChatPanelWindow.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ final class ChatPanelWindow: NSWindow {
8282
}
8383

8484
private func setInitialFrame() {
85-
let frame = UpdateLocationStrategy.getChatPanelFrame(isAttachedToXcodeEnabled: false)
85+
let frame = UpdateLocationStrategy.getChatPanelFrame()
8686
setFrame(frame, display: false, animate: true)
8787
}
8888

Core/Sources/SuggestionWidget/WidgetPositionStrategy.swift

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -320,46 +320,38 @@ enum UpdateLocationStrategy {
320320
return selectionFrame
321321
}
322322

323-
static func getChatPanelFrame(
324-
isAttachedToXcodeEnabled: Bool = false,
325-
xcodeApp: XcodeAppInstanceInspector? = nil
326-
) -> CGRect {
327-
let screen = NSScreen.main ?? NSScreen.screens.first!
328-
return getChatPanelFrame(screen, isAttachedToXcodeEnabled: isAttachedToXcodeEnabled, xcodeApp: xcodeApp)
329-
}
330-
331-
static func getChatPanelFrame(
332-
_ screen: NSScreen,
333-
isAttachedToXcodeEnabled: Bool = false,
334-
xcodeApp: XcodeAppInstanceInspector? = nil
335-
) -> CGRect {
323+
static func getChatPanelFrame(_ screen: NSScreen? = nil) -> CGRect {
324+
let screen = screen ?? NSScreen.main ?? NSScreen.screens.first!
325+
336326
let visibleScreenFrame = screen.visibleFrame
337327

338328
// Default Frame
339-
var width = min(Style.panelWidth, visibleScreenFrame.width * 0.3)
340-
var height = visibleScreenFrame.height
341-
var x = visibleScreenFrame.maxX - width
342-
var y = visibleScreenFrame.minY
329+
let width = min(Style.panelWidth, visibleScreenFrame.width * 0.3)
330+
let height = visibleScreenFrame.height
331+
let x = visibleScreenFrame.maxX - width
332+
let y = visibleScreenFrame.minY
343333

344-
if isAttachedToXcodeEnabled,
345-
let latestActiveXcode = xcodeApp ?? XcodeInspector.shared.latestActiveXcode,
346-
let xcodeWindow = latestActiveXcode.appElement.focusedWindow,
347-
let xcodeScreen = latestActiveXcode.appScreen,
348-
let xcodeRect = xcodeWindow.rect,
349-
let mainDisplayScreen = NSScreen.screens.first(where: { $0.frame.origin == .zero }) // The main display should exist
350-
{
351-
let minWidth = Style.minChatPanelWidth
352-
let visibleXcodeScreenFrame = xcodeScreen.visibleFrame
353-
354-
width = max(visibleXcodeScreenFrame.maxX - xcodeRect.maxX, minWidth)
355-
height = xcodeRect.height
356-
x = visibleXcodeScreenFrame.maxX - width
357-
358-
// AXUIElement coordinates: Y=0 at top-left
359-
// NSWindow coordinates: Y=0 at bottom-left
360-
y = mainDisplayScreen.frame.maxY - xcodeRect.maxY + mainDisplayScreen.frame.minY
334+
return CGRect(x: x, y: y, width: width, height: height)
335+
}
336+
337+
static func getAttachedChatPanelFrame(_ screen: NSScreen, workspaceWindowElement: AXUIElement) -> CGRect {
338+
guard let xcodeScreen = workspaceWindowElement.maxIntersectionScreen,
339+
let xcodeRect = workspaceWindowElement.rect,
340+
let mainDisplayScreen = NSScreen.screens.first(where: { $0.frame.origin == .zero })
341+
else {
342+
return getChatPanelFrame()
361343
}
362344

345+
let minWidth = Style.minChatPanelWidth
346+
let visibleXcodeScreenFrame = xcodeScreen.visibleFrame
347+
348+
let width = max(visibleXcodeScreenFrame.maxX - xcodeRect.maxX, minWidth)
349+
let height = xcodeRect.height
350+
let x = visibleXcodeScreenFrame.maxX - width
351+
352+
// AXUIElement coordinates: Y=0 at top-left
353+
// NSWindow coordinates: Y=0 at bottom-left
354+
let y = mainDisplayScreen.frame.maxY - xcodeRect.maxY + mainDisplayScreen.frame.minY
363355

364356
return CGRect(x: x, y: y, width: width, height: height)
365357
}

Core/Sources/SuggestionWidget/WidgetWindowsController.swift

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ extension WidgetWindowsController {
349349

350350
// Generate a default location when no workspace is opened
351351
private func generateDefaultLocation() -> WidgetLocation {
352-
let chatPanelFrame = UpdateLocationStrategy.getChatPanelFrame(isAttachedToXcodeEnabled: false)
352+
let chatPanelFrame = UpdateLocationStrategy.getChatPanelFrame()
353353

354354
return WidgetLocation(
355355
widgetFrame: .zero,
@@ -459,7 +459,8 @@ extension WidgetWindowsController {
459459
guard let currentXcodeApp = (await currentXcodeApp),
460460
let currentFocusedWindow = currentXcodeApp.appElement.focusedWindow,
461461
let currentXcodeScreen = currentXcodeApp.appScreen,
462-
let currentXcodeRect = currentFocusedWindow.rect
462+
let currentXcodeRect = currentFocusedWindow.rect,
463+
let notif = notif
463464
else { return }
464465

465466
if let previousXcodeApp = (await previousXcodeApp),
@@ -472,16 +473,13 @@ extension WidgetWindowsController {
472473
let isAttachedToXcodeEnabled = UserDefaults.shared.value(for: \.autoAttachChatToXcode)
473474
guard isAttachedToXcodeEnabled else { return }
474475

475-
if let notif = notif {
476-
let dialogIdentifiers = ["open_quickly", "alert"]
477-
if dialogIdentifiers.contains(notif.element.identifier) { return }
478-
}
476+
guard notif.element.isXcodeWorkspaceWindow else { return }
479477

480478
let state = store.withState { $0 }
481479
if state.chatPanelState.isPanelDisplayed && !windows.chatPanelWindow.isWindowHidden {
482-
var frame = UpdateLocationStrategy.getChatPanelFrame(
483-
isAttachedToXcodeEnabled: true,
484-
xcodeApp: currentXcodeApp
480+
var frame = UpdateLocationStrategy.getAttachedChatPanelFrame(
481+
NSScreen.main ?? NSScreen.screens.first!,
482+
workspaceWindowElement: notif.element
485483
)
486484

487485
let screenMaxX = currentXcodeScreen.visibleFrame.maxX

ReleaseNotes.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
### GitHub Copilot for Xcode 0.37.0
1+
### GitHub Copilot for Xcode 0.38.0
22

33
**🚀 Highlights**
44

5-
* **Advanced** settings: Added option to configure **Custom Instructions** for GitHub Copilot during chat sessions.
6-
* **Advanced** settings: Added option to keep the chat window automatically attached to Xcode.
7-
* Added support for dragging-and-dropping files into the chat panel to provide context.
5+
* Support for Claude 4 in Chat.
6+
* Support for Copilot Vision (image attachments).
7+
* Support for remote MCP servers.
8+
9+
**💪 Improvements**
10+
* Automatically suggests a title for conversations created in agent mode.
11+
* Improved restoration of MCP tool status after Copilot restarts.
12+
* Reduced duplication of MCP server instances.
813

914
**🛠️ Bug Fixes**
1015

11-
* "Add Context" menu didn’t show files in workspaces organized with Xcode’s group feature.
12-
* Chat didn’t respond when the workspace was in a system folder (like Desktop, Downloads, or Documents) and access permission hadn’t been granted.
16+
* Switching accounts now correctly refreshes the auth token and models.
17+
* Fixed file create/edit issues in agent mode.

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