WIP: show filtered result in clipboard
This commit is contained in:
@@ -12,9 +12,10 @@ The project is intentionally dependency-light: Swift Package Manager, AppKit, Ca
|
|||||||
- `Command + Option + V` toggles the clipboard panel
|
- `Command + Option + V` toggles the clipboard panel
|
||||||
- `Command + ,` opens settings
|
- `Command + ,` opens settings
|
||||||
- `Command + 1` through `Command + 9` paste the numbered visible card; add `Shift` to paste that card as plain text
|
- `Command + 1` through `Command + 9` paste the numbered visible card; add `Shift` to paste that card as plain text
|
||||||
|
- `Command + G` shows a filtered result back in the full clipboard history
|
||||||
- Clipboard history for text, URLs with local preview thumbnails when available, images, audio, RTF/HTML rich text, PDFs, and file references
|
- Clipboard history for text, URLs with local preview thumbnails when available, images, audio, RTF/HTML rich text, PDFs, and file references
|
||||||
- SQLite persistence with bounded history, pinned-item retention, and encrypted app-managed payloads
|
- SQLite persistence with bounded history, pinned-item retention, and encrypted app-managed payloads
|
||||||
- Search with independent token matching, structured filters such as `app:Safari`, `type:image`, `date:2026-06-30`, and optional local OCR for copied images
|
- Search with independent token matching, structured filters such as `app:Safari`, `type:image`, `date:2026-06-30`, result jump-back to full history, and optional local OCR for copied images
|
||||||
- Sort modes for recent, most used, images, links, text, files, audio, and pinned items
|
- Sort modes for recent, most used, images, links, text, files, audio, and pinned items
|
||||||
- Custom named collections for organizing clips from the card Collect control, context menu, or by dragging cards onto collection chips
|
- Custom named collections for organizing clips from the card Collect control, context menu, or by dragging cards onto collection chips
|
||||||
- Copy and paste actions with Accessibility permission fallback
|
- Copy and paste actions with Accessibility permission fallback
|
||||||
|
|||||||
@@ -34,16 +34,17 @@ Use this checklist before a release or after changes to panel, pasteboard, setti
|
|||||||
1. Open the panel and confirm the search field is focused.
|
1. Open the panel and confirm the search field is focused.
|
||||||
2. Type a query and confirm results filter immediately.
|
2. Type a query and confirm results filter immediately.
|
||||||
3. Use arrow keys to move selection while the search field is focused.
|
3. Use arrow keys to move selection while the search field is focused.
|
||||||
4. Press `Esc` once with a non-empty search field and confirm search clears.
|
4. Right-click a filtered result and choose Show in Clipboard, or press `Command + G`, and confirm search clears while the same card stays selected in Most Recent.
|
||||||
5. Press `Esc` again and confirm the panel closes.
|
5. Press `Esc` once with a non-empty search field and confirm search clears.
|
||||||
6. Reopen the panel, change sort segments, and confirm each segment updates results.
|
6. Press `Esc` again and confirm the panel closes.
|
||||||
7. Right-click a card, choose Add to Collection > New Collection..., enter `Client Work`, and confirm a Client Work chip appears with the item count.
|
7. Reopen the panel, change sort segments, and confirm each segment updates results.
|
||||||
8. Select another card and confirm its Collect button offers Client Work as a reusable destination.
|
8. Right-click a card, choose Add to Collection > New Collection..., enter `Client Work`, and confirm a Client Work chip appears with the item count.
|
||||||
9. Select the Client Work chip and confirm the rail filters to assigned items; quit and reopen ClipBored and confirm the assignment persists.
|
9. Select another card and confirm its Collect button offers Client Work as a reusable destination.
|
||||||
10. Double-click an item and confirm it attempts to paste or falls back to copy without creating a duplicate history entry.
|
10. Select the Client Work chip and confirm the rail filters to assigned items; quit and reopen ClipBored and confirm the assignment persists.
|
||||||
11. Right-click a card, use Capture Rules to ignore its source app, copy from that app again, and confirm the new item is skipped.
|
11. Double-click an item and confirm it attempts to paste or falls back to copy without creating a duplicate history entry.
|
||||||
12. Drag an unassigned card onto the Client Work chip and confirm the chip count increases and the card appears when Client Work is selected.
|
12. Right-click a card, use Capture Rules to ignore its source app, copy from that app again, and confirm the new item is skipped.
|
||||||
13. Resize or test on a narrow display and confirm the bottom shelf switches to compact cards that still show two recent clips cleanly.
|
13. Drag an unassigned card onto the Client Work chip and confirm the chip count increases and the card appears when Client Work is selected.
|
||||||
|
14. Resize or test on a narrow display and confirm the bottom shelf switches to compact cards that still show two recent clips cleanly.
|
||||||
|
|
||||||
## Copy And Paste
|
## Copy And Paste
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ enum ClipboardPanelShortcutAction: Equatable {
|
|||||||
case pasteStackNext
|
case pasteStackNext
|
||||||
case preview
|
case preview
|
||||||
case reveal
|
case reveal
|
||||||
|
case showInClipboard
|
||||||
case toggleStack
|
case toggleStack
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,6 +408,8 @@ final class ClipboardPanelController: NSObject, NSWindowDelegate, QLPreviewPanel
|
|||||||
previewSelected()
|
previewSelected()
|
||||||
case .reveal:
|
case .reveal:
|
||||||
viewModel.revealSelected()
|
viewModel.revealSelected()
|
||||||
|
case .showInClipboard:
|
||||||
|
panelView.showSelectedInClipboard()
|
||||||
case .toggleStack:
|
case .toggleStack:
|
||||||
viewModel.toggleSelectedStackMembership()
|
viewModel.toggleSelectedStackMembership()
|
||||||
}
|
}
|
||||||
@@ -471,6 +474,8 @@ final class ClipboardPanelController: NSObject, NSWindowDelegate, QLPreviewPanel
|
|||||||
return .copy
|
return .copy
|
||||||
case 31:
|
case 31:
|
||||||
return .open
|
return .open
|
||||||
|
case 5:
|
||||||
|
return .showInClipboard
|
||||||
case 16:
|
case 16:
|
||||||
return .preview
|
return .preview
|
||||||
case 15:
|
case 15:
|
||||||
|
|||||||
@@ -638,7 +638,8 @@ final class ClipboardPanelView: NSVisualEffectView, NSSearchFieldDelegate {
|
|||||||
layout: layout,
|
layout: layout,
|
||||||
collectionNames: collectionNames,
|
collectionNames: collectionNames,
|
||||||
isStacked: viewModel.isItemStacked(at: index),
|
isStacked: viewModel.isItemStacked(at: index),
|
||||||
stackCount: viewModel.stackCount
|
stackCount: viewModel.stackCount,
|
||||||
|
canShowInClipboard: viewModel.canShowVisibleItemsInClipboard
|
||||||
)
|
)
|
||||||
card.onSelect = { [weak self] selected in
|
card.onSelect = { [weak self] selected in
|
||||||
self?.viewModel.selectItem(at: selected)
|
self?.viewModel.selectItem(at: selected)
|
||||||
@@ -672,6 +673,9 @@ final class ClipboardPanelView: NSVisualEffectView, NSSearchFieldDelegate {
|
|||||||
card.onClearStack = { [weak self] in
|
card.onClearStack = { [weak self] in
|
||||||
self?.viewModel.clearStack()
|
self?.viewModel.clearStack()
|
||||||
}
|
}
|
||||||
|
card.onShowInClipboard = { [weak self] selected in
|
||||||
|
self?.showSelectedInClipboard(at: selected)
|
||||||
|
}
|
||||||
card.onEditText = { [weak self] selected in
|
card.onEditText = { [weak self] selected in
|
||||||
self?.editText(at: selected)
|
self?.editText(at: selected)
|
||||||
}
|
}
|
||||||
@@ -821,7 +825,7 @@ final class ClipboardPanelView: NSVisualEffectView, NSSearchFieldDelegate {
|
|||||||
if lower.hasPrefix("captured") || lower.contains("capture running") || lower.contains("capture is running") || lower.contains("capture resumed") {
|
if lower.hasPrefix("captured") || lower.contains("capture running") || lower.contains("capture is running") || lower.contains("capture resumed") {
|
||||||
return .ready
|
return .ready
|
||||||
}
|
}
|
||||||
if lower.hasPrefix("copied") || lower.hasPrefix("pasted") || lower.hasPrefix("updated") || lower.hasPrefix("added") || lower.hasPrefix("removed") || lower.hasPrefix("cleared") || lower.hasPrefix("ignored") {
|
if lower.hasPrefix("copied") || lower.hasPrefix("pasted") || lower.hasPrefix("updated") || lower.hasPrefix("added") || lower.hasPrefix("removed") || lower.hasPrefix("cleared") || lower.hasPrefix("ignored") || lower.hasPrefix("showing") {
|
||||||
return .action
|
return .action
|
||||||
}
|
}
|
||||||
if lower.hasPrefix("error") || lower.contains("failed") {
|
if lower.hasPrefix("error") || lower.contains("failed") {
|
||||||
@@ -1240,6 +1244,14 @@ final class ClipboardPanelView: NSVisualEffectView, NSSearchFieldDelegate {
|
|||||||
addSelectedClipToCollection()
|
addSelectedClipToCollection()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func debugShowFirstCardInClipboard() {
|
||||||
|
showSelectedInClipboard(at: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var debugSearchFieldText: String {
|
||||||
|
searchField.stringValue
|
||||||
|
}
|
||||||
|
|
||||||
func debugDropFirstCard(onCollectionNamed collectionName: String) {
|
func debugDropFirstCard(onCollectionNamed collectionName: String) {
|
||||||
guard let itemID = cardViews.first?.debugItemID else { return }
|
guard let itemID = cardViews.first?.debugItemID else { return }
|
||||||
customCollectionButtons[collectionName]?.debugDropItem(itemID)
|
customCollectionButtons[collectionName]?.debugDropItem(itemID)
|
||||||
@@ -1298,6 +1310,16 @@ final class ClipboardPanelView: NSVisualEffectView, NSSearchFieldDelegate {
|
|||||||
onSettings()
|
onSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func showSelectedInClipboard() {
|
||||||
|
showSelectedInClipboard(at: viewModel.selectedIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showSelectedInClipboard(at index: Int) {
|
||||||
|
viewModel.selectItem(at: index)
|
||||||
|
viewModel.showSelectedInClipboard()
|
||||||
|
searchField.stringValue = viewModel.searchText
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func addSelectedClipToCollection() {
|
@objc private func addSelectedClipToCollection() {
|
||||||
guard viewModel.selectedItem != nil,
|
guard viewModel.selectedItem != nil,
|
||||||
let name = requestCollectionName() else {
|
let name = requestCollectionName() else {
|
||||||
@@ -1598,6 +1620,7 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
|
|||||||
var onPasteStackNext: () -> Void = {}
|
var onPasteStackNext: () -> Void = {}
|
||||||
var onCopyStackNext: () -> Void = {}
|
var onCopyStackNext: () -> Void = {}
|
||||||
var onClearStack: () -> Void = {}
|
var onClearStack: () -> Void = {}
|
||||||
|
var onShowInClipboard: (Int) -> Void = { _ in }
|
||||||
var onEditText: (Int) -> Void = { _ in }
|
var onEditText: (Int) -> Void = { _ in }
|
||||||
var onPreview: (Int) -> Void = { _ in }
|
var onPreview: (Int) -> Void = { _ in }
|
||||||
var onPasteboardWriters: (Int) -> [NSPasteboardWriting] = { _ in [] }
|
var onPasteboardWriters: (Int) -> [NSPasteboardWriting] = { _ in [] }
|
||||||
@@ -1616,6 +1639,7 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
|
|||||||
private let itemIsPinned: Bool
|
private let itemIsPinned: Bool
|
||||||
private let itemIsStacked: Bool
|
private let itemIsStacked: Bool
|
||||||
private let stackCount: Int
|
private let stackCount: Int
|
||||||
|
private let canShowInClipboard: Bool
|
||||||
private let itemSourceAppName: String?
|
private let itemSourceAppName: String?
|
||||||
private let itemSourceAppBundleID: String?
|
private let itemSourceAppBundleID: String?
|
||||||
private let itemCollectionName: String?
|
private let itemCollectionName: String?
|
||||||
@@ -1639,7 +1663,8 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
|
|||||||
layout: ClipboardItemCardLayout = .regular,
|
layout: ClipboardItemCardLayout = .regular,
|
||||||
collectionNames: [String] = [],
|
collectionNames: [String] = [],
|
||||||
isStacked: Bool = false,
|
isStacked: Bool = false,
|
||||||
stackCount: Int = 0
|
stackCount: Int = 0,
|
||||||
|
canShowInClipboard: Bool = false
|
||||||
) {
|
) {
|
||||||
self.index = index
|
self.index = index
|
||||||
self.itemID = item.id
|
self.itemID = item.id
|
||||||
@@ -1648,6 +1673,7 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
|
|||||||
self.itemIsPinned = item.isPinned
|
self.itemIsPinned = item.isPinned
|
||||||
self.itemIsStacked = isStacked
|
self.itemIsStacked = isStacked
|
||||||
self.stackCount = stackCount
|
self.stackCount = stackCount
|
||||||
|
self.canShowInClipboard = canShowInClipboard
|
||||||
self.itemSourceAppName = Self.presentSourceText(item.sourceApp)
|
self.itemSourceAppName = Self.presentSourceText(item.sourceApp)
|
||||||
self.itemSourceAppBundleID = Self.presentSourceText(item.sourceAppBundleId)
|
self.itemSourceAppBundleID = Self.presentSourceText(item.sourceAppBundleId)
|
||||||
self.itemCollectionName = ClipboardCollectionDefaults.normalizedName(item.collectionName)
|
self.itemCollectionName = ClipboardCollectionDefaults.normalizedName(item.collectionName)
|
||||||
@@ -1840,6 +1866,9 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
|
|||||||
addMenuItem("Paste Plain Text", action: #selector(pastePlainTextFromMenu), to: menu)
|
addMenuItem("Paste Plain Text", action: #selector(pastePlainTextFromMenu), to: menu)
|
||||||
addMenuItem("Copy Plain Text", action: #selector(copyPlainTextFromMenu), to: menu)
|
addMenuItem("Copy Plain Text", action: #selector(copyPlainTextFromMenu), to: menu)
|
||||||
}
|
}
|
||||||
|
if canShowInClipboard {
|
||||||
|
addMenuItem("Show in Clipboard", action: #selector(showInClipboardFromMenu), to: menu)
|
||||||
|
}
|
||||||
addMenuItem(itemIsStacked ? "Remove from Stack" : "Add to Stack", action: #selector(toggleStackFromMenu), to: menu)
|
addMenuItem(itemIsStacked ? "Remove from Stack" : "Add to Stack", action: #selector(toggleStackFromMenu), to: menu)
|
||||||
if stackCount > 0 {
|
if stackCount > 0 {
|
||||||
addMenuItem("Paste Stack Next", action: #selector(pasteStackNextFromMenu), to: menu)
|
addMenuItem("Paste Stack Next", action: #selector(pasteStackNextFromMenu), to: menu)
|
||||||
@@ -2138,6 +2167,10 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
|
|||||||
onClearStack()
|
onClearStack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func showInClipboardFromMenu() {
|
||||||
|
onShowInClipboard(index)
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func editTextFromMenu() {
|
@objc private func editTextFromMenu() {
|
||||||
onEditText(index)
|
onEditText(index)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,18 @@ final class ClipboardPanelViewModel {
|
|||||||
return visibleItems[selectedIndex]
|
return visibleItems[selectedIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var canShowSelectedInClipboard: Bool {
|
||||||
|
selectedItem != nil && canShowVisibleItemsInClipboard
|
||||||
|
}
|
||||||
|
|
||||||
|
var canShowVisibleItemsInClipboard: Bool {
|
||||||
|
!visibleItems.isEmpty
|
||||||
|
&& (!searchText.clipboardTrimmed.isEmpty
|
||||||
|
|| sortMode != .mostRecent
|
||||||
|
|| selectedCollectionName != nil
|
||||||
|
|| isStackFilterSelected)
|
||||||
|
}
|
||||||
|
|
||||||
var totalItemCount: Int {
|
var totalItemCount: Int {
|
||||||
items.count
|
items.count
|
||||||
}
|
}
|
||||||
@@ -464,6 +476,30 @@ final class ClipboardPanelViewModel {
|
|||||||
searchText = ""
|
searchText = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func showSelectedInClipboard() {
|
||||||
|
guard canShowSelectedInClipboard, let item = selectedItem else { return }
|
||||||
|
selectedItemID = item.id
|
||||||
|
|
||||||
|
if !searchText.isEmpty {
|
||||||
|
searchText = ""
|
||||||
|
}
|
||||||
|
if isStackFilterSelected {
|
||||||
|
isStackFilterSelected = false
|
||||||
|
}
|
||||||
|
if selectedCollectionName != nil {
|
||||||
|
selectedCollectionName = nil
|
||||||
|
}
|
||||||
|
if sortMode != .mostRecent {
|
||||||
|
sortMode = .mostRecent
|
||||||
|
}
|
||||||
|
|
||||||
|
if let index = visibleItems.firstIndex(where: { $0.id == item.id }) {
|
||||||
|
selectedIndex = index
|
||||||
|
selectedItemID = item.id
|
||||||
|
}
|
||||||
|
statusMessage = "Showing in Clipboard"
|
||||||
|
}
|
||||||
|
|
||||||
func recomputeVisibleItems() {
|
func recomputeVisibleItems() {
|
||||||
pruneStackItems()
|
pruneStackItems()
|
||||||
let previousSelection = selectedItemID
|
let previousSelection = selectedItemID
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ final class ClipboardPanelControllerTests: XCTestCase {
|
|||||||
|
|
||||||
func testCommandActionShortcutsMapToSelectedClipActions() {
|
func testCommandActionShortcutsMapToSelectedClipActions() {
|
||||||
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 8, modifiers: .command), .copy)
|
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 8, modifiers: .command), .copy)
|
||||||
|
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 5, modifiers: .command), .showInClipboard)
|
||||||
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 16, modifiers: .command), .preview)
|
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 16, modifiers: .command), .preview)
|
||||||
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 31, modifiers: .command), .open)
|
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 31, modifiers: .command), .open)
|
||||||
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 15, modifiers: .command), .reveal)
|
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 15, modifiers: .command), .reveal)
|
||||||
|
|||||||
@@ -256,6 +256,44 @@ final class ClipboardPanelViewModelTests: XCTestCase {
|
|||||||
XCTAssertEqual(viewModel.selectedItem?.payload, "needle note")
|
XCTAssertEqual(viewModel.selectedItem?.payload, "needle note")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testShowSelectedInClipboardClearsFiltersAndKeepsHistoryPosition() {
|
||||||
|
let settings = makeSettings()
|
||||||
|
let cacheService = makeCacheService()
|
||||||
|
let store = makeStore(settings: settings, cacheService: cacheService)
|
||||||
|
let older = makeTextItem("release needle", createdAt: Date(timeIntervalSince1970: 100))
|
||||||
|
let newer = makeTextItem("meeting note", createdAt: Date(timeIntervalSince1970: 200))
|
||||||
|
store.upsert(older)
|
||||||
|
store.upsert(newer)
|
||||||
|
store.flushPersistenceForTesting()
|
||||||
|
|
||||||
|
let viewModel = ClipboardPanelViewModel(store: store, settings: settings, cacheService: cacheService)
|
||||||
|
waitForVisibleItems(in: viewModel, count: 2)
|
||||||
|
XCTAssertEqual(viewModel.visibleItems.map(\.payload), ["meeting note", "release needle"])
|
||||||
|
|
||||||
|
viewModel.selectItem(at: 1)
|
||||||
|
viewModel.assignSelected(to: "Client Work")
|
||||||
|
store.flushPersistenceForTesting()
|
||||||
|
waitForVisibleItems(in: viewModel, count: 2)
|
||||||
|
|
||||||
|
viewModel.selectCollection(named: "Client Work")
|
||||||
|
viewModel.searchText = "release"
|
||||||
|
|
||||||
|
XCTAssertTrue(viewModel.canShowSelectedInClipboard)
|
||||||
|
XCTAssertEqual(viewModel.visibleItems.map(\.payload), ["release needle"])
|
||||||
|
|
||||||
|
viewModel.showSelectedInClipboard()
|
||||||
|
|
||||||
|
XCTAssertEqual(viewModel.searchText, "")
|
||||||
|
XCTAssertNil(viewModel.selectedCollectionName)
|
||||||
|
XCTAssertFalse(viewModel.isStackFilterSelected)
|
||||||
|
XCTAssertEqual(viewModel.sortMode, .mostRecent)
|
||||||
|
XCTAssertEqual(viewModel.visibleItems.map(\.payload), ["meeting note", "release needle"])
|
||||||
|
XCTAssertEqual(viewModel.selectedItem?.id, older.id)
|
||||||
|
XCTAssertEqual(viewModel.selectedIndex, 1)
|
||||||
|
XCTAssertFalse(viewModel.canShowSelectedInClipboard)
|
||||||
|
XCTAssertEqual(viewModel.statusMessage, "Showing in Clipboard")
|
||||||
|
}
|
||||||
|
|
||||||
func testSelectFirstItemSelectsFirstVisibleItem() {
|
func testSelectFirstItemSelectsFirstVisibleItem() {
|
||||||
let settings = makeSettings()
|
let settings = makeSettings()
|
||||||
let cacheService = makeCacheService()
|
let cacheService = makeCacheService()
|
||||||
|
|||||||
@@ -407,6 +407,36 @@ final class ClipboardPanelViewTests: XCTestCase {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testFilteredCardsExposeShowInClipboardContextMenuAction() {
|
||||||
|
let fixture = makePanelFixture()
|
||||||
|
var release = makeTextItem("Release needle", store: fixture.store)
|
||||||
|
release.createdAt = Date(timeIntervalSince1970: 100)
|
||||||
|
release.lastUsedAt = release.createdAt
|
||||||
|
var meeting = makeTextItem("Meeting note", store: fixture.store)
|
||||||
|
meeting.createdAt = Date(timeIntervalSince1970: 200)
|
||||||
|
meeting.lastUsedAt = meeting.createdAt
|
||||||
|
fixture.store.upsert(release)
|
||||||
|
fixture.store.upsert(meeting)
|
||||||
|
drainMainQueue()
|
||||||
|
|
||||||
|
fixture.viewModel.searchText = "release"
|
||||||
|
drainMainQueue()
|
||||||
|
fixture.window.contentView?.layoutSubtreeIfNeeded()
|
||||||
|
|
||||||
|
XCTAssertEqual(
|
||||||
|
fixture.view.debugFirstCardMenuTitles,
|
||||||
|
["Paste", "Copy", "Show in Clipboard", "Add to Stack", "Edit", "Pin", "Add to Collection", "Capture Rules", "-", "Open", "Reveal in Finder", "-", "Delete"]
|
||||||
|
)
|
||||||
|
|
||||||
|
fixture.view.debugShowFirstCardInClipboard()
|
||||||
|
drainMainQueue()
|
||||||
|
fixture.window.contentView?.layoutSubtreeIfNeeded()
|
||||||
|
|
||||||
|
XCTAssertEqual(fixture.view.debugSearchFieldText, "")
|
||||||
|
XCTAssertEqual(fixture.viewModel.visibleItems.map(\.payload), ["Meeting note", "Release needle"])
|
||||||
|
XCTAssertEqual(fixture.viewModel.selectedItem?.payload, "Release needle")
|
||||||
|
}
|
||||||
|
|
||||||
func testPreviewableCardsExposeQuickLookContextMenuAction() {
|
func testPreviewableCardsExposeQuickLookContextMenuAction() {
|
||||||
let fixture = makePanelFixture()
|
let fixture = makePanelFixture()
|
||||||
fixture.store.upsert(makeItem(kind: .file, text: "/tmp/report.txt", store: fixture.store))
|
fixture.store.upsert(makeItem(kind: .file, text: "/tmp/report.txt", store: fixture.store))
|
||||||
|
|||||||
Reference in New Issue
Block a user