11b6a4b5b2
Default theme now uses colorScheme: .light with the canonical leuven palette (fniessen/emacs-leuven-theme) so it never changes when switching away from dark themes: background #FFFFFF, secondaryBackground #F0F0F0 (ol1 heading), accent #006DAF (link color), codeBackground #FFFFE0 (classic Org src block yellow), codeBorder #A7A6AA, primaryText #333333, secondaryText #9A9FA4.
157 lines
5.5 KiB
Swift
157 lines
5.5 KiB
Swift
import SwiftUI
|
||
|
||
struct AppTheme: Identifiable, Equatable {
|
||
let id: String
|
||
let name: String
|
||
let colorScheme: ColorScheme?
|
||
let accent: Color
|
||
let background: Color
|
||
let secondaryBackground: Color
|
||
let primaryText: Color
|
||
let secondaryText: Color
|
||
let codeBackground: Color
|
||
let codeBorder: Color
|
||
/// highlight.js theme name used for syntax highlighting
|
||
let highlightTheme: String
|
||
|
||
static func == (lhs: AppTheme, rhs: AppTheme) -> Bool { lhs.id == rhs.id }
|
||
}
|
||
|
||
// MARK: - Built-in themes
|
||
|
||
extension AppTheme {
|
||
static let all: [AppTheme] = [.default, .emacs, .dracula, .oneDark, .monokai, .material, .thankfulEyes]
|
||
|
||
// Default – Org mode / leuven palette (fniessen/emacs-leuven-theme)
|
||
static let `default` = AppTheme(
|
||
id: "default",
|
||
name: "Default",
|
||
colorScheme: .light,
|
||
accent: Color(hex: "#006DAF"), // leuven link color
|
||
background: Color(hex: "#FFFFFF"),
|
||
secondaryBackground: Color(hex: "#F0F0F0"), // ol1 heading background
|
||
primaryText: Color(hex: "#333333"),
|
||
secondaryText: Color(hex: "#9A9FA4"), // org-tag foreground
|
||
codeBackground: Color(hex: "#FFFFE0"), // leuven code-block background
|
||
codeBorder: Color(hex: "#A7A6AA"), // org-block-begin-line underline
|
||
highlightTheme: "xcode"
|
||
)
|
||
|
||
// Emacs – inspired by modus-vivendi (dark accessibility theme)
|
||
static let emacs = AppTheme(
|
||
id: "emacs",
|
||
name: "Emacs",
|
||
colorScheme: .dark,
|
||
accent: Color(hex: "#4ac1f7"),
|
||
background: Color(hex: "#1c1e1f"),
|
||
secondaryBackground: Color(hex: "#252729"),
|
||
primaryText: Color(hex: "#dcdccc"),
|
||
secondaryText: Color(hex: "#7f9f7f"),
|
||
codeBackground: Color(hex: "#0d0e0f"),
|
||
codeBorder: Color(hex: "#3a3c3e"),
|
||
highlightTheme: "emacs"
|
||
)
|
||
|
||
// Dracula – official palette (draculatheme.com)
|
||
static let dracula = AppTheme(
|
||
id: "dracula",
|
||
name: "Dracula",
|
||
colorScheme: .dark,
|
||
accent: Color(hex: "#bd93f9"),
|
||
background: Color(hex: "#282a36"),
|
||
secondaryBackground: Color(hex: "#44475a"), // official Selection/Current Line
|
||
primaryText: Color(hex: "#f8f8f2"),
|
||
secondaryText: Color(hex: "#6272a4"),
|
||
codeBackground: Color(hex: "#21222c"),
|
||
codeBorder: Color(hex: "#44475a"),
|
||
highlightTheme: "dracula"
|
||
)
|
||
|
||
// One Dark – Atom / VS Code One Dark Pro palette
|
||
static let oneDark = AppTheme(
|
||
id: "one_dark",
|
||
name: "One Dark",
|
||
colorScheme: .dark,
|
||
accent: Color(hex: "#61afef"),
|
||
background: Color(hex: "#282c34"),
|
||
secondaryBackground: Color(hex: "#21252b"), // official sideBar.background
|
||
primaryText: Color(hex: "#abb2bf"),
|
||
secondaryText: Color(hex: "#5c6370"),
|
||
codeBackground: Color(hex: "#21252b"),
|
||
codeBorder: Color(hex: "#3e4451"),
|
||
highlightTheme: "atom-one-dark"
|
||
)
|
||
|
||
// Monokai – TextMate classic (textmate/monokai.tmbundle)
|
||
static let monokai = AppTheme(
|
||
id: "monokai",
|
||
name: "Monokai",
|
||
colorScheme: .dark,
|
||
accent: Color(hex: "#a6e22e"),
|
||
background: Color(hex: "#272822"),
|
||
secondaryBackground: Color(hex: "#49483e"), // official line highlight color
|
||
primaryText: Color(hex: "#f8f8f2"),
|
||
secondaryText: Color(hex: "#75715e"),
|
||
codeBackground: Color(hex: "#1e1f1a"),
|
||
codeBorder: Color(hex: "#75715e"),
|
||
highlightTheme: "monokai"
|
||
)
|
||
|
||
// Material – Material Design Dark (Oceanic variant)
|
||
static let material = AppTheme(
|
||
id: "material",
|
||
name: "Material",
|
||
colorScheme: .dark,
|
||
accent: Color(hex: "#80cbc4"),
|
||
background: Color(hex: "#263238"),
|
||
secondaryBackground: Color(hex: "#2e3c43"),
|
||
primaryText: Color(hex: "#eeffff"),
|
||
secondaryText: Color(hex: "#546e7a"),
|
||
codeBackground: Color(hex: "#1b2226"),
|
||
codeBorder: Color(hex: "#37474f"),
|
||
highlightTheme: "atom-one-dark"
|
||
)
|
||
|
||
// Thankful Eyes – high-contrast dark teal theme for accessibility (tanrax/thankful-eyes-theme.el)
|
||
static let thankfulEyes = AppTheme(
|
||
id: "thankful_eyes",
|
||
name: "Thankful Eyes",
|
||
colorScheme: .dark,
|
||
accent: Color(hex: "#a8e1fe"), // font-lock-function-name-face
|
||
background: Color(hex: "#122b3b"), // default background
|
||
secondaryBackground: Color(hex: "#1c2f3b"), // hl-line / mode-line background
|
||
primaryText: Color(hex: "#faf6e4"), // default foreground
|
||
secondaryText: Color(hex: "#6c8b9f"), // font-lock-comment-face
|
||
codeBackground: Color(hex: "#0d1f2b"), // darker than bg for code blocks
|
||
codeBorder: Color(hex: "#4e5d62"), // region/selection color
|
||
highlightTheme: "atom-one-dark"
|
||
)
|
||
}
|
||
|
||
// MARK: - Hex convenience init
|
||
|
||
extension Color {
|
||
init(hex: String) {
|
||
let hex = hex.trimmingCharacters(in: .alphanumerics.inverted)
|
||
var value: UInt64 = 0
|
||
Scanner(string: hex).scanHexInt64(&value)
|
||
let r = Double((value >> 16) & 0xff) / 255
|
||
let g = Double((value >> 8) & 0xff) / 255
|
||
let b = Double(value & 0xff) / 255
|
||
self.init(red: r, green: g, blue: b)
|
||
}
|
||
}
|
||
|
||
// MARK: - Environment key
|
||
|
||
private struct AppThemeKey: EnvironmentKey {
|
||
static let defaultValue: AppTheme = .default
|
||
}
|
||
|
||
extension EnvironmentValues {
|
||
var appTheme: AppTheme {
|
||
get { self[AppThemeKey.self] }
|
||
set { self[AppThemeKey.self] = newValue }
|
||
}
|
||
}
|