Skip to content
Draft
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
144 changes: 144 additions & 0 deletions Cotabby.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion Cotabby/App/Coordinators/SettingsCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ final class SettingsCoordinator: NSObject, NSWindowDelegate {
private let systemMetricsStore: SystemMetricsStore
private let onShowWelcome: () -> Void
private let clearEmojiHistory: () -> Void
private let translationPreferences: TranslationPreferencesStore
private let translationService: TranslationService

private var settingsWindowController: NSWindowController?

Expand All @@ -49,7 +51,9 @@ final class SettingsCoordinator: NSObject, NSWindowDelegate {
qualityMetricsStore: SuggestionQualityMetricsStore,
systemMetricsStore: SystemMetricsStore,
onShowWelcome: @escaping () -> Void,
clearEmojiHistory: @escaping () -> Void
clearEmojiHistory: @escaping () -> Void,
translationPreferences: TranslationPreferencesStore,
translationService: TranslationService
) {
self.appUpdateManager = appUpdateManager
self.permissionManager = permissionManager
Expand All @@ -65,6 +69,8 @@ final class SettingsCoordinator: NSObject, NSWindowDelegate {
self.systemMetricsStore = systemMetricsStore
self.onShowWelcome = onShowWelcome
self.clearEmojiHistory = clearEmojiHistory
self.translationPreferences = translationPreferences
self.translationService = translationService
}

/// Shows the settings window, reusing the existing instance if it is already open.
Expand Down Expand Up @@ -94,6 +100,8 @@ final class SettingsCoordinator: NSObject, NSWindowDelegate {
systemMetricsStore: systemMetricsStore,
onShowWelcome: onShowWelcome,
clearEmojiHistory: clearEmojiHistory,
translationPreferences: translationPreferences,
translationService: translationService,
onQuit: { NSApplication.shared.terminate(nil) }
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ extension SuggestionCoordinator {
temporarilyPaused: settingsSnapshot.isTemporarilyPaused,
isLowPowerModeActive: lowPowerModeProvider.isLowPowerModeEnabled,
isLowPowerModeAutoDisableEnabled: settingsSnapshot.isLowPowerModeAutoDisableEnabled,
disabledAppBundleIdentifiers: settingsSnapshot.disabledAppBundleIdentifiers,
disabledAppBundleIdentifiers: disabledApps(for: focusModel.snapshot),
disabledDomains: PerDomainDisableSettings.disabledDomains(),
suggestInIntegratedTerminals: settingsSnapshot.suggestInIntegratedTerminals,
inputMonitoringGranted: permissionManager.inputMonitoringGranted,
Expand All @@ -45,7 +45,7 @@ extension SuggestionCoordinator {
temporarilyPaused: settingsSnapshot.isTemporarilyPaused,
isLowPowerModeActive: lowPowerModeProvider.isLowPowerModeEnabled,
isLowPowerModeAutoDisableEnabled: settingsSnapshot.isLowPowerModeAutoDisableEnabled,
disabledAppBundleIdentifiers: settingsSnapshot.disabledAppBundleIdentifiers,
disabledAppBundleIdentifiers: disabledApps(for: focusModel.snapshot),
disabledDomains: PerDomainDisableSettings.disabledDomains(),
suggestInIntegratedTerminals: settingsSnapshot.suggestInIntegratedTerminals,
inputMonitoringGranted: permissionManager.inputMonitoringGranted,
Expand Down Expand Up @@ -84,7 +84,7 @@ extension SuggestionCoordinator {
temporarilyPaused: settingsSnapshot.isTemporarilyPaused,
isLowPowerModeActive: lowPowerModeProvider.isLowPowerModeEnabled,
isLowPowerModeAutoDisableEnabled: settingsSnapshot.isLowPowerModeAutoDisableEnabled,
disabledAppBundleIdentifiers: settingsSnapshot.disabledAppBundleIdentifiers,
disabledAppBundleIdentifiers: disabledApps(for: snapshot),
disabledDomains: PerDomainDisableSettings.disabledDomains(),
suggestInIntegratedTerminals: settingsSnapshot.suggestInIntegratedTerminals,
inputMonitoringGranted: permissionManager.inputMonitoringGranted,
Expand Down Expand Up @@ -132,7 +132,7 @@ extension SuggestionCoordinator {
temporarilyPaused: settingsSnapshot.isTemporarilyPaused,
isLowPowerModeActive: lowPowerModeProvider.isLowPowerModeEnabled,
isLowPowerModeAutoDisableEnabled: settingsSnapshot.isLowPowerModeAutoDisableEnabled,
disabledAppBundleIdentifiers: settingsSnapshot.disabledAppBundleIdentifiers,
disabledAppBundleIdentifiers: disabledApps(for: snapshot),
disabledDomains: PerDomainDisableSettings.disabledDomains(),
suggestInIntegratedTerminals: settingsSnapshot.suggestInIntegratedTerminals,
inputMonitoringGranted: permissionManager.inputMonitoringGranted,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,20 @@ extension SuggestionCoordinator {

CotabbyLogger.suggestion.info("Settings changed, resetting suggestion state")
settingsSnapshot = snapshot
resetAfterAvailabilityChange()
}

/// The field icon changed a window's autocomplete choice. That is an availability change the
/// settings snapshot cannot see (window choices live in `WindowFeatureOverrideStore`), so it
/// takes the same reset-and-maybe-restart path a settings change does.
func handleWindowFeatureOverrideChange() {
CotabbyLogger.suggestion.info("Window feature choice changed, resetting suggestion state")
resetAfterAvailabilityChange()
}

/// Drops in-flight work and the visible suggestion, then restarts context capture and
/// prediction only where the current settings and window choices still allow them.
private func resetAfterAvailabilityChange() {
cancelPredictionWork()
resetCachedGenerationContext()
clearSuggestion(clearDiagnostics: true)
Expand All @@ -68,7 +82,7 @@ extension SuggestionCoordinator {
temporarilyPaused: settingsSnapshot.isTemporarilyPaused,
isLowPowerModeActive: lowPowerModeProvider.isLowPowerModeEnabled,
isLowPowerModeAutoDisableEnabled: settingsSnapshot.isLowPowerModeAutoDisableEnabled,
disabledAppBundleIdentifiers: settingsSnapshot.disabledAppBundleIdentifiers,
disabledAppBundleIdentifiers: disabledApps(for: focusModel.snapshot),
disabledDomains: PerDomainDisableSettings.disabledDomains(),
suggestInIntegratedTerminals: settingsSnapshot.suggestInIntegratedTerminals,
inputMonitoringGranted: permissionManager.inputMonitoringGranted,
Expand All @@ -86,7 +100,7 @@ extension SuggestionCoordinator {
temporarilyPaused: settingsSnapshot.isTemporarilyPaused,
isLowPowerModeActive: lowPowerModeProvider.isLowPowerModeEnabled,
isLowPowerModeAutoDisableEnabled: settingsSnapshot.isLowPowerModeAutoDisableEnabled,
disabledAppBundleIdentifiers: settingsSnapshot.disabledAppBundleIdentifiers,
disabledAppBundleIdentifiers: disabledApps(for: focusModel.snapshot),
disabledDomains: PerDomainDisableSettings.disabledDomains(),
suggestInIntegratedTerminals: settingsSnapshot.suggestInIntegratedTerminals,
inputMonitoringGranted: permissionManager.inputMonitoringGranted,
Expand All @@ -109,7 +123,7 @@ extension SuggestionCoordinator {
temporarilyPaused: settingsSnapshot.isTemporarilyPaused,
isLowPowerModeActive: lowPowerModeProvider.isLowPowerModeEnabled,
isLowPowerModeAutoDisableEnabled: settingsSnapshot.isLowPowerModeAutoDisableEnabled,
disabledAppBundleIdentifiers: settingsSnapshot.disabledAppBundleIdentifiers,
disabledAppBundleIdentifiers: disabledApps(for: snapshot),
disabledDomains: PerDomainDisableSettings.disabledDomains(),
suggestInIntegratedTerminals: settingsSnapshot.suggestInIntegratedTerminals,
inputMonitoringGranted: permissionManager.inputMonitoringGranted,
Expand All @@ -120,3 +134,15 @@ extension SuggestionCoordinator {
return context
}
}

extension SuggestionCoordinator {
/// The disabled-apps set adjusted for the focused window's own choice, so every availability
/// gate honors "off in this chat" and "on in this chat" without learning about windows.
func disabledApps(for focusSnapshot: FocusSnapshot) -> Set<String> {
WindowFeatureScope.effectiveDisabledApps(
settingsSnapshot.disabledAppBundleIdentifiers,
bundleIdentifier: focusSnapshot.bundleIdentifier,
windowOverride: windowAutocompleteOverride(focusSnapshot)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1277,7 +1277,7 @@ extension SuggestionCoordinator {
temporarilyPaused: settingsSnapshot.isTemporarilyPaused,
isLowPowerModeActive: lowPowerModeProvider.isLowPowerModeEnabled,
isLowPowerModeAutoDisableEnabled: settingsSnapshot.isLowPowerModeAutoDisableEnabled,
disabledAppBundleIdentifiers: settingsSnapshot.disabledAppBundleIdentifiers,
disabledAppBundleIdentifiers: disabledApps(for: focusSnapshot),
disabledDomains: PerDomainDisableSettings.disabledDomains(),
suggestInIntegratedTerminals: settingsSnapshot.suggestInIntegratedTerminals,
inputMonitoringGranted: permissionManager.inputMonitoringGranted,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ final class SuggestionCoordinator: ObservableObject {
let suggestionEngine: any SuggestionGenerating
let suggestionSettings: any SuggestionSettingsProviding
let clipboardContextProvider: any ClipboardContextProviding
/// The focused window's own autocomplete choice from the field icon (nil: follow the app).
/// A closure rather than the concrete store so the coordinator stays testable without
/// `UserDefaults`; `CotabbyAppEnvironment` points it at `WindowFeatureOverrideStore`.
var windowAutocompleteOverride: @MainActor (FocusSnapshot) -> Bool? = { _ in nil }
let clipboardRelevanceFilter: any ClipboardRelevanceFiltering
let visualContextCoordinator: any VisualContextCoordinating
let interactionState: SuggestionInteractionState
Expand Down
Loading