From 5b1015d00632797d87a5b1b0a7c65e51326e4d2a Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Wed, 21 Oct 2026 22:41:00 -0700 Subject: [PATCH] Allow pasting and attaching images in question Other fields on iPhone --- mac/Sources/Byre/Attention.swift | 45 ++++++++++++++++++++------------ mac/Sources/Byre/Composer.swift | 26 +++++++++++++++--- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/mac/Sources/Byre/Attention.swift b/mac/Sources/Byre/Attention.swift index ec190ce..1a06f33 100644 --- a/mac/Sources/Byre/Attention.swift +++ b/mac/Sources/Byre/Attention.swift @@ -84,6 +84,34 @@ struct OtherEntry: View { var body: some View { let focused = focus.wrappedValue == otherStop + HStack(alignment: .bottom, spacing: 8) { + entry(focused) + #if os(iOS) + ComposerMenu(key: key, photos: true) + .disabled(busy) + #endif + } + .onAppear { + text = store.drafts?.textAnswer(key) ?? "" + } + .onChange(of: text) { + if text != store.drafts?.textAnswer(key) ?? "" { + store.drafts?.setTextAnswer(key, text) + } + store.paneFocus.setDraft(key, !trimmed(text).isEmpty) + } + .onChange(of: store.drafts?.textAnswer(key)) { _, new in + if (new ?? "") != text { + text = new ?? "" + } + } + .onDisappear { + store.paneFocus.setDraft(key, false) + } + } + + @ViewBuilder + private func entry(_ focused: Bool) -> some View { Group { if otherOpen(store, key) { VStack(alignment: .leading, spacing: 6) { @@ -135,23 +163,6 @@ struct OtherEntry: View { .onHover { hover = $0 } } } - .onAppear { - text = store.drafts?.textAnswer(key) ?? "" - } - .onChange(of: text) { - if text != store.drafts?.textAnswer(key) ?? "" { - store.drafts?.setTextAnswer(key, text) - } - store.paneFocus.setDraft(key, !trimmed(text).isEmpty) - } - .onChange(of: store.drafts?.textAnswer(key)) { _, new in - if (new ?? "") != text { - text = new ?? "" - } - } - .onDisappear { - store.paneFocus.setDraft(key, false) - } } private func open() { diff --git a/mac/Sources/Byre/Composer.swift b/mac/Sources/Byre/Composer.swift index 28f1485..2371670 100644 --- a/mac/Sources/Byre/Composer.swift +++ b/mac/Sources/Byre/Composer.swift @@ -267,12 +267,12 @@ extension View { } #if os(iOS) -private struct ComposerMenu: View { +struct ComposerMenu: View { let key: String let photos: Bool - let models: [String] - let selected: String - let choose: @MainActor @Sendable (String) -> Void + var models: [String] = [] + var selected = "" + var choose: @MainActor @Sendable (String) -> Void = { _ in } @Environment(Store.self) private var store @Environment(Overlays.self) private var overlays @ViewState private var picking = false @@ -286,6 +286,11 @@ private struct ComposerMenu: View { } label: { Label("Attach photos", systemImage: "photo") } + Button { + paste() + } label: { + Label("Paste image", systemImage: "doc.on.clipboard") + } } if !models.isEmpty { Picker("Model for the next turn", selection: Binding(get: { selected }, set: choose)) { @@ -316,6 +321,19 @@ private struct ComposerMenu: View { } } } + + private func paste() { + let images = UIPasteboard.general.images ?? [] + if images.isEmpty { + overlays.showAlert("The clipboard has no image.") + return + } + for image in images { + if let data = image.jpegData(compressionQuality: 0.85) { + store.images.add(key, data, name: nil, overlays) + } + } + } } #endif