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() } } }