aboutsummaryrefslogtreecommitdiff
path: root/Sources/NorgUI/Theme/NorgTheme.swift
blob: 47dd1610f1b1476a108affb37f78f8c496085443 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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"
      }
    }
  )
}