aboutsummaryrefslogtreecommitdiff
path: root/Sources/NorgUI/Math/NorgInlineText.swift
diff options
context:
space:
mode:
authorRuben Beltran del Rio <jj@r.bdr.sh>2026-06-18 23:53:25 +0200
committerRuben Beltran del Rio <jj@r.bdr.sh>2026-06-19 17:56:23 +0200
commit49b0f4a3fb0064313d16782839865b763ef760ce (patch)
treea58439f1433de9ff66dc0be28f025afe56e79cfe /Sources/NorgUI/Math/NorgInlineText.swift
parentcb91a73bfc845d74c3e4d1115bce5dfed10d456d (diff)
Support Math rendering1.2.0
Diffstat (limited to 'Sources/NorgUI/Math/NorgInlineText.swift')
-rw-r--r--Sources/NorgUI/Math/NorgInlineText.swift52
1 files changed, 52 insertions, 0 deletions
diff --git a/Sources/NorgUI/Math/NorgInlineText.swift b/Sources/NorgUI/Math/NorgInlineText.swift
new file mode 100644
index 0000000..0d607f5
--- /dev/null
+++ b/Sources/NorgUI/Math/NorgInlineText.swift
@@ -0,0 +1,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()
+ }
+ }
+}