Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ struct ChatPanelMessages: View {
}
}
}

var body: some View {
ScrollView {
vstack {
Expand All @@ -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 {
Expand All @@ -89,8 +90,9 @@ struct ChatPanelMessages: View {
)
.xcodeStyleFrame()
.rotationEffect(Angle(degrees: 180))
.scaleEffect(x: -1, y: 1, anchor: .center)
}

ForEach(chat.history.reversed(), id: \.id) { message in
let text = message.text.isEmpty && !message.isUser ? "..." : message
.text
Expand All @@ -112,31 +114,39 @@ 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)
}
}

struct ChatPanelInputArea: View {
@ObservedObject var chat: ChatRoom
var inputAreaNamespace: Namespace.ID
@Binding var typedMessage: String
@FocusState var isInputAreaFocused: Bool

var body: some View {
HStack {
// clear button
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()
Expand All @@ -145,9 +155,13 @@ 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)
.lineLimit(3)
.multilineTextAlignment(.leading)
.textFieldStyle(.plain)
Expand Down Expand Up @@ -179,6 +193,9 @@ struct ChatPanelInputArea: View {
.buttonStyle(.plain)
.xcodeStyleFrame()
}
.onAppear {
isInputAreaFocused = true
}
}
}

Expand Down Expand Up @@ -246,7 +263,7 @@ struct ChatPanel_InputMultilineText_Preview: PreviewProvider {
history: ChatPanel_Preview.history,
isReceivingMessage: false
),
typedMessage: "Hello\nWorld"
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)
Expand Down
33 changes: 25 additions & 8 deletions Core/Sources/SuggestionWidget/SuggestionWidgetController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -219,19 +219,26 @@ public final class SuggestionWidgetController {
}

Task { @MainActor in
suggestionPanelViewModel.onActiveTabChanged = { [weak self] activeTab in
var switchTask: Task<Void, Error>?
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 {
Task {
switch activeTab {
case .suggestion:
guard NSApp.activationPolicy() != .prohibited else { return }
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)
}
}
}
Expand Down Expand Up @@ -292,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
Expand Down Expand Up @@ -335,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 }
Expand Down Expand Up @@ -373,12 +388,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 }
Expand Down
1 change: 1 addition & 0 deletions Core/Sources/SuggestionWidget/TabView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct TabView: View {
}
.opacity(panelViewModel.isPanelDisplayed ? 1 : 0)
.preferredColorScheme(panelViewModel.colorScheme)
.frame(maxWidth: Style.widgetWidth, maxHeight: Style.widgetHeight)
}
}

Expand Down