From 7b9f419d54bb25c18ca716eccd8754b224e4d18c Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Tue, 28 Mar 2023 14:40:39 +0800 Subject: [PATCH 1/9] Update the switch activationPolicy logic --- .../SuggestionWidget/SuggestionWidgetController.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift b/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift index e2bcfeee..58eaf5b2 100644 --- a/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift +++ b/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift @@ -219,19 +219,22 @@ public final class SuggestionWidgetController { } Task { @MainActor in - suggestionPanelViewModel.onActiveTabChanged = { [weak self] activeTab in + suggestionPanelViewModel.onActiveTabChanged = { activeTab in #warning(""" TODO: There should be a better way for that Currently, we have to make the app an accessory so that we can type things in the chat mode. But in other modes, we want to keep it prohibited so the helper app won't take over the focus. """) - if case .chat = activeTab, NSApp.activationPolicy() != .accessory { - NSApp.setActivationPolicy(.accessory) - } else if NSApp.activationPolicy() != .prohibited { + switch activeTab { + case .suggestion: + guard NSApp.activationPolicy() != .prohibited else { return } Task { try await Environment.makeXcodeActive() NSApp.setActivationPolicy(.prohibited) } + case .chat: + guard NSApp.activationPolicy() != .accessory else { return } + NSApp.setActivationPolicy(.accessory) } } } From c3064b0c9717003341f671382fcd17707ac483f8 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Tue, 28 Mar 2023 19:34:01 +0800 Subject: [PATCH 2/9] Avoid update location on scroll when Xcode is not active So that the chat panel will be disappear when it is activated and we scroll in Xcode --- .../SuggestionWidget/SuggestionWidgetController.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift b/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift index 58eaf5b2..44735397 100644 --- a/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift +++ b/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift @@ -219,6 +219,7 @@ public final class SuggestionWidgetController { } Task { @MainActor in + var switchTask: Task? suggestionPanelViewModel.onActiveTabChanged = { activeTab in #warning(""" TODO: There should be a better way for that @@ -228,12 +229,15 @@ public final class SuggestionWidgetController { switch activeTab { case .suggestion: guard NSApp.activationPolicy() != .prohibited else { return } - Task { + switchTask?.cancel() + switchTask = Task { try await Environment.makeXcodeActive() + try Task.checkCancellation() NSApp.setActivationPolicy(.prohibited) } case .chat: guard NSApp.activationPolicy() != .accessory else { return } + switchTask?.cancel() NSApp.setActivationPolicy(.accessory) } } @@ -376,12 +380,14 @@ extension SuggestionWidgetController { scroll ) { guard let self else { return } + guard ActiveApplicationMonitor.activeXcode != nil else { return } try Task.checkCancellation() self.updateWindowLocation(animated: false) } } else { for await _ in merge(selectionRangeChange, scroll) { guard let self else { return } + guard ActiveApplicationMonitor.activeXcode != nil else { return } try Task.checkCancellation() let mode = UserDefaults.shared.value(for: \.suggestionWidgetPositionMode) if mode != .alignToTextCursor { break } From 859ca64104717149fc09e545f904066ced7bb21a Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Tue, 28 Mar 2023 22:45:22 +0800 Subject: [PATCH 3/9] Make chat input field the first responder --- .../SuggestionWidget/SuggestionPanelContent/ChatPanel.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift b/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift index ad68a818..0a9aa936 100644 --- a/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift +++ b/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift @@ -124,6 +124,7 @@ struct ChatPanelInputArea: View { @ObservedObject var chat: ChatRoom var inputAreaNamespace: Namespace.ID @Binding var typedMessage: String + @FocusState var isInputAreaFocused: Bool var body: some View { HStack { @@ -148,6 +149,7 @@ struct ChatPanelInputArea: View { TextField("Type a message", text: $typedMessage) } } + .focused($isInputAreaFocused) .lineLimit(3) .multilineTextAlignment(.leading) .textFieldStyle(.plain) @@ -179,6 +181,9 @@ struct ChatPanelInputArea: View { .buttonStyle(.plain) .xcodeStyleFrame() } + .onAppear { + isInputAreaFocused = true + } } } From 45073049580c52db22dcf05e242fa30e926b6d8a Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Tue, 28 Mar 2023 23:01:53 +0800 Subject: [PATCH 4/9] Use TextEditor in chatbot for Monterey --- .../SuggestionPanelContent/ChatPanel.swift | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift b/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift index 0a9aa936..ed4ba745 100644 --- a/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift +++ b/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift @@ -55,7 +55,7 @@ struct ChatPanelMessages: View { } } } - + var body: some View { ScrollView { vstack { @@ -90,7 +90,7 @@ struct ChatPanelMessages: View { .xcodeStyleFrame() .rotationEffect(Angle(degrees: 180)) } - + ForEach(chat.history.reversed(), id: \.id) { message in let text = message.text.isEmpty && !message.isUser ? "..." : message .text @@ -112,7 +112,6 @@ struct ChatPanelMessages: View { ) .xcodeStyleFrame() .rotationEffect(Angle(degrees: 180)) - } } } @@ -146,7 +145,10 @@ struct ChatPanelInputArea: View { if #available(macOS 13.0, *) { TextField("Type a message", text: $typedMessage, axis: .vertical) } else { - TextField("Type a message", text: $typedMessage) + TextEditor(text: $typedMessage) + .frame(height: 42, alignment: .leading) + .font(.body) + .background(Color.clear) } } .focused($isInputAreaFocused) @@ -251,7 +253,7 @@ struct ChatPanel_InputMultilineText_Preview: PreviewProvider { history: ChatPanel_Preview.history, isReceivingMessage: false ), - typedMessage: "Hello\nWorld" + typedMessage: "Hello\nWorld\nPikachu\nCat" ) .padding(8) .background(Color.contentBackground) From bc4ad25c34b6d62d7fdd6b34d2142472ecf84120 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Tue, 28 Mar 2023 23:03:32 +0800 Subject: [PATCH 5/9] Use trash icon to clear history in Monterey --- .../SuggestionPanelContent/ChatPanel.swift | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift b/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift index ed4ba745..bf76422a 100644 --- a/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift +++ b/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift @@ -131,12 +131,18 @@ struct ChatPanelInputArea: View { Button(action: { chat.clear() }) { - Image(systemName: "eraser.line.dashed.fill") - .padding(8) - .background( - .regularMaterial, - in: RoundedRectangle(cornerRadius: 8, style: .continuous) - ) + Group { + if #available(macOS 13.0, *) { + Image(systemName: "eraser.line.dashed.fill") + } else { + Image(systemName: "trash.fill") + } + } + .padding(8) + .background( + .regularMaterial, + in: RoundedRectangle(cornerRadius: 8, style: .continuous) + ) } .buttonStyle(.plain) .xcodeStyleFrame() From 583f6ac96dccf3e173e5af476da688bbb1bbfc44 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Tue, 28 Mar 2023 23:07:21 +0800 Subject: [PATCH 6/9] Move scrollbar back to right --- .../SuggestionWidget/SuggestionPanelContent/ChatPanel.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift b/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift index bf76422a..7394d241 100644 --- a/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift +++ b/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift @@ -77,6 +77,7 @@ struct ChatPanelMessages: View { .buttonStyle(.plain) .xcodeStyleFrame() .matchedGeometryEffect(id: "input", in: inputAreaNamespace) + .scaleEffect(x: -1, y: 1, anchor: .center) } if chat.history.isEmpty { @@ -89,6 +90,7 @@ struct ChatPanelMessages: View { ) .xcodeStyleFrame() .rotationEffect(Angle(degrees: 180)) + .scaleEffect(x: -1, y: 1, anchor: .center) } ForEach(chat.history.reversed(), id: \.id) { message in @@ -112,10 +114,12 @@ struct ChatPanelMessages: View { ) .xcodeStyleFrame() .rotationEffect(Angle(degrees: 180)) + .scaleEffect(x: -1, y: 1, anchor: .center) } } } .rotationEffect(Angle(degrees: 180)) + .scaleEffect(x: -1, y: 1, anchor: .center) } } From a646e29805d231ccc31311ef66cbdb3276558216 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Tue, 28 Mar 2023 23:08:30 +0800 Subject: [PATCH 7/9] Fix that tab view position may be incorrect --- Core/Sources/SuggestionWidget/TabView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/Sources/SuggestionWidget/TabView.swift b/Core/Sources/SuggestionWidget/TabView.swift index 03e6ba87..1e757488 100644 --- a/Core/Sources/SuggestionWidget/TabView.swift +++ b/Core/Sources/SuggestionWidget/TabView.swift @@ -38,6 +38,7 @@ struct TabView: View { } .opacity(panelViewModel.isPanelDisplayed ? 1 : 0) .preferredColorScheme(panelViewModel.colorScheme) + .frame(maxWidth: Style.widgetWidth, maxHeight: Style.widgetHeight) } } From e703f8faf9619072711dc9ad5ad37054419b70a1 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Tue, 28 Mar 2023 23:39:49 +0800 Subject: [PATCH 8/9] Automatically activate the text field when a chat box is created --- .../SuggestionWidgetController.swift | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift b/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift index 44735397..4cadafb8 100644 --- a/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift +++ b/Core/Sources/SuggestionWidget/SuggestionWidgetController.swift @@ -79,7 +79,7 @@ public final class SuggestionWidgetController { ) it.setIsVisible(true) it.canBecomeKeyChecker = { [suggestionPanelViewModel] in - if case .chat = suggestionPanelViewModel.activeTab { return false } + if case .chat = suggestionPanelViewModel.activeTab { return true } return false } return it @@ -299,6 +299,11 @@ public extension SuggestionWidgetController { func presentChatRoom(_ chatRoom: ChatRoom, fileURL: URL) { if fileURL == currentFileURL || currentFileURL == nil { suggestionPanelViewModel.chat = chatRoom + Task { @MainActor in + // looks like we need a delay. + try await Task.sleep(nanoseconds: 100_000_000) + NSApplication.shared.activate(ignoringOtherApps: true) + } } widgetViewModel.isProcessing = false chatForFiles[fileURL] = chatRoom @@ -342,8 +347,11 @@ extension SuggestionWidgetController { observeEditorChangeIfNeeded(app) guard let fileURL = try? await Environment.fetchCurrentFileURL() else { - suggestionPanelViewModel.content = nil - suggestionPanelViewModel.chat = nil + // if it's switching to a ui component that is not a text area. + if ActiveApplicationMonitor.activeApplication?.isXcode ?? false { + suggestionPanelViewModel.content = nil + suggestionPanelViewModel.chat = nil + } continue } guard fileURL != currentFileURL else { continue } From 51f965c09bf5e26451f687ca197e178321687c97 Mon Sep 17 00:00:00 2001 From: Shx Guo Date: Tue, 28 Mar 2023 23:40:04 +0800 Subject: [PATCH 9/9] Update preview content --- .../SuggestionWidget/SuggestionPanelContent/ChatPanel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift b/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift index 7394d241..60f1ac6a 100644 --- a/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift +++ b/Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift @@ -263,7 +263,7 @@ struct ChatPanel_InputMultilineText_Preview: PreviewProvider { history: ChatPanel_Preview.history, isReceivingMessage: false ), - typedMessage: "Hello\nWorld\nPikachu\nCat" + typedMessage: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce turpis dolor, malesuada quis fringilla sit amet, placerat at nunc. Suspendisse orci tortor, tempor nec blandit a, malesuada vel tellus. Nunc sed leo ligula. Ut at ligula eget turpis pharetra tristique. Integer luctus leo non elit rhoncus fermentum." ) .padding(8) .background(Color.contentBackground)