Skip to content

Commit fabc66e

Browse files
committed
Pre-release 0.36.124
1 parent d3cd006 commit fabc66e

File tree

14 files changed

+414
-133
lines changed

14 files changed

+414
-133
lines changed

Core/Sources/HostApp/AdvancedSettings/ChatSection.swift

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,27 @@ import Toast
55
import XcodeInspector
66

77
struct ChatSection: View {
8+
@AppStorage(\.autoAttachChatToXcode) var autoAttachChatToXcode
9+
810
var body: some View {
911
SettingsSection(title: "Chat Settings") {
10-
VStack(spacing: 10) {
11-
// Response language picker
12-
ResponseLanguageSetting()
13-
.padding(.horizontal, 10)
14-
15-
Divider()
16-
17-
// Custom instructions
18-
CustomInstructionSetting()
19-
.padding(.horizontal, 10)
20-
}
21-
.padding(.vertical, 10)
12+
// Auto Attach toggle
13+
SettingsToggle(
14+
title: "Auto-attach Chat Window to Xcode",
15+
isOn: $autoAttachChatToXcode
16+
)
17+
18+
Divider()
19+
20+
// Response language picker
21+
ResponseLanguageSetting()
22+
.padding(SettingsToggle.defaultPadding)
23+
24+
Divider()
25+
26+
// Custom instructions
27+
CustomInstructionSetting()
28+
.padding(SettingsToggle.defaultPadding)
2229
}
2330
}
2431
}

Core/Sources/HostApp/SharedComponents/SettingsToggle.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import SwiftUI
22

33
struct SettingsToggle: View {
4+
static let defaultPadding: CGFloat = 10
5+
46
let title: String
57
let isOn: Binding<Bool>
68

@@ -11,7 +13,7 @@ struct SettingsToggle: View {
1113
Toggle(isOn: isOn) {}
1214
.toggleStyle(.switch)
1315
}
14-
.padding(10)
16+
.padding(SettingsToggle.defaultPadding)
1517
}
1618
}
1719

Core/Sources/SuggestionWidget/ChatPanelWindow.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ final class ChatPanelWindow: NSWindow {
7676
}
7777
}
7878
}
79+
80+
setInitialFrame()
81+
}
82+
83+
private func setInitialFrame() {
84+
let frame = UpdateLocationStrategy.getChatPanelFrame(isAttachedToXcodeEnabled: false)
85+
setFrame(frame, display: false, animate: true)
7986
}
8087

