Files
focus-fixer/FocusFixer/Preferences.swift
T
sttlab-tech 561a547b70 Initial FocusFixer implementation
Menu-bar utility that fixes cross-display keyboard focus loss: a
listen-only CGEvent tap locates the window under the cursor and
reasserts app activation + AX focus on it, with debounce, tap
auto-recovery, and an Accessibility permission gate.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 12:02:29 +02:00

55 lines
1.6 KiB
Swift

import Foundation
/// UserDefaults-backed settings for FocusFixer.
final class Preferences {
static let shared = Preferences()
private enum Key {
static let enabled = "enabled"
static let excludedBundleIDs = "excludedBundleIDs"
static let debounceIntervalMS = "debounceIntervalMS"
}
private static let defaultDebounceIntervalMS = 150
private let defaults: UserDefaults
init(defaults: UserDefaults = .standard) {
self.defaults = defaults
defaults.register(defaults: [
Key.enabled: true,
Key.excludedBundleIDs: [String](),
Key.debounceIntervalMS: Self.defaultDebounceIntervalMS
])
}
var isEnabled: Bool {
get { defaults.bool(forKey: Key.enabled) }
set { defaults.set(newValue, forKey: Key.enabled) }
}
var excludedBundleIDs: Set<String> {
get { Set(defaults.stringArray(forKey: Key.excludedBundleIDs) ?? []) }
set { defaults.set(Array(newValue), forKey: Key.excludedBundleIDs) }
}
var debounceInterval: TimeInterval {
get { TimeInterval(defaults.integer(forKey: Key.debounceIntervalMS)) / 1000.0 }
set { defaults.set(Int(newValue * 1000), forKey: Key.debounceIntervalMS) }
}
func isExcluded(bundleID: String) -> Bool {
excludedBundleIDs.contains(bundleID)
}
func setExcluded(_ excluded: Bool, bundleID: String) {
var ids = excludedBundleIDs
if excluded {
ids.insert(bundleID)
} else {
ids.remove(bundleID)
}
excludedBundleIDs = ids
}
}