The card window's ⌘V drops its picture branch — the attachments header now offers one instead
Raw image data on a card window's ⌘V was a keyboard shortcut with no visible trigger; the sidebar's Attachments header now grows a quiet control — beside the existing add affordance, present only while the pasteboard holds a picture this card could take — that pastes it through the exact seam the retired branch used (ClipboardStore.pasteImage(intoCard:in:), the board's "Paste Image into Card" row's own call). The file-URL branch stays on ⌘V; a Finder copy is still unambiguous. CardBodyTextView's paste-yield mechanism needed no change at all — it forwards by capability, not by picture-specific logic, so a screenshot ⌘V with the body editor focused is now a genuine no-op there, served by the new control instead. The pasteboard's re-read gains a fourth checkpoint — a window becoming key — alongside menu-tracking, ⌘-down and app activation: a persistent visible control has to read true continuously while its window is frontmost, not only at the instant a menu or chord probes it. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -80,7 +80,14 @@ struct CardAttachmentsSection: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: CardWindowMetrics.attachmentRowPadding(bodyPointSize: pointSize)) {
|
||||
CardSidebarSectionHeader(title: "Attachments") { addAffordance }
|
||||
CardSidebarSectionHeader(title: "Attachments") {
|
||||
HStack(spacing: 8) {
|
||||
if attachments.canPasteImage?() == true {
|
||||
pasteImageAffordance
|
||||
}
|
||||
addAffordance
|
||||
}
|
||||
}
|
||||
|
||||
if names.isEmpty {
|
||||
emptyHint
|
||||
@@ -96,6 +103,30 @@ struct CardAttachmentsSection: View {
|
||||
|
||||
// MARK: - Header
|
||||
|
||||
/// **The paste-image affordance** — the ⌘V image-data branch's replacement (04-interactions.md ▸
|
||||
/// Clipboard, re-ruled 2026-08-09): present only while the pasteboard holds a picture this card
|
||||
/// could take, rather than a permanent row that would spend most of its life disabled. Its
|
||||
/// visibility and its tap read and call the exact same seam, `CardAttachments.canPasteImage`/
|
||||
/// `.pasteImage` — the header's own `CardPasteImageAffordance.isVisible` — so the button can never
|
||||
/// promise a paste it does not deliver.
|
||||
///
|
||||
/// **Present-or-absent, the hero rows' own posture** (below): a disabled picture-paste icon
|
||||
/// sitting in this header the other 99% of the time a card window is open would be furniture
|
||||
/// nobody reads, where "it showed up" is the whole point of the control replacing a keyboard
|
||||
/// shortcut nobody could see.
|
||||
private var pasteImageAffordance: some View {
|
||||
Button {
|
||||
attachments.pasteImage?()
|
||||
} label: {
|
||||
Image(systemName: "photo.badge.plus")
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.help("Paste the copied picture as an attachment")
|
||||
.accessibilityLabel("Paste Image as Attachment")
|
||||
}
|
||||
|
||||
/// The header's **quiet add affordance** — "a pointer twin of File ▸ Add Attachment…, no
|
||||
/// separate behavior" (11-command-nexus.md), which is why it calls the same method the menu row
|
||||
/// does rather than opening a panel of its own.
|
||||
|
||||
Reference in New Issue
Block a user