WIP: add plain text paste mode

This commit is contained in:
Akshay Kolli
2026-06-30 02:04:12 -07:00
parent 0567c62310
commit 475e387f7f
8 changed files with 287 additions and 1 deletions

View File

@@ -157,4 +157,15 @@ final class ClipboardPanelControllerTests: XCTestCase {
XCTAssertNil(ClipboardPanelController.commandShortcutAction(forKeyCode: 8, modifiers: [.command, .shift]))
XCTAssertNil(ClipboardPanelController.commandShortcutAction(forKeyCode: 9, modifiers: .command))
}
func testModifiedShortcutsMapToPlainTextActions() {
XCTAssertEqual(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 8, modifiers: [.command, .shift]), .copyPlainText)
XCTAssertEqual(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 9, modifiers: [.command, .shift]), .pastePlainText)
}
func testModifiedShortcutsRequireCommandShiftOnly() {
XCTAssertNil(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 8, modifiers: .command))
XCTAssertNil(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 8, modifiers: [.command, .option, .shift]))
XCTAssertNil(ClipboardPanelController.modifiedShortcutAction(forKeyCode: 31, modifiers: [.command, .shift]))
}
}

View File

@@ -271,6 +271,39 @@ final class ClipboardPanelViewModelTests: XCTestCase {
XCTAssertEqual(NSPasteboard.general.string(forType: .URL), item.payload)
}
func testCopySelectedPlainTextWritesOnlyStringRepresentation() {
let settings = makeSettings()
let cacheService = makeCacheService()
let store = makeStore(settings: settings, cacheService: cacheService)
let item = ClipboardItem(
id: UUID(),
kind: .url,
displayText: "Example",
payload: "https://example.com",
payloadHash: hash("https://example.com"),
createdAt: Date(timeIntervalSince1970: 200),
lastUsedAt: Date(timeIntervalSince1970: 200),
useCount: 0,
sourceApp: nil,
imagePath: nil,
thumbnailPath: nil
)
store.upsert(item)
store.flushPersistenceForTesting()
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: cacheService)
waitForVisibleItems(in: viewModel, count: 1)
viewModel.copySelectedPlainText()
store.flushPersistenceForTesting()
XCTAssertEqual(viewModel.statusMessage, "Copied Plain Text")
XCTAssertEqual(NSPasteboard.general.string(forType: .string), item.payload)
XCTAssertNil(NSPasteboard.general.string(forType: .URL))
XCTAssertEqual(store.items.first?.id, item.id)
XCTAssertEqual(store.items.first?.useCount, 1)
}
func testUpdateSelectedTextRefreshesVisibleItemAndSearch() {
let settings = makeSettings()
let cacheService = makeCacheService()

View File

@@ -349,7 +349,7 @@ final class ClipboardPanelViewTests: XCTestCase {
XCTAssertEqual(
fixture.view.debugFirstCardMenuTitles,
["Paste", "Copy", "Quick Look", "Pin", "Add to Collection", "-", "Open", "Reveal in Finder", "-", "Delete"]
["Paste", "Copy", "Paste Plain Text", "Copy Plain Text", "Quick Look", "Pin", "Add to Collection", "-", "Open", "Reveal in Finder", "-", "Delete"]
)
}

View File

@@ -124,6 +124,41 @@ final class PasteActionServiceTests: XCTestCase {
XCTAssertEqual(NSPasteboard.general.string(forType: .string), "Paste into target")
}
func testAutomaticPlainTextPasteActivatesTargetAndSchedulesKeyboardPasteWhenPermissionGranted() throws {
var activatedProcessID: pid_t?
let targetApp = try makeRunningTargetApp()
var didScheduleKeyboardPaste = false
let service = PasteActionService(
accessibilityPermissionProvider: { true },
targetActivator: { app in
activatedProcessID = app.processIdentifier
return true
},
keyboardPasteScheduler: { _ in
didScheduleKeyboardPaste = true
}
)
let item = ClipboardItem(
id: UUID(),
kind: .url,
displayText: "Apple",
payload: "https://apple.com",
payloadHash: "hash",
createdAt: Date(),
lastUsedAt: Date(),
useCount: 0,
sourceApp: nil,
imagePath: nil,
thumbnailPath: nil
)
XCTAssertEqual(service.pastePlainText(item, targetApp: targetApp), .pastedPlainText)
XCTAssertEqual(activatedProcessID, targetApp.processIdentifier)
XCTAssertTrue(didScheduleKeyboardPaste)
XCTAssertEqual(NSPasteboard.general.string(forType: .string), "https://apple.com")
XCTAssertNil(NSPasteboard.general.string(forType: .URL))
}
func testAutomaticPasteDoesNotPostShortcutWhenTargetActivationFails() throws {
var didAttemptActivation = false
let targetApp = try makeRunningTargetApp()
@@ -264,6 +299,37 @@ final class PasteActionServiceTests: XCTestCase {
XCTAssertEqual(NSPasteboard.general.string(forType: .string), attributed.string)
}
func testCopyPlainTextStripsRichTextFormatting() throws {
let directory = try makeTempDirectory()
let cacheService = ClipboardCacheService(baseURL: directory, encryptionService: fixedEncryptionService())
let attributed = NSAttributedString(
string: "Styled clipboard text",
attributes: [.font: NSFont.boldSystemFont(ofSize: 15)]
)
let rtfData = try XCTUnwrap(
attributed.rtf(from: NSRange(location: 0, length: attributed.length), documentAttributes: [:])
)
let path = try XCTUnwrap(cacheService.cacheRichText(rtfData, id: UUID()))
let service = PasteActionService(cacheService: cacheService)
let item = ClipboardItem(
id: UUID(),
kind: .richText,
displayText: attributed.string,
payload: path,
payloadHash: "hash",
createdAt: Date(),
lastUsedAt: Date(),
useCount: 0,
sourceApp: nil,
imagePath: nil,
thumbnailPath: nil
)
XCTAssertEqual(service.copyPlainText(item), .copiedPlainText)
XCTAssertEqual(NSPasteboard.general.string(forType: .string), attributed.string)
XCTAssertNil(NSPasteboard.general.data(forType: .rtf))
}
func testCopyLegacyRichTextWritesPlainPayloadWhenRTFCacheIsUnavailable() {
let service = PasteActionService()
let item = ClipboardItem(
@@ -307,6 +373,28 @@ final class PasteActionServiceTests: XCTestCase {
XCTAssertEqual(NSPasteboard.general.string(forType: .string), "Readable rich text")
}
func testCopyPlainTextForURLOmitsURLPasteboardTypes() {
let service = PasteActionService()
let item = ClipboardItem(
id: UUID(),
kind: .url,
displayText: "Apple",
payload: "https://apple.com",
payloadHash: "hash",
createdAt: Date(),
lastUsedAt: Date(),
useCount: 0,
sourceApp: nil,
imagePath: nil,
thumbnailPath: nil
)
XCTAssertEqual(service.copyPlainText(item), .copiedPlainText)
XCTAssertEqual(NSPasteboard.general.string(forType: .string), "https://apple.com")
XCTAssertNil(NSPasteboard.general.string(forType: .URL))
XCTAssertNil(NSPasteboard.general.string(forType: NSPasteboard.PasteboardType(rawValue: "public.url-name")))
}
func testCopyWritesFileReferenceType() throws {
let fileURL = try makeTempFile(contents: "file contents")
let service = PasteActionService()