NorgUI
NorgUI provides themable SwiftUI views to render .norg / Neorg documents
parsed with 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
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.
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 DisclosureGroups.
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.
NorgView(document: NorgParser.parse(source), collapsible: true)
Tasks
You can render tasks independently (eg. if you got them via TaskScanner) with
NorgTaskView.
NorgTaskView(
status: task.status,
text: AttributedString(task.text),
onToggle: { setStatus(task, .done) },
onSetStatus: { setStatus(task, $0) }
)
Rendering Math
NorgUI will not render math by default, but provides a trait SwiftMathSupport
that adds this capability and includes SwiftMath as a dependency.
You can also roll your own math renderer by implementing the NorgMathRenderer
protocol and passing it to the NorgTheme's mathRenderer property.
Theming
Fonts and colors can be changed by creating a NorgTheme and using the
.norgTheme modifier.
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.
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.
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