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:
@@ -66,6 +66,19 @@ private func purgeFromTrash(_ url: URL?) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
/// A `ClipboardStore` for the tests below that only need to satisfy `configureAttachments`'s
|
||||
/// paste-image seam — the affordance itself is `PasteImageTests`' subject, not this file's. A
|
||||
/// `FakePasteboard` (`ClipboardTests.swift`'s double, never the machine's) and a throwaway staging
|
||||
/// directory the caller tears down alongside its board fixture.
|
||||
@MainActor
|
||||
private func makeScratchClipboard() throws -> (clipboard: ClipboardStore, staging: URL) {
|
||||
let staging = FileManager.default.temporaryDirectory
|
||||
.appendingPathComponent("CardAttachmentsTests-\(UUID().uuidString)", isDirectory: true)
|
||||
try FileManager.default.createDirectory(at: staging, withIntermediateDirectories: true)
|
||||
let clipboard = ClipboardStore(pasteboard: FakePasteboard(), stagingRoot: staging, observesActivation: false)
|
||||
return (clipboard, staging)
|
||||
}
|
||||
|
||||
/// The same, by name, for the paths that do not hand the resulting URL back (the store's).
|
||||
@discardableResult
|
||||
private func purgeFromTrash(named name: String) -> Bool {
|
||||
@@ -246,9 +259,13 @@ struct AddAttachmentTargetTests {
|
||||
defer { sources.tearDown() }
|
||||
let shot = try sources.file("shot.png", Data([0x89, 0x50]))
|
||||
let store = try BoardStore(rootURL: fixture.root)
|
||||
let (clipboard, staging) = try makeScratchClipboard()
|
||||
defer { try? FileManager.default.removeItem(at: staging) }
|
||||
|
||||
let attachments = CardAttachments()
|
||||
CardWindowHost.configureAttachments(attachments, store: store, cardID: card1, undo: CardWindowUndo())
|
||||
CardWindowHost.configureAttachments(
|
||||
attachments, store: store, cardID: card1, undo: CardWindowUndo(), clipboard: clipboard
|
||||
)
|
||||
attachments.importFiles?([shot])
|
||||
|
||||
#expect(try fixture.data("\(Ident.lane1)/\(Ident.card1)/attachments/shot.png") == Data([0x89, 0x50]))
|
||||
@@ -264,9 +281,13 @@ struct AddAttachmentTargetTests {
|
||||
try fixture.file("\(Ident.lane1)/\(Ident.card1)/attachments/\(doomed)", Data([0x01]))
|
||||
try fixture.file("\(Ident.lane1)/\(Ident.card2)/attachments/\(doomed)", Data([0x02]))
|
||||
let store = try BoardStore(rootURL: fixture.root)
|
||||
let (clipboard, staging) = try makeScratchClipboard()
|
||||
defer { try? FileManager.default.removeItem(at: staging) }
|
||||
|
||||
let attachments = CardAttachments()
|
||||
CardWindowHost.configureAttachments(attachments, store: store, cardID: card1, undo: CardWindowUndo())
|
||||
CardWindowHost.configureAttachments(
|
||||
attachments, store: store, cardID: card1, undo: CardWindowUndo(), clipboard: clipboard
|
||||
)
|
||||
attachments.removeFile?(doomed)
|
||||
|
||||
#expect(try fixture.entryNames("\(Ident.lane1)/\(Ident.card1)/attachments").isEmpty)
|
||||
@@ -283,9 +304,13 @@ struct AddAttachmentTargetTests {
|
||||
defer { fixture.tearDown() }
|
||||
try fixture.file("\(Ident.lane1)/\(Ident.card1)/attachments/shot.png", Data([0x01]))
|
||||
let store = try BoardStore(rootURL: fixture.root)
|
||||
let (clipboard, staging) = try makeScratchClipboard()
|
||||
defer { try? FileManager.default.removeItem(at: staging) }
|
||||
|
||||
let attachments = CardAttachments()
|
||||
CardWindowHost.configureAttachments(attachments, store: store, cardID: card1, undo: CardWindowUndo())
|
||||
CardWindowHost.configureAttachments(
|
||||
attachments, store: store, cardID: card1, undo: CardWindowUndo(), clipboard: clipboard
|
||||
)
|
||||
attachments.cardFolder = cardFolder(fixture)
|
||||
attachments.names = ["shot.png"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user