Skip to content

Commit 8156bdd

Browse files
committed
Simplified chat panel transform for correct debouncing behavior
1 parent 0a3c616 commit 8156bdd

2 files changed

Lines changed: 134 additions & 118 deletions

File tree

Core/Sources/SuggestionWidget/NSScrollView/CustomScrollView.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ private struct ComputeHeight<Content: View>: NSViewRepresentable {
5050
rootView: contentView.frame(width: nsView.frame.width == 0 ? 200 : nsView.frame.width)
5151
)
5252
let size = hostingView.fittingSize
53-
print(size)
5453

5554
if height != size.height {
5655
Task { @MainActor in

Core/Sources/SuggestionWidget/SuggestionPanelContent/ChatPanel.swift

Lines changed: 134 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import AppKit
22
import MarkdownUI
33
import SwiftUI
44

5+
private let r: Double = 8
6+
57
struct ChatPanel: View {
68
let chat: ChatProvider
79
@Namespace var inputAreaNamespace
@@ -12,13 +14,11 @@ struct ChatPanel: View {
1214
ChatPanelToolbar(chat: chat)
1315
Divider()
1416
ChatPanelMessages(
15-
chat: chat,
16-
inputAreaNamespace: inputAreaNamespace
17+
chat: chat
1718
)
1819
Divider()
1920
ChatPanelInputArea(
2021
chat: chat,
21-
inputAreaNamespace: inputAreaNamespace,
2222
typedMessage: $typedMessage
2323
)
2424
}
@@ -60,8 +60,6 @@ struct ChatPanelToolbar: View {
6060

6161
struct ChatPanelMessages: View {
6262
@ObservedObject var chat: ChatProvider
63-
var inputAreaNamespace: Namespace.ID
64-
@Environment(\.colorScheme) var colorScheme
6563
@AppStorage(\.disableLazyVStack) var disableLazyVStack
6664

6765
@ViewBuilder
@@ -79,132 +77,151 @@ struct ChatPanelMessages: View {
7977

8078
var body: some View {
8179
CustomScrollView {
82-
vstack {
83-
let r = 8 as Double
84-
85-
Spacer()
86-
87-
if chat.isReceivingMessage {
88-
Button(action: {
89-
chat.stop()
90-
}) {
91-
HStack(spacing: 4) {
92-
Image(systemName: "stop.fill")
93-
Text("Stop Responding")
94-
}
95-
.rotationEffect(Angle(degrees: 180))
96-
.padding(8)
97-
.background(
98-
.regularMaterial,
99-
in: RoundedRectangle(cornerRadius: r, style: .continuous)
100-
)
101-
.overlay {
102-
RoundedRectangle(cornerRadius: r, style: .continuous)
103-
.stroke(Color(nsColor: .separatorColor), lineWidth: 1)
104-
}
80+
VStack(spacing: 0) {
81+
vstack {
82+
Spacer()
83+
84+
if chat.isReceivingMessage {
85+
StopRespondingButton(chat: chat)
10586
}
106-
.buttonStyle(.borderless)
107-
.scaleEffect(x: -1, y: 1, anchor: .center)
108-
}
10987

110-
if chat.history.isEmpty {
111-
Text("New Chat")
112-
.frame(maxWidth: .infinity, alignment: .center)
113-
.padding()
114-
.rotationEffect(Angle(degrees: 180))
115-
.scaleEffect(x: -1, y: 1, anchor: .center)
116-
.foregroundStyle(.secondary)
117-
}
118-
119-
ForEach(chat.history.reversed(), id: \.id) { message in
120-
let text = message.text.isEmpty && !message.isUser ? "..." : message
121-
.text
122-
123-
if message.isUser {
124-
Markdown(text)
125-
.textSelection(.enabled)
126-
.markdownTheme(.gitHub.text {
127-
BackgroundColor(Color.clear)
128-
})
129-
.markdownCodeSyntaxHighlighter(
130-
ChatCodeSyntaxHighlighter(brightMode: colorScheme != .dark)
131-
)
132-
.frame(alignment: .trailing)
88+
if chat.history.isEmpty {
89+
Text("New Chat")
90+
.frame(maxWidth: .infinity, alignment: .center)
13391
.padding()
134-
.background {
135-
RoundedCorners(tl: r, tr: r, bl: r, br: 0)
136-
.fill(Color.userChatContentBackground)
137-
}
138-
.overlay {
139-
RoundedCorners(tl: r, tr: r, bl: r, br: 0)
140-
.stroke(Color(nsColor: .separatorColor), lineWidth: 1)
141-
}
142-
.padding(.leading)
143-
.padding(.trailing, 8)
144-
.rotationEffect(Angle(degrees: 180))
145-
.scaleEffect(x: -1, y: 1, anchor: .center)
146-
.shadow(color: .black.opacity(0.1), radius: 2)
147-
.frame(maxWidth: .infinity, alignment: .trailing)
148-
.contextMenu {
149-
Button("Copy") {
150-
NSPasteboard.general.clearContents()
151-
NSPasteboard.general.setString(text, forType: .string)
152-
}
153-
}
154-
} else {
155-
HStack(alignment: .bottom, spacing: 2) {
156-
Markdown(text)
157-
.textSelection(.enabled)
158-
.markdownTheme(.gitHub.text {
159-
BackgroundColor(Color.clear)
160-
})
161-
.markdownCodeSyntaxHighlighter(
162-
ChatCodeSyntaxHighlighter(brightMode: colorScheme != .dark)
163-
)
164-
.frame(alignment: .leading)
165-
.padding()
166-
.background {
167-
RoundedCorners(tl: r, tr: r, bl: 0, br: r)
168-
.fill(Color.contentBackground)
169-
}
170-
.overlay {
171-
RoundedCorners(tl: r, tr: r, bl: 0, br: r)
172-
.stroke(Color(nsColor: .separatorColor), lineWidth: 1)
173-
}
174-
.padding(.leading, 8)
175-
.rotationEffect(Angle(degrees: 180))
176-
.scaleEffect(x: -1, y: 1, anchor: .center)
177-
.shadow(color: .black.opacity(0.1), radius: 2)
178-
.contextMenu {
179-
Button("Copy") {
180-
NSPasteboard.general.clearContents()
181-
NSPasteboard.general.setString(text, forType: .string)
182-
}
183-
}
184-
185-
CopyButton {
186-
NSPasteboard.general.clearContents()
187-
NSPasteboard.general.setString(text, forType: .string)
188-
}
189-
.rotationEffect(Angle(degrees: 180))
190-
.scaleEffect(x: -1, y: 1, anchor: .center)
92+
.scaleEffect(x: -1, y: -1, anchor: .center)
93+
.foregroundStyle(.secondary)
94+
}
95+
96+
ForEach(chat.history.reversed(), id: \.id) { message in
97+
let text = message.text.isEmpty && !message.isUser ? "..." : message
98+
.text
99+
100+
if message.isUser {
101+
UserMessage(text: text)
102+
} else {
103+
BotMessage(text: text)
191104
}
192-
.frame(maxWidth: .infinity, alignment: .leading)
193-
.padding(.trailing, 2)
194105
}
106+
107+
Spacer()
195108
}
109+
}
110+
}
111+
.scaleEffect(x: -1, y: -1, anchor: .center)
112+
}
113+
}
114+
115+
private struct StopRespondingButton: View {
116+
let chat: ChatProvider
196117

197-
Spacer()
118+
var body: some View {
119+
Button(action: {
120+
chat.stop()
121+
}) {
122+
HStack(spacing: 4) {
123+
Image(systemName: "stop.fill")
124+
Text("Stop Responding")
198125
}
126+
.padding(8)
127+
.background(
128+
.regularMaterial,
129+
in: RoundedRectangle(cornerRadius: r, style: .continuous)
130+
)
131+
.overlay {
132+
RoundedRectangle(cornerRadius: r, style: .continuous)
133+
.stroke(Color(nsColor: .separatorColor), lineWidth: 1)
134+
}
135+
}
136+
.buttonStyle(.borderless)
137+
.scaleEffect(x: -1, y: -1, anchor: .center)
138+
}
139+
}
140+
141+
private struct UserMessage: View {
142+
let text: String
143+
@Environment(\.colorScheme) var colorScheme
144+
145+
var body: some View {
146+
Markdown(text)
147+
.textSelection(.enabled)
148+
.markdownTheme(.gitHub.text {
149+
BackgroundColor(Color.clear)
150+
})
151+
.markdownCodeSyntaxHighlighter(
152+
ChatCodeSyntaxHighlighter(brightMode: colorScheme != .dark)
153+
)
154+
.frame(alignment: .leading)
155+
.padding()
156+
.background {
157+
RoundedCorners(tl: r, tr: r, bl: r, br: 0)
158+
.fill(Color.userChatContentBackground)
159+
}
160+
.overlay {
161+
RoundedCorners(tl: r, tr: r, bl: r, br: 0)
162+
.stroke(Color(nsColor: .separatorColor), lineWidth: 1)
163+
}
164+
.padding(.leading)
165+
.padding(.trailing, 8)
166+
.scaleEffect(x: -1, y: -1, anchor: .center)
167+
.shadow(color: .black.opacity(0.1), radius: 2)
168+
.frame(maxWidth: .infinity, alignment: .leading)
169+
.contextMenu {
170+
Button("Copy") {
171+
NSPasteboard.general.clearContents()
172+
NSPasteboard.general.setString(text, forType: .string)
173+
}
174+
}
175+
}
176+
}
177+
178+
private struct BotMessage: View {
179+
let text: String
180+
@Environment(\.colorScheme) var colorScheme
181+
182+
var body: some View {
183+
HStack(alignment: .bottom, spacing: 2) {
184+
CopyButton {
185+
NSPasteboard.general.clearContents()
186+
NSPasteboard.general.setString(text, forType: .string)
187+
}
188+
.scaleEffect(x: -1, y: -1, anchor: .center)
189+
190+
Markdown(text)
191+
.textSelection(.enabled)
192+
.markdownTheme(.gitHub.text {
193+
BackgroundColor(Color.clear)
194+
})
195+
.markdownCodeSyntaxHighlighter(
196+
ChatCodeSyntaxHighlighter(brightMode: colorScheme != .dark)
197+
)
198+
.frame(alignment: .trailing)
199+
.padding()
200+
.background {
201+
RoundedCorners(tl: r, tr: r, bl: 0, br: r)
202+
.fill(Color.contentBackground)
203+
}
204+
.overlay {
205+
RoundedCorners(tl: r, tr: r, bl: 0, br: r)
206+
.stroke(Color(nsColor: .separatorColor), lineWidth: 1)
207+
}
208+
.padding(.leading, 8)
209+
.scaleEffect(x: -1, y: -1, anchor: .center)
210+
.shadow(color: .black.opacity(0.1), radius: 2)
211+
.contextMenu {
212+
Button("Copy") {
213+
NSPasteboard.general.clearContents()
214+
NSPasteboard.general.setString(text, forType: .string)
215+
}
216+
}
199217
}
200-
.rotationEffect(Angle(degrees: 180))
201-
.scaleEffect(x: -1, y: 1, anchor: .center)
218+
.frame(maxWidth: .infinity, alignment: .trailing)
219+
.padding(.trailing, 2)
202220
}
203221
}
204222

205223
struct ChatPanelInputArea: View {
206224
@ObservedObject var chat: ChatProvider
207-
var inputAreaNamespace: Namespace.ID
208225
@Binding var typedMessage: String
209226
@FocusState var isInputAreaFocused: Bool
210227

0 commit comments

Comments
 (0)