aboutsummaryrefslogtreecommitdiff
path: root/Sources/NorgUI/Theme/NorgTheme.swift
diff options
context:
space:
mode:
Diffstat (limited to 'Sources/NorgUI/Theme/NorgTheme.swift')
-rw-r--r--Sources/NorgUI/Theme/NorgTheme.swift128
1 files changed, 128 insertions, 0 deletions
diff --git a/Sources/NorgUI/Theme/NorgTheme.swift b/Sources/NorgUI/Theme/NorgTheme.swift
new file mode 100644
index 0000000..47dd161
--- /dev/null
+++ b/Sources/NorgUI/Theme/NorgTheme.swift
@@ -0,0 +1,128 @@
+import NorgKit
+import SwiftUI
+
+/// Typography and colours used when rendering a Norg document.
+public struct NorgTheme: Sendable {
+
+ /// Font for a heading at the given (1-based) level.
+ public var heading: @Sendable (Int) -> Font
+ /// Base font for body text, list items and paragraphs.
+ public var body: Font
+ /// Font for the title of a definition, footnote or table cell.
+ public var termFont: Font
+ /// Foreground colour for `verbatim` inline spans.
+ public var verbatimColor: Color
+ /// Foreground colour for `spoiler` inline spans.
+ public var spoilerForeground: Color
+ /// Background colour for `spoiler` inline spans.
+ public var spoilerBackground: Color
+ /// Foreground colour applied to link spans.
+ public var linkColor: Color
+ /// Baseline offset applied to `superscript` spans.
+ public var superscriptOffset: CGFloat
+ /// Baseline offset applied to `subscript` spans.
+ public var subscriptOffset: CGFloat
+ /// Foreground colour for block quotes.
+ public var quoteColor: Color
+ /// Fill colour for the vertical bar drawn alongside a quote.
+ public var quoteBarColor: Color
+ /// Background fill for fenced code blocks and ranged tags.
+ public var codeBackground: AnyShapeStyle
+ /// Font for code block and ranged-tag contents.
+ public var codeFont: Font
+ /// Horizontal indentation applied per nesting level.
+ public var indentWidth: CGFloat
+ /// Tint for a task's status symbol.
+ public var statusTint: @Sendable (TaskStatus) -> Color
+ /// SF Symbol name for a task's status symbol.
+ public var statusSymbol: @Sendable (TaskStatus) -> String
+ /// Localized label for a task status, shown in the status menu and used
+ /// for accessibility. Defaults to ``TaskStatus/displayName``; override to
+ /// supply wording from your own string catalog.
+ public var statusLabel: @Sendable (TaskStatus) -> LocalizedStringResource
+
+ public init(
+ heading: @escaping @Sendable (Int) -> Font,
+ body: Font,
+ termFont: Font,
+ verbatimColor: Color,
+ spoilerForeground: Color,
+ spoilerBackground: Color,
+ linkColor: Color,
+ superscriptOffset: CGFloat,
+ subscriptOffset: CGFloat,
+ quoteColor: Color,
+ quoteBarColor: Color,
+ codeBackground: AnyShapeStyle,
+ codeFont: Font,
+ indentWidth: CGFloat,
+ statusTint: @escaping @Sendable (TaskStatus) -> Color,
+ statusSymbol: @escaping @Sendable (TaskStatus) -> String,
+ statusLabel: @escaping @Sendable (TaskStatus) -> LocalizedStringResource = { $0.displayName }
+ ) {
+ self.heading = heading
+ self.body = body
+ self.termFont = termFont
+ self.verbatimColor = verbatimColor
+ self.spoilerForeground = spoilerForeground
+ self.spoilerBackground = spoilerBackground
+ self.linkColor = linkColor
+ self.superscriptOffset = superscriptOffset
+ self.subscriptOffset = subscriptOffset
+ self.quoteColor = quoteColor
+ self.quoteBarColor = quoteBarColor
+ self.codeBackground = codeBackground
+ self.codeFont = codeFont
+ self.indentWidth = indentWidth
+ self.statusTint = statusTint
+ self.statusSymbol = statusSymbol
+ self.statusLabel = statusLabel
+ }
+
+ /// The default theme.
+ public static let `default` = NorgTheme(
+ heading: { level in
+ switch level {
+ case 1: return .title.bold()
+ case 2: return .title2.bold()
+ case 3: return .title3.bold()
+ case 4: return .headline
+ case 5: return .subheadline.bold()
+ default: return .subheadline
+ }
+ },
+ body: .body,
+ termFont: .body.bold(),
+ verbatimColor: .pink,
+ spoilerForeground: .secondary,
+ spoilerBackground: .secondary.opacity(0.25),
+ linkColor: .accentColor,
+ superscriptOffset: 5,
+ subscriptOffset: -3,
+ quoteColor: .secondary,
+ quoteBarColor: .secondary,
+ codeBackground: AnyShapeStyle(.quaternary),
+ codeFont: .system(.callout, design: .monospaced),
+ indentWidth: 18,
+ statusTint: { status in
+ switch status {
+ case .done: return .green
+ case .urgent: return .red
+ case .pending: return .blue
+ default: return .secondary
+ }
+ },
+ statusSymbol: { status in
+ switch status {
+ case .undone: return "circle"
+ case .done: return "checkmark.circle.fill"
+ case .needsInput: return "questionmark.circle"
+ case .urgent: return "exclamationmark.circle"
+ case .recurring: return "repeat.circle"
+ case .pending: return "ellipsis.circle"
+ case .onHold: return "pause.circle"
+ case .cancelled: return "xmark.circle"
+ }
+ }
+ )
+}