Add OrgSwiftUI, a native renderer for the shared tree !2

merged merged by cmc on 2026-08-31 17:52 UTC · krz/org-swift:swiftui-renderer into main

Discussion

cmc

Adds a second product, OrgSwiftUI, that renders org as native SwiftUI views instead of HTML in a web view. OrgSwift stays Foundation-only.

Fixes found along the way

  • OrgAttributedStringRenderer emitted .file link targets as raw relative paths and footnote references as their label, so a natively rendered README got broken links and unnumbered notes where the HTML one did not.
  • Nested emphasis was flattened: *bold /italic/* rendered bold only, because the intent was assigned over the whole range rather than merged per run.
  • OrgHTMLTreeRenderer claimed Sendable while storing a CodeHighlighter, which a real implementation backs with a thread-confined engine. Dropped; the package is warning-free under strict concurrency now.

Structure

Footnote numbering and URL resolution were private to the HTML renderer. Both are now package-scoped (OrgFootnotes, OrgURLResolver) and shared by all three renderers, so a link that resolves in HTML resolves natively and note 1 is note 1 in both. Public API is unchanged.

OrgLayoutBuilder walks OrgDocument into a display model; the views are layout over it. The walk runs off body, in a task keyed to the source: footnote numbering is sequential and cannot happen in a @ViewBuilder, and syntax highlighting must not re-run per layout pass.

Covers every OrgElement case — recursive lists with checkboxes and description terms, tables in a Grid with org's column alignments and horizontal overflow scrolling, AsyncImage figures, and the footnote section. Highlighting arrives through OrgCodeStyler, so the package keeps no dependencies.

Deliberate divergence

An image source that will not resolve to an absolute URL degrades to its alt text. HTML can emit a relative src for a document base URL to resolve; a view has no base.

Verification

75 tests green (63 existing, unchanged, including corpus conformance at 11/12). Builds clean for iOS. The views have not been rendered on a device yet — that happens when gitbay-ios is wired up to this.