Skip to content

Commit 418fdd8

Browse files
committed
Tweak widget transition
1 parent 6a75f10 commit 418fdd8

File tree

3 files changed

+52
-6
lines changed

3 files changed

+52
-6
lines changed

Core/Sources/Service/GUI/PromptToCodeProvider+Service.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,14 @@ extension PromptToCodeProvider {
5555
service.stopResponding()
5656
}
5757

58-
onAcceptSuggestionTapped = {
59-
Task {
58+
onAcceptSuggestionTapped = { [weak self] in
59+
Task { [weak self] in
6060
let handler = PseudoCommandHandler()
6161
await handler.acceptSuggestion()
62-
if let app = ActiveApplicationMonitor.shared.previousApp, app.isXcode {
62+
if let app = ActiveApplicationMonitor.shared.previousApp,
63+
app.isXcode,
64+
!(self?.isContinuous ?? false)
65+
{
6366
try await Task.sleep(nanoseconds: 200_000_000)
6467
app.activate()
6568
}

Core/Sources/SuggestionWidget/FeatureReducers/WidgetFeature.swift

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public struct WidgetFeature: ReducerProtocol {
2323
}
2424

2525
public struct State: Equatable {
26+
var focusingDocumentURL: URL?
2627
public var colorScheme: ColorScheme = .light
2728

2829
// MARK: Panels
@@ -107,6 +108,7 @@ public struct WidgetFeature: ReducerProtocol {
107108

108109
case updateWindowLocation(animated: Bool)
109110
case updateWindowOpacity
111+
case updateFocusingDocumentURL
110112

111113
case panel(PanelFeature.Action)
112114
case chatPanel(ChatPanelFeature.Action)
@@ -121,6 +123,11 @@ public struct WidgetFeature: ReducerProtocol {
121123
@Dependency(\.suggestionWidgetControllerDependency) var suggestionWidgetControllerDependency
122124
@Dependency(\.activeApplicationMonitor) var activeApplicationMonitor
123125
@Dependency(\.xcodeInspector) var xcodeInspector
126+
@Dependency(\.mainQueue) var mainQueue
127+
128+
public enum DebounceKey: Hashable {
129+
case updateWindowOpacity
130+
}
124131

125132
public init() {}
126133

@@ -301,6 +308,8 @@ public struct WidgetFeature: ReducerProtocol {
301308
case .observeWindowChange:
302309
guard let app = activeApplicationMonitor.activeApplication else { return .none }
303310
guard app.isXcode else { return .none }
311+
312+
let documentURL = state.focusingDocumentURL
304313

305314
return .run { send in
306315
await send(.observeEditorChange)
@@ -319,17 +328,31 @@ public struct WidgetFeature: ReducerProtocol {
319328
kAXWindowMiniaturizedNotification,
320329
kAXWindowDeminiaturizedNotification
321330
)
331+
322332
for await notification in notifications {
323333
try Task.checkCancellation()
324334

335+
// Hide the widgets before switching to another window/editor
336+
// so the transition looks better.
337+
if [
338+
kAXFocusedUIElementChangedNotification,
339+
kAXFocusedWindowChangedNotification,
340+
].contains(notification.name) {
341+
let newDocumentURL = xcodeInspector.realtimeActiveDocumentURL
342+
if documentURL != newDocumentURL {
343+
await send(.panel(.removeDisplayedContent))
344+
await hidePanelWindows()
345+
}
346+
await send(.updateFocusingDocumentURL)
347+
}
348+
349+
// update widgets.
325350
if [
326351
kAXFocusedUIElementChangedNotification,
327352
kAXApplicationActivatedNotification,
328353
kAXMainWindowChangedNotification,
329354
kAXFocusedWindowChangedNotification,
330355
].contains(notification.name) {
331-
await hidePanelWindows()
332-
await send(.panel(.removeDisplayedContent))
333356
await send(.updateWindowLocation(animated: false))
334357
await send(.updateWindowOpacity)
335358
await send(.observeEditorChange)
@@ -421,6 +444,10 @@ public struct WidgetFeature: ReducerProtocol {
421444
state.panelState.suggestionPanelState.colorScheme = scheme
422445
state.chatPanelState.colorScheme = scheme
423446
return .none
447+
448+
case .updateFocusingDocumentURL:
449+
state.focusingDocumentURL = xcodeInspector.realtimeActiveDocumentURL
450+
return .none
424451

425452
case let .updateWindowLocation(animated):
426453
guard let widgetLocation = generateWidgetLocation() else { return .none }
@@ -486,8 +513,8 @@ public struct WidgetFeature: ReducerProtocol {
486513
case .updateWindowOpacity:
487514
let isChatPanelDetached = state.chatPanelState.chatPanelInASeparateWindow
488515
let hasChat = !state.chatPanelState.chatTabGroup.tabInfo.isEmpty
489-
490516
return .run { _ in
517+
try await mainQueue.sleep(for: .seconds(0.2))
491518
Task { @MainActor in
492519
if let app = activeApplicationMonitor.activeApplication, app.isXcode {
493520
let application = AXUIElementCreateApplication(app.processIdentifier)
@@ -538,6 +565,7 @@ public struct WidgetFeature: ReducerProtocol {
538565
}
539566
}
540567
}
568+
.cancellable(id: DebounceKey.updateWindowOpacity, cancelInFlight: true)
541569

542570
case .circularWidget:
543571
return .none

Tool/Sources/XcodeInspector/XcodeInspector.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ public final class XcodeInspector: ObservableObject {
2323
@Published public internal(set) var focusedElement: AXUIElement?
2424
@Published public internal(set) var completionPanel: AXUIElement?
2525

26+
public var realtimeActiveDocumentURL: URL {
27+
latestActiveXcode?.realtimeDocumentURL ?? URL(fileURLWithPath: "/")
28+
}
29+
2630
init() {
2731
let runningApplications = NSWorkspace.shared.runningApplications
2832
xcodes = runningApplications
@@ -176,6 +180,17 @@ public final class XcodeAppInstanceInspector: AppInstanceInspector {
176180

177181
@Published public private(set) var completionPanel: AXUIElement?
178182

183+
public var realtimeDocumentURL: URL {
184+
guard let window = appElement.focusedWindow,
185+
window.identifier == "Xcode.WorkspaceWindow"
186+
else {
187+
return URL(fileURLWithPath: "/")
188+
}
189+
190+
return WorkspaceXcodeWindowInspector.extractDocumentURL(windowElement: window)
191+
?? URL(fileURLWithPath: "/")
192+
}
193+
179194
var _version: String?
180195
public var version: String? {
181196
if let _version { return _version }

0 commit comments

Comments
 (0)