8188
func setFloatOnTop(_ isFloatOnTop: Bool) {

Core/Sources/SuggestionWidget/ChatWindowView.swift

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ struct ChatLoadingView: View {
141141
struct ChatTitleBar: View {
142142
let store: StoreOf<ChatPanelFeature>
143143
@State var isHovering = false
144+
@AppStorage(\.autoAttachChatToXcode) var autoAttachChatToXcode
144145

145146
var body: some View {
146147
WithPerceptionTracking {
@@ -167,18 +168,20 @@ struct ChatTitleBar: View {
167168

168169
Spacer()
169170

170-
TrafficLightButton(
171-
isHovering: isHovering,
172-
isActive: store.isDetached,
173-
color: Color(nsColor: .systemCyan),
174-
action: {
175-
store.send(.toggleChatPanelDetachedButtonClicked)
171+
if !autoAttachChatToXcode {
172+
TrafficLightButton(
173+
isHovering: isHovering,
174+
isActive: store.isDetached,
175+
color: Color(nsColor: .systemCyan),
176+
action: {
177+
store.send(.toggleChatPanelDetachedButtonClicked)
178+
}
179+
) {
180+
Image(systemName: "pin.fill")
181+
.foregroundStyle(.black.opacity(0.5))
182+
.font(Font.system(size: 6).weight(.black))
183+
.transformEffect(.init(translationX: 0, y: 0.5))
176184
}
177-
) {
178-
Image(systemName: "pin.fill")
179-
.foregroundStyle(.black.opacity(0.5))
180-
.font(Font.system(size: 6).weight(.black))
181-
.transformEffect(.init(translationX: 0, y: 0.5))
182185
}
183186
}
184187
.buttonStyle(.plain)

Core/Sources/SuggestionWidget/Styles.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import SwiftUI
66
enum Style {
77
static let panelHeight: Double = 560
88
static let panelWidth: Double = 504
9+
static let minChatPanelWidth: Double = 242 // Following the minimal width of Navigator in Xcode
910
static let inlineSuggestionMaxHeight: Double = 400
1011
static let inlineSuggestionPadding: Double = 25
1112
static let widgetHeight: Double = 20

Core/Sources/SuggestionWidget/WidgetPositionStrategy.swift

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import AppKit
22
import Foundation
3+
import XcodeInspector
34

45
public struct WidgetLocation: Equatable {
56
struct PanelLocation: Equatable {
@@ -319,14 +320,41 @@ enum UpdateLocationStrategy {
319320
return selectionFrame
320321
}
321322

322-
static func getChatPanelFrame(_ screen: NSScreen) -> CGRect {
323+
static func getChatPanelFrame(isAttachedToXcodeEnabled: Bool = false) -> CGRect {
324+
let screen = NSScreen.main ?? NSScreen.screens.first!
325+
return getChatPanelFrame(screen, isAttachedToXcodeEnabled: isAttachedToXcodeEnabled)
326+
}
327+
328+
static func getChatPanelFrame(_ screen: NSScreen, isAttachedToXcodeEnabled: Bool = false) -> CGRect {
323329
let visibleScreenFrame = screen.visibleFrame
324-
// avoid too wide
325-
let width = min(Style.panelWidth, visibleScreenFrame.width * 0.3)
326-
let height = visibleScreenFrame.height
327-
let x = visibleScreenFrame.width - width
328-
329-
return CGRect(x: x, y: visibleScreenFrame.height, width: width, height: height)
330+
331+
// Default Frame
332+
var width = min(Style.panelWidth, visibleScreenFrame.width * 0.3)
333+
var height = visibleScreenFrame.height
334+
var x = visibleScreenFrame.maxX - width
335+
var y = visibleScreenFrame.minY
336+
337+
if isAttachedToXcodeEnabled,
338+
let latestActiveXcode = XcodeInspector.shared.latestActiveXcode,
339+
let xcodeWindow = latestActiveXcode.appElement.focusedWindow,
340+
let xcodeScreen = latestActiveXcode.appScreen,
341+
let xcodeRect = xcodeWindow.rect,
342+
let mainDisplayScreen = NSScreen.screens.first(where: { $0.frame.origin == .zero }) // The main display should exist
343+
{
344+
let minWidth = Style.minChatPanelWidth
345+
let visibleXcodeScreenFrame = xcodeScreen.visibleFrame
346+
347+
width = max(visibleXcodeScreenFrame.maxX - xcodeRect.maxX, minWidth)
348+
height = xcodeRect.height
349+
x = visibleXcodeScreenFrame.maxX - width
350+
351+
// AXUIElement coordinates: Y=0 at top-left
352+
// NSWindow coordinates: Y=0 at bottom-left
353+
y = mainDisplayScreen.frame.maxY - xcodeRect.maxY + mainDisplayScreen.frame.minY
354+
}
355+
356+
357+
return CGRect(x: x, y: y, width: width, height: height)
330358
}
331359
}
332360

Core/Sources/SuggestionWidget/WidgetWindowsController.swift

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,14 @@ private extension WidgetWindowsController {
142142
await updateWidgetsAndNotifyChangeOfEditor(immediately: false)
143143
case .mainWindowChanged:
144144
await updateWidgetsAndNotifyChangeOfEditor(immediately: false)
145-
case .moved,
146-
.resized,
147-
.windowMoved,
148-
.windowResized,
149-
.windowMiniaturized,
150-
.windowDeminiaturized:
145+
case .windowMiniaturized, .windowDeminiaturized:
151146
await updateWidgets(immediately: false)
147+
case .resized,
148+
.moved,
149+
.windowMoved,
150+
.windowResized:
151+
await updateWidgets(immediately: false)
152+
await updateChatWindowLocation()
152153
case .created, .uiElementDestroyed, .xcodeCompletionPanelChanged,
153154
.applicationDeactivated:
154155
continue
@@ -339,8 +340,7 @@ extension WidgetWindowsController {
339340

340341
// Generate a default location when no workspace is opened
341342
private func generateDefaultLocation() -> WidgetLocation {
342-
let mainScreen = NSScreen.main ?? NSScreen.screens.first!
343-
let chatPanelFrame = UpdateLocationStrategy.getChatPanelFrame(mainScreen)
343+
let chatPanelFrame = UpdateLocationStrategy.getChatPanelFrame(isAttachedToXcodeEnabled: false)
344344

345345
return WidgetLocation(
346346
widgetFrame: .zero,
@@ -444,6 +444,18 @@ extension WidgetWindowsController {
444444

445445
updateWindowOpacityTask = task
446446
}
447+
448+
@MainActor
449+
func updateChatWindowLocation() {
450+
let state = store.withState { $0 }
451+
let isAttachedToXcodeEnabled = UserDefaults.shared.value(for: \.autoAttachChatToXcode)
452+
if isAttachedToXcodeEnabled {
453+
if state.chatPanelState.isPanelDisplayed && !windows.chatPanelWindow.isWindowHidden {
454+
let frame = UpdateLocationStrategy.getChatPanelFrame(isAttachedToXcodeEnabled: isAttachedToXcodeEnabled)
455+
windows.chatPanelWindow.setFrame(frame, display: true, animate: true)
456+
}
457+
}
458+
}
447459

448460
func updateWindowLocation(
449461
animated: Bool,
@@ -481,8 +493,11 @@ extension WidgetWindowsController {
481493
animate: animated
482494
)
483495
}
484-
485-
if isChatPanelDetached {
496+
497+
let isAttachedToXcodeEnabled = UserDefaults.shared.value(for: \.autoAttachChatToXcode)
498+
if isAttachedToXcodeEnabled {
499+
// update in `updateChatWindowLocation`
500+
} else if isChatPanelDetached {
486501
// don't update it!
487502
} else {
488503
windows.chatPanelWindow.setFrame(
@@ -523,10 +538,10 @@ extension WidgetWindowsController {
523538

524539
@MainActor
525540
func adjustChatPanelWindowLevel() async {
541+
let window = windows.chatPanelWindow
542+
526543
let disableFloatOnTopWhenTheChatPanelIsDetached = UserDefaults.shared
527544
.value(for: \.disableFloatOnTopWhenTheChatPanelIsDetached)
528-
529-
let window = windows.chatPanelWindow
530545
guard disableFloatOnTopWhenTheChatPanelIsDetached else {
531546
window.setFloatOnTop(true)
532547
return
@@ -549,7 +564,7 @@ extension WidgetWindowsController {
549564
} else {
550565
false
551566
}
552-
567+
553568
if !floatOnTopWhenOverlapsXcode || !latestAppIsXcodeOrExtension {
554569
window.setFloatOnTop(false)
555570
} else {

Server/package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"build": "webpack"
88
},
99
"dependencies": {
10-
"@github/copilot-language-server": "^1.334.0",
10+
"@github/copilot-language-server": "^1.335.0",
1111
"@xterm/addon-fit": "^0.10.0",
1212
"@xterm/xterm": "^5.5.0",
1313
"monaco-editor": "0.52.2"

Tool/Sources/Preferences/Keys.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,10 @@ public extension UserDefaultPreferenceKeys {
303303
var globalCopilotInstructions: PreferenceKey<String> {
304304
.init(defaultValue: "", key: "GlobalCopilotInstructions")
305305
}
306+
307+
var autoAttachChatToXcode: PreferenceKey<Bool> {
308+
.init(defaultValue: true, key: "AutoAttachChatToXcode")
309+
}
306310
}
307311

308312
// MARK: - Theme

Tool/Sources/Preferences/UserDefaults.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public extension UserDefaults {
1515
shared.setupDefaultValue(for: \.realtimeSuggestionToggle)
1616
shared.setupDefaultValue(for: \.realtimeSuggestionDebounce)
1717
shared.setupDefaultValue(for: \.suggestionPresentationMode)
18+
shared.setupDefaultValue(for: \.autoAttachChatToXcode)
1819
shared.setupDefaultValue(for: \.widgetColorScheme)
1920
shared.setupDefaultValue(for: \.customCommands)
2021
shared.setupDefaultValue(

0 commit comments

Comments
 (0)