Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
b031405
Merge tag '0.36.0' into develop
intitni Oct 23, 2025
118e7b3
Tweak tab to accept
intitni Oct 24, 2025
538911d
Add OverlayWindowController
intitni Nov 4, 2025
e5c5e0e
Rename package
intitni Nov 4, 2025
2b7cb17
Start OverlayWindowController at launch
intitni Nov 4, 2025
962649e
Fix that the overlay panel is not visible
intitni Nov 4, 2025
9c12f99
Fix overlay position
intitni Nov 4, 2025
7fd0ffe
Fix hittesting
intitni Nov 4, 2025
4edb610
Adjust implementation
intitni Nov 5, 2025
f1a838f
Add apis for AXUIElement
intitni Nov 5, 2025
2271497
Provide overlay debug boolean for child views
intitni Nov 6, 2025
29fb5b2
Fix overlay
intitni Nov 7, 2025
6ae9af3
Add @Sendable to blocks
intitni Nov 7, 2025
b71677d
Update /run to /shell
intitni Nov 8, 2025
d7eeb3f
Fix completion panel close tracking
intitni Nov 8, 2025
f41df22
Add userReadableContent to functions
intitni Nov 8, 2025
3eb3417
Update
intitni Nov 9, 2025
8db2391
Fix that command could be triggered when Xcode is not active
intitni Nov 9, 2025
7c10dcf
Fix overlay dimming when Xcode is not active
intitni Nov 9, 2025
80873d3
Fix the case when Xcode is full screen
intitni Nov 9, 2025
c41f4ca
Get the settings from UserDefaults
intitni Nov 10, 2025
134506a
Destroy workspaces when possible
intitni Nov 10, 2025
10e7ad3
Fix a but that fetches incorrect filespace
intitni Nov 10, 2025
aeeab2c
Bump version
intitni Nov 10, 2025
2c3022f
Merge branch 'feature/per-window-overlay' into develop
intitni Nov 10, 2025
0ca850c
Remove the toggle
intitni Nov 10, 2025
024fcca
Fix overlay window destruction
intitni Nov 10, 2025
a512b6c
Rename command
intitni Nov 10, 2025
914f6b4
Make the request Sendable
intitni Nov 11, 2025
92cc4c6
Post explanation from ModificationAgent
intitni Nov 11, 2025
d71bdd0
Add new app icon
intitni Nov 13, 2025
10066b2
Adjust icon color
intitni Nov 15, 2025
4a77d3e
Fix that elements can become invalid on sleep
intitni Nov 16, 2025
06ecb6e
Handle window miniaturize
intitni Nov 16, 2025
2c04b70
Bind overlay windows to window IDs
intitni Nov 16, 2025
81ca09d
Use ObjectIdentifier instead
intitni Nov 16, 2025
93751c8
Throttle event handling
intitni Nov 18, 2025
81b2474
Add latestFocusedEditor
intitni Nov 18, 2025
e19dfc1
Improve performance of children traversing
intitni Nov 18, 2025
ad8f6ec
Fix modification agent
intitni Nov 23, 2025
5bfe0a6
Adjust animation
intitni Nov 24, 2025
162d52b
Fix
intitni Nov 24, 2025
73ff288
Update toast to skip same content
intitni Nov 24, 2025
62d0444
Bump GitHub Copilot language server
intitni Nov 24, 2025
12abf6a
Bump Codeium language server
intitni Nov 24, 2025
2d23f8f
Bump version
intitni Nov 25, 2025
47e497b
Bump build number
intitni Nov 25, 2025
ca47ad8
Merge branch 'release/0.37.0'
intitni Nov 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified AppIcon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import XcodeInspector

