Skip to content

Commit ea70a95

Browse files
committed
Merge branch 'feature/tweak-chat-box-behavior' into develop
2 parents 1643f92 + 51f965c commit ea70a95

3 files changed

Lines changed: 54 additions & 19 deletions

File tree

Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ struct ChatPanelMessages: View {
5555
}
5656
}
5757
}
58-
58+
5959
var body: some View {
6060
ScrollView {
6161
vstack {
@@ -77,6 +77,7 @@ struct ChatPanelMessages: View {
7777
.buttonStyle(.plain)
7878
.xcodeStyleFrame()
7979
.matchedGeometryEffect(id: "input", in: inputAreaNamespace)
80+
.scaleEffect(x: -1, y: 1, anchor: .center)
8081
}
8182

8283
if chat.history.isEmpty {
@@ -89,8 +90,9 @@ struct ChatPanelMessages: View {
8990
)
9091
.xcodeStyleFrame()
9192
.rotationEffect(Angle(degrees: 180))
93+
.scaleEffect(x: -1, y: 1, anchor: .center)
9294
}
93-
95+
9496
ForEach(chat.history.reversed(), id: \.id) { message in
9597
let text = message.text.isEmpty && !message.isUser ? "..." : message
9698
.text
@@ -112,31 +114,39 @@ struct ChatPanelMessages: View {
112114
)
113115
.xcodeStyleFrame()
114116
.rotationEffect(Angle(degrees: 180))
115-
117+
.scaleEffect(x: -1, y: 1, anchor: .center)
116118
}
117119
}
118120
}
119121
.rotationEffect(Angle(degrees: 180))
122+
.scaleEffect(x: -1, y: 1, anchor: .center)
120123
}
121124
}
122125

123126
struct ChatPanelInputArea: View {
124127
@ObservedObject var chat: ChatRoom
125128
var inputAreaNamespace: Namespace.ID
126129
@Binding var typedMessage: String
130+
@FocusState var isInputAreaFocused: Bool
127131

128132
var body: some View {
129133
HStack {
130134
// clear button
131135
Button(action: {
132136
chat.clear()
133137
}) {
134-
Image(systemName: "eraser.line.dashed.fill")
135-
.padding(8)
136-
.background(
137-
.regularMaterial,
138-
in: RoundedRectangle(cornerRadius: 8, style: .continuous)
139-
)
138+
Group {
139+
if #available(macOS 13.0, *) {
140+
Image(systemName: "eraser.line.dashed.fill")
141+
} else {
142+
Image(systemName: "trash.fill")
143+
}
144+
}
145+
.padding(8)
146+
.background(
147+
.regularMaterial,
148+
in: RoundedRectangle(cornerRadius: 8, style: .continuous)
149+
)
140150
}
141151
.buttonStyle(.plain)
142152
.xcodeStyleFrame()
@@ -145,9 +155,13 @@ struct ChatPanelInputArea: View {
145155
if #available(macOS 13.0, *) {
146156
TextField("Type a message", text: $typedMessage, axis: .vertical)
147157
} else {
148-
TextField("Type a message", text: $typedMessage)
158+
TextEditor(text: $typedMessage)
159+
.frame(height: 42, alignment: .leading)
160+
.font(.body)
161+
.background(Color.clear)
149162
}
150163
}
164+
.focused($isInputAreaFocused)
151165
.lineLimit(3)
152166
.multilineTextAlignment(.leading)
153167
.textFieldStyle(.plain)
@@ -179,6 +193,9 @@ struct ChatPanelInputArea: View {
179193
.buttonStyle(.plain)
180194
.xcodeStyleFrame()
181195
}
196+
.onAppear {
197+
isInputAreaFocused = true
198+
}
182199
}
183200
}
184201

@@ -246,7 +263,7 @@ struct ChatPanel_InputMultilineText_Preview: PreviewProvider {
246263
history: ChatPanel_Preview.history,
247264
isReceivingMessage: false
248265
),
249-
typedMessage: "Hello\nWorld"
266+
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."
250267
)
251268
.padding(8)
252269
.background(Color.contentBackground)

