Skip to content

Commit 9afce3e

Browse files
committed
feat: Improve answer streaming and text rendering efficiency
This commit improves the efficiency of answer streaming and text rendering, significantly reducing the bottleneck on the main thread. The changes include: - Stream answers every 1 second: Implemented a mechanism to stream the generated answer in 1-second intervals, providing a continuous update to the user. This avoids rendering the entire answer at once, improving perceived responsiveness. - Utilize TextEditor for long text strings: Replaced the @State property used for displaying the answer with a TextEditor(text: Binding<String>). This allows TextEditor to efficiently handle long text strings, preventing layout issues and improving rendering performance. - Optimized text rendering: The TextEditor component is now used to display the answer, offering a more robust and performant solution for handling extended text content. This optimization will result in faster response times and a smoother user experience, especially when generating long answers.
1 parent c42e41a commit 9afce3e

File tree

2 files changed

+34
-9
lines changed

2 files changed

+34
-9
lines changed

macLlama/Views/ConversationView/ChatBubbleView.swift

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ struct ChatBubbleView: View {
1515
@State private var messageAnimationFactor: CGFloat = 0.0
1616
@State private var messageAnimated: Bool = false
1717
@State private var isMarkdownEnabled: Bool = false
18+
@State private var chatMessage: String = ""
1819

19-
let chatData: (isUser: Bool, modelName: String, message: String)
20+
@Binding var chatData: (isUser: Bool, modelName: String, message: String)
2021

2122
var body: some View {
2223
HStack(alignment: .top) {
@@ -103,7 +104,7 @@ struct ChatBubbleView: View {
103104
VStack {
104105
if isMarkdownEnabled {
105106
Markdown {
106-
MarkdownContent(chatData.message)
107+
MarkdownContent(chatMessage)
107108
}
108109
.markdownTextStyle(\.code) {
109110
FontFamilyVariant(.monospaced)
@@ -115,18 +116,31 @@ struct ChatBubbleView: View {
115116
.textSelection(.enabled)
116117
.markdownTheme(.gitHub)
117118
} else {
118-
Text(chatData.message)
119-
.font(.system(size: CGFloat(chatFontSize)))
120-
.lineSpacing(CGFloat(chatFontSize / 3))
121-
.textSelection(.enabled)
122-
.frame(alignment: chatData.isUser ? .trailing : .leading)
119+
if !chatData.isUser {
120+
TextEditor(text: $chatMessage)
121+
.font(.system(size: CGFloat(chatFontSize)))
122+
.lineSpacing(CGFloat(chatFontSize / 3))
123+
.scrollDisabled(true)
124+
.textSelection(.enabled)
125+
.scrollContentBackground(.hidden)
126+
.frame(minHeight: 50, alignment: .leading)
127+
} else {
128+
Text(chatData.message)
129+
.font(.system(size: CGFloat(chatFontSize)))
130+
.lineSpacing(CGFloat(chatFontSize / 3))
131+
.textSelection(.enabled)
132+
.frame(alignment: .trailing)
133+
}
123134
}
124135
}
125136
.padding(.horizontal, chatData.isUser ? Units.normalGap * 1.3 : 0)
126137
.padding(.vertical, chatData.isUser ? Units.normalGap / 1.5 : 0)
127138
.background(chatData.isUser ? .black.opacity(0.15) : .clear)
128139
.clipShape(chatData.isUser ? RoundedRectangle(cornerRadius: 8) : RoundedRectangle(cornerRadius: 0))
129140
.frame(maxWidth: .infinity, alignment: chatData.isUser ? .trailing : .leading)
141+
.onChange(of: self.chatData.message) { _, newValue in
142+
self.chatMessage = newValue
143+
}
130144
}
131145

132146
//Remains for future features (User's chat avatar)

macLlama/Views/ConversationView/ConversationChatView.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77

88
import SwiftUI
9+
import Combine
910

1011
struct ConversationChatView: View {
1112
@Environment(\.colorScheme) var colorScheme
@@ -62,7 +63,7 @@ struct ConversationChatView: View {
6263
ForEach(0..<self.history.count, id: \.self) { index in
6364
LazyVStack {
6465
if history[index].message != "" {
65-
ChatBubbleView(isThinking: self.$isThinking, chatData: history[index])
66+
ChatBubbleView(isThinking: self.$isThinking, chatData: $history[index])
6667
.padding()
6768
.id(index)
6869

@@ -164,9 +165,19 @@ extension ConversationChatView {
164165

165166
//Start stream from model
166167
Task {
168+
var cancellable: Set<AnyCancellable> = []
169+
let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
170+
var count: Int = 0
171+
timer.sink { _ in
172+
count += 1
173+
}.store(in: &cancellable)
174+
167175
let stream = try await chatService.sendMessage(model: model, userInput: prompt, images: images)
168176
for await update in stream {
169-
self.history[self.history.count - 1].message = update
177+
let outputText = update
178+
if count % 2 == 0 { //update stream in every 2 seconds
179+
self.history[self.history.count - 1].message = outputText
180+
}
170181
}
171182

172183
//Save last response to history

0 commit comments

Comments
 (0)