diff options
| author | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-06-15 14:35:36 +0200 |
|---|---|---|
| committer | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-06-16 14:47:30 +0200 |
| commit | 657aa83a5ca24dc35572c040df64a0abb85e8612 (patch) | |
| tree | fe0b6fd425d8cad9dc6a28ada4ad8600e42b367e /Sources/NorgUI/Theme | |
Initial extraction from Norganize
Diffstat (limited to 'Sources/NorgUI/Theme')
| -rw-r--r-- | Sources/NorgUI/Theme/EnvironmentValues+NorgTheme.swift | 19 | ||||
| -rw-r--r-- | Sources/NorgUI/Theme/NorgTheme.swift | 128 |
2 files changed, 147 insertions, 0 deletions
diff --git a/Sources/NorgUI/Theme/EnvironmentValues+NorgTheme.swift b/Sources/NorgUI/Theme/EnvironmentValues+NorgTheme.swift new file mode 100644 index 0000000..58c2c00 --- /dev/null +++ b/Sources/NorgUI/Theme/EnvironmentValues+NorgTheme.swift @@ -0,0 +1,19 @@ +import SwiftUI + +private struct NorgThemeKey: EnvironmentKey { + static let defaultValue = NorgTheme.default +} + +/// Makes NorgTheme values accessible via the environment. +extension EnvironmentValues { + public var norgTheme: NorgTheme { + get { self[NorgThemeKey.self] } + set { self[NorgThemeKey.self] = newValue } + } +} + +extension View { + public func norgTheme(_ theme: NorgTheme) -> some View { + environment(\.norgTheme, theme) + } +} 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" + } + } + ) +} |