From 4a40f02cc19eede4f162d3b695daf8147928f9f0 Mon Sep 17 00:00:00 2001 From: lk340 Date: Mon, 7 Apr 2025 15:13:30 -0400 Subject: [PATCH 1/2] Add incognito mode features. --- .../AccessibilityNotificationsManager.swift | 11 +-- .../Icons/incog-off.imageset/Contents.json | 21 ++++++ .../Icons/incog-off.imageset/incog-off.svg | 3 + .../Icons/incog-on.imageset/Contents.json | 21 ++++++ .../Icons/incog-on.imageset/incog-on.svg | 3 + macos/Onit/Data/Model/Model+Chat.swift | 3 +- macos/Onit/Data/Model/Model+Input.swift | 20 +++++- macos/Onit/Data/Persistence/Defaults.swift | 1 + .../KeyboardShortcutsManager.swift | 10 ++- macos/Onit/UI/Chat/ChatView.swift | 1 + .../Dismissables/IncognitoDismissable.swift | 67 +++++++++++++++++++ macos/Onit/UI/History/HistoryRowView.swift | 4 ++ macos/Onit/UI/OS/ContextPickerView.swift | 14 +++- .../UI/Prompt/Files/PaperclipButton.swift | 5 +- macos/Onit/UI/Prompt/Toolbar.swift | 16 +++++ 15 files changed, 188 insertions(+), 12 deletions(-) create mode 100644 macos/Onit/Assets.xcassets/Icons/incog-off.imageset/Contents.json create mode 100644 macos/Onit/Assets.xcassets/Icons/incog-off.imageset/incog-off.svg create mode 100644 macos/Onit/Assets.xcassets/Icons/incog-on.imageset/Contents.json create mode 100644 macos/Onit/Assets.xcassets/Icons/incog-on.imageset/incog-on.svg create mode 100644 macos/Onit/UI/Dismissables/IncognitoDismissable.swift diff --git a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift index a0aa90f3..4b8f9905 100644 --- a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift +++ b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift @@ -6,6 +6,7 @@ // import ApplicationServices +import Defaults import Foundation import PostHog import SwiftUI @@ -319,7 +320,9 @@ class AccessibilityNotificationsManager: ObservableObject { return } - guard FeatureFlagManager.shared.accessibilityAutoContext else { + guard FeatureFlagManager.shared.accessibilityAutoContext, + !Defaults[.incognitoModeEnabled] + else { self.screenResult = .init() return } @@ -419,13 +422,13 @@ class AccessibilityNotificationsManager: ObservableObject { guard FeatureFlagManager.shared.accessibilityInput, let selectedText = text, let selectedElement = element, - !selectedText.isEmpty + !selectedText.isEmpty, + !Defaults[.incognitoModeEnabled] else { - selectedSource = nil model?.pendingInput = nil HighlightHintWindowController.shared.hide() - + return } screenResult.userInteraction.selectedText = selectedText diff --git a/macos/Onit/Assets.xcassets/Icons/incog-off.imageset/Contents.json b/macos/Onit/Assets.xcassets/Icons/incog-off.imageset/Contents.json new file mode 100644 index 00000000..846c0fd7 --- /dev/null +++ b/macos/Onit/Assets.xcassets/Icons/incog-off.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "incog-off.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macos/Onit/Assets.xcassets/Icons/incog-off.imageset/incog-off.svg b/macos/Onit/Assets.xcassets/Icons/incog-off.imageset/incog-off.svg new file mode 100644 index 00000000..0dcb6f12 --- /dev/null +++ b/macos/Onit/Assets.xcassets/Icons/incog-off.imageset/incog-off.svg @@ -0,0 +1,3 @@ + + + diff --git a/macos/Onit/Assets.xcassets/Icons/incog-on.imageset/Contents.json b/macos/Onit/Assets.xcassets/Icons/incog-on.imageset/Contents.json new file mode 100644 index 00000000..3bd7d2e0 --- /dev/null +++ b/macos/Onit/Assets.xcassets/Icons/incog-on.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "incog-on.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/macos/Onit/Assets.xcassets/Icons/incog-on.imageset/incog-on.svg b/macos/Onit/Assets.xcassets/Icons/incog-on.imageset/incog-on.svg new file mode 100644 index 00000000..6407225a --- /dev/null +++ b/macos/Onit/Assets.xcassets/Icons/incog-on.imageset/incog-on.svg @@ -0,0 +1,3 @@ + + + diff --git a/macos/Onit/Data/Model/Model+Chat.swift b/macos/Onit/Data/Model/Model+Chat.swift index fab096d8..facc5710 100644 --- a/macos/Onit/Data/Model/Model+Chat.swift +++ b/macos/Onit/Data/Model/Model+Chat.swift @@ -35,7 +35,8 @@ extension OnitModel { currentPrompts = [] SystemPromptState.shared.shouldShowSystemPrompt = false let modelContext = container.mainContext - modelContext.insert(currentChat!) + + if !Defaults[.incognitoModeEnabled] { modelContext.insert(currentChat!) } finishPromptCreation(prompt) } diff --git a/macos/Onit/Data/Model/Model+Input.swift b/macos/Onit/Data/Model/Model+Input.swift index c18ce431..a0df7837 100644 --- a/macos/Onit/Data/Model/Model+Input.swift +++ b/macos/Onit/Data/Model/Model+Input.swift @@ -6,11 +6,13 @@ // import AppKit +import Defaults extension OnitModel { func addAutoContext() { guard FeatureFlagManager.shared.accessibility, - FeatureFlagManager.shared.accessibilityAutoContext + FeatureFlagManager.shared.accessibilityAutoContext, + !Defaults[.incognitoModeEnabled] else { return } @@ -44,6 +46,22 @@ extension OnitModel { pendingContextList.insert(autoContext, at: 0) } + func clearAutoContext() { + pendingInput = nil + HighlightHintWindowController.shared.hide() + + pendingContextList.removeAll { contextItem in + if case .auto = contextItem { return true } + else { return false } + } + + for contextItem in contextWindowControllers.keys { + if case .auto = contextItem { + closeContextWindow(context: contextItem) + } + } + } + func getPendingContextList() -> [Context] { return pendingContextList } diff --git a/macos/Onit/Data/Persistence/Defaults.swift b/macos/Onit/Data/Persistence/Defaults.swift index 4654f9ec..25e6bf30 100644 --- a/macos/Onit/Data/Persistence/Defaults.swift +++ b/macos/Onit/Data/Persistence/Defaults.swift @@ -65,6 +65,7 @@ extension Defaults.Keys { static let availableRemoteModels = Key<[AIModel]>("availableRemoteModels", default: []) static let availableCustomProviders = Key<[CustomProvider]>( "availableCustomProvider", default: []) + static let incognitoModeEnabled = Key("incognitoModeEnabled", default: false) // Stores unique model identifiers in the format "provider-id" or "customProviderName-id" for custom providers static let visibleModelIds = Key>("visibleModelIds", default: Set([])) diff --git a/macos/Onit/KeyboardShortcuts/KeyboardShortcutsManager.swift b/macos/Onit/KeyboardShortcuts/KeyboardShortcutsManager.swift index a63e6f4a..a96ed3d2 100644 --- a/macos/Onit/KeyboardShortcuts/KeyboardShortcutsManager.swift +++ b/macos/Onit/KeyboardShortcuts/KeyboardShortcutsManager.swift @@ -86,11 +86,17 @@ struct KeyboardShortcutsManager { model.launchShortcutAction() case .launchWithAutoContext: let eventProperties: [String: Any] = [ - "app_hidden": model.panel == nil + "app_hidden": model.panel == nil, + "incognito_mode_enabled": Defaults[.incognitoModeEnabled] ] + PostHogSDK.shared.capture( "shortcut_launch_with_auto_context", properties: eventProperties) - model.addAutoContext() + + if !Defaults[.incognitoModeEnabled] { + model.addAutoContext() + } + if (model.panel == nil) { model.launchPanel() } diff --git a/macos/Onit/UI/Chat/ChatView.swift b/macos/Onit/UI/Chat/ChatView.swift index fd6b76f7..3c453c22 100644 --- a/macos/Onit/UI/Chat/ChatView.swift +++ b/macos/Onit/UI/Chat/ChatView.swift @@ -8,6 +8,7 @@ struct ChatView: View { var body: some View { VStack(alignment: .leading, spacing: 0) { + IncognitoDismissable() SetUpDialogs() if let systemPrompt = model.currentChat?.systemPrompt { ChatSystemPromptView(systemPrompt: systemPrompt) diff --git a/macos/Onit/UI/Dismissables/IncognitoDismissable.swift b/macos/Onit/UI/Dismissables/IncognitoDismissable.swift new file mode 100644 index 00000000..0c7688f4 --- /dev/null +++ b/macos/Onit/UI/Dismissables/IncognitoDismissable.swift @@ -0,0 +1,67 @@ +// +// IncognitoDismissable.swift +// Onit +// +// Created by Loyd Kim on 4/7/25. +// + +import Defaults +import SwiftUI + +struct IncognitoDismissable: View { + @AppStorage("closedIncognitoDismissable") private var closedIncognitoDismissable = false + @Default(.incognitoModeEnabled) var incognitoModeEnabled + + @State var closeHovered: Bool = false + + var body: some View { + if incognitoModeEnabled && !closedIncognitoDismissable { + VStack(alignment: .leading, spacing: 6) { + title + text + } + .padding(14) + .background(.gray900) + .overlay( + RoundedRectangle(cornerRadius: 16) + .inset(by: 0.5) + .stroke(Color(red: 0.28, green: 0.29, blue: 0.31), lineWidth: 1) + ) + .cornerRadius(16) + .padding(.vertical, 8) + .padding(.horizontal, 14) + } + } +} + + +/// Child Components +extension IncognitoDismissable { + var title: some View { + HStack { + Text("Incognito Mode") + .foregroundColor(.white) + .font(.system(size: 14, weight: .semibold)) + + Spacer() + + Button { + closedIncognitoDismissable = true + } label: { + Image(.smallCross) + .renderingMode(.template) + .foregroundStyle(.white) + .frame(width: 10, height: 10) + } + .opacity(closeHovered ? 0.5 : 1) + .animation(.easeInOut(duration: 0.15), value: closeHovered) + .onHover{ isHovering in closeHovered = isHovering} + } + } + + var text: some View { + Text("When on, Onit won't suggest or store auto-context based on your current window activity.") + .foregroundColor(.gray100) + .font(.system(size: 13, weight: .medium)) + } +} diff --git a/macos/Onit/UI/History/HistoryRowView.swift b/macos/Onit/UI/History/HistoryRowView.swift index 1f328f2b..272b0e0c 100644 --- a/macos/Onit/UI/History/HistoryRowView.swift +++ b/macos/Onit/UI/History/HistoryRowView.swift @@ -5,9 +5,12 @@ // Created by Benjamin Sage on 11/4/24. // +import Defaults import SwiftUI struct HistoryRowView: View { + @Default(.incognitoModeEnabled) var incognitoModeEnabled + @Environment(\.model) var model @State private var showDelete: Bool = false @@ -17,6 +20,7 @@ struct HistoryRowView: View { var body: some View { Button { + incognitoModeEnabled = false model.setChat(chat: chat, index: index) } label: { HStack { diff --git a/macos/Onit/UI/OS/ContextPickerView.swift b/macos/Onit/UI/OS/ContextPickerView.swift index d70cbc45..51503aa7 100644 --- a/macos/Onit/UI/OS/ContextPickerView.swift +++ b/macos/Onit/UI/OS/ContextPickerView.swift @@ -5,6 +5,7 @@ // Created by Kévin Naudin on 25/01/2025. // +import Defaults import SwiftUI struct ContextPickerView: View { @@ -23,15 +24,22 @@ struct ContextPickerView: View { .buttonStyle(.plain) Button(action: { - OverlayManager.shared.dismissOverlay() - model.addAutoContext() + if !Defaults[.incognitoModeEnabled] { + OverlayManager.shared.dismissOverlay() + model.addAutoContext() + } }) { ContextPickerItemView( - imageRes: .stars, title: "Auto-context", subtitle: "Current window activity") + imageRes: .stars, + title: "Auto-context", + subtitle: Defaults[.incognitoModeEnabled] ? "Disabled in incognito mode" : "Current window activity" + ) } .buttonStyle(.plain) .foregroundColor(.gray200) .padding(.bottom, 6) + .opacity(Defaults[.incognitoModeEnabled] ? 0.5 : 1) + .disabled(Defaults[.incognitoModeEnabled]) } .background(Color(.gray600)) .cornerRadius(12) diff --git a/macos/Onit/UI/Prompt/Files/PaperclipButton.swift b/macos/Onit/UI/Prompt/Files/PaperclipButton.swift index fc5122fd..f4123ca3 100644 --- a/macos/Onit/UI/Prompt/Files/PaperclipButton.swift +++ b/macos/Onit/UI/Prompt/Files/PaperclipButton.swift @@ -5,10 +5,13 @@ // Created by Benjamin Sage on 10/23/24. // +import Defaults import KeyboardShortcuts import SwiftUI struct PaperclipButton: View { + @Default(.incognitoModeEnabled) var incognitoModeEnabled + @Environment(\.model) var model @ObservedObject var featureFlagsManager = FeatureFlagManager.shared @ObservedObject var notificationsManager = AccessibilityNotificationsManager.shared @@ -31,7 +34,7 @@ struct PaperclipButton: View { .tooltip(prompt: accessibilityAutoContextEnabled ? "Add context" : "Upload file") if model.pendingContextList.isEmpty { - if !accessibilityAutoContextEnabled && !closedAutocontext { + if !accessibilityAutoContextEnabled && !closedAutocontext && !incognitoModeEnabled { EnableAutocontextTag() } else if accessibilityAutoContextEnabled { Button { diff --git a/macos/Onit/UI/Prompt/Toolbar.swift b/macos/Onit/UI/Prompt/Toolbar.swift index 5821c964..bf5409d2 100644 --- a/macos/Onit/UI/Prompt/Toolbar.swift +++ b/macos/Onit/UI/Prompt/Toolbar.swift @@ -20,6 +20,7 @@ struct Toolbar: View { @Default(.localModel) var localModel @Default(.isRegularApp) var isRegularApp @Default(.fitActiveWindow) var fitActiveWindow + @Default(.incognitoModeEnabled) var incognitoModeEnabled private var isAccessibilityFlagsEnabled: Bool { featureFlagsManager.accessibility && featureFlagsManager.accessibilityAutoContext @@ -59,6 +60,7 @@ struct Toolbar: View { fitActiveWindowButton } localMode + incognitoMode history settings @@ -242,6 +244,20 @@ struct Toolbar: View { } .tooltip(prompt: "Local Mode", shortcut: .keyboardShortcuts(.toggleLocalMode)) } + + var incognitoMode: some View { + Button { + incognitoModeEnabled.toggle() + if incognitoModeEnabled { model.clearAutoContext() } + model.newChat() + } label: { + Image(incognitoModeEnabled ? .incogOn : .incogOff) + .renderingMode(.template) + .padding(4) + .foregroundColor(incognitoModeEnabled ? .blue400 : .gray200) + } + .tooltip(prompt: "Incognito mode (auto-context off)") + } var showHistoryBinding: Binding { Binding( From 378476d5c46ce3e740596cb08f7078b339397303 Mon Sep 17 00:00:00 2001 From: lk340 Date: Wed, 9 Apr 2025 08:10:51 -0400 Subject: [PATCH 2/2] Remove auto-context blockers. Update NUX verbiage. --- .../AccessibilityNotificationsManager.swift | 9 ++------- macos/Onit/Data/Model/Model+Input.swift | 20 +------------------ .../KeyboardShortcutsManager.swift | 9 ++------- .../Dismissables/IncognitoDismissable.swift | 2 +- macos/Onit/UI/OS/ContextPickerView.swift | 11 +++------- .../UI/Prompt/Files/PaperclipButton.swift | 5 +---- macos/Onit/UI/Prompt/Toolbar.swift | 1 - 7 files changed, 10 insertions(+), 47 deletions(-) diff --git a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift index 4b8f9905..902387b9 100644 --- a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift +++ b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift @@ -6,7 +6,6 @@ // import ApplicationServices -import Defaults import Foundation import PostHog import SwiftUI @@ -320,9 +319,7 @@ class AccessibilityNotificationsManager: ObservableObject { return } - guard FeatureFlagManager.shared.accessibilityAutoContext, - !Defaults[.incognitoModeEnabled] - else { + guard FeatureFlagManager.shared.accessibilityAutoContext else { self.screenResult = .init() return } @@ -422,13 +419,11 @@ class AccessibilityNotificationsManager: ObservableObject { guard FeatureFlagManager.shared.accessibilityInput, let selectedText = text, let selectedElement = element, - !selectedText.isEmpty, - !Defaults[.incognitoModeEnabled] + !selectedText.isEmpty else { selectedSource = nil model?.pendingInput = nil HighlightHintWindowController.shared.hide() - return } screenResult.userInteraction.selectedText = selectedText diff --git a/macos/Onit/Data/Model/Model+Input.swift b/macos/Onit/Data/Model/Model+Input.swift index a0df7837..c18ce431 100644 --- a/macos/Onit/Data/Model/Model+Input.swift +++ b/macos/Onit/Data/Model/Model+Input.swift @@ -6,13 +6,11 @@ // import AppKit -import Defaults extension OnitModel { func addAutoContext() { guard FeatureFlagManager.shared.accessibility, - FeatureFlagManager.shared.accessibilityAutoContext, - !Defaults[.incognitoModeEnabled] + FeatureFlagManager.shared.accessibilityAutoContext else { return } @@ -46,22 +44,6 @@ extension OnitModel { pendingContextList.insert(autoContext, at: 0) } - func clearAutoContext() { - pendingInput = nil - HighlightHintWindowController.shared.hide() - - pendingContextList.removeAll { contextItem in - if case .auto = contextItem { return true } - else { return false } - } - - for contextItem in contextWindowControllers.keys { - if case .auto = contextItem { - closeContextWindow(context: contextItem) - } - } - } - func getPendingContextList() -> [Context] { return pendingContextList } diff --git a/macos/Onit/KeyboardShortcuts/KeyboardShortcutsManager.swift b/macos/Onit/KeyboardShortcuts/KeyboardShortcutsManager.swift index a96ed3d2..3d48abde 100644 --- a/macos/Onit/KeyboardShortcuts/KeyboardShortcutsManager.swift +++ b/macos/Onit/KeyboardShortcuts/KeyboardShortcutsManager.swift @@ -86,16 +86,11 @@ struct KeyboardShortcutsManager { model.launchShortcutAction() case .launchWithAutoContext: let eventProperties: [String: Any] = [ - "app_hidden": model.panel == nil, - "incognito_mode_enabled": Defaults[.incognitoModeEnabled] + "app_hidden": model.panel == nil ] - PostHogSDK.shared.capture( "shortcut_launch_with_auto_context", properties: eventProperties) - - if !Defaults[.incognitoModeEnabled] { - model.addAutoContext() - } + model.addAutoContext() if (model.panel == nil) { model.launchPanel() diff --git a/macos/Onit/UI/Dismissables/IncognitoDismissable.swift b/macos/Onit/UI/Dismissables/IncognitoDismissable.swift index 0c7688f4..1bd297b2 100644 --- a/macos/Onit/UI/Dismissables/IncognitoDismissable.swift +++ b/macos/Onit/UI/Dismissables/IncognitoDismissable.swift @@ -60,7 +60,7 @@ extension IncognitoDismissable { } var text: some View { - Text("When on, Onit won't suggest or store auto-context based on your current window activity.") + Text("When on, Onit won’t store your chats in history.") .foregroundColor(.gray100) .font(.system(size: 13, weight: .medium)) } diff --git a/macos/Onit/UI/OS/ContextPickerView.swift b/macos/Onit/UI/OS/ContextPickerView.swift index 51503aa7..584a9de4 100644 --- a/macos/Onit/UI/OS/ContextPickerView.swift +++ b/macos/Onit/UI/OS/ContextPickerView.swift @@ -5,7 +5,6 @@ // Created by Kévin Naudin on 25/01/2025. // -import Defaults import SwiftUI struct ContextPickerView: View { @@ -24,22 +23,18 @@ struct ContextPickerView: View { .buttonStyle(.plain) Button(action: { - if !Defaults[.incognitoModeEnabled] { - OverlayManager.shared.dismissOverlay() - model.addAutoContext() - } + OverlayManager.shared.dismissOverlay() + model.addAutoContext() }) { ContextPickerItemView( imageRes: .stars, title: "Auto-context", - subtitle: Defaults[.incognitoModeEnabled] ? "Disabled in incognito mode" : "Current window activity" + subtitle: "Current window activity" ) } .buttonStyle(.plain) .foregroundColor(.gray200) .padding(.bottom, 6) - .opacity(Defaults[.incognitoModeEnabled] ? 0.5 : 1) - .disabled(Defaults[.incognitoModeEnabled]) } .background(Color(.gray600)) .cornerRadius(12) diff --git a/macos/Onit/UI/Prompt/Files/PaperclipButton.swift b/macos/Onit/UI/Prompt/Files/PaperclipButton.swift index f4123ca3..fc5122fd 100644 --- a/macos/Onit/UI/Prompt/Files/PaperclipButton.swift +++ b/macos/Onit/UI/Prompt/Files/PaperclipButton.swift @@ -5,13 +5,10 @@ // Created by Benjamin Sage on 10/23/24. // -import Defaults import KeyboardShortcuts import SwiftUI struct PaperclipButton: View { - @Default(.incognitoModeEnabled) var incognitoModeEnabled - @Environment(\.model) var model @ObservedObject var featureFlagsManager = FeatureFlagManager.shared @ObservedObject var notificationsManager = AccessibilityNotificationsManager.shared @@ -34,7 +31,7 @@ struct PaperclipButton: View { .tooltip(prompt: accessibilityAutoContextEnabled ? "Add context" : "Upload file") if model.pendingContextList.isEmpty { - if !accessibilityAutoContextEnabled && !closedAutocontext && !incognitoModeEnabled { + if !accessibilityAutoContextEnabled && !closedAutocontext { EnableAutocontextTag() } else if accessibilityAutoContextEnabled { Button { diff --git a/macos/Onit/UI/Prompt/Toolbar.swift b/macos/Onit/UI/Prompt/Toolbar.swift index bf5409d2..16c35e44 100644 --- a/macos/Onit/UI/Prompt/Toolbar.swift +++ b/macos/Onit/UI/Prompt/Toolbar.swift @@ -248,7 +248,6 @@ struct Toolbar: View { var incognitoMode: some View { Button { incognitoModeEnabled.toggle() - if incognitoModeEnabled { model.clearAutoContext() } model.newChat() } label: { Image(incognitoModeEnabled ? .incogOn : .incogOff)