macOS menu bar app that switches typed/selected text between English and Russian keyboard layouts. Press the action key (Option by default, configurable) to swap the last typed word to the other layout.
- SwiftUI App lifecycle (
BabblerApp.swift) with@NSApplicationDelegateAdaptorfor AppKit hooks - Menu bar UI via
MenuBarExtrawith.windowstyle (SwiftUI view, notNSMenu) - Global keyboard event listener via
NSEvent.addGlobalMonitorForEvents - Carbon TIS APIs for input source management — must be called on the main thread
- Preferences stored in
UserDefaultsviaPreferenceStore(globalpreferenceStoreinstance) and@AppStoragebindings in views InputSourceUtils.inputSourcesstarts asnil; must callInputSourceUtils.initInputSources()before use
AppDelegate.swift— app lifecycle, event handling loop (handleGlobalSystemEvent), keystroke recording (wordRecord,lineRecord,pendingRecord)BabblerApp.swift— SwiftUI entry point,MenuBarExtrasetup, clipboard history start/stop observerViews/MenuView.swift— menu bar dropdown: text-replace toggle, input source switcher, clipboard history,MenuItemButtonStyleViews/SettingsView.swift— settings form: action key, input indicator, clipboard history toggle, per-app input source mappingKeyboardUtils.swift— CGEvent keyboard simulation, text replacement, action key detection (word vs line action)InputSourceUtils.swift— TIS input source management:initInputSources(),swapLang(),switchLang(),getCurrentInputSource(), input source change callbackTISInputSourceExtension.swift— Swift extensions onTISInputSource(id,name,iconImage, etc.)PreferenceStore.swift—UserDefaultswrapper; keys defined as top-level constants (isTextReplaceEnabledKey,clipboardHistoryEnabledKey, etc.)ClipboardHistory.swift—ObservableObjectpollingNSPasteboardevery 0.5 s; deduplicating, capped history (default 20 items)KeyDictionary.swift— EN↔RU character mapping dictionariesImageUtils.swift— menu bar icon generation; emoji map for common layouts, custom-drawn fallback; grayscale for secure-input stateSecurityInputUtils.swift— polls IORegistry every 2 s to detect secure input mode; returns the app that enabled itWorkspaceUtils.swift— active app change callbacks; app enumeration for the settings pickerCrashLogger.swift— installs global exception/signal handlers; writes logs to~/Library/Application Support/Babbler/
Carbon TIS APIs (TISCopyCurrentKeyboardInputSource, TISSelectInputSource, String(describing: TISInputSource)) are not thread-safe. Any code path that touches them must run on the main thread. Use MainActor.run when dispatching from a Task.
InputSourceUtils.inputSourcesisniluntilinitInputSources()runs infinishApplicationSetup(). Code that validates saved preferences against available sources must guard against anilvalue — otherwise it will wipe all saved data on first load.ClipboardHistory.start()is called conditionally based onclipboardHistoryEnabledKey; toggling the setting live goes throughBabblerApp.onChange.
Xcode project, no SPM dependencies. Requires Accessibility permission at runtime.
- Indent with 2 spaces, not tabs
- Follow the existing style of each file when editing