-
Notifications
You must be signed in to change notification settings - Fork 39
Text Highlight: Pin #341
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feature/text-highlight/updated-input-view-behavior
Are you sure you want to change the base?
Text Highlight: Pin #341
Changes from all commits
49d910b
2087d26
3a0dcdd
7e8507f
916f84b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| { | ||
| "images" : [ | ||
| { | ||
| "filename" : "pin.svg", | ||
| "idiom" : "universal" | ||
| } | ||
| ], | ||
| "info" : { | ||
| "author" : "xcode", | ||
| "version" : 1 | ||
| }, | ||
| "properties" : { | ||
| "preserves-vector-representation" : true | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,7 +12,7 @@ import SwiftData | |
|
|
||
| var instruction: String | ||
| var timestamp: Date | ||
| var input: Input? | ||
| var inputs: [Input] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Were you able to get this code to run on your computer? It's insta-crashing for me. We can't change anything in any of the SwiftData model objects (Prompt, Chat, SystemPrompt, etc - search for the @model decorator) without writing a migration. In this case, transitioning from an optional Input to a non-optional input array results in a fatal error. Many of my old chats have a value of 'nil' for the 'input', which can't be converted into a non-optional array. To reproduce the issue, checkout an older version of the app and send a prompt with no Input. Then, check out this branch and run it. You'll get a fatal error. You can see some examples of migrations I've written in the past ('maybeUpdatePromptPriorInstructions' and 'maybeCleanupHangingPromptReferences') in SwiftDataContainer. It's a pain in the ass, but you have to do it if you want to change anything in Swift Data. |
||
| 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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting. I wonder if this should only clear out the most recent, instead of all of them. It seems like it might be irritating if you hit Escape by accident and it clears your entire context window. Without really playing with it, my intuition is that we should:
|
||
| } else { | ||
| PanelStateCoordinator.shared.closePanel() | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,10 +21,12 @@ struct ContextTag: View { | |
| private let borderColor: Color? | ||
| private let iconBundleURL: URL? | ||
| private let iconView: (any View)? | ||
| private let iconViewCornerIcon: ImageResource? | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I'd probably call this the 'leftIcon' and 'rightIcon' instead of 'iconView' and 'iconViewCornerIcon'. That's what this new field is, right? It's a new right-aligned icon? |
||
| 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, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Calling this 'pinAction' makes it specific to pinning, which defeats the purpose of a general name like 'iconViewCornerIcon'. If we're only supporting a pin action, there's no need to pass in the CornerIcon; it can just be hardcoded. Alternatively, if we want to make this general, we should give both new fields corresponding generic names, like "rightIcon" and "rightIconAction." Either approach works for me since we have no immediate plans for anything other than pinning, but it should be one or the other. |
||
| 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) | ||
|
Comment on lines
+119
to
+128
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From reading the code, I can't tell the difference between this icon and the hoverActionButton added below. Do we need both? |
||
| } | ||
| } | ||
| } | ||
|
|
||
| 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) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also update this to respect the ordering of the selected text. For example, if there's 4 highlighted text sections pinned and they send a prompt like "rephrase this", I think they're probably referring to the most recently added highlight.
So this prompt should be modified to say like "the selected text should be given priority in the order they appear", and then we need to make sure that the most recent highlighted text is added first (which probably isn't the default, since we most likely use 'append').