import OrgSwift import SwiftUI /// Renders prepared blocks. All the org work happened in ``OrgLayoutBuilder``; this is /// layout only. struct OrgBlockListView: View { let blocks: [OrgBlock] var body: some View { VStack(alignment: .leading, spacing: 12) { ForEach(Array(blocks.enumerated()), id: \.offset) { _, block in OrgBlockView(block: block) } } .frame(maxWidth: .infinity, alignment: .leading) } } struct OrgBlockView: View { let block: OrgBlock @Environment(\.orgMath) private var math var body: some View { switch block { case .heading(let text, let level): orgText(text, math: math) .padding(.top, level <= 2 ? 8 : 2) .fixedSize(horizontal: false, vertical: true) case .paragraph(let text): orgText(text, math: math) .fixedSize(horizontal: false, vertical: true) case .code(let code): ScrollView(.horizontal) { Text(code) .textSelection(.enabled) .padding(10) } .background( Color.secondary.opacity(0.12), in: RoundedRectangle(cornerRadius: 4) ) case .quote(let blocks): HStack(spacing: 10) { RoundedRectangle(cornerRadius: 2) .fill(.tertiary) .frame(width: 3) OrgBlockListView(blocks: blocks) .foregroundStyle(.secondary) } .fixedSize(horizontal: false, vertical: true) case .center(let blocks): OrgBlockListView(blocks: blocks) .multilineTextAlignment(.center) .frame(maxWidth: .infinity, alignment: .center) case .verse(let lines): // Verse keeps its line breaks; that is the whole point of the block. VStack(alignment: .leading, spacing: 0) { ForEach(Array(lines.enumerated()), id: \.offset) { _, line in orgText(line, math: math).fixedSize(horizontal: false, vertical: true) } } case .special(_, let blocks): OrgBlockListView(blocks: blocks) case .list(let list): OrgListView(list: list) case .table(let table): OrgTableView(table: table) case .image(let source, let alt, let caption): VStack(alignment: .leading, spacing: 4) { AsyncImage(url: source) { phase in switch phase { case .success(let image): image.resizable().scaledToFit() case .failure: Text(alt.isEmpty ? source.lastPathComponent : alt) .font(.footnote) .foregroundStyle(.secondary) default: ProgressView() } } .accessibilityLabel(alt) if let caption { orgText(caption, math: math).foregroundStyle(.secondary) } } case .badges(let badges): OrgBadgeRowView(badges: badges) case .captioned(let caption, let blocks): VStack(alignment: .leading, spacing: 4) { OrgBlockListView(blocks: blocks) if let caption { orgText(caption, math: math).foregroundStyle(.secondary) } } case .rule: Divider() case .math(let expression): OrgMathBlockView(math: expression) } } }