Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion BifcodePackage/Sources/BifcodeFeature/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ public struct ContentView: View {
ThemeMode(rawValue: themeModeRaw) ?? .dark
}

/// The effective theme mode, resolving system mode to dark or light.
private var effectiveThemeMode: ThemeMode {
themeMode.effectiveMode
}

public init() {}

/// Check if both code panels are empty (export should be disabled)
Expand Down Expand Up @@ -222,7 +227,7 @@ public struct ContentView: View {
showTitle: showTitle,
fontSize: fontSize,
theme: selectedTheme.editorTheme,
themeMode: themeMode,
themeMode: effectiveThemeMode,
panelWidth: panelWidth,
doPanelHeight: doPanelHeight,
dontPanelHeight: dontPanelHeight
Expand Down
23 changes: 23 additions & 0 deletions BifcodePackage/Sources/BifcodeFeature/Models/SettingsTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,16 @@ public enum WindowLayout: String, CaseIterable, Sendable {
///
/// ### Modes
///
/// - ``system``
/// - ``dark``
/// - ``light``
public enum ThemeMode: String, CaseIterable, Sendable {
/// Follows the macOS system appearance.
///
/// Automatically switches between dark and light themes based on
/// the user's macOS appearance setting.
case system

/// Dark mode themes with dark backgrounds.
///
/// Ideal for low-light environments and reducing eye strain.
Expand All @@ -280,10 +287,26 @@ public enum ThemeMode: String, CaseIterable, Sendable {
/// A human-readable label for display in pickers.
public var label: String {
switch self {
case .system: "System"
case .dark: "Dark"
case .light: "Light"
}
}

/// Returns the effective mode based on the current system appearance.
///
/// For `.system` mode, this resolves to either `.dark` or `.light`
/// based on the current macOS appearance setting.
///
/// - Returns: The effective theme mode (always `.dark` or `.light`).
public var effectiveMode: ThemeMode {
switch self {
case .system:
NSApp.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua ? .dark : .light
case .dark, .light:
self
}
}
}

// MARK: - Editor Themes
Expand Down
13 changes: 9 additions & 4 deletions BifcodePackage/Sources/BifcodeFeature/Views/CodeEditorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ public struct CodeEditorView: View {
ThemeMode(rawValue: themeModeRaw) ?? .dark
}

/// The effective theme mode, resolving system mode to dark or light.
private var effectiveThemeMode: ThemeMode {
themeMode.effectiveMode
}

/// Creates a new code editor view for the specified panel.
///
/// - Parameters:
Expand Down Expand Up @@ -129,7 +134,7 @@ public struct CodeEditorView: View {
.clipShape(RoundedRectangle(cornerRadius: 12))
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(Color.windowBorder(for: themeMode), lineWidth: 1)
.stroke(Color.windowBorder(for: effectiveThemeMode), lineWidth: 1)
)
}

Expand All @@ -155,13 +160,13 @@ public struct CodeEditorView: View {
HStack(spacing: 14) {
// Traffic light placeholder
Circle()
.fill(Color.editorCloseButton(for: themeMode))
.fill(Color.editorCloseButton(for: effectiveThemeMode))
.frame(width: 14, height: 14)

// Title - single line with truncation
Label(panel.title, systemImage: "text.document.fill")
.font(.system(size: 13, weight: .medium))
.foregroundStyle(Color.titleText(for: themeMode))
.foregroundStyle(Color.titleText(for: effectiveThemeMode))
.lineLimit(1)
.truncationMode(.tail)

Expand All @@ -170,7 +175,7 @@ public struct CodeEditorView: View {
.padding(.horizontal, 12)
.padding(.vertical, 10)
.frame(maxWidth: .infinity)
.background(Color.titleBarBackground(for: themeMode))
.background(Color.titleBarBackground(for: effectiveThemeMode))
.clipShape(UnevenRoundedRectangle(topLeadingRadius: 12, topTrailingRadius: 12))
.accessibilityElement(children: .combine)
.accessibilityLabel("\(panel.title) panel header")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ struct ExportPanelView: View {
/// The current theme mode for window styling.
let themeMode: ThemeMode

/// The effective theme mode, resolving system mode to dark or light.
private var effectiveThemeMode: ThemeMode {
themeMode.effectiveMode
}

/// Local editor state (not shared with interactive editor).
@State private var editorState = SourceEditorState()

Expand All @@ -83,7 +88,7 @@ struct ExportPanelView: View {
.clipShape(RoundedRectangle(cornerRadius: 12))
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(Color.windowBorder(for: themeMode), lineWidth: 1)
.stroke(Color.windowBorder(for: effectiveThemeMode), lineWidth: 1)
)
}

Expand All @@ -93,13 +98,13 @@ struct ExportPanelView: View {
HStack(spacing: 14) {
// Traffic light placeholder
Circle()
.fill(Color.editorCloseButton(for: themeMode))
.fill(Color.editorCloseButton(for: effectiveThemeMode))
.frame(width: 14, height: 14)

// Title - single line with truncation
Label(panel.title, systemImage: "text.document.fill")
.font(.system(size: 13, weight: .medium))
.foregroundStyle(Color.titleText(for: themeMode))
.foregroundStyle(Color.titleText(for: effectiveThemeMode))
.lineLimit(1)
.truncationMode(.tail)

Expand All @@ -108,7 +113,7 @@ struct ExportPanelView: View {
.padding(.horizontal, 12)
.padding(.vertical, 10)
.frame(maxWidth: .infinity)
.background(Color.titleBarBackground(for: themeMode))
.background(Color.titleBarBackground(for: effectiveThemeMode))
.clipShape(UnevenRoundedRectangle(topLeadingRadius: 12, topTrailingRadius: 12))
.accessibilityElement(children: .combine)
.accessibilityLabel("\(panel.title) panel header")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,13 +309,14 @@ public struct ToolBarView: View {

private var themeModeToggle: some View {
Picker("", selection: $themeModeRaw) {
Image(systemName: "circle.lefthalf.filled").tag(ThemeMode.system.rawValue)
Image(systemName: "moon.fill").tag(ThemeMode.dark.rawValue)
Image(systemName: "sun.max.fill").tag(ThemeMode.light.rawValue)
}
.pickerStyle(.segmented)
.fixedSize()
.accessibilityLabel("Theme Mode")
.accessibilityHint("Toggle dark or light mode")
.accessibilityHint("System follows macOS appearance, or choose dark or light mode")
}

// MARK: - Theme Picker
Expand Down