blob: 0d607f58fde6e342f1e1385cb96d4931f034dfc2 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
import NorgKit
import SwiftUI
/// Renders a run of inline spans as a single `Text`, splicing in rendered
/// images for `.math` spans when a ``NorgMathRenderer`` is configured.
struct NorgInlineText: View {
@Environment(\.norgTheme) private var theme
let spans: [InlineSpan]
var baseFont: Font = .body
@State private var images: [Int: NorgMathImage] = [:]
var body: some View {
text
.task(id: renderKey) { await renderMath() }
}
private var text: Text {
var result = Text(verbatim: "")
for (index, span) in spans.enumerated() {
if span.styles.contains(.math), let image = images[index] {
result = result + Text(image.swiftUIImage).baselineOffset(-image.descent)
} else {
result = result + Text(AttributedString(norg: [span], baseFont: baseFont, theme: theme))
}
}
return result
}
private var mathSpans: [(offset: Int, element: InlineSpan)] {
spans.enumerated().filter { $0.element.styles.contains(.math) }.map { ($0.offset, $0.element) }
}
private var renderKey: String {
mathSpans.map { "\($0.offset):\($0.element.text)" }.joined(separator: "|")
+ "@\(theme.mathFontSize)"
}
@MainActor
private func renderMath() async {
guard let renderer = theme.mathRenderer, !mathSpans.isEmpty else { return }
for (index, span) in mathSpans where images[index] == nil {
if let image = await renderer.render(
latex: span.text, mode: .inline, fontSize: theme.mathFontSize, color: nil)
{
images[index] = image
}
await Task.yield()
}
}
}
|