Skip to content

Commit 209c25d

Browse files
committed
Merge branch 'new-launch-method-followup' into develop
2 parents 6b84efe + 95daea1 commit 209c25d

17 files changed

Lines changed: 448 additions & 302 deletions

Core/Sources/ChatGPTChatTab/ChatPanel.swift

Lines changed: 40 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -277,42 +277,51 @@ struct ChatHistory: View {
277277
var body: some View {
278278
WithViewStore(chat, observe: \.history) { viewStore in
279279
ForEach(viewStore.state, id: \.id) { message in
280-
let text = message.text
281-
282-
switch message.role {
283-
case .user:
284-
UserMessage(id: message.id, text: text, chat: chat)
285-
.listRowInsets(EdgeInsets(
286-
top: 0,
287-
leading: -8,
288-
bottom: 0,
289-
trailing: -8
290-
))
291-
.padding(.vertical, 4)
292-
case .assistant:
293-
BotMessage(
294-
id: message.id,
295-
text: text,
296-
references: message.references,
297-
chat: chat
298-
)
299-
.listRowInsets(EdgeInsets(
300-
top: 0,
301-
leading: -8,
302-
bottom: 0,
303-
trailing: -8
304-
))
305-
.padding(.vertical, 4)
306-
case .tool:
307-
FunctionMessage(id: message.id, text: text)
308-
case .ignored:
309-
EmptyView()
310-
}
280+
ChatHistoryItem(chat: chat, message: message).id(message.id)
311281
}
312282
}
313283
}
314284
}
315285

286+
struct ChatHistoryItem: View {
287+
let chat: StoreOf<Chat>
288+
let message: DisplayedChatMessage
289+
290+
var body: some View {
291+
let text = message.text
292+
293+
switch message.role {
294+
case .user:
295+
UserMessage(id: message.id, text: text, chat: chat)
296+
.listRowInsets(EdgeInsets(
297+
top: 0,
298+
leading: -8,
299+
bottom: 0,
300+
trailing: -8
301+
))
302+
.padding(.vertical, 4)
303+
case .assistant:
304+
BotMessage(
305+
id: message.id,
306+
text: text,
307+
references: message.references,
308+
chat: chat
309+
)
310+
.listRowInsets(EdgeInsets(
311+
top: 0,
312+
leading: -8,
313+
bottom: 0,
314+
trailing: -8
315+
))
316+
.padding(.vertical, 4)
317+
case .tool:
318+
FunctionMessage(id: message.id, text: text)
319+
case .ignored:
320+
EmptyView()
321+
}
322+
}
323+
}
324+
316325
private struct StopRespondingButton: View {
317326
let chat: StoreOf<Chat>
318327

@@ -563,29 +572,6 @@ struct ChatPanel_EmptyChat_Preview: PreviewProvider {
563572
}
564573
}
565574

566-
struct ChatCodeSyntaxHighlighter: CodeSyntaxHighlighter {
567-
let brightMode: Bool
568-
let font: NSFont
569-
let colorChange: Color?
570-
571-
init(brightMode: Bool, font: NSFont, colorChange: Color?) {
572-
self.brightMode = brightMode
573-
self.font = font
574-
self.colorChange = colorChange
575-
}
576-
577-
func highlightCode(_ content: String, language: String?) -> Text {
578-
let content = highlightedCodeBlock(
579-
code: content,
580-
language: language ?? "",
581-
scenario: "chat",
582-
brightMode: brightMode,
583-
font: font
584-
)
585-
return Text(AttributedString(content))
586-
}
587-
}
588-
589575
struct ChatPanel_InputText_Preview: PreviewProvider {
590576
static var previews: some View {
591577
ChatPanel(chat: .init(
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import Combine
2+
import DebounceFunction
3+
import Foundation
4+
import MarkdownUI
5+
import SharedUIComponents
6+
import SwiftUI
7+
8+
/// Use this instead of the built in ``CodeBlockView`` to highlight code blocks asynchronously,
9+
/// so that the UI doesn't freeze when rendering large code blocks.
10+
struct AsyncCodeBlockView: View {
11+
class Storage: ObservableObject {
12+
static let queue = DispatchQueue(
13+
label: "chat-code-block-highlight",
14+
qos: .userInteractive
15+
)
16+
17+
@Published var highlighted: AttributedString?
18+
var debounceFunction: DebounceFunction<AsyncCodeBlockView>?
19+
20+
init() {
21+
self.debounceFunction = .init(duration: 0.5, block: { [weak self] view in
22+
self?.highlight(for: view)
23+
})
24+
}
25+
26+
func highlight(debounce: Bool, for view: AsyncCodeBlockView) {
27+
if debounce {
28+
Task { await debounceFunction?(view) }
29+
} else {
30+
highlight(for: view)
31+
}
32+
}
33+
34+
func highlight(for view: AsyncCodeBlockView) {
35+
let content = view.content
36+
let language = view.fenceInfo ?? ""
37+
let brightMode = view.colorScheme != .dark
38+
let font = view.font
39+
Self.queue.async {
40+
let content = highlightedCodeBlock(
41+
code: content,
42+
language: language,
43+
scenario: "chat",
44+
brightMode: brightMode,
45+
font: font
46+
)
47+
let string = AttributedString(content)
48+
DispatchQueue.main.async {
49+
self.highlighted = string
50+
}
51+
}
52+
}
53+
}
54+
55+
let fenceInfo: String?
56+
let content: String
57+
let font: NSFont
58+
59+
@Environment(\.colorScheme) var colorScheme
60+
@StateObject var storage = Storage()
61+
@AppStorage(\.syncChatCodeHighlightTheme) var syncCodeHighlightTheme
62+
@AppStorage(\.codeForegroundColorLight) var codeForegroundColorLight
63+
@AppStorage(\.codeBackgroundColorLight) var codeBackgroundColorLight
64+
@AppStorage(\.codeForegroundColorDark) var codeForegroundColorDark
65+
@AppStorage(\.codeBackgroundColorDark) var codeBackgroundColorDark
66+
67+
init(fenceInfo: String?, content: String, font: NSFont) {
68+
self.fenceInfo = fenceInfo
69+
self.content = content.hasSuffix("\n") ? String(content.dropLast()) : content
70+
self.font = font
71+
}
72+
73+
var body: some View {
74+
Group {
75+
if let highlighted = storage.highlighted {
76+
Text(highlighted)
77+
} else {
78+
Text(content).font(.init(font))
79+
}
80+
}
81+
.onAppear {
82+
storage.highlight(debounce: false, for: self)
83+
}
84+
.onChange(of: colorScheme) { _ in
85+
storage.highlight(debounce: false, for: self)
86+
}
87+
.onChange(of: syncCodeHighlightTheme) { _ in
88+
storage.highlight(debounce: true, for: self)
89+
}
90+
.onChange(of: codeForegroundColorLight) { _ in
91+
storage.highlight(debounce: true, for: self)
92+
}
93+
.onChange(of: codeBackgroundColorLight) { _ in
94+
storage.highlight(debounce: true, for: self)
95+
}
96+
.onChange(of: codeForegroundColorDark) { _ in
97+
storage.highlight(debounce: true, for: self)
98+
}
99+
.onChange(of: codeBackgroundColorDark) { _ in
100+
storage.highlight(debounce: true, for: self)
101+
}
102+
}
103+
}
104+

0 commit comments

Comments
 (0)