WIP: preview from empty search with space
This commit is contained in:
@@ -14,6 +14,7 @@ The project is intentionally dependency-light: Swift Package Manager, AppKit, Ca
|
||||
- `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
|
||||
- `Shift + Command + N` creates a new collection
|
||||
- `Space` previews the selected card when the focused search field is empty
|
||||
- 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
|
||||
- Search with independent token matching, structured filters such as `app:Safari`, `type:image,pdf`, `pinboard:"Client Work","Read Later"`, `date:2026-06-30`, result jump-back to full history, and optional local OCR for copied images
|
||||
|
||||
@@ -34,20 +34,21 @@ Use this checklist before a release or after changes to panel, pasteboard, setti
|
||||
1. Open the panel and confirm the search field is focused.
|
||||
2. Type a query and confirm results filter immediately.
|
||||
3. Type a structured query such as `pinboard:"Client Work","Read Later" type:image,pdf` and confirm only clips from those collections and content types remain.
|
||||
4. Use arrow keys to move selection while the search field is focused.
|
||||
5. 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.
|
||||
6. Press `Esc` once with a non-empty search field and confirm search clears.
|
||||
7. Press `Esc` again and confirm the panel closes.
|
||||
8. Reopen the panel, change sort segments, and confirm each segment updates results.
|
||||
9. Press `Shift + Command + N` or the collection rail `+`, enter `Client Work`, choose a color, and confirm a Client Work chip appears with 0 clips and an empty collection view.
|
||||
10. Return to Clipboard, select a card, use its Collect button to choose Client Work, and confirm the Client Work chip count increases.
|
||||
11. Select the Client Work chip and confirm the rail filters to assigned items, cards use the Client Work name/color in their headers, and the collection/color/assignment persists after quitting and reopening ClipBored.
|
||||
12. Right-click the Client Work chip, choose Edit Collection..., rename it, change its color, and confirm the chip and assigned card headers update.
|
||||
13. Right-click a media, file, link, PDF, audio, or text card, choose Rename..., give it a title, and confirm the card title and search results use the custom title while paste/copy still uses the original payload.
|
||||
14. Double-click an item and confirm it attempts to paste or falls back to copy without creating a duplicate history entry.
|
||||
15. Right-click a card, use Capture Rules to ignore its source app, copy from that app again, and confirm the new item is skipped.
|
||||
16. Drag an unassigned card onto the renamed collection chip and confirm the chip count increases and the card appears when that collection is selected.
|
||||
17. Resize or test on a narrow display and confirm the bottom shelf switches to compact cards that still show two recent clips cleanly.
|
||||
4. Clear the search field, press `Space`, and confirm the selected previewable clip opens in Quick Look instead of inserting a blank query.
|
||||
5. Use arrow keys to move selection while the search field is focused.
|
||||
6. 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.
|
||||
7. Press `Esc` once with a non-empty search field and confirm search clears.
|
||||
8. Press `Esc` again and confirm the panel closes.
|
||||
9. Reopen the panel, change sort segments, and confirm each segment updates results.
|
||||
10. Press `Shift + Command + N` or the collection rail `+`, enter `Client Work`, choose a color, and confirm a Client Work chip appears with 0 clips and an empty collection view.
|
||||
11. Return to Clipboard, select a card, use its Collect button to choose Client Work, and confirm the Client Work chip count increases.
|
||||
12. Select the Client Work chip and confirm the rail filters to assigned items, cards use the Client Work name/color in their headers, and the collection/color/assignment persists after quitting and reopening ClipBored.
|
||||
13. Right-click the Client Work chip, choose Edit Collection..., rename it, change its color, and confirm the chip and assigned card headers update.
|
||||
14. Right-click a media, file, link, PDF, audio, or text card, choose Rename..., give it a title, and confirm the card title and search results use the custom title while paste/copy still uses the original payload.
|
||||
15. Double-click an item and confirm it attempts to paste or falls back to copy without creating a duplicate history entry.
|
||||
16. Right-click a card, use Capture Rules to ignore its source app, copy from that app again, and confirm the new item is skipped.
|
||||
17. Drag an unassigned card onto the renamed collection chip and confirm the chip count increases and the card appears when that collection is selected.
|
||||
18. 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
|
||||
|
||||
|
||||
@@ -339,6 +339,16 @@ final class ClipboardPanelController: NSObject, NSWindowDelegate, QLPreviewPanel
|
||||
removeKeyMonitor()
|
||||
keyMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
|
||||
guard let self else { return event }
|
||||
if self.shouldHandlePanelKeyEvent(event, allowSearchFieldEditing: true),
|
||||
self.panelView.isSearchFieldEditing,
|
||||
Self.searchFieldPreviewShortcut(
|
||||
forKeyCode: event.keyCode,
|
||||
modifiers: event.modifierFlags,
|
||||
searchText: self.panelView.searchTextForKeyboardShortcut
|
||||
) {
|
||||
self.previewSelected()
|
||||
return nil
|
||||
}
|
||||
if self.shouldHandlePanelKeyEvent(event, allowSearchFieldEditing: true),
|
||||
let index = Self.quickPasteIndex(forKeyCode: event.keyCode, modifiers: event.modifierFlags) {
|
||||
self.viewModel.pasteItem(at: index)
|
||||
@@ -469,6 +479,11 @@ final class ClipboardPanelController: NSObject, NSWindowDelegate, QLPreviewPanel
|
||||
return collectionShortcuts[keyCode]
|
||||
}
|
||||
|
||||
static func searchFieldPreviewShortcut(forKeyCode keyCode: UInt16, modifiers: NSEvent.ModifierFlags, searchText: String) -> Bool {
|
||||
let relevantModifiers = modifiers.intersection(.deviceIndependentFlagsMask)
|
||||
return keyCode == 49 && relevantModifiers.isEmpty && searchText.clipboardTrimmed.isEmpty
|
||||
}
|
||||
|
||||
static func commandShortcutAction(forKeyCode keyCode: UInt16, modifiers: NSEvent.ModifierFlags) -> ClipboardPanelShortcutAction? {
|
||||
let relevantModifiers = modifiers.intersection(.deviceIndependentFlagsMask)
|
||||
guard relevantModifiers == .command else { return nil }
|
||||
|
||||
@@ -1102,6 +1102,10 @@ final class ClipboardPanelView: NSVisualEffectView, NSSearchFieldDelegate {
|
||||
return false
|
||||
}
|
||||
|
||||
var searchTextForKeyboardShortcut: String {
|
||||
searchField.stringValue
|
||||
}
|
||||
|
||||
override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -164,6 +164,14 @@ final class ClipboardPanelControllerTests: XCTestCase {
|
||||
XCTAssertNil(ClipboardPanelController.collectionShortcutMode(forKeyCode: 29, modifiers: .command))
|
||||
}
|
||||
|
||||
func testSearchFieldSpacePreviewShortcutRequiresEmptySearchAndNoModifiers() {
|
||||
XCTAssertTrue(ClipboardPanelController.searchFieldPreviewShortcut(forKeyCode: 49, modifiers: [], searchText: ""))
|
||||
XCTAssertTrue(ClipboardPanelController.searchFieldPreviewShortcut(forKeyCode: 49, modifiers: [], searchText: " "))
|
||||
XCTAssertFalse(ClipboardPanelController.searchFieldPreviewShortcut(forKeyCode: 49, modifiers: [], searchText: "release note"))
|
||||
XCTAssertFalse(ClipboardPanelController.searchFieldPreviewShortcut(forKeyCode: 49, modifiers: .command, searchText: ""))
|
||||
XCTAssertFalse(ClipboardPanelController.searchFieldPreviewShortcut(forKeyCode: 36, modifiers: [], searchText: ""))
|
||||
}
|
||||
|
||||
func testCommandActionShortcutsMapToSelectedClipActions() {
|
||||
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 8, modifiers: .command), .copy)
|
||||
XCTAssertEqual(ClipboardPanelController.commandShortcutAction(forKeyCode: 5, modifiers: .command), .showInClipboard)
|
||||
|
||||
Reference in New Issue
Block a user