import OrgSwift import SwiftUI /// Renders org-mode as native SwiftUI. /// /// ```swift /// OrgView(source, options: OrgRenderOptions(host: "gitbay.org", owner: "krz", /// repositoryName: "org-swift")) /// ``` /// /// The parse, footnote numbering, URL resolution and syntax highlighting run once off /// `body`, in a task keyed to the source *and the color scheme*, so scrolling and Dynamic /// Type changes do not re-derive them — but switching to dark mode does, because a /// ``OrgCodeStyler`` bakes its theme's colors into the text it returns. Text is selectable, /// links open through the environment's `openURL`, and everything scales with Dynamic Type. public struct OrgView: View { private let source: String private let options: OrgRenderOptions private let styler: OrgCodeStyler private let keywords: OrgKeywordStyle @Environment(\.colorScheme) private var colorScheme @State private var layout = OrgLayout() public init( _ source: String, options: OrgRenderOptions = .init(), styler: OrgCodeStyler = PlainOrgCodeStyler(), keywords: OrgKeywordStyle = .init() ) { self.source = source self.options = options self.styler = styler self.keywords = keywords } public var body: some View { VStack(alignment: .leading, spacing: 12) { OrgBlockListView(blocks: layout.blocks) if !layout.footnotes.isEmpty { OrgFootnotesView(footnotes: layout.footnotes) } } .frame(maxWidth: .infinity, alignment: .leading) .textSelection(.enabled) .task(id: LayoutKey(source: source, colorScheme: colorScheme)) { layout = OrgLayoutBuilder(options: options, styler: styler, keywords: keywords) .layout(OrgParser.parse(source)) } } } /// What the prepared layout depends on. Highlighted code carries baked-in colors, so the /// scheme belongs in the key alongside the source. private struct LayoutKey: Equatable { var source: String var colorScheme: ColorScheme } /// The notes section org's exporter puts at the end of a document. struct OrgFootnotesView: View { let footnotes: [OrgFootnote] var body: some View { VStack(alignment: .leading, spacing: 6) { Divider() ForEach(footnotes) { note in HStack(alignment: .firstTextBaseline, spacing: 6) { Text("\(note.number).") .font(.footnote) .monospacedDigit() .foregroundStyle(.secondary) Text(note.text) .fixedSize(horizontal: false, vertical: true) .frame(maxWidth: .infinity, alignment: .leading) } } } .padding(.top, 8) .accessibilityLabel("Footnotes") } }