# NorgUI NorgUI provides themable SwiftUI views to render `.norg` / [Neorg](https://github.com/nvim-neorg/neorg) documents parsed with [NorgKit](https://git.sr.ht/~rbdr/norgkit). `NorgUI` doesn't provide editing capabilities. ## Installation Add the package with Xcode package manager: `https://git.r.bdr.sh/norgui`, and depend on `NorgUI` from your target. ## Usage Parse a document with `NorgKit` and render it with `NorgView`: import SwiftUI import NorgKit import NorgUI ```swift struct NoteView: View { let source: String var body: some View { ScrollView { NorgView(document: NorgParser.parse(source)) { line, status in // Persist the new status back to your file at `line`. } .padding() } } } ``` The `onSetStatus` callback is optional (defaults to a no-op) and fires when the user taps or long-presses a task's status symbol, reporting the source `line` and the chosen `TaskStatus`. ### Links Links are handled via an `onOpenLink` optional callback which will give you the `InlineLink`. If you don't provide one, external links will be handled by the system, while internal links and anchors will be inert. ```swift NorgView(document: document) { link in switch link.target { case let target? where target.hasPrefix("http"): openURL(URL(string: target)!) default: scrollTo(target: link.target) // a heading/anchor in this document } } ``` ### Collapsible View Pass `collapsible: true` to render the document as a tree of `DisclosureGroup`s. Headings collapse their content, nested lists and quotes collapse their children, and definitions/footnotes/table cells collapse their body. Each section manages its own expand/collapse state and starts expanded. ```swift NorgView(document: NorgParser.parse(source), collapsible: true) ``` ### Tasks You can render tasks independently (eg. if you got them via `TaskScanner`) with `NorgTaskView`. ```swift NorgTaskView( status: task.status, text: AttributedString(task.text), onToggle: { setStatus(task, .done) }, onSetStatus: { setStatus(task, $0) } ) ``` ## Theming Fonts and colors can be changed by creating a `NorgTheme` and using the `.norgTheme` modifier. ```swift var theme = NorgTheme.default theme.linkColor = .purple theme.heading = { level in level == 1 ? .largeTitle.bold() : .title2 } theme.statusSymbol = { $0 == .done ? "checkmark.square.fill" : "square" } NorgView(document: document) .norgTheme(theme) ``` Note: `AttributedString(norg:)` can't read the environment, so you need to pass the theme explicitly if you build text outside the views. ```swift let styled = AttributedString(norg: spans, theme: theme) ``` ### NorgTheme API | Property | Type | Default | Styles | | --- | --- | --- | --- | | `heading` | `(Int) -> Font` | `.title.bold()` (lvl 1) down to `.subheadline` | Heading text, per level | | `body` | `Font` | `.body` | Paragraphs, list items, quotes | | `termFont` | `Font` | `.body.bold()` | Definition / footnote / table-cell titles | | `verbatimColor` | `Color` | `.pink` | `verbatim` inline spans | | `spoilerForeground` | `Color` | `.secondary` | `spoiler` inline text | | `spoilerBackground` | `Color` | `.secondary.opacity(0.25)` | `spoiler` inline background | | `linkColor` | `Color` | `.accentColor` | Links and anchors | | `superscriptOffset` | `CGFloat` | `5` | `superscript` baseline offset | | `subscriptOffset` | `CGFloat` | `-3` | `subscript` baseline offset | | `quoteColor` | `Color` | `.secondary` | Quote text | | `quoteBarColor` | `Color` | `.secondary` | Quote's vertical bar | | `codeBackground` | `AnyShapeStyle` | `.quaternary` | Code blocks and ranged tags | | `codeFont` | `Font` | `.system(.callout, design: .monospaced)` | Code blocks and ranged tags | | `indentWidth` | `CGFloat` | `18` | Indentation per nesting level | | `statusTint` | `(TaskStatus) -> Color` | `.green` / `.red` / `.blue` / `.secondary` | Task status symbol colour | | `statusSymbol` | `(TaskStatus) -> String` | SF Symbols (`circle`, `checkmark.circle.fill`, …) | Task status symbol | | `statusLabel` | `(TaskStatus) -> LocalizedStringResource` | `TaskStatus.displayName` | Task status menu label & accessibility | ## Localization NorgUI provides labels for the long-press menu in tasks. These can be localized either through the package's own string catalog, but can be overridden in the theme. ```swift var theme = NorgTheme.default theme.statusLabel = { status in LocalizedStringResource(String.LocalizationValue(status.rawValue), table: "Tasks") } ``` `statusLabel` defaults to `TaskStatus.displayName`. ## Requirements - Swift 6 tools - iOS 18 / macOS 15 / visionOS 2