krz/org-swift

A dependency-free Swift library that renders org-mode to sanitized HTML. html library org-mode swift

Commit ca2a0de207

ca2a0de2077b96dc2405d59def877f53e1933063

parent: d1d98c0638

Unsigned

cmc <hello@cleberg.net> · 2026-08-31 18:13 UTC

Rebuild the layout when the color scheme changes

A code styler bakes its theme's colors into the AttributedString it
returns, so a layout built in light mode kept light-theme code after a
switch to dark — dark grey on a dark background. The task now keys on the
color scheme as well as the source.

Found switching the app to dark mode on krz/gitbay's README.

Layout: unified · split

Sources/OrgSwiftUI/OrgView.swift +13 −4
@@ -9,14 +9,16 @@ import SwiftUI
9/// ``` 9/// ```
10/// 10///
11/// The parse, footnote numbering, URL resolution and syntax highlighting run once off 11/// The parse, footnote numbering, URL resolution and syntax highlighting run once off
12/// `body`, in a task keyed to the source, so scrolling and Dynamic Type changes do not 12/// `body`, in a task keyed to the source *and the color scheme*, so scrolling and Dynamic
13/// re-derive them. Text is selectable, links open through the environment's `openURL`, and 13/// Type changes do not re-derive them — but switching to dark mode does, because a
14/// everything scales with Dynamic Type. 14/// ``OrgCodeStyler`` bakes its theme's colors into the text it returns. Text is selectable,
15/// links open through the environment's `openURL`, and everything scales with Dynamic Type.
15public struct OrgView: View { 16public struct OrgView: View {
16 private let source: String 17 private let source: String
17 private let options: OrgRenderOptions 18 private let options: OrgRenderOptions
18 private let styler: OrgCodeStyler 19 private let styler: OrgCodeStyler
19 20
21 @Environment(\.colorScheme) private var colorScheme
20 @State private var layout = OrgLayout() 22 @State private var layout = OrgLayout()
21 23
22 public init( 24 public init(
@@ -38,13 +40,20 @@ public struct OrgView: View {
38 } 40 }
39 .frame(maxWidth: .infinity, alignment: .leading) 41 .frame(maxWidth: .infinity, alignment: .leading)
40 .textSelection(.enabled) 42 .textSelection(.enabled)
41 .task(id: source) { 43 .task(id: LayoutKey(source: source, colorScheme: colorScheme)) {
42 layout = OrgLayoutBuilder(options: options, styler: styler) 44 layout = OrgLayoutBuilder(options: options, styler: styler)
43 .layout(OrgParser.parse(source)) 45 .layout(OrgParser.parse(source))
44 } 46 }
45 } 47 }
46} 48}
47 49
50/// What the prepared layout depends on. Highlighted code carries baked-in colors, so the
51/// scheme belongs in the key alongside the source.
52private struct LayoutKey: Equatable {
53 var source: String
54 var colorScheme: ColorScheme
55}
56
48/// The notes section org's exporter puts at the end of a document. 57/// The notes section org's exporter puts at the end of a document.
49struct OrgFootnotesView: View { 58struct OrgFootnotesView: View {
50 let footnotes: [OrgFootnote] 59 let footnotes: [OrgFootnote]