11import Combine
2+ import ComposableArchitecture
23import DebounceFunction
34import Foundation
45import MarkdownUI
6+ import Perception
57import SharedUIComponents
68import SwiftUI
79
810/// Use this instead of the built in ``CodeBlockView`` to highlight code blocks asynchronously,
911/// so that the UI doesn't freeze when rendering large code blocks.
1012struct AsyncCodeBlockView : View {
11- class Storage : ObservableObject {
13+ @Perceptible
14+ class Storage {
1215 static let queue = DispatchQueue (
1316 label: " chat-code-block-highlight " ,
14- qos: . userInteractive,
17+ qos: . userInteractive,
1518 attributes: . concurrent
1619 )
1720
18- @ Published var highlighted : AttributedString ?
19- var debounceFunction : DebounceFunction < AsyncCodeBlockView > ?
20- private var highlightTask : Task < Void , Error > ?
21-
21+ var highlighted : AttributedString ?
22+ @ PerceptionIgnored var debounceFunction : DebounceFunction < AsyncCodeBlockView > ?
23+ @ PerceptionIgnored private var highlightTask : Task < Void , Error > ?
24+
2225 init ( ) {
23- self . debounceFunction = . init( duration: 0.5 , block: { [ weak self] view in
26+ debounceFunction = . init( duration: 0.5 , block: { [ weak self] view in
2427 self ? . highlight ( for: view)
2528 } )
2629 }
@@ -32,7 +35,7 @@ struct AsyncCodeBlockView: View {
3235 highlight ( for: view)
3336 }
3437 }
35-
38+
3639 func highlight( for view: AsyncCodeBlockView ) {
3740 highlightTask? . cancel ( )
3841 let content = view. content
@@ -65,7 +68,7 @@ struct AsyncCodeBlockView: View {
6568 let font : NSFont
6669
6770 @Environment ( \. colorScheme) var colorScheme
68- @StateObject var storage = Storage ( )
71+ @State var storage = Storage ( )
6972 @AppStorage ( \. syncChatCodeHighlightTheme) var syncCodeHighlightTheme
7073 @AppStorage ( \. codeForegroundColorLight) var codeForegroundColorLight
7174 @AppStorage ( \. codeBackgroundColorLight) var codeBackgroundColorLight
@@ -79,33 +82,35 @@ struct AsyncCodeBlockView: View {
7982 }
8083
8184 var body : some View {
82- Group {
83- if let highlighted = storage. highlighted {
84- Text ( highlighted)
85- } else {
86- Text ( content) . font ( . init( font) )
85+ WithPerceptionTracking {
86+ Group {
87+ if let highlighted = storage. highlighted {
88+ Text ( highlighted)
89+ } else {
90+ Text ( content) . font ( . init( font) )
91+ }
92+ }
93+ . onAppear {
94+ storage. highlight ( debounce: false , for: self )
95+ }
96+ . onChange ( of: colorScheme) { _ in
97+ storage. highlight ( debounce: false , for: self )
98+ }
99+ . onChange ( of: syncCodeHighlightTheme) { _ in
100+ storage. highlight ( debounce: true , for: self )
101+ }
102+ . onChange ( of: codeForegroundColorLight) { _ in
103+ storage. highlight ( debounce: true , for: self )
104+ }
105+ . onChange ( of: codeBackgroundColorLight) { _ in
106+ storage. highlight ( debounce: true , for: self )
107+ }
108+ . onChange ( of: codeForegroundColorDark) { _ in
109+ storage. highlight ( debounce: true , for: self )
110+ }
111+ . onChange ( of: codeBackgroundColorDark) { _ in
112+ storage. highlight ( debounce: true , for: self )
87113 }
88- }
89- . onAppear {
90- storage. highlight ( debounce: false , for: self )
91- }
92- . onChange ( of: colorScheme) { _ in
93- storage. highlight ( debounce: false , for: self )
94- }
95- . onChange ( of: syncCodeHighlightTheme) { _ in
96- storage. highlight ( debounce: true , for: self )
97- }
98- . onChange ( of: codeForegroundColorLight) { _ in
99- storage. highlight ( debounce: true , for: self )
100- }
101- . onChange ( of: codeBackgroundColorLight) { _ in
102- storage. highlight ( debounce: true , for: self )
103- }
104- . onChange ( of: codeForegroundColorDark) { _ in
105- storage. highlight ( debounce: true , for: self )
106- }
107- . onChange ( of: codeBackgroundColorDark) { _ in
108- storage. highlight ( debounce: true , for: self )
109114 }
110115 }
111116}
0 commit comments