Core/Sources/SuggestionWidget/SuggestionWidgetController.swift

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public final class SuggestionWidgetController {
7979
)
8080
it.setIsVisible(true)
8181
it.canBecomeKeyChecker = { [suggestionPanelViewModel] in
82-
if case .chat = suggestionPanelViewModel.activeTab { return false }
82+
if case .chat = suggestionPanelViewModel.activeTab { return true }
8383
return false
8484
}
8585
return it
@@ -219,19 +219,26 @@ public final class SuggestionWidgetController {
219219
}
220220

221221
Task { @MainActor in
222-
suggestionPanelViewModel.onActiveTabChanged = { [weak self] activeTab in
222+
var switchTask: Task<Void, Error>?
223+
suggestionPanelViewModel.onActiveTabChanged = { activeTab in
223224
#warning("""
224225
TODO: There should be a better way for that
225226
Currently, we have to make the app an accessory so that we can type things in the chat mode.
226227
But in other modes, we want to keep it prohibited so the helper app won't take over the focus.
227228
""")
228-
if case .chat = activeTab, NSApp.activationPolicy() != .accessory {
229-
NSApp.setActivationPolicy(.accessory)
230-
} else if NSApp.activationPolicy() != .prohibited {
231-
Task {
229+
switch activeTab {
230+
case .suggestion:
231+
guard NSApp.activationPolicy() != .prohibited else { return }
232+
switchTask?.cancel()
233+
switchTask = Task {
232234
try await Environment.makeXcodeActive()
235+
try Task.checkCancellation()
233236
NSApp.setActivationPolicy(.prohibited)
234237
}
238+
case .chat:
239+
guard NSApp.activationPolicy() != .accessory else { return }
240+
switchTask?.cancel()
241+
NSApp.setActivationPolicy(.accessory)
235242
}
236243
}
237244
}
@@ -292,6 +299,11 @@ public extension SuggestionWidgetController {
292299
func presentChatRoom(_ chatRoom: ChatRoom, fileURL: URL) {
293300
if fileURL == currentFileURL || currentFileURL == nil {
294301
suggestionPanelViewModel.chat = chatRoom
302+
Task { @MainActor in
303+
// looks like we need a delay.
304+
try await Task.sleep(nanoseconds: 100_000_000)
305+
NSApplication.shared.activate(ignoringOtherApps: true)
306+
}
295307
}
296308
widgetViewModel.isProcessing = false
297309
chatForFiles[fileURL] = chatRoom
@@ -335,8 +347,11 @@ extension SuggestionWidgetController {
335347
observeEditorChangeIfNeeded(app)
336348
337349
guard let fileURL = try? await Environment.fetchCurrentFileURL() else {
338-
suggestionPanelViewModel.content = nil
339-
suggestionPanelViewModel.chat = nil
350+
// if it's switching to a ui component that is not a text area.
351+
if ActiveApplicationMonitor.activeApplication?.isXcode ?? false {
352+
suggestionPanelViewModel.content = nil
353+
suggestionPanelViewModel.chat = nil
354+
}
340355
continue
341356
}
342357
guard fileURL != currentFileURL else { continue }
@@ -373,12 +388,14 @@ extension SuggestionWidgetController {
373388
scroll
374389
) {
375390
guard let self else { return }
391+
guard ActiveApplicationMonitor.activeXcode != nil else { return }
376392
try Task.checkCancellation()
377393
self.updateWindowLocation(animated: false)
378394
}
379395
} else {
380396
for await _ in merge(selectionRangeChange, scroll) {
381397
guard let self else { return }
398+
guard ActiveApplicationMonitor.activeXcode != nil else { return }
382399
try Task.checkCancellation()
383400
let mode = UserDefaults.shared.value(for: \.suggestionWidgetPositionMode)
384401
if mode != .alignToTextCursor { break }

Core/Sources/SuggestionWidget/TabView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ struct TabView: View {
3838
}
3939
.opacity(panelViewModel.isPanelDisplayed ? 1 : 0)
4040
.preferredColorScheme(panelViewModel.colorScheme)
41+
.frame(maxWidth: Style.widgetWidth, maxHeight: Style.widgetHeight)
4142
}
4243
}
4344

0 commit comments

Comments
 (0)