Paste and drop images into every box that talks to an agent

This commit is contained in:
Ian Gulliver
2026-10-20 17:32:32 -07:00
parent 49f57b73fc
commit 15482473f7
9 changed files with 133 additions and 83 deletions
+47 -32
View File
@@ -1481,42 +1481,53 @@ private struct ReadyBubble: View {
.overlay(RoundedRectangle(cornerRadius: 10).stroke(current ? Palette.warn.opacity(0.55) : Palette.border))
}
private func imageKey(_ task: ByreTask) -> String {
"ready:\(task.id):\(when.timeIntervalSince1970)"
}
@ViewBuilder
private func otherEntry(_ task: ByreTask) -> some View {
if let text = other {
TextEditor(text: Binding(get: { text }, set: { other = $0 }))
.font(Fonts.font(13))
.scrollContentBackground(.hidden)
.writingToolsBehavior(.disabled)
.focused($editing)
.disabled(dismissing)
.onKeyPress(.return, phases: .down) { press in
let marked = (NSApp.keyWindow?.firstResponder as? NSTextView)?.hasMarkedText() ?? false
if press.modifiers.contains(.shift) || marked || trimmed(text).isEmpty {
return .ignored
let key = imageKey(task)
let hasImages = store.images.has(key)
if other != nil || hasImages {
let text = other ?? ""
VStack(alignment: .leading, spacing: 6) {
Thumbnails(key: key)
TextEditor(text: Binding(get: { text }, set: { other = $0 }))
.font(Fonts.font(13))
.scrollContentBackground(.hidden)
.writingToolsBehavior(.disabled)
.focused($editing)
.disabled(dismissing)
.onKeyPress(.return, phases: .down) { press in
let marked = (NSApp.keyWindow?.firstResponder as? NSTextView)?.hasMarkedText() ?? false
if press.modifiers.contains(.shift) || marked || (trimmed(text).isEmpty && !hasImages) {
return .ignored
}
dismiss(task, then: text)
return .handled
}
dismiss(task, then: text)
return .handled
}
.padding(.horizontal, 5)
.padding(.vertical, 6)
.frame(height: 66)
.background(Palette.raised, in: RoundedRectangle(cornerRadius: 6))
.overlay(alignment: .topLeading) {
if text.isEmpty {
Text("Something else to tell the agent; sending it dismisses this")
.foregroundStyle(Palette.muted)
.padding(.horizontal, 10)
.padding(.vertical, 6)
.allowsHitTesting(false)
.padding(.horizontal, 5)
.padding(.vertical, 6)
.frame(height: 66)
.background(Palette.raised, in: RoundedRectangle(cornerRadius: 6))
.overlay(alignment: .topLeading) {
if text.isEmpty {
Text("Something else to tell the agent; sending it dismisses this")
.foregroundStyle(Palette.muted)
.padding(.horizontal, 10)
.padding(.vertical, 6)
.allowsHitTesting(false)
}
}
}
.overlay(RoundedRectangle(cornerRadius: 6).stroke(editing ? Palette.accent : Palette.border))
.onChange(of: editing) {
if !editing && trimmed(other ?? "").isEmpty {
other = nil
.overlay(RoundedRectangle(cornerRadius: 6).stroke(editing ? Palette.accent : Palette.border))
.imageInput(key)
.onChange(of: editing) {
if !editing && trimmed(other ?? "").isEmpty && !store.images.has(key) {
other = nil
}
}
}
}
} else {
Text("Other…")
.foregroundStyle(Palette.muted)
@@ -1537,14 +1548,18 @@ private struct ReadyBubble: View {
private func dismiss(_ task: ByreTask, then text: String?) {
dismissing = true
let key = imageKey(task)
let imgs = store.images.take(key)
Task {
do {
let ids = text == nil ? nil : try await uploadImages(store.api, imgs)
_ = try await store.api.client.dismissReady(path: .init(taskId: task.id)).ok
if let text {
try await store.messageTask(task.id, text)
try await store.messageTask(task.id, text, images: ids)
other = nil
}
} catch {
store.images.restore(key, imgs)
overlays.showAlert(errorText(error))
}
dismissing = false