import AppKit import Combine /// Application delegate that bootstraps the notch overlay system. @MainActor class AppDelegate: NSObject, NSApplicationDelegate { private enum UITestLaunchArgument { static let regularActivation = "--uitest-regular-activation" static let showSettings = "--uitest-show-settings" static let openNotch = "--uitest-open-notch" } private var cancellables = Set() private let settingsController = AppSettingsController.shared func applicationDidFinishLaunching(_ notification: Notification) { NotchSettings.registerDefaults() if isRunningUITests { NSApp.setActivationPolicy(.regular) } else { NSApp.setActivationPolicy(.accessory) } // Sync the launch-at-login toggle with the actual system state // in case the user toggled it from System Settings. settingsController.update { $0.display.launchAtLogin = LaunchAtLoginHelper.isEnabled } ScreenManager.shared.start() observeDisplayPreference() observeSizePreferences() observeFontSizeChanges() observeTerminalThemeChanges() applyUITestLaunchBehaviorIfNeeded() } func applicationWillTerminate(_ notification: Notification) { ScreenManager.shared.stop() } // MARK: - Preference observers /// Only rebuild windows when the display-count preference changes. private func observeDisplayPreference() { settingsController.$settings .map(\.display.showOnAllDisplays) .removeDuplicates() .dropFirst() .sink { _ in ScreenManager.shared.rebuildWindows() } .store(in: &cancellables) } /// Reposition (not rebuild) when any sizing preference changes. private func observeSizePreferences() { settingsController.$settings .map(\.display.layoutSignature) .removeDuplicates() .dropFirst() .debounce(for: .milliseconds(300), scheduler: RunLoop.main) .sink { _ in ScreenManager.shared.repositionWindows() } .store(in: &cancellables) } /// Live-update terminal font size across all sessions. private func observeFontSizeChanges() { settingsController.$settings .map(\.terminal.fontSize) .removeDuplicates() .sink { newSize in guard newSize > 0 else { return } WorkspaceRegistry.shared.updateAllWorkspacesFontSizes(CGFloat(newSize)) } .store(in: &cancellables) } /// Live-update terminal colors across all sessions. private func observeTerminalThemeChanges() { settingsController.$settings .map(\.terminal.themeRawValue) .removeDuplicates() .sink { newTheme in WorkspaceRegistry.shared.updateAllWorkspacesThemes(TerminalTheme.resolve(newTheme)) } .store(in: &cancellables) } private var launchArguments: [String] { ProcessInfo.processInfo.arguments } private var isRunningUITests: Bool { launchArguments.contains(UITestLaunchArgument.regularActivation) || launchArguments.contains(UITestLaunchArgument.showSettings) || launchArguments.contains(UITestLaunchArgument.openNotch) } private func applyUITestLaunchBehaviorIfNeeded() { guard isRunningUITests else { return } DispatchQueue.main.async { @MainActor in if self.launchArguments.contains(UITestLaunchArgument.showSettings) { SettingsWindowController.shared.showSettings() } if self.launchArguments.contains(UITestLaunchArgument.openNotch), let screenID = ScreenRegistry.shared.activeScreenID() { ScreenManager.shared.openNotch(screenID: screenID) } } } }