aboutsummaryrefslogtreecommitdiff

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 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) }
)

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