public final class TerminalChatPlugin: ChatPlugin {
public static var id: String { "com.intii.terminal" }
public static var command: String { "run" }
public static var name: String { "Terminal" }
public static var command: String { "shell" }
public static var name: String { "Shell" }
public static var description: String { """
Run the command in the message from terminal.
Run the command in the message from shell.

You can use environment variable `$FILE_PATH` and `$PROJECT_ROOT` to access the current file path and project root.
""" }
Expand Down
2 changes: 2 additions & 0 deletions Copilot for Xcode.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@
C87B03AA293B262E00C77EAE /* PreviousSuggestionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviousSuggestionCommand.swift; sourceTree = "<group>"; };
C887BC832965D96000931567 /* DEVELOPMENT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = DEVELOPMENT.md; sourceTree = "<group>"; };
C89E75C22A46FB32000DD64F /* AppDelegate+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Menu.swift"; sourceTree = "<group>"; };
C8BE64922EB9B42E00EDB2D7 /* OverlayWindow */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = OverlayWindow; sourceTree = "<group>"; };
C8CD828229B88006008D044D /* TestPlan.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = TestPlan.xctestplan; sourceTree = "<group>"; };
C8DCEFFF29CE11D500FDDDD7 /* OpenChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenChat.swift; sourceTree = "<group>"; };
C8DD9CB02BC673F80036641C /* CloseIdleTabsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseIdleTabsCommand.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -342,6 +343,7 @@
C81458AE293A009800135263 /* Config.debug.xcconfig */,
C8CD828229B88006008D044D /* TestPlan.xctestplan */,
C828B27D2B1F241500E7612A /* ExtensionPoint.appextensionpoint */,
C8BE64922EB9B42E00EDB2D7 /* OverlayWindow */,
C84FD9D72CC671C600BE5093 /* ChatPlugins */,
C81D181E2A1B509B006C1B70 /* Tool */,
C8189B282938979000C9DCDA /* Core */,
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
20 changes: 10 additions & 10 deletions Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/Contents.json
Original file line number Diff line number Diff line change
@@ -1,61 +1,61 @@
{
"images" : [
{
"filename" : "1024 x 1024 your icon@16w.png",
"filename" : "app-icon@16w.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"filename" : "1024 x 1024 your icon@32w 1.png",
"filename" : "app-icon@32w.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"filename" : "1024 x 1024 your icon@32w.png",
"filename" : "app-icon@32w.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"filename" : "1024 x 1024 your icon@64w.png",
"filename" : "app-icon@64w.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"filename" : "1024 x 1024 your icon@128w.png",
"filename" : "app-icon@128w.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"filename" : "1024 x 1024 your icon@256w 1.png",
"filename" : "app-icon@256w.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"filename" : "1024 x 1024 your icon@256w.png",
"filename" : "app-icon@256w.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"filename" : "1024 x 1024 your icon@512w 1.png",
"filename" : "app-icon@512w.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"filename" : "1024 x 1024 your icon@512w.png",
"filename" : "app-icon@512w.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"filename" : "1024 x 1024 your icon.png",
"filename" : "app-icon.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions Core/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ let package = Package(
dependencies: [
.package(path: "../Tool"),
.package(path: "../ChatPlugins"),
.package(path: "../OverlayWindow"),
.package(url: "https://github.com/apple/swift-async-algorithms", from: "1.0.0"),
.package(url: "https://github.com/gonzalezreal/swift-markdown-ui", from: "2.4.1"),
.package(url: "https://github.com/sparkle-project/Sparkle", from: "2.6.4"),
Expand Down Expand Up @@ -93,6 +94,7 @@ let package = Package(
.product(name: "OpenAIService", package: "Tool"),
.product(name: "Preferences", package: "Tool"),
.product(name: "CommandHandler", package: "Tool"),
.product(name: "OverlayWindow", package: "OverlayWindow"),
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms"),
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "Dependencies", package: "swift-dependencies"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ struct QueryWebsiteFunction: ChatGPTFunction {
var botReadableContent: String {
return answers.joined(separator: "\n")
}

var userReadableContent: ChatGPTFunctionResultUserReadableContent {
.text(botReadableContent)
}
}

var name: String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ struct SearchFunction: ChatGPTFunction {
"""
}.joined(separator: "\n")
}

var userReadableContent: ChatGPTFunctionResultUserReadableContent {
.text(botReadableContent)
}
}

let maxTokens: Int
Expand Down
2 changes: 1 addition & 1 deletion Core/Sources/ChatGPTChatTab/Views/Instructions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct Instruction: View {

| Plugin Name | Description |
| --- | --- |
| `/run` | Runs a command under the project root |
| `/shell` | Runs a command under the project root |
| `/shortcut(name)` | Runs a shortcut from the Shortcuts.app, with the previous message as input |

To use plugins, you can prefix a message with `/pluginName`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ struct GitHubCopilotView: View {
"node"
)
) {
Text("Path to Node (v20.8+)")
Text("Path to Node (v22.0+)")
}

Text(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ struct CustomCommandView: View {
}
SubSection(title: Text("Single Round Dialog")) {
Text(
"This command allows you to send a message to a temporary chat without opening the chat panel. It is particularly useful for one-time commands, such as running a terminal command with `/run`. For example, you can set the prompt to `/run open .` to open the project in Finder."
"This command allows you to send a message to a temporary chat without opening the chat panel. It is particularly useful for one-time commands, such as running a terminal command with `/shell`. For example, you can set the prompt to `/shell open .` to open the project in Finder."
)
}
}
Expand Down
4 changes: 3 additions & 1 deletion Core/Sources/KeyBindingManager/TabToAcceptSuggestion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,9 @@ final class TabToAcceptSuggestion {
}
guard let presentingSuggestion = filespace.presentingSuggestion
else {
Logger.service.info("TabToAcceptSuggestion: No Suggestions found")
Logger.service.info(
"TabToAcceptSuggestion: No presenting found for \(filespace.fileURL.lastPathComponent), found \(filespace.suggestions.count) suggestion, index \(filespace.suggestionIndex)."
)
return .unchanged
}

Expand Down
46 changes: 26 additions & 20 deletions Core/Sources/PromptToCodeService/OpenAIPromptToCodeService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public final class SimpleModificationAgent: ModificationAgent {
generateDescriptionRequirement: false
)

for try await (code, description) in stream {
continuation.yield(.code(code))
for try await response in stream {
continuation.yield(response)
}

continuation.finish()
Expand All @@ -51,7 +51,7 @@ public final class SimpleModificationAgent: ModificationAgent {
isDetached: Bool,
extraSystemPrompt: String?,
generateDescriptionRequirement: Bool?
) async throws -> AsyncThrowingStream<(code: String, description: String), Error> {
) async throws -> AsyncThrowingStream<Response, Error> {
let userPreferredLanguage = UserDefaults.shared.value(for: \.chatGPTLanguage)
let textLanguage = {
if !UserDefaults.shared
Expand Down Expand Up @@ -226,32 +226,38 @@ public final class SimpleModificationAgent: ModificationAgent {
history.append(.init(role: .user, content: requirement))
}
}
let stream = chatGPTService.send(memory).compactMap { response in
switch response {
case let .partialText(token): return token
default: return nil
}
}.eraseToThrowingStream()

let stream = chatGPTService.send(memory)

return .init { continuation in
Task {
var content = ""
var extracted = extractCodeAndDescription(from: content)
let task = Task {
let parser = ExplanationThenCodeStreamParser()
do {
for try await fragment in stream {
content.append(fragment)
extracted = extractCodeAndDescription(from: content)
if !content.isEmpty, extracted.code.isEmpty {
continuation.yield((code: content, description: ""))
} else {
continuation.yield(extracted)
func yield(fragments: [ExplanationThenCodeStreamParser.Fragment]) {
for fragment in fragments {
switch fragment {
case let .code(code):
continuation.yield(.code(code))
case let .explanation(explanation):
continuation.yield(.explanation(explanation))
}
}
}

for try await response in stream {
guard case let .partialText(fragment) = response else { continue }
try Task.checkCancellation()
await yield(fragments: parser.yield(fragment))
}
await yield(fragments: parser.finish())
continuation.finish()
} catch {
continuation.finish(throwing: error)
}
}

continuation.onTermination = { _ in
task.cancel()
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions Core/Sources/Service/RealtimeSuggestionController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ public actor RealtimeSuggestionController {

Task { @WorkspaceActor in // Get cache ready for real-time suggestions.
guard UserDefaults.shared.value(for: \.preCacheOnFileOpen) else { return }
guard await XcodeInspector.shared.activeApplication?.isXcode ?? false else { return }
guard let fileURL = XcodeInspector.shared.realtimeActiveDocumentURL
else { return }
let (_, filespace) = try await Service.shared.workspacePool
Expand Down
4 changes: 4 additions & 0 deletions Core/Sources/Service/Service.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Foundation
import GitHubCopilotService
import KeyBindingManager
import Logger
import OverlayWindow
import SuggestionService
import Toast
import Workspace
Expand Down Expand Up @@ -37,6 +38,7 @@ public final class Service {
let globalShortcutManager: GlobalShortcutManager
let keyBindingManager: KeyBindingManager
let xcodeThemeController: XcodeThemeController = .init()
let overlayWindowController: OverlayWindowController

#if canImport(ProService)
let proService: ProService
Expand All @@ -54,6 +56,7 @@ public final class Service {

realtimeSuggestionController = .init()
scheduledCleaner = .init()
overlayWindowController = .init()

#if canImport(ProService)
proService = ProService()
Expand Down Expand Up @@ -94,6 +97,7 @@ public final class Service {
#if canImport(ProService)
proService.start()
#endif
overlayWindowController.start()
DependencyUpdater().update()
globalShortcutManager.start()
keyBindingManager.start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ extension PseudoCommandHandler {
guard let filespace = await getFilespace(),
let sourceEditor = await {
if let sourceEditor { sourceEditor }
else { await XcodeInspector.shared.focusedEditor }
else { await XcodeInspector.shared.latestFocusedEditor }
}()
else { return nil }
if Task.isCancelled { return nil }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,33 +173,48 @@ public struct PromptToCodePanel {
range: snippet.attachedRange,
references: context.references,
topics: context.topics
)).timedDebounce(for: 0.4)
)).map {
switch $0 {
case let .code(code):
return (code: code, description: "")
case let .explanation(explanation):
return (code: "", description: explanation)
}
}.timedDebounce(for: 0.4) { lhs, rhs in
(
code: lhs.code + rhs.code,
description: lhs.description + rhs.description
)
}

do {
for try await response in stream {
try Task.checkCancellation()

switch response {
case let .code(code):
await send(.snippetPanel(.element(
id: snippet.id,
action: .modifyCodeChunkReceived(
code: code,
description: ""
)
)))
}
await send(.snippetPanel(.element(
id: snippet.id,
action: .modifyCodeChunkReceived(
code: response.code,
description: response.description
)
)))
}
await send(.snippetPanel(.element(
id: snippet.id,
action: .modifyCodeFinished
)))
} catch is CancellationError {
await send(.snippetPanel(.element(
id: snippet.id,
action: .modifyCodeFinished
)))
throw CancellationError()
} catch {
try Task.checkCancellation()
if (error as NSError).code == NSURLErrorCancelled {
await send(.snippetPanel(.element(
id: snippet.id,
action: .modifyCodeFailed(error: "Cancelled")
action: .modifyCodeFinished
)))
return
throw CancellationError()
}
await send(.snippetPanel(.element(
id: snippet.id,
Expand Down Expand Up @@ -316,8 +331,8 @@ public struct PromptToCodeSnippetPanel {
return .none

case let .modifyCodeChunkReceived(code, description):
state.snippet.modifiedCode = code
state.snippet.description = description
state.snippet.modifiedCode += code
state.snippet.description += description
return .none

case let .modifyCodeFailed(error):
Expand Down
Loading