From f2485a2d2a37ed7537a71cf2d35015d81b226c3a Mon Sep 17 00:00:00 2001 From: lk340 Date: Wed, 16 Jul 2025 10:56:17 -0400 Subject: [PATCH 1/7] Update highlighted text behavior. * Add `showHighlightedTextInput` Defaults property. This handles the permanent showing/hiding of the `inputView`. * `inputView` can now be permanently hidden. * Update settings "General" tab to allow for toggling of `showHighlightedTextInput` boolean, which determines the show/hide behavior of `inputView`. * FileRow now shows highlighted text as a ContextTag. Clicking on the ContextTag toggles `showHighlightedTextInput` to `true`, revealing the `inputView` if it were hidden. --- macos/Onit/Data/Persistence/Defaults.swift | 1 + macos/Onit/UI/Prompt/Files/FileRow.swift | 13 ++++++++++ macos/Onit/UI/Prompt/Input/InputButtons.swift | 14 +++++++++++ macos/Onit/UI/Prompt/Input/InputView.swift | 24 +++++++++++-------- macos/Onit/UI/Settings/GeneralTab.swift | 23 ++++++++++++++++++ 5 files changed, 65 insertions(+), 10 deletions(-) diff --git a/macos/Onit/Data/Persistence/Defaults.swift b/macos/Onit/Data/Persistence/Defaults.swift index ac45f55fb..9304b745c 100644 --- a/macos/Onit/Data/Persistence/Defaults.swift +++ b/macos/Onit/Data/Persistence/Defaults.swift @@ -114,6 +114,7 @@ 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) + static let showHighlightedTextInput = Key("showHighlightedTextInput", default: true) // Local model advanced options static let localKeepAlive = Key("localKeepAlive", default: nil) diff --git a/macos/Onit/UI/Prompt/Files/FileRow.swift b/macos/Onit/UI/Prompt/Files/FileRow.swift index a942864e4..9fc4d9347 100644 --- a/macos/Onit/UI/Prompt/Files/FileRow.swift +++ b/macos/Onit/UI/Prompt/Files/FileRow.swift @@ -121,6 +121,7 @@ struct FileRow: View { addForegroundWindowToContextButton pendingWindowContextItems + highlightedTextContext addedWindowContextItems } @@ -202,6 +203,18 @@ extension FileRow { } } + @ViewBuilder + private var highlightedTextContext: some View { + if let pendingInput = windowState?.pendingInput { + ContextTag( + text: pendingInput.selectedText, + iconView: Image(.text).addIconStyles(iconSize: 14) + ) { + Defaults[.showHighlightedTextInput] = true + } + } + } + @ViewBuilder private var addedWindowContextItems: some View { if !contextList.isEmpty { diff --git a/macos/Onit/UI/Prompt/Input/InputButtons.swift b/macos/Onit/UI/Prompt/Input/InputButtons.swift index c3c38aca8..e8672a58e 100644 --- a/macos/Onit/UI/Prompt/Input/InputButtons.swift +++ b/macos/Onit/UI/Prompt/Input/InputButtons.swift @@ -5,6 +5,7 @@ // Created by Benjamin Sage on 10/8/24. // +import Defaults import SwiftUI struct InputButtons: View { @@ -40,9 +41,22 @@ struct InputButtons: View { .rotationEffect(inputExpanded ? .degrees(90) : .zero) } } + + closeButton } .foregroundStyle(.gray200) } + + // MARK: - Child Components + + private var closeButton: some View { + IconButton( + icon: .cross, + iconSize: 9 + ) { + Defaults[.showHighlightedTextInput] = false + } + } } #if DEBUG diff --git a/macos/Onit/UI/Prompt/Input/InputView.swift b/macos/Onit/UI/Prompt/Input/InputView.swift index 92234111d..bbc85420e 100644 --- a/macos/Onit/UI/Prompt/Input/InputView.swift +++ b/macos/Onit/UI/Prompt/Input/InputView.swift @@ -5,9 +5,11 @@ // Created by Benjamin Sage on 10/3/24. // +import Defaults import SwiftUI struct InputView: View { + @Default(.showHighlightedTextInput) var showHighlightedTextInput @State var inputExpanded: Bool = true @@ -15,17 +17,19 @@ struct InputView: View { var isEditing: Bool = true var body: some View { - VStack(spacing: 0) { - InputTitle(inputExpanded: $inputExpanded, input: input) - divider - InputBody(inputExpanded: $inputExpanded, input: input) + if showHighlightedTextInput { + VStack(spacing: 0) { + InputTitle(inputExpanded: $inputExpanded, input: input) + divider + InputBody(inputExpanded: $inputExpanded, input: input) + } + .background { + RoundedRectangle(cornerRadius: 10) + .fill(.gray800) + .strokeBorder(.gray600) + } + .padding([.horizontal, .top], isEditing ? 12 : 0) } - .background { - RoundedRectangle(cornerRadius: 10) - .fill(.gray800) - .strokeBorder(.gray600) - } - .padding([.horizontal, .top], isEditing ? 12 : 0) } var divider: some View { diff --git a/macos/Onit/UI/Settings/GeneralTab.swift b/macos/Onit/UI/Settings/GeneralTab.swift index 45814d372..7a28d74a1 100644 --- a/macos/Onit/UI/Settings/GeneralTab.swift +++ b/macos/Onit/UI/Settings/GeneralTab.swift @@ -18,6 +18,7 @@ struct GeneralTab: View { @Default(.tetheredButtonHiddenApps) var tetheredButtonHiddenApps @Default(.tetheredButtonHideAllApps) var tetheredButtonHideAllApps @Default(.tetheredButtonHideAllAppsTimerDate) var tetheredButtonHideAllAppsTimerDate + @Default(.showHighlightedTextInput) var showHighlightedTextInput @State var isLaunchAtStartupEnabled: Bool = SMAppService.mainApp.status == .enabled @State var isAnalyticsEnabled: Bool = PostHogSDK.shared.isOptOut() == false @@ -73,6 +74,8 @@ struct GeneralTab: View { appearanceSection GeneralTabVoice() + + showHighlightedTextInputSection hiddenAppsSection @@ -414,6 +417,26 @@ struct GeneralTab: View { } } + var showHighlightedTextInputSection: some View { + SettingsSection( + iconImage: .text, + title: "Highlighted Text" + ) { + VStack(alignment: .leading, spacing: 20) { + HStack { + Text("Show highlighted text input.") + .font(.system(size: 13)) + + Spacer() + + Toggle("", isOn: $showHighlightedTextInput) + .toggleStyle(.switch) + .controlSize(.small) + } + } + } + } + var hiddenAppsSection: some View { SettingsSection( iconSystem: "eye.slash", From 88630a42a0374d89c778577304cf24cf71b4edd1 Mon Sep 17 00:00:00 2001 From: lk340 Date: Wed, 16 Jul 2025 16:03:22 -0400 Subject: [PATCH 2/7] Remove period from highlight text settings --- macos/Onit/UI/Settings/GeneralTab.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos/Onit/UI/Settings/GeneralTab.swift b/macos/Onit/UI/Settings/GeneralTab.swift index 7a28d74a1..c4fa99846 100644 --- a/macos/Onit/UI/Settings/GeneralTab.swift +++ b/macos/Onit/UI/Settings/GeneralTab.swift @@ -424,7 +424,7 @@ struct GeneralTab: View { ) { VStack(alignment: .leading, spacing: 20) { HStack { - Text("Show highlighted text input.") + Text("Show highlighted text input") .font(.system(size: 13)) Spacer() From ae1102e6df86463bdaaa57715081aaba6a65834a Mon Sep 17 00:00:00 2001 From: lk340 Date: Thu, 17 Jul 2025 09:30:24 -0400 Subject: [PATCH 3/7] Address feedback * Fix whitespace and new lines being included in highlighted text. * Remove copy and close buttons in InputTitle. Caret button no longer toggles height of `inputView`; it now handles removing the `inputView` from the UI (previous close button behavior). * Because the caret button no longer toggles the height of `inputView`, height toggle logic has been removed. --- .../AccessibilityNotificationsManager.swift | 5 +++- macos/Onit/UI/Prompt/Input/InputBody.swift | 8 +++---- macos/Onit/UI/Prompt/Input/InputButtons.swift | 24 +++---------------- macos/Onit/UI/Prompt/Input/InputTitle.swift | 5 ++-- macos/Onit/UI/Prompt/Input/InputView.swift | 7 ++---- 5 files changed, 14 insertions(+), 35 deletions(-) diff --git a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift index d5d09f3b1..d21542211 100644 --- a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift +++ b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift @@ -598,7 +598,10 @@ class AccessibilityNotificationsManager: ObservableObject { private func processSelectedText(_ text: String?) { guard Defaults[.autoContextFromHighlights], - let selectedText = text, + let selectedText = text? + .replacingOccurrences(of: "\\r?\\n", with: " ", options: .regularExpression) + .replacingOccurrences(of: " +", with: " ", options: .regularExpression) + .trimmingCharacters(in: .whitespacesAndNewlines), HighlightedTextValidator.isValid(text: selectedText) else { screenResult.userInteraction.selectedText = nil diff --git a/macos/Onit/UI/Prompt/Input/InputBody.swift b/macos/Onit/UI/Prompt/Input/InputBody.swift index c68bcaf52..2cd039ef0 100644 --- a/macos/Onit/UI/Prompt/Input/InputBody.swift +++ b/macos/Onit/UI/Prompt/Input/InputBody.swift @@ -9,14 +9,12 @@ import HighlightSwift import SwiftUI struct InputBody: View { - @Binding var inputExpanded: Bool @State var text: String? @State var textHeight: CGFloat = 0 var input: Input - init(inputExpanded: Binding, input: Input) { - _inputExpanded = inputExpanded + init(input: Input) { text = input.selectedText.count <= 500 ? input.selectedText : nil self.input = input } @@ -41,7 +39,7 @@ struct InputBody: View { } } } - .frame(height: inputExpanded ? height : 0) + .frame(height: height) .onChange(of: input.selectedText, initial: true) { DispatchQueue.main.async { text = input.selectedText @@ -74,6 +72,6 @@ struct InputBody: View { #if DEBUG #Preview { - InputBody(inputExpanded: .constant(true), input: .sample) + InputBody(input: .sample) } #endif diff --git a/macos/Onit/UI/Prompt/Input/InputButtons.swift b/macos/Onit/UI/Prompt/Input/InputButtons.swift index e8672a58e..a1d7d68e4 100644 --- a/macos/Onit/UI/Prompt/Input/InputButtons.swift +++ b/macos/Onit/UI/Prompt/Input/InputButtons.swift @@ -10,8 +10,6 @@ import SwiftUI struct InputButtons: View { @Environment(\.windowState) private var state - - @Binding var inputExpanded: Bool var input: Input @@ -27,40 +25,24 @@ struct InputButtons: View { .buttonStyle(DarkerButtonStyle()) } - CopyButton(text: input.selectedText) - .frame(width: 20, height: 20) - Button { - inputExpanded.toggle() + Defaults[.showHighlightedTextInput] = false } label: { Color.clear .frame(width: 20, height: 20) .overlay { Image(.smallChevRight) .renderingMode(.template) - .rotationEffect(inputExpanded ? .degrees(90) : .zero) + .rotationEffect(.degrees(90)) } } - - closeButton } .foregroundStyle(.gray200) } - - // MARK: - Child Components - - private var closeButton: some View { - IconButton( - icon: .cross, - iconSize: 9 - ) { - Defaults[.showHighlightedTextInput] = false - } - } } #if DEBUG #Preview { - InputButtons(inputExpanded: .constant(true), input: .sample) + InputButtons(input: .sample) } #endif diff --git a/macos/Onit/UI/Prompt/Input/InputTitle.swift b/macos/Onit/UI/Prompt/Input/InputTitle.swift index b2dee297c..d1eb67f59 100644 --- a/macos/Onit/UI/Prompt/Input/InputTitle.swift +++ b/macos/Onit/UI/Prompt/Input/InputTitle.swift @@ -8,7 +8,6 @@ import SwiftUI struct InputTitle: View { - @Binding var inputExpanded: Bool var input: Input var sourceString: String { @@ -26,7 +25,7 @@ struct InputTitle: View { .appFont(.medium13) .textSelection(.enabled) Spacer() - InputButtons(inputExpanded: $inputExpanded, input: input) + InputButtons(input: input) } .foregroundStyle(.gray100) .padding(.horizontal, 12) @@ -35,5 +34,5 @@ struct InputTitle: View { } #Preview { - InputTitle(inputExpanded: .constant(true), input: .sample) + InputTitle(input: .sample) } diff --git a/macos/Onit/UI/Prompt/Input/InputView.swift b/macos/Onit/UI/Prompt/Input/InputView.swift index bbc85420e..fee07f57b 100644 --- a/macos/Onit/UI/Prompt/Input/InputView.swift +++ b/macos/Onit/UI/Prompt/Input/InputView.swift @@ -11,17 +11,15 @@ import SwiftUI struct InputView: View { @Default(.showHighlightedTextInput) var showHighlightedTextInput - @State var inputExpanded: Bool = true - var input: Input var isEditing: Bool = true var body: some View { if showHighlightedTextInput { VStack(spacing: 0) { - InputTitle(inputExpanded: $inputExpanded, input: input) + InputTitle(input: input) divider - InputBody(inputExpanded: $inputExpanded, input: input) + InputBody(input: input) } .background { RoundedRectangle(cornerRadius: 10) @@ -35,7 +33,6 @@ struct InputView: View { var divider: some View { Color.gray600 .frame(height: 1) - .opacity(inputExpanded ? 1 : 0) } } From 6226de8ce8e78608ad5538ec2d7ba2b751442d47 Mon Sep 17 00:00:00 2001 From: Loyd Kim <40570343+lk340@users.noreply.github.com> Date: Fri, 18 Jul 2025 08:59:09 -0400 Subject: [PATCH 4/7] Text Highlight: Auto-add To Context (#340) * 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. * Update to show both ghost tags in FileRow. --- .../AccessibilityNotificationsManager.swift | 8 ++- macos/Onit/Data/Persistence/Defaults.swift | 2 + .../Onit/UI/Panels/State/OnitPanelState.swift | 2 + macos/Onit/UI/Prompt/Files/FileRow.swift | 70 +++++++++++++++---- macos/Onit/UI/Settings/GeneralTab.swift | 14 ++++ 5 files changed, 83 insertions(+), 13 deletions(-) diff --git a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift index d21542211..e49f95897 100644 --- a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift +++ b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift @@ -606,13 +606,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 9304b745c..b56747406 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 49df49820..ece64a2f8 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 9fc4d9347..a1c5e39ef 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,8 @@ struct FileRow: View { FlowLayout(spacing: 6) { PaperclipButton() - addForegroundWindowToContextButton + addHighlightedTextToContextButton + addWindowToContextButton pendingWindowContextItems highlightedTextContext addedWindowContextItems @@ -150,28 +152,70 @@ 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 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 + } + } + } + } + @ViewBuilder - private var addForegroundWindowToContextButton: some View { + private var addWindowToContextButton: some View { if accessibilityEnabled, autoContextFromCurrentWindow, !(windowBeingAddedToContext || windowAlreadyInContext), - let foregroundWindow = windowState?.foregroundWindow + let windowState = windowState, + let foregroundWindow = windowState.foregroundWindow { let foregroundWindowName = WindowHelpers.getWindowName(window: foregroundWindow.element) let iconBundleURL = WindowHelpers.getWindowAppBundleUrl(window: foregroundWindow.element) - - ContextTag( + + ghostContextTag( 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) + windowState.addWindowToContext(window: foregroundWindow.element) } } } @@ -211,6 +255,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 c4fa99846..5d0b902fc 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 fb623872d20e65aba2f046978074653f5f629fd1 Mon Sep 17 00:00:00 2001 From: lk340 Date: Fri, 18 Jul 2025 10:44:59 -0400 Subject: [PATCH 5/7] Address Feedback & Fixes * Remove trimming on the `processSelectedText()` level and apply it only on the context tags level instead. * Add new StringHelpers struct for reusable removable of whitespace + new lines. * Update ordering of context tags in FileRow. * Update hiding of InputView to the PromptCore level. InputView can now be seen in the FinalContextView, regardless of show state. * Update InputView to be a collapsible element when in FinalContextView. --- .../AccessibilityNotificationsManager.swift | 5 +--- macos/Onit/Helpers/StringHelpers.swift | 14 ++++++++++ macos/Onit/UI/Prompt/Files/FileRow.swift | 8 +++--- macos/Onit/UI/Prompt/Input/InputBody.swift | 8 +++--- macos/Onit/UI/Prompt/Input/InputButtons.swift | 13 ++++++--- macos/Onit/UI/Prompt/Input/InputTitle.swift | 6 +++-- macos/Onit/UI/Prompt/Input/InputView.swift | 27 +++++++++---------- macos/Onit/UI/Prompt/PromptCore.swift | 5 ++-- 8 files changed, 54 insertions(+), 32 deletions(-) create mode 100644 macos/Onit/Helpers/StringHelpers.swift diff --git a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift index e49f95897..94dca9daa 100644 --- a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift +++ b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift @@ -598,10 +598,7 @@ class AccessibilityNotificationsManager: ObservableObject { private func processSelectedText(_ text: String?) { guard Defaults[.autoContextFromHighlights], - let selectedText = text? - .replacingOccurrences(of: "\\r?\\n", with: " ", options: .regularExpression) - .replacingOccurrences(of: " +", with: " ", options: .regularExpression) - .trimmingCharacters(in: .whitespacesAndNewlines), + let selectedText = text, HighlightedTextValidator.isValid(text: selectedText) else { screenResult.userInteraction.selectedText = nil diff --git a/macos/Onit/Helpers/StringHelpers.swift b/macos/Onit/Helpers/StringHelpers.swift new file mode 100644 index 000000000..0c05601ad --- /dev/null +++ b/macos/Onit/Helpers/StringHelpers.swift @@ -0,0 +1,14 @@ +// +// StringHelpers.swift +// Onit +// +// Created by Loyd Kim on 7/18/25. +// + +struct StringHelpers { + static func removeWhiteSpaceAndNewLines(_ str: String) -> String { + return str.replacingOccurrences(of: "\\r?\\n", with: " ", options: .regularExpression) + .replacingOccurrences(of: " +", with: " ", options: .regularExpression) + .trimmingCharacters(in: .whitespacesAndNewlines) + } +} diff --git a/macos/Onit/UI/Prompt/Files/FileRow.swift b/macos/Onit/UI/Prompt/Files/FileRow.swift index a1c5e39ef..16950e223 100644 --- a/macos/Onit/UI/Prompt/Files/FileRow.swift +++ b/macos/Onit/UI/Prompt/Files/FileRow.swift @@ -120,11 +120,11 @@ struct FileRow: View { FlowLayout(spacing: 6) { PaperclipButton() - addHighlightedTextToContextButton addWindowToContextButton + addHighlightedTextToContextButton pendingWindowContextItems - highlightedTextContext addedWindowContextItems + highlightedTextContext } ocrDetailsLink @@ -183,7 +183,7 @@ extension FileRow { let trackedPendingInput = windowState.trackedPendingInput { ghostContextTag( - text: trackedPendingInput.selectedText, + text: StringHelpers.removeWhiteSpaceAndNewLines(trackedPendingInput.selectedText), iconView: Image(.text).addIconStyles(iconSize: 14), tooltip: "Add Highlighted Text To Context" ) { @@ -251,7 +251,7 @@ extension FileRow { private var highlightedTextContext: some View { if let pendingInput = windowState?.pendingInput { ContextTag( - text: pendingInput.selectedText, + text: StringHelpers.removeWhiteSpaceAndNewLines(pendingInput.selectedText), iconView: Image(.text).addIconStyles(iconSize: 14) ) { Defaults[.showHighlightedTextInput] = true diff --git a/macos/Onit/UI/Prompt/Input/InputBody.swift b/macos/Onit/UI/Prompt/Input/InputBody.swift index 2cd039ef0..c68bcaf52 100644 --- a/macos/Onit/UI/Prompt/Input/InputBody.swift +++ b/macos/Onit/UI/Prompt/Input/InputBody.swift @@ -9,12 +9,14 @@ import HighlightSwift import SwiftUI struct InputBody: View { + @Binding var inputExpanded: Bool @State var text: String? @State var textHeight: CGFloat = 0 var input: Input - init(input: Input) { + init(inputExpanded: Binding, input: Input) { + _inputExpanded = inputExpanded text = input.selectedText.count <= 500 ? input.selectedText : nil self.input = input } @@ -39,7 +41,7 @@ struct InputBody: View { } } } - .frame(height: height) + .frame(height: inputExpanded ? height : 0) .onChange(of: input.selectedText, initial: true) { DispatchQueue.main.async { text = input.selectedText @@ -72,6 +74,6 @@ struct InputBody: View { #if DEBUG #Preview { - InputBody(input: .sample) + InputBody(inputExpanded: .constant(true), input: .sample) } #endif diff --git a/macos/Onit/UI/Prompt/Input/InputButtons.swift b/macos/Onit/UI/Prompt/Input/InputButtons.swift index a1d7d68e4..8738a0e89 100644 --- a/macos/Onit/UI/Prompt/Input/InputButtons.swift +++ b/macos/Onit/UI/Prompt/Input/InputButtons.swift @@ -10,8 +10,11 @@ import SwiftUI struct InputButtons: View { @Environment(\.windowState) private var state + + @Binding var inputExpanded: Bool var input: Input + var isEditing: Bool var body: some View { Group { @@ -26,14 +29,18 @@ struct InputButtons: View { } Button { - Defaults[.showHighlightedTextInput] = false + if isEditing { + Defaults[.showHighlightedTextInput] = false + } else { + inputExpanded.toggle() + } } label: { Color.clear .frame(width: 20, height: 20) .overlay { Image(.smallChevRight) .renderingMode(.template) - .rotationEffect(.degrees(90)) + .rotationEffect(isEditing ? .degrees(90) : inputExpanded ? .degrees(90) : .zero) } } } @@ -43,6 +50,6 @@ struct InputButtons: View { #if DEBUG #Preview { - InputButtons(input: .sample) + InputButtons(inputExpanded: .constant(true), input: .sample, isEditing: true) } #endif diff --git a/macos/Onit/UI/Prompt/Input/InputTitle.swift b/macos/Onit/UI/Prompt/Input/InputTitle.swift index d1eb67f59..e099504be 100644 --- a/macos/Onit/UI/Prompt/Input/InputTitle.swift +++ b/macos/Onit/UI/Prompt/Input/InputTitle.swift @@ -8,7 +8,9 @@ import SwiftUI struct InputTitle: View { + @Binding var inputExpanded: Bool var input: Input + var isEditing: Bool var sourceString: String { guard let sourceText = input.application else { return "" } @@ -25,7 +27,7 @@ struct InputTitle: View { .appFont(.medium13) .textSelection(.enabled) Spacer() - InputButtons(input: input) + InputButtons(inputExpanded: $inputExpanded, input: input, isEditing: isEditing) } .foregroundStyle(.gray100) .padding(.horizontal, 12) @@ -34,5 +36,5 @@ struct InputTitle: View { } #Preview { - InputTitle(input: .sample) + InputTitle(inputExpanded: .constant(true), input: .sample, isEditing: true) } diff --git a/macos/Onit/UI/Prompt/Input/InputView.swift b/macos/Onit/UI/Prompt/Input/InputView.swift index fee07f57b..3361024fb 100644 --- a/macos/Onit/UI/Prompt/Input/InputView.swift +++ b/macos/Onit/UI/Prompt/Input/InputView.swift @@ -9,30 +9,29 @@ import Defaults import SwiftUI struct InputView: View { - @Default(.showHighlightedTextInput) var showHighlightedTextInput - var input: Input var isEditing: Bool = true + + @State var inputExpanded: Bool = false var body: some View { - if showHighlightedTextInput { - VStack(spacing: 0) { - InputTitle(input: input) - divider - InputBody(input: input) - } - .background { - RoundedRectangle(cornerRadius: 10) - .fill(.gray800) - .strokeBorder(.gray600) - } - .padding([.horizontal, .top], isEditing ? 12 : 0) + VStack(spacing: 0) { + InputTitle(inputExpanded: $inputExpanded, input: input, isEditing: isEditing) + divider + InputBody(inputExpanded: $inputExpanded, input: input) + } + .background { + RoundedRectangle(cornerRadius: 10) + .fill(.gray800) + .strokeBorder(.gray600) } + .padding([.horizontal, .top], isEditing ? 12 : 0) } var divider: some View { Color.gray600 .frame(height: 1) + .opacity(inputExpanded ? 1 : 0) } } diff --git a/macos/Onit/UI/Prompt/PromptCore.swift b/macos/Onit/UI/Prompt/PromptCore.swift index 2db00dc4b..5c2cd8e48 100644 --- a/macos/Onit/UI/Prompt/PromptCore.swift +++ b/macos/Onit/UI/Prompt/PromptCore.swift @@ -17,6 +17,7 @@ struct PromptCore: View { @Default(.mode) var mode @Default(.showTwoWeekProTrialEndedAlert) var showTwoWeekProTrialEndedAlert + @Default(.showHighlightedTextInput) var showHighlightedTextInput private var chats: [Chat] { let chatsFilteredByAccount = allChats @@ -70,8 +71,8 @@ struct PromptCore: View { var body: some View { VStack(spacing: 0) { - if let windowState = windowState, let pendingInput = windowState.pendingInput { - InputView(input: pendingInput) + if showHighlightedTextInput, let windowState = windowState, let pendingInput = windowState.pendingInput { + InputView(input: pendingInput, inputExpanded: true) } VStack(spacing: 6) { From d66a7cb01d984a3b319e4c710208b024bd8150c6 Mon Sep 17 00:00:00 2001 From: lk340 Date: Fri, 18 Jul 2025 11:29:35 -0400 Subject: [PATCH 6/7] Apply new UI update to text highlight experience * Increase max height of InputView. * `highlightedTextContext` button now shows border when InputView is active. Update ContextTag to make this type of update more reusable. * Update `inputString` output in InputTitle. * Update background, borders and spacing around InputView. * InputView is now inside of PromptCore to reflect design updates. * Update spacing in PromptCore. --- macos/Onit/UI/Components/ContextTag.swift | 10 ++++++++-- macos/Onit/UI/Prompt/Files/FileRow.swift | 8 ++++++-- macos/Onit/UI/Prompt/Input/InputBody.swift | 2 +- macos/Onit/UI/Prompt/Input/InputTitle.swift | 10 +++++----- macos/Onit/UI/Prompt/Input/InputView.swift | 11 ++++++----- macos/Onit/UI/Prompt/PromptCore.swift | 21 +++++++++++++-------- 6 files changed, 39 insertions(+), 23 deletions(-) diff --git a/macos/Onit/UI/Components/ContextTag.swift b/macos/Onit/UI/Components/ContextTag.swift index 2a19b3f42..7ec0e8876 100644 --- a/macos/Onit/UI/Components/ContextTag.swift +++ b/macos/Onit/UI/Components/ContextTag.swift @@ -14,9 +14,11 @@ struct ContextTag: View { private let background: Color private let hoverBackground: Color private let hasHoverBorder: Bool + private let hasDottedBorder: Bool private let maxWidth: CGFloat private let isLoading: Bool private let shouldFadeIn: Bool + private let borderColor: Color? private let iconBundleURL: URL? private let iconView: (any View)? private let caption: String? @@ -32,9 +34,11 @@ struct ContextTag: View { background: Color = .gray500, hoverBackground: Color = .gray400, hasHoverBorder: Bool = false, + hasDottedBorder: Bool = false, maxWidth: CGFloat = 155, isLoading: Bool = false, shouldFadeIn: Bool = false, + borderColor: Color? = nil, iconBundleURL: URL? = nil, iconView: (any View)? = nil, caption: String? = nil, @@ -49,9 +53,11 @@ struct ContextTag: View { self.background = background self.hoverBackground = hoverBackground self.hasHoverBorder = hasHoverBorder + self.hasDottedBorder = hasDottedBorder self.maxWidth = maxWidth self.isLoading = isLoading self.shouldFadeIn = shouldFadeIn + self.borderColor = borderColor self.iconBundleURL = iconBundleURL self.iconView = iconView self.caption = caption @@ -151,8 +157,8 @@ struct ContextTag: View { .addAnimation(dependency: isHoveredBody) .addBorder( cornerRadius: 4, - stroke: hasHoverBorder && isHoveredBody ? .T_4 : .clear, - dotted: true + stroke: hasHoverBorder && isHoveredBody ? .T_4 : borderColor ?? .clear, + dotted: hasDottedBorder ) .addButtonEffects( background: background, diff --git a/macos/Onit/UI/Prompt/Files/FileRow.swift b/macos/Onit/UI/Prompt/Files/FileRow.swift index 16950e223..80f4625c2 100644 --- a/macos/Onit/UI/Prompt/Files/FileRow.swift +++ b/macos/Onit/UI/Prompt/Files/FileRow.swift @@ -15,6 +15,8 @@ struct FileRow: View { @Default(.autoContextFromCurrentWindow) var autoContextFromCurrentWindow @Default(.autoAddHighlightedTextToContext) var autoAddHighlightedTextToContext + @Default(.autoContextFromHighlights) var autoContextFromHighlights + @Default(.showHighlightedTextInput) var showHighlightedTextInput @State private var ocrComparisonResult: OCRComparisonResult? = nil @State private var showOCRDetails = false @@ -166,6 +168,7 @@ extension FileRow { background: contextTagBackground, hoverBackground: contextTagHoverBackground, hasHoverBorder: true, + hasDottedBorder: true, shouldFadeIn: true, iconBundleURL: iconBundleURL, iconView: iconView, @@ -178,7 +181,7 @@ extension FileRow { @ViewBuilder private var addHighlightedTextToContextButton: some View { if accessibilityEnabled, - Defaults[.autoContextFromHighlights], + autoContextFromHighlights, let windowState = windowState, let trackedPendingInput = windowState.trackedPendingInput { @@ -252,9 +255,10 @@ extension FileRow { if let pendingInput = windowState?.pendingInput { ContextTag( text: StringHelpers.removeWhiteSpaceAndNewLines(pendingInput.selectedText), + borderColor: showHighlightedTextInput ? .gray400 : .clear, iconView: Image(.text).addIconStyles(iconSize: 14) ) { - Defaults[.showHighlightedTextInput] = true + showHighlightedTextInput = true } removeAction: { windowState?.pendingInput = nil } diff --git a/macos/Onit/UI/Prompt/Input/InputBody.swift b/macos/Onit/UI/Prompt/Input/InputBody.swift index c68bcaf52..be39ef6ce 100644 --- a/macos/Onit/UI/Prompt/Input/InputBody.swift +++ b/macos/Onit/UI/Prompt/Input/InputBody.swift @@ -22,7 +22,7 @@ struct InputBody: View { } var height: CGFloat { - min(textHeight, 73) + min(textHeight, 222) } var body: some View { diff --git a/macos/Onit/UI/Prompt/Input/InputTitle.swift b/macos/Onit/UI/Prompt/Input/InputTitle.swift index e099504be..a8ebc7b5f 100644 --- a/macos/Onit/UI/Prompt/Input/InputTitle.swift +++ b/macos/Onit/UI/Prompt/Input/InputTitle.swift @@ -14,24 +14,24 @@ struct InputTitle: View { var sourceString: String { guard let sourceText = input.application else { return "" } - return " - \(sourceText)" + return " [\(sourceText)]" } var inputString: String { - "Input\(sourceString)" + "From\(sourceString)" } var body: some View { - HStack(spacing: 8) { + HStack(alignment: .center, spacing: 8) { Text(inputString) - .appFont(.medium13) + .appFont(.medium12) .textSelection(.enabled) Spacer() InputButtons(inputExpanded: $inputExpanded, input: input, isEditing: isEditing) } .foregroundStyle(.gray100) .padding(.horizontal, 12) - .padding(.vertical, 8) + .padding(.vertical, 4) } } diff --git a/macos/Onit/UI/Prompt/Input/InputView.swift b/macos/Onit/UI/Prompt/Input/InputView.swift index 3361024fb..b3632da04 100644 --- a/macos/Onit/UI/Prompt/Input/InputView.swift +++ b/macos/Onit/UI/Prompt/Input/InputView.swift @@ -21,15 +21,16 @@ struct InputView: View { InputBody(inputExpanded: $inputExpanded, input: input) } .background { - RoundedRectangle(cornerRadius: 10) - .fill(.gray800) - .strokeBorder(.gray600) + RoundedRectangle(cornerRadius: 6) + .fill(.gray500) + .strokeBorder(.gray400) } - .padding([.horizontal, .top], isEditing ? 12 : 0) + .padding(.top, isEditing ? 6 : 0) + .padding([.horizontal], isEditing ? 8 : 0) } var divider: some View { - Color.gray600 + Color.gray400 .frame(height: 1) .opacity(inputExpanded ? 1 : 0) } diff --git a/macos/Onit/UI/Prompt/PromptCore.swift b/macos/Onit/UI/Prompt/PromptCore.swift index 5c2cd8e48..1250ff369 100644 --- a/macos/Onit/UI/Prompt/PromptCore.swift +++ b/macos/Onit/UI/Prompt/PromptCore.swift @@ -71,10 +71,6 @@ struct PromptCore: View { var body: some View { VStack(spacing: 0) { - if showHighlightedTextInput, let windowState = windowState, let pendingInput = windowState.pendingInput { - InputView(input: pendingInput, inputExpanded: true) - } - VStack(spacing: 6) { contextAndInput PromptCoreFooter( @@ -177,7 +173,11 @@ extension PromptCore { } private var contextAndInput: some View { - VStack(alignment: .leading, spacing: 8) { + VStack(alignment: .leading, spacing: 0) { + if showHighlightedTextInput, let windowState = windowState, let pendingInput = windowState.pendingInput { + InputView(input: pendingInput, inputExpanded: true) + } + if !appState.subscriptionPlanError.isEmpty { Text(appState.subscriptionPlanError) .styleText( @@ -187,10 +187,15 @@ extension PromptCore { ) } - FileRow(contextList: windowState?.pendingContextList ?? []) - textField + VStack(alignment: .leading, spacing: 8) { + FileRow(contextList: windowState?.pendingContextList ?? []) + textField + } + .padding(.top, 6) + .padding([.horizontal, .bottom], 12) } - .padding(12) + .padding(.top, 2) + .padding([.horizontal, .bottom], 0) .background(.gray800) .addGradientBorder( cornerRadius: 8, From c636755a7e75ea99e73ab881cddb556fa4c6f003 Mon Sep 17 00:00:00 2001 From: timl Date: Fri, 18 Jul 2025 10:39:06 -0700 Subject: [PATCH 7/7] small update to arrow rotation in finalcontextview --- macos/Onit/UI/Prompt/Input/InputButtons.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos/Onit/UI/Prompt/Input/InputButtons.swift b/macos/Onit/UI/Prompt/Input/InputButtons.swift index 8738a0e89..9502384f6 100644 --- a/macos/Onit/UI/Prompt/Input/InputButtons.swift +++ b/macos/Onit/UI/Prompt/Input/InputButtons.swift @@ -40,7 +40,7 @@ struct InputButtons: View { .overlay { Image(.smallChevRight) .renderingMode(.template) - .rotationEffect(isEditing ? .degrees(90) : inputExpanded ? .degrees(90) : .zero) + .rotationEffect(isEditing ? .degrees(90) : inputExpanded ? .degrees(-90) : .degrees(90)) } } }