Paste and drop images into every box that talks to an agent
This commit is contained in:
@@ -93,6 +93,10 @@ final class ImageDrafts {
|
||||
byKey[key] ?? []
|
||||
}
|
||||
|
||||
func has(_ key: String) -> Bool {
|
||||
byKey[key] != nil
|
||||
}
|
||||
|
||||
func remove(_ key: String, _ id: UUID) {
|
||||
let rest = images(key).filter { $0.id != id }
|
||||
byKey[key] = rest.isEmpty ? nil : rest
|
||||
@@ -218,6 +222,38 @@ struct PasteboardCommands: Commands {
|
||||
}
|
||||
}
|
||||
|
||||
private struct ImageInput: ViewModifier {
|
||||
let key: String
|
||||
@Environment(Store.self) private var store
|
||||
@Environment(Overlays.self) private var overlays
|
||||
@ViewState private var targeted = false
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.focusedValue(\.composer, ComposerTarget(paste: { pb in store.images.add(key, pasteboard: pb, overlays) }))
|
||||
.overlay {
|
||||
if targeted {
|
||||
RoundedRectangle(cornerRadius: 6).stroke(Palette.accent)
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
Color.clear
|
||||
.contentShape(Rectangle())
|
||||
.allowsHitTesting(false)
|
||||
.onDrop(of: [.image, .fileURL], isTargeted: $targeted) { providers in
|
||||
store.images.add(key, providers: providers, overlays)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func imageInput(_ key: String) -> some View {
|
||||
modifier(ImageInput(key: key))
|
||||
}
|
||||
}
|
||||
|
||||
struct Thumbnails: View {
|
||||
let key: String
|
||||
@Environment(Store.self) private var store
|
||||
@@ -282,7 +318,6 @@ struct ComposerView: View {
|
||||
@Environment(\.inRightPane) private var inRightPane
|
||||
@ViewState private var text = ""
|
||||
@ViewState private var pick: String?
|
||||
@ViewState private var targeted = false
|
||||
@ViewState private var shownOverride: Bool?
|
||||
@FocusState private var focused: Bool
|
||||
|
||||
@@ -467,7 +502,6 @@ struct ComposerView: View {
|
||||
.scrollContentBackground(.hidden)
|
||||
.writingToolsBehavior(.disabled)
|
||||
.focused($focused)
|
||||
.focusedValue(\.composer, ComposerTarget(paste: { pb in store.images.add(draftKey, pasteboard: pb, overlays) }))
|
||||
.onKeyPress(.return, phases: .down) { press in
|
||||
let marked = (NSApp.keyWindow?.firstResponder as? NSTextView)?.hasMarkedText() ?? false
|
||||
if press.modifiers.contains(.shift) || marked {
|
||||
@@ -489,16 +523,8 @@ struct ComposerView: View {
|
||||
.allowsHitTesting(false)
|
||||
}
|
||||
}
|
||||
.overlay(RoundedRectangle(cornerRadius: 6).stroke(targeted ? Palette.accent : inRightPane ? accent : focused ? Palette.accent : Palette.border, lineWidth: inRightPane && focused ? 2 : 1))
|
||||
.overlay {
|
||||
Color.clear
|
||||
.contentShape(Rectangle())
|
||||
.allowsHitTesting(false)
|
||||
.onDrop(of: [.image, .fileURL], isTargeted: $targeted) { providers in
|
||||
store.images.add(draftKey, providers: providers, overlays)
|
||||
return true
|
||||
}
|
||||
}
|
||||
.overlay(RoundedRectangle(cornerRadius: 6).stroke(inRightPane ? accent : focused ? Palette.accent : Palette.border, lineWidth: inRightPane && focused ? 2 : 1))
|
||||
.imageInput(draftKey)
|
||||
}
|
||||
|
||||
private func reportDraft() {
|
||||
@@ -510,7 +536,7 @@ struct ComposerView: View {
|
||||
private func submit(_ m: ModelState) {
|
||||
let value = text
|
||||
let images = store.images
|
||||
if trimmed(value).isEmpty && images.images(draftKey).isEmpty {
|
||||
if trimmed(value).isEmpty && !images.has(draftKey) {
|
||||
return
|
||||
}
|
||||
guard let drafts = store.drafts else {
|
||||
|
||||
Reference in New Issue
Block a user