From 6d2b8df28d85a8334a7a72e4dac2ee9f9ea91e2a Mon Sep 17 00:00:00 2001 From: lk340 Date: Wed, 16 Jul 2025 13:03:21 -0400 Subject: [PATCH 1/2] Add toggle feature to auto-adding of highlighted text to context. * Add new `autoAddHighlightedTextToContext` boolean property to Defaults store. Property defaults to `true`. * Add new switch button to settings "General" tab to toggle `autoAddHighlightedTextToContext` boolean. * Add new `trackedPendingInput` property to OnitPanelState to track highlighted text when `autoAddHighlightedTextToContext` is `false`. * When `autoAddHighlightedTextToContext` is `false`, highlighting text reveals the "ghost" context tag in FileRow to manually add highlighted text to context. * When `autoAddHighlightedTextToContext` is `true`, the app maintains previous highlight behavior (auto-adding to context). If some text was already highlighted, this switch will automatically add the highlighted text to context. * Update highlighted text ContextTag to have a remove action for highlighted text context. --- .../AccessibilityNotificationsManager.swift | 8 +- macos/Onit/Data/Persistence/Defaults.swift | 2 + .../Onit/UI/Panels/State/OnitPanelState.swift | 2 + macos/Onit/UI/Prompt/Files/FileRow.swift | 82 ++++++++++++++----- macos/Onit/UI/Settings/GeneralTab.swift | 14 ++++ 5 files changed, 85 insertions(+), 23 deletions(-) diff --git a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift index d5d09f3b..94dca9da 100644 --- a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift +++ b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift @@ -603,13 +603,19 @@ class AccessibilityNotificationsManager: ObservableObject { screenResult.userInteraction.selectedText = nil PanelStateCoordinator.shared.state.pendingInput = nil + PanelStateCoordinator.shared.state.trackedPendingInput = nil return } screenResult.userInteraction.selectedText = selectedText let input = Input(selectedText: selectedText, application: currentSource ?? "") - PanelStateCoordinator.shared.state.pendingInput = input + + if Defaults[.autoAddHighlightedTextToContext] { + PanelStateCoordinator.shared.state.pendingInput = input + } else { + PanelStateCoordinator.shared.state.trackedPendingInput = input + } } // MARK: Caret Position Handling diff --git a/macos/Onit/Data/Persistence/Defaults.swift b/macos/Onit/Data/Persistence/Defaults.swift index 9304b745..b5674740 100644 --- a/macos/Onit/Data/Persistence/Defaults.swift +++ b/macos/Onit/Data/Persistence/Defaults.swift @@ -114,7 +114,9 @@ extension Defaults.Keys { static let lineHeight = Key("lineHeight", default: 1.5) static let voiceSilenceThreshold = Key("voiceSilenceThreshold", default: -40) static let voiceSpeechPassThreshold = Key("voiceSpeechPassThreshold", default: 0.7) + /// Highlighted Text static let showHighlightedTextInput = Key("showHighlightedTextInput", default: true) + static let autoAddHighlightedTextToContext = Key("autoAddHighlightedTextToContext", default: true) // Local model advanced options static let localKeepAlive = Key("localKeepAlive", default: nil) diff --git a/macos/Onit/UI/Panels/State/OnitPanelState.swift b/macos/Onit/UI/Panels/State/OnitPanelState.swift index 49df4982..ece64a2f 100644 --- a/macos/Onit/UI/Panels/State/OnitPanelState.swift +++ b/macos/Onit/UI/Panels/State/OnitPanelState.swift @@ -130,6 +130,8 @@ class OnitPanelState: NSObject { } } + var trackedPendingInput: Input? = nil + var systemPromptId: String = SystemPrompt.outputOnly.id var imageUploads: [URL: UploadProgress] = [:] diff --git a/macos/Onit/UI/Prompt/Files/FileRow.swift b/macos/Onit/UI/Prompt/Files/FileRow.swift index 9fc4d934..4d725b25 100644 --- a/macos/Onit/UI/Prompt/Files/FileRow.swift +++ b/macos/Onit/UI/Prompt/Files/FileRow.swift @@ -14,6 +14,7 @@ struct FileRow: View { @ObservedObject private var debugManager = DebugManager.shared @Default(.autoContextFromCurrentWindow) var autoContextFromCurrentWindow + @Default(.autoAddHighlightedTextToContext) var autoAddHighlightedTextToContext @State private var ocrComparisonResult: OCRComparisonResult? = nil @State private var showOCRDetails = false @@ -119,7 +120,7 @@ struct FileRow: View { FlowLayout(spacing: 6) { PaperclipButton() - addForegroundWindowToContextButton + addToContextButton pendingWindowContextItems highlightedTextContext addedWindowContextItems @@ -150,28 +151,63 @@ struct FileRow: View { // MARK: - Child Components extension FileRow { + private func ghostContextTag( + text: String, + iconBundleURL: URL? = nil, + iconView: (any View)? = nil, + tooltip: String, + action: @escaping () -> Void + ) -> some View { + ContextTag( + text: text, + textColor: .T_2, + hoverTextColor: .white, + background: contextTagBackground, + hoverBackground: contextTagHoverBackground, + hasHoverBorder: true, + shouldFadeIn: true, + iconBundleURL: iconBundleURL, + iconView: iconView, + tooltip: tooltip + ) { + action() + } + } + @ViewBuilder - private var addForegroundWindowToContextButton: some View { - if accessibilityEnabled, - autoContextFromCurrentWindow, - !(windowBeingAddedToContext || windowAlreadyInContext), - let foregroundWindow = windowState?.foregroundWindow - { - let foregroundWindowName = WindowHelpers.getWindowName(window: foregroundWindow.element) - let iconBundleURL = WindowHelpers.getWindowAppBundleUrl(window: foregroundWindow.element) - - ContextTag( - text: contextTagText, - textColor: .T_2, - hoverTextColor: .white, - background: contextTagBackground, - hoverBackground: contextTagHoverBackground, - hasHoverBorder: true, - shouldFadeIn: true, - iconBundleURL: iconBundleURL, - tooltip: "Add \(foregroundWindowName) Context" - ) { - windowState?.addWindowToContext(window: foregroundWindow.element) + private var addToContextButton: some View { + if accessibilityEnabled { + if let windowState = windowState, + let trackedPendingInput = windowState.trackedPendingInput + { + ghostContextTag( + text: trackedPendingInput.selectedText, + iconView: Image(.text).addIconStyles(iconSize: 14), + tooltip: "Add Highlighted Text To Context" + ) { + windowState.pendingInput = trackedPendingInput + windowState.trackedPendingInput = nil + } + .onChange(of: autoAddHighlightedTextToContext) { _, autoAddHighlightedText in + if autoAddHighlightedText { + windowState.pendingInput = trackedPendingInput + windowState.trackedPendingInput = nil + } + } + } else if autoContextFromCurrentWindow, + !(windowBeingAddedToContext || windowAlreadyInContext), + let foregroundWindow = windowState?.foregroundWindow + { + let foregroundWindowName = WindowHelpers.getWindowName(window: foregroundWindow.element) + let iconBundleURL = WindowHelpers.getWindowAppBundleUrl(window: foregroundWindow.element) + + ghostContextTag( + text: contextTagText, + iconBundleURL: iconBundleURL, + tooltip: "Add \(foregroundWindowName) Context" + ) { + windowState?.addWindowToContext(window: foregroundWindow.element) + } } } } @@ -211,6 +247,8 @@ extension FileRow { iconView: Image(.text).addIconStyles(iconSize: 14) ) { Defaults[.showHighlightedTextInput] = true + } removeAction: { + windowState?.pendingInput = nil } } } diff --git a/macos/Onit/UI/Settings/GeneralTab.swift b/macos/Onit/UI/Settings/GeneralTab.swift index 7a28d74a..27c0d587 100644 --- a/macos/Onit/UI/Settings/GeneralTab.swift +++ b/macos/Onit/UI/Settings/GeneralTab.swift @@ -19,6 +19,7 @@ struct GeneralTab: View { @Default(.tetheredButtonHideAllApps) var tetheredButtonHideAllApps @Default(.tetheredButtonHideAllAppsTimerDate) var tetheredButtonHideAllAppsTimerDate @Default(.showHighlightedTextInput) var showHighlightedTextInput + @Default(.autoAddHighlightedTextToContext) var autoAddHighlightedTextToContext @State var isLaunchAtStartupEnabled: Bool = SMAppService.mainApp.status == .enabled @State var isAnalyticsEnabled: Bool = PostHogSDK.shared.isOptOut() == false @@ -434,6 +435,19 @@ struct GeneralTab: View { .controlSize(.small) } } + + VStack(alignment: .leading, spacing: 20) { + HStack { + Text("Auto-add highlighted text to context.") + .font(.system(size: 13)) + + Spacer() + + Toggle("", isOn: $autoAddHighlightedTextToContext) + .toggleStyle(.switch) + .controlSize(.small) + } + } } } From 0b5a177e233a06ce411b2cbc2b2fce426a72b90a Mon Sep 17 00:00:00 2001 From: lk340 Date: Thu, 17 Jul 2025 09:48:49 -0400 Subject: [PATCH 2/2] Update to show both ghost tags in FileRow. --- macos/Onit/UI/Prompt/Files/FileRow.swift | 70 +++++++++++++----------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/macos/Onit/UI/Prompt/Files/FileRow.swift b/macos/Onit/UI/Prompt/Files/FileRow.swift index 4d725b25..a1c5e39e 100644 --- a/macos/Onit/UI/Prompt/Files/FileRow.swift +++ b/macos/Onit/UI/Prompt/Files/FileRow.swift @@ -120,7 +120,8 @@ struct FileRow: View { FlowLayout(spacing: 6) { PaperclipButton() - addToContextButton + addHighlightedTextToContextButton + addWindowToContextButton pendingWindowContextItems highlightedTextContext addedWindowContextItems @@ -175,39 +176,46 @@ extension FileRow { } @ViewBuilder - private var addToContextButton: some View { - if accessibilityEnabled { - if let windowState = windowState, - let trackedPendingInput = windowState.trackedPendingInput - { - ghostContextTag( - text: trackedPendingInput.selectedText, - iconView: Image(.text).addIconStyles(iconSize: 14), - tooltip: "Add Highlighted Text To Context" - ) { + private var addHighlightedTextToContextButton: some View { + if accessibilityEnabled, + Defaults[.autoContextFromHighlights], + let windowState = windowState, + let trackedPendingInput = windowState.trackedPendingInput + { + ghostContextTag( + text: trackedPendingInput.selectedText, + iconView: Image(.text).addIconStyles(iconSize: 14), + tooltip: "Add Highlighted Text To Context" + ) { + windowState.pendingInput = trackedPendingInput + windowState.trackedPendingInput = nil + } + .onChange(of: autoAddHighlightedTextToContext) { _, autoAddHighlightedText in + if autoAddHighlightedText { windowState.pendingInput = trackedPendingInput windowState.trackedPendingInput = nil } - .onChange(of: autoAddHighlightedTextToContext) { _, autoAddHighlightedText in - if autoAddHighlightedText { - windowState.pendingInput = trackedPendingInput - windowState.trackedPendingInput = nil - } - } - } else if autoContextFromCurrentWindow, - !(windowBeingAddedToContext || windowAlreadyInContext), - let foregroundWindow = windowState?.foregroundWindow - { - let foregroundWindowName = WindowHelpers.getWindowName(window: foregroundWindow.element) - let iconBundleURL = WindowHelpers.getWindowAppBundleUrl(window: foregroundWindow.element) - - ghostContextTag( - text: contextTagText, - iconBundleURL: iconBundleURL, - tooltip: "Add \(foregroundWindowName) Context" - ) { - windowState?.addWindowToContext(window: foregroundWindow.element) - } + } + } + } + + @ViewBuilder + private var addWindowToContextButton: some View { + if accessibilityEnabled, + autoContextFromCurrentWindow, + !(windowBeingAddedToContext || windowAlreadyInContext), + let windowState = windowState, + let foregroundWindow = windowState.foregroundWindow + { + let foregroundWindowName = WindowHelpers.getWindowName(window: foregroundWindow.element) + let iconBundleURL = WindowHelpers.getWindowAppBundleUrl(window: foregroundWindow.element) + + ghostContextTag( + text: contextTagText, + iconBundleURL: iconBundleURL, + tooltip: "Add \(foregroundWindowName) Context" + ) { + windowState.addWindowToContext(window: foregroundWindow.element) } } }