diff --git a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift index 94dca9da..240586bc 100644 --- a/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift +++ b/macos/Onit/Accessibility/Notifications/AccessibilityNotificationsManager.swift @@ -595,6 +595,24 @@ class AccessibilityNotificationsManager: ObservableObject { showDebug() } + + private func clearPendingInputsOnInvalidSelectedText() { + let currentHighlightedTextIsInView = PanelStateCoordinator.shared.state.selectedPendingInput == PanelStateCoordinator.shared.state.unpinnedPendingInput + + + /// Clearing `InputView` when it's showing the current highlighted text that's about to be cleared. + if currentHighlightedTextIsInView { + PanelStateCoordinator.shared.state.selectedPendingInput = nil + } + + PanelStateCoordinator.shared.state.unpinnedPendingInput = nil + PanelStateCoordinator.shared.state.trackedPendingInput = nil + } + + private func addSelectedTextToContextAndShowInView(_ input: Input) { + PanelStateCoordinator.shared.state.selectedPendingInput = input + PanelStateCoordinator.shared.state.unpinnedPendingInput = input + } private func processSelectedText(_ text: String?) { guard Defaults[.autoContextFromHighlights], @@ -602,8 +620,7 @@ class AccessibilityNotificationsManager: ObservableObject { HighlightedTextValidator.isValid(text: selectedText) else { screenResult.userInteraction.selectedText = nil - PanelStateCoordinator.shared.state.pendingInput = nil - PanelStateCoordinator.shared.state.trackedPendingInput = nil + clearPendingInputsOnInvalidSelectedText() return } @@ -612,7 +629,7 @@ class AccessibilityNotificationsManager: ObservableObject { let input = Input(selectedText: selectedText, application: currentSource ?? "") if Defaults[.autoAddHighlightedTextToContext] { - PanelStateCoordinator.shared.state.pendingInput = input + addSelectedTextToContextAndShowInView(input) } else { PanelStateCoordinator.shared.state.trackedPendingInput = input } diff --git a/macos/Onit/Assets.xcassets/Icons/pin.imageset/Contents.json b/macos/Onit/Assets.xcassets/Icons/pin.imageset/Contents.json new file mode 100644 index 00000000..3dad0e97 --- /dev/null +++ b/macos/Onit/Assets.xcassets/Icons/pin.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "pin.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/macos/Onit/Assets.xcassets/Icons/pin.imageset/pin.svg b/macos/Onit/Assets.xcassets/Icons/pin.imageset/pin.svg new file mode 100644 index 00000000..e14da339 --- /dev/null +++ b/macos/Onit/Assets.xcassets/Icons/pin.imageset/pin.svg @@ -0,0 +1,3 @@ + + + diff --git a/macos/Onit/Data/Fetching/ChatEndpointMessagesBuilder.swift b/macos/Onit/Data/Fetching/ChatEndpointMessagesBuilder.swift index 5f1ee13e..bad2da1b 100644 --- a/macos/Onit/Data/Fetching/ChatEndpointMessagesBuilder.swift +++ b/macos/Onit/Data/Fetching/ChatEndpointMessagesBuilder.swift @@ -12,7 +12,7 @@ import Foundation */ struct ChatEndpointMessagesBuilder { - static func user(instructions: [String], inputs: [Input?], files: [[URL]], autoContexts: [[String: String]], webSearchContexts: [[(title: String, content: String, source: String, url: URL?)]]) -> [String] { + static func user(instructions: [String], inputs: [[Input]], files: [[URL]], autoContexts: [[String: String]], webSearchContexts: [[(title: String, content: String, source: String, url: URL?)]]) -> [String] { var userMessages: [String] = [] for (index, instruction) in instructions.enumerated() { var message = "" @@ -46,13 +46,18 @@ struct ChatEndpointMessagesBuilder { message += "\n\(webSearchContext.content)" } } + + if !inputs[index].isEmpty { + let pendingInputs = inputs[index] - if let input = inputs[index], !input.selectedText.isEmpty { message += "\n\nUse the following selected text as context. When present, selected text should take priority over other context." - if let application = input.application { - message += "\n\nSelected Text from \(application): \(input.selectedText)" - } else { - message += "\n\nSelected Text: \(input.selectedText)" + + for input in pendingInputs { + if let application = input.application { + message += "\n\nSelected Text from \(application): \(input.selectedText)" + } else { + message += "\n\nSelected Text: \(input.selectedText)" + } } } diff --git a/macos/Onit/Data/Fetching/FetchingClient.swift b/macos/Onit/Data/Fetching/FetchingClient.swift index 4fd59edf..8cd13bad 100644 --- a/macos/Onit/Data/Fetching/FetchingClient.swift +++ b/macos/Onit/Data/Fetching/FetchingClient.swift @@ -28,7 +28,7 @@ actor FetchingClient { func chat( systemMessage: String, instructions: [String], - inputs: [Input?], + inputs: [[Input]], files: [[URL]], images: [[URL]], autoContexts: [[String: String]], @@ -75,7 +75,7 @@ actor FetchingClient { func localChat( systemMessage: String, instructions: [String], - inputs: [Input?], + inputs: [[Input]], files: [[URL]], images: [[URL]], autoContexts: [[String: String]], diff --git a/macos/Onit/Data/Fetching/Streaming/StreamingClient.swift b/macos/Onit/Data/Fetching/Streaming/StreamingClient.swift index 8950c4d9..305e6747 100644 --- a/macos/Onit/Data/Fetching/Streaming/StreamingClient.swift +++ b/macos/Onit/Data/Fetching/Streaming/StreamingClient.swift @@ -13,7 +13,7 @@ actor StreamingClient { func chat(systemMessage: String, instructions: [String], - inputs: [Input?], + inputs: [[Input]], files: [[URL]], images: [[URL]], autoContexts: [[String: String]], @@ -56,7 +56,7 @@ actor StreamingClient { func localChat(systemMessage: String, instructions: [String], - inputs: [Input?], + inputs: [[Input]], files: [[URL]], images: [[URL]], autoContexts: [[String: String]], diff --git a/macos/Onit/Data/Persistence/Prompt.swift b/macos/Onit/Data/Persistence/Prompt.swift index 7ca0ab13..e3250ff0 100644 --- a/macos/Onit/Data/Persistence/Prompt.swift +++ b/macos/Onit/Data/Persistence/Prompt.swift @@ -12,7 +12,7 @@ import SwiftData var instruction: String var timestamp: Date - var input: Input? + var inputs: [Input] var contextList: [Context] = [] // @Relationship(deleteRule: .cascade, inverse: \Response.prompt) @@ -30,12 +30,12 @@ import SwiftData var generationIndex = -1 init( - instruction: String, timestamp: Date, input: Input? = nil, + instruction: String, timestamp: Date, inputs: [Input] = [], contextList: [Context] = [], responses: [Response] = [] ) { self.instruction = instruction self.timestamp = timestamp - self.input = input + self.inputs = inputs self.contextList = contextList self.responses = responses self.generationState = GenerationState.done diff --git a/macos/Onit/Data/Structures/Input.swift b/macos/Onit/Data/Structures/Input.swift index 7024dcee..13219bff 100644 --- a/macos/Onit/Data/Structures/Input.swift +++ b/macos/Onit/Data/Structures/Input.swift @@ -7,7 +7,7 @@ import Foundation -struct Input: Codable, Equatable { +struct Input: Codable, Equatable, Hashable { var selectedText: String var application: String? } diff --git a/macos/Onit/KeyboardShortcuts/KeyboardShortcutsManager.swift b/macos/Onit/KeyboardShortcuts/KeyboardShortcutsManager.swift index 1d6b6e9e..37b706cf 100644 --- a/macos/Onit/KeyboardShortcuts/KeyboardShortcutsManager.swift +++ b/macos/Onit/KeyboardShortcuts/KeyboardShortcutsManager.swift @@ -106,8 +106,8 @@ struct KeyboardShortcutsManager { if state.panel != nil { if state.showContextMenuBrowserTabs { state.showContextMenuBrowserTabs = false - } else if state.pendingInput != nil { - state.pendingInput = nil + } else if state.hasHighlightedText { + state.clearHighlightedTextStates() } else { PanelStateCoordinator.shared.closePanel() } diff --git a/macos/Onit/UI/Components/ContextTag.swift b/macos/Onit/UI/Components/ContextTag.swift index 7ec0e887..41452cc2 100644 --- a/macos/Onit/UI/Components/ContextTag.swift +++ b/macos/Onit/UI/Components/ContextTag.swift @@ -21,10 +21,12 @@ struct ContextTag: View { private let borderColor: Color? private let iconBundleURL: URL? private let iconView: (any View)? + private let iconViewCornerIcon: ImageResource? private let caption: String? private let tooltip: String? private let errorDotColor: Color? private let action: (() -> Void)? + private let pinAction: (() -> Void)? private let removeAction: (() -> Void)? init( @@ -41,10 +43,12 @@ struct ContextTag: View { borderColor: Color? = nil, iconBundleURL: URL? = nil, iconView: (any View)? = nil, + iconViewCornerIcon: ImageResource? = nil, caption: String? = nil, tooltip: String? = nil, errorDotColor: Color? = nil, action: (() -> Void)? = nil, + pinAction: (() -> Void)? = nil, removeAction: (() -> Void)? = nil ) { self.text = text @@ -60,10 +64,12 @@ struct ContextTag: View { self.borderColor = borderColor self.iconBundleURL = iconBundleURL self.iconView = iconView + self.iconViewCornerIcon = iconViewCornerIcon self.caption = caption self.tooltip = tooltip self.errorDotColor = errorDotColor self.action = action + self.pinAction = pinAction self.removeAction = removeAction } @@ -78,6 +84,10 @@ struct ContextTag: View { return NSWorkspace.shared.icon(forFile: bundleUrl.path) } + private var hasHoverActions: Bool { + pinAction != nil || removeAction != nil + } + var body: some View { ZStack(alignment: .leading) { HStack(alignment: .center, spacing: 6) { @@ -103,7 +113,21 @@ struct ContextTag: View { } if let iconView = iconView { - AnyView(iconView) + ZStack(alignment: .bottomTrailing) { + AnyView(iconView) + + if let cornerIcon = iconViewCornerIcon { + ZStack(alignment: .center) { + Circle() + .fill(isHoveredBody ? hoverBackground : background) + .frame(width: 13, height: 13) + + Image(cornerIcon) + .addIconStyles(iconSize: 7.45) + } + .offset(x: 4, y: 4) + } + } } if isLoading { textView.shimmering() } @@ -120,16 +144,29 @@ struct ContextTag: View { } } - HStack(spacing: 0) { - Spacer() - - if let removeAction = removeAction { + if hasHoverActions { + HStack(spacing: 0) { + Spacer() + FadeHorizontal(color: hoverBackground) - removeButton(removeAction) + + HStack(spacing: 6) { + if let pinAction = pinAction { + hoverActionButton(icon: .pin) { + pinAction() + } + } + + if let removeAction = removeAction { + hoverActionButton(icon: .cross) { + removeAction() + } + } + } } + .frame(height: height) + .opacity(isHoveredBody ? 1 : 0) } - .frame(height: height) - .opacity(isHoveredBody ? 1 : 0) } .padding(.leading, 4) .padding(.trailing, 6) @@ -185,13 +222,13 @@ extension ContextTag { .addAnimation(dependency: [isHoveredBody, isHoveredRemove]) } - private func removeButton(_ removeAction: @escaping () -> Void) -> some View { + private func hoverActionButton(icon: ImageResource, hoverAction: @escaping () -> Void) -> some View { Button { - removeAction() + hoverAction() } label: { - Image(.cross) + Image(icon) .addIconStyles( - foregroundColor: isHoveredRemove ? .white : .gray100, + foregroundColor: isHoveredRemove ? Color.primary : .gray100, iconSize: 9 ) .addAnimation(dependency: isHoveredRemove) diff --git a/macos/Onit/UI/Content/ExternalTetheredButton.swift b/macos/Onit/UI/Content/ExternalTetheredButton.swift index 82163f38..9ac2c414 100644 --- a/macos/Onit/UI/Content/ExternalTetheredButton.swift +++ b/macos/Onit/UI/Content/ExternalTetheredButton.swift @@ -54,7 +54,7 @@ struct ExternalTetheredButton: View { } private var capturedHighlightedText: Bool { - return windowState?.pendingInput != nil + return windowState?.unpinnedPendingInput != nil || windowState?.trackedPendingInput != nil } private var foregroundWindowIcon: NSImage? { diff --git a/macos/Onit/UI/Panels/State/OnitPanelState+Chat.swift b/macos/Onit/UI/Panels/State/OnitPanelState+Chat.swift index 0615b813..14ec9aff 100644 --- a/macos/Onit/UI/Panels/State/OnitPanelState+Chat.swift +++ b/macos/Onit/UI/Panels/State/OnitPanelState+Chat.swift @@ -14,10 +14,18 @@ import SwiftData extension OnitPanelState { func createAndSavePrompt(accountId: Int?) { + var inputs = pinnedPendingInputs + + if let unpinnedPendingInput = unpinnedPendingInput, + !inputs.contains(unpinnedPendingInput) + { + inputs.append(unpinnedPendingInput) + } + let prompt = Prompt( instruction: pendingInstruction, timestamp: .now, - input: pendingInput, + inputs: inputs, contextList: pendingContextList ) @@ -62,7 +70,7 @@ extension OnitPanelState { currentChat?.prompts.append(prompt) currentPrompts?.append(prompt) pendingInstruction = "" - pendingInput = nil + clearHighlightedTextStates() do { try container.mainContext.save() @@ -91,7 +99,7 @@ extension OnitPanelState { let curInstruction = prompt.instruction var filesHistory: [[URL]] = [prompt.contextList.files] - var inputsHistory: [Input?] = [prompt.input] + var inputsHistory: [[Input]] = [prompt.inputs] var imagesHistory: [[URL]] = [prompt.contextList.images] var instructionsHistory: [String] = [curInstruction] var autoContextsHistory: [[String: String]] = [prompt.contextList.autoContexts] @@ -110,7 +118,7 @@ extension OnitPanelState { if response.type != .error { instructionsHistory.insert(currentPrompt!.instruction, at: 0) - inputsHistory.insert(currentPrompt!.input, at: 0) + inputsHistory.insert(currentPrompt!.inputs, at: 0) filesHistory.insert(currentPrompt!.contextList.files, at: 0) imagesHistory.insert(currentPrompt!.contextList.images, at: 0) autoContextsHistory.insert(currentPrompt!.contextList.autoContexts, at: 0) diff --git a/macos/Onit/UI/Panels/State/OnitPanelState+Input.swift b/macos/Onit/UI/Panels/State/OnitPanelState+Input.swift index 7aee9670..b003eccf 100644 --- a/macos/Onit/UI/Panels/State/OnitPanelState+Input.swift +++ b/macos/Onit/UI/Panels/State/OnitPanelState+Input.swift @@ -215,7 +215,7 @@ extension OnitPanelState { pendingInstruction = "" if (clearContext) { pendingContextList.removeAll() - pendingInput = nil + clearHighlightedTextStates() } focusText() diff --git a/macos/Onit/UI/Panels/State/OnitPanelState.swift b/macos/Onit/UI/Panels/State/OnitPanelState.swift index ece64a2f..037587f6 100644 --- a/macos/Onit/UI/Panels/State/OnitPanelState.swift +++ b/macos/Onit/UI/Panels/State/OnitPanelState.swift @@ -124,13 +124,29 @@ class OnitPanelState: NSObject { notifyDelegateInputsChange() } } - var pendingInput: Input? = nil { + + var selectedPendingInput: Input? = nil + + var trackedPendingInput: Input? = nil + + var unpinnedPendingInput: Input? = nil { didSet { notifyDelegateInputsChange() } } + + var pinnedPendingInputs: [Input] = [] - var trackedPendingInput: Input? = nil + var hasHighlightedText: Bool { + selectedPendingInput != nil || trackedPendingInput != nil || unpinnedPendingInput != nil || !pinnedPendingInputs.isEmpty + } + + func clearHighlightedTextStates() { + selectedPendingInput = nil + trackedPendingInput = nil + unpinnedPendingInput = nil + pinnedPendingInputs = [] + } var systemPromptId: String = SystemPrompt.outputOnly.id @@ -219,7 +235,7 @@ class OnitPanelState: NSObject { delegate.userInputsDidChange( instruction: pendingInstruction, contexts: pendingContextList, - input: pendingInput + input: unpinnedPendingInput ) } } diff --git a/macos/Onit/UI/Prompt/Files/FileRow.swift b/macos/Onit/UI/Prompt/Files/FileRow.swift index 80f4625c..3f0bb7d6 100644 --- a/macos/Onit/UI/Prompt/Files/FileRow.swift +++ b/macos/Onit/UI/Prompt/Files/FileRow.swift @@ -126,7 +126,8 @@ struct FileRow: View { addHighlightedTextToContextButton pendingWindowContextItems addedWindowContextItems - highlightedTextContext + unpinnedHighlightedTextContextItem + pinnedHighlightedTextContextItems } ocrDetailsLink @@ -145,6 +146,13 @@ struct FileRow: View { .onChange(of: debugManager.ocrComparisonResults) { _, newResults in updateOCRComparisonResult(from: newResults) } + .onChange(of: autoAddHighlightedTextToContext) { _, autoAddHighlightedText in + if autoAddHighlightedText { + morphHighlightedTextGhostTagToContextTag() + } else { + morphHighlightedTextUnpinnedContextTagToGhostTag() + } + } .onDisappear { windowState?.cleanUpPendingWindowContextTasks() } @@ -183,21 +191,15 @@ extension FileRow { if accessibilityEnabled, autoContextFromHighlights, let windowState = windowState, - let trackedPendingInput = windowState.trackedPendingInput + let trackedPendingInput = windowState.trackedPendingInput, + !windowState.pinnedPendingInputs.contains(trackedPendingInput) { ghostContextTag( text: StringHelpers.removeWhiteSpaceAndNewLines(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 - } + addTrackedGhostHighlightedTextToContext(trackedPendingInput) } } } @@ -251,16 +253,39 @@ extension FileRow { } @ViewBuilder - private var highlightedTextContext: some View { - if let pendingInput = windowState?.pendingInput { + private var unpinnedHighlightedTextContextItem: some View { + if let windowState = windowState, + let unpinnedPendingInput = windowState.unpinnedPendingInput, + !windowState.pinnedPendingInputs.contains(unpinnedPendingInput) + { ContextTag( - text: StringHelpers.removeWhiteSpaceAndNewLines(pendingInput.selectedText), - borderColor: showHighlightedTextInput ? .gray400 : .clear, + text: StringHelpers.removeWhiteSpaceAndNewLines(unpinnedPendingInput.selectedText), + borderColor: unpinnedPendingInput == windowState.selectedPendingInput ? .gray400 : .clear, iconView: Image(.text).addIconStyles(iconSize: 14) ) { - showHighlightedTextInput = true + showHighlightedText(input: unpinnedPendingInput) + } pinAction: { + pinUnpinnedHighlightedText(unpinnedPendingInput) } removeAction: { - windowState?.pendingInput = nil + removeHighlightedText() + } + } + } + + @ViewBuilder + private var pinnedHighlightedTextContextItems: some View { + if let windowState = windowState { + ForEach(windowState.pinnedPendingInputs, id: \.self) { pinnedPendingInput in + ContextTag( + text: pinnedPendingInput.selectedText, + borderColor: pinnedPendingInput == windowState.selectedPendingInput ? .gray400 : .clear, + iconView: Image(.text).addIconStyles(iconSize: 14), + iconViewCornerIcon: .pin + ) { + showHighlightedText(input: pinnedPendingInput) + } removeAction: { + removeHighlightedText(pinnedPendingInput) + } } } } @@ -294,6 +319,86 @@ extension FileRow { } } +// MARK: - Private Functions + +extension FileRow { + private func morphHighlightedTextGhostTagToContextTag() { + guard let windowState = windowState else { return } + + windowState.selectedPendingInput = windowState.trackedPendingInput + windowState.unpinnedPendingInput = windowState.trackedPendingInput + windowState.trackedPendingInput = nil + } + + private func morphHighlightedTextUnpinnedContextTagToGhostTag() { + guard let windowState = windowState else { return } + + windowState.trackedPendingInput = windowState.unpinnedPendingInput + windowState.selectedPendingInput = nil + windowState.unpinnedPendingInput = nil + } + + private func pinHighlightedText(_ windowState: OnitPanelState, _ input: Input) { + if !windowState.pinnedPendingInputs.contains(input) { + windowState.pinnedPendingInputs.append(input) + } + } + + private func addTrackedGhostHighlightedTextToContext(_ trackedPendingInput: Input) { + guard let windowState = windowState else { return } + + pinHighlightedText(windowState, trackedPendingInput) + + windowState.selectedPendingInput = trackedPendingInput + windowState.trackedPendingInput = nil + } + + private func pinUnpinnedHighlightedText(_ unpinnedPendingInput: Input) { + guard let windowState = windowState else { return } + + pinHighlightedText(windowState, unpinnedPendingInput) + + windowState.unpinnedPendingInput = nil + } + + private func showHighlightedText(input: Input) { + guard let windowState = windowState else { return } + + windowState.selectedPendingInput = input + Defaults[.showHighlightedTextInput] = true + } + + private func removeUnpinnedHighlightedTextItem(_ windowState: OnitPanelState) { + let showingUnpinnedHighlightedText = windowState.selectedPendingInput == windowState.unpinnedPendingInput + + /// Removing `InputView` from the UI if it matches the removed unpinned highlighted text. + if showingUnpinnedHighlightedText { + windowState.selectedPendingInput = nil + } + + windowState.unpinnedPendingInput = nil + } + private func removePinnedHighlightedTextItem(_ windowState: OnitPanelState, _ pinnedPendingInput: Input) { + let showingPinnedHighlightedText = windowState.selectedPendingInput == pinnedPendingInput + + /// Removing `InputView` from the UI if it matches the removed pinned highlighted text. + if showingPinnedHighlightedText { + windowState.selectedPendingInput = nil + } + + windowState.pinnedPendingInputs.removeAll { $0 == pinnedPendingInput } + } + private func removeHighlightedText(_ pinnedPendingInput: Input? = nil) { + guard let windowState = windowState else { return } + + if let pinnedPendingInput = pinnedPendingInput { + removePinnedHighlightedTextItem(windowState, pinnedPendingInput) + } else { + removeUnpinnedHighlightedTextItem(windowState) + } + } +} + // MARK: - OCR Comparison Helpers extension FileRow { diff --git a/macos/Onit/UI/Prompt/FinalContextView.swift b/macos/Onit/UI/Prompt/FinalContextView.swift index 0fc57b05..be38c41a 100644 --- a/macos/Onit/UI/Prompt/FinalContextView.swift +++ b/macos/Onit/UI/Prompt/FinalContextView.swift @@ -25,7 +25,7 @@ struct FinalContextView: View { let prompt: Prompt var usingContextOrInput: Bool { - usingContext || prompt.input != nil + usingContext || !prompt.inputs.isEmpty } var usingContext: Bool { @@ -164,8 +164,10 @@ struct FinalContextView: View { .buttonStyle(.plain) if isExpanded { - if let input = prompt.input { - InputView(input: input, isEditing: false) + if !prompt.inputs.isEmpty { + ForEach(prompt.inputs, id: \.self) { input in + InputView(input: input, isEditing: false) + } } if usingContext { diff --git a/macos/Onit/UI/Prompt/Generated/GeneratedView.swift b/macos/Onit/UI/Prompt/Generated/GeneratedView.swift index ecac38a4..61e05b20 100644 --- a/macos/Onit/UI/Prompt/Generated/GeneratedView.swift +++ b/macos/Onit/UI/Prompt/Generated/GeneratedView.swift @@ -36,7 +36,7 @@ struct GeneratedView: View { #if DEBUG #Preview { var prompt = Prompt.sample - prompt.input = Input(selectedText: "blablabla", application: "Xcode") + prompt.inputs = [Input(selectedText: "blablabla", application: "Xcode")] return GeneratedView(prompt: prompt) } diff --git a/macos/Onit/UI/Prompt/Input/InputButtons.swift b/macos/Onit/UI/Prompt/Input/InputButtons.swift index 8738a0e8..4a89f8e0 100644 --- a/macos/Onit/UI/Prompt/Input/InputButtons.swift +++ b/macos/Onit/UI/Prompt/Input/InputButtons.swift @@ -18,9 +18,11 @@ struct InputButtons: View { var body: some View { Group { - if let state = state, input == state.pendingInput { + if let state = state, input == state.selectedPendingInput { Button { - state.pendingInput = nil + clearHighlightedText(state: state) + + state.selectedPendingInput = nil } label: { Image(.smallRemove) .renderingMode(.template) @@ -46,6 +48,14 @@ struct InputButtons: View { } .foregroundStyle(.gray200) } + + // MARK: - Private Functions + + private func clearHighlightedText(state: OnitPanelState) { + if state.selectedPendingInput == state.unpinnedPendingInput { + state.unpinnedPendingInput = nil + } + } } #if DEBUG diff --git a/macos/Onit/UI/Prompt/PromptCore.swift b/macos/Onit/UI/Prompt/PromptCore.swift index 1250ff36..d82ddbed 100644 --- a/macos/Onit/UI/Prompt/PromptCore.swift +++ b/macos/Onit/UI/Prompt/PromptCore.swift @@ -174,8 +174,8 @@ extension PromptCore { private var contextAndInput: some View { VStack(alignment: .leading, spacing: 0) { - if showHighlightedTextInput, let windowState = windowState, let pendingInput = windowState.pendingInput { - InputView(input: pendingInput, inputExpanded: true) + if showHighlightedTextInput, let windowState = windowState, let selectedPendingInput = windowState.selectedPendingInput { + InputView(input: selectedPendingInput, inputExpanded: true) } if !appState.subscriptionPlanError.isEmpty {