Polish clipboard panel UX and interactions

This commit is contained in:
Akshay Kolli
2026-07-09 19:32:57 -04:00
parent 23cd8b64a9
commit 52f712eb73
18 changed files with 2109 additions and 3273 deletions

View File

@@ -1,64 +1,76 @@
# Architecture
ClipBored is a single-process AppKit utility built with Swift Package Manager.
ClipBored is a single-process AppKit utility built with Swift Package Manager. UI, capture, persistence, preview generation, and paste orchestration stay in-process. Capture persistence, card-thumbnail loading, archive and sync operations, image rotation, and OCR use bounded background queues; their UI state and completion feedback return to the main thread.
## Runtime Shape
- `ClipBoredApp` creates `NSApplication`, sets accessory activation, installs `AppDelegate`, and starts the run loop.
- `AppDelegate` wires shared services, status menu items, settings observers, and global shortcuts.
- `ClipboardMonitorService` polls `NSPasteboard.changeCount` on a utility queue with adaptive active/idle intervals.
- `ClipboardStore` keeps the in-memory item list and persists rows to SQLite on a serial queue.
- `ClipboardCacheService` stores bounded image previews under Application Support and keeps a small `NSCache`.
- `ClipboardCloudSyncService` resolves the app-private iCloud ubiquity container when sync is enabled and pushes or pulls the portable archive file.
- `ShortcutManager` registers Carbon hotkeys for app-wide commands.
- `ClipboardPanelController` owns the side panel lifecycle, Dock-aware frame planning, configured left/right placement, and target-app tracking.
- `ClipboardPanelViewModel` filters, sorts, selects, copies, pastes, pins, organizes, deletes, opens, and reveals items.
- `LinkPreviewWindowController` opens selected HTTP(S) links in an ephemeral WebKit preview window instead of handing them to another browser.
- `OnboardingWindowController` shows the first-run setup assistant for shortcut, retention, system entry points, launch-at-login, iCloud sync, and Accessibility permission choices.
- `SettingsWindowController` exposes native controls for capture, privacy, performance, shortcuts, and data management.
- `ClipBoredApp` creates `NSApplication`, installs `AppDelegate`, and starts the run loop.
- `AppDelegate` wires services, menu-bar commands, settings observers, and global shortcuts.
- `ClipboardMonitorService` watches `NSPasteboard.changeCount` on a utility queue with adaptive active/idle polling intervals selected by the Performance setting.
- `ClipboardStore` owns the in-memory item list and SQLite persistence on a serial queue.
- `ClipboardCacheService` stores bounded encrypted preview sidecars under Application Support and maintains a small in-memory cache.
- `ClipboardCloudSyncService` resolves the private iCloud ubiquity container and pushes or pulls portable archives only when sync is enabled.
- `ShortcutManager` registers only intentional system-wide Carbon hotkeys (open shelf and Stack capture). The Settings binding is handled by the shelf's local key monitor and is never registered globally.
- `ClipboardPanelController` owns shelf lifecycle, current-screen placement, left/right frame planning, target-app tracking, and show/hide/reflow animation.
- `ClipboardPanelViewModel` owns query parsing, indexed category unions, sorting, selection, copy/paste, pinning, Pinboards, Stack, deletion, opening, and asynchronous thumbnail request coalescing.
- `ClipboardPanelView` renders the toolbar, vertical category icon rail, and viewport-aware card list beside it.
- `LinkPreviewWindowController` opens user-selected HTTP(S) links in an ephemeral WebKit preview window.
- `OnboardingWindowController` handles first-run shortcut, retention, lifecycle, sync, and Accessibility choices.
- `SettingsWindowController` presents six resizable, vertically scrollable settings pages and routes common settings changes through targeted control refreshes.
## Data Flow
## Capture And Presentation Flow
1. The monitor notices a pasteboard change.
2. Source app metadata is checked against ignored apps.
3. Pasteboard content is normalized into a `ClipboardItem`.
4. Sensitive text is skipped when exclusion is enabled.
5. Copied images run local Vision OCR only when `Search in image labels` is enabled; image cards can also run the same local OCR on demand from their quick actions.
6. The store deduplicates, preserves pinned and collection-assigned items, enforces limits, and persists the mutation.
7. The panel view model receives store updates and recomputes the visible list.
2. Capture rules check paused state, ignored source apps, allowed content kinds, and optional sensitive-text exclusion.
3. Pasteboard content is normalized into a `ClipboardItem`; local Vision OCR runs when image-label search is enabled.
4. The store deduplicates, preserves pinned and Pinboard-assigned items, enforces retention/length limits, and persists the mutation.
5. The panel view model maintains category/Pinboard indexes, applies the text query and selected category union, and caches parsed search matches across counts and category changes.
6. The view lays out card slots beside the category rail in one vertical document and materializes cards near the visible viewport.
7. Cards render immediately with a fallback presentation. Preview thumbnails load on a bounded operation queue; identical in-flight requests share work, and a still-relevant card is replaced in place on the main thread when its image arrives.
## Persistence
## Shelf Interaction Model
History is stored in SQLite at:
The shelf uses a fixed vertical layout on the configured left or right edge of the active screen.
- Header row one contains the collapsed/expanded search control plus clear-history and settings actions.
- Header row two is a labeled, horizontally scrollable category rail. Built-in categories without clips are omitted unless currently selected; custom Pinboards remain available when empty.
- A normal chip click replaces the active category filter. Command-click adds or removes chips from a union. Hover changes chrome only and never changes filtering.
- An empty, unfocused search field collapses to an icon. Click, typing, or `Command + F` expands and focuses it; clicking elsewhere collapses it only when the query is empty. Repeated `Command + F` keeps focus in the same field.
- Cards scroll vertically and fill the usable shelf width. Hover expansion changes visual presentation only: it does not mutate keyboard selection, the selected range, or query/category state.
- Card commands are discoverable through context menus, VoiceOver descriptions, and keyboard shortcuts. Hover-only action controls are not part of the interaction contract.
- Category changes and card/search expansion use short AppKit/Core Animation transitions. Both panel-controller and panel-view durations resolve to zero when macOS Reduce Motion is enabled.
The panel has no user-facing resize lip, alternate density/layout mode, close button, new-text action, or persistent status bar. It is dismissed with `Esc` or the configured global shortcut.
## Settings UI
Settings uses a custom segmented selector backed by a borderless `NSTabView`:
- `General` - history, sorting, shelf side, launch, and menu-bar/Dock presence
- `Shortcuts` - a system-wide open-shelf binding and a pane-local open-settings binding
- `Capture` - pause state, content kinds, image-label search, likely-secret exclusion, ignored apps, and capture status
- `Privacy` - local-data behavior, screen-capture hiding, Accessibility permission, and paste status
- `Performance` - adaptive polling profile and thumbnail-cache cap
- `Data` - iCloud archive sync, local archive import/export, storage location, and destructive clears
Each tab is a top-aligned document inside its own vertical scroll view. The window has a practical minimum size, no horizontal scrollers, and commits focused text/shortcut drafts when it closes. Common narrow settings changes update their bound controls without rebuilding the whole window; expensive cloud status checks are cached across unrelated refreshes.
## Persistence And Privacy Boundaries
History is stored in:
```text
~/Library/Application Support/ClipBored/history.sqlite
```
Images are stored under:
Image previews are stored under `images/`; restorable audio, video, rich-text, and PDF payloads are stored under `attachments/`. Legacy JSON import remains for migration from early builds.
```text
~/Library/Application Support/ClipBored/images/
```
Portable `.clipboredarchive` files preserve item metadata, Pinboards, and decrypted bytes for app-managed sidecars so another Mac can re-cache them with its own storage paths and encryption key. External file references remain path-based. Optional iCloud sync uses the same unencrypted archive format inside the app-private ubiquity container and is disabled by default.
Restorable non-image payloads such as audio clips, rich text, and PDFs are stored under:
```text
~/Library/Application Support/ClipBored/attachments/
```
Legacy JSON import still exists for migration from early builds.
Portable `.clipboredarchive` files are JSON exports created by `ClipboardArchiveService`. They preserve item metadata and include decrypted bytes for app-managed image, URL-thumbnail, audio, video, rich-text, and PDF sidecars so a different Mac can re-cache them under its own storage directory and encryption key. External file references remain path-based and are not copied into the archive.
Optional iCloud Sync uses that same archive format at `Documents/ClipBored/ClipBored.clipboredarchive` inside the app-private ubiquity container. It is disabled by default, requires a signed build with iCloud entitlement access, pulls once when enabled at launch, and debounces pushes after local store changes. Shared Pinboard collaboration is not implemented.
Textual SQLite fields, including optional collection names and image OCR text, are encrypted and decrypted at the `ClipboardStore` boundary. App-managed image cache files, URL preview thumbnails, audio clips, rich text sidecars, and PDF attachments are encrypted and decrypted at the `ClipboardCacheService` boundary. The encryption key is stored in Keychain when available, with an owner-only app-local fallback key if Keychain access blocks or fails. Full history clears remove the local fallback key when present and reset cached key state after SQLite deletion succeeds. Runtime `ClipboardItem` values remain plaintext in memory so search, duplicate detection, copy, paste, organization, archive export/import, and cache cleanup operate normally. Opening or revealing encrypted media creates a temporary decrypted copy for macOS handoff; stale temporary previews are cleared on launch, cache/history clear, and quit. Link previews are user-triggered and use a non-persistent WebKit data store.
Textual SQLite fields are encrypted and decrypted at the `ClipboardStore` boundary. Managed cache and attachment files are encrypted and decrypted at the `ClipboardCacheService` boundary. The key lives in Keychain when available, with an owner-only local fallback if Keychain access fails. Runtime `ClipboardItem` values remain plaintext in memory for search and clipboard operations. Opening encrypted media may create a temporary decrypted file; stale previews are cleared on launch, cache/history clear, and quit. Link previews are user-triggered and use a non-persistent WebKit data store.
## Size And Power Constraints
The release build intentionally avoids SwiftUI, Combine, Swift Concurrency, third-party packages, bundled media, and app resources beyond `Info.plist`.
The release build intentionally avoids SwiftUI, Combine, Swift Concurrency, third-party packages, and bundled media. The shelf avoids continuous layout work, renders only cards near the viewport, coalesces preview requests, keeps card-thumbnail decoding off the main thread, and bounds both memory and disk caches. Clipboard monitoring uses change-count polling with selectable adaptive profiles rather than continuous file scans.
The side shelf is anchored to the current screen's visible frame and can be placed on the left or right edge. It uses compact horizontal rows in a vertical list; the active or hovered row expands in place while the panel stays clear of side Docks and reserves bottom Dock space for content padding.
The build script uses `-Osize`, whole-module optimization, disabled reflection metadata, linker dead stripping, symbol stripping, and hardened-runtime signing. The current public targets, enforced by `scripts/build-macos-app.sh`, are 2 MiB gates for both the executable and app bundle.
The build script uses `-Osize`, whole-module optimization, disabled reflection metadata, linker dead stripping, symbol stripping, and hardened-runtime signing. `scripts/build-macos-app.sh` enforces 2 MiB gates for both the executable and app bundle.

View File

@@ -15,7 +15,8 @@ This roadmap keeps future work aligned with the project's constraints: small exe
## Product Polish
- Improve keyboard focus states and VoiceOver labels.
- Keep keyboard focus, VoiceOver descriptions, Command-click category unions, and context-menu parity covered as the side-rail shelf evolves.
- Validate motion, cross-display placement, and card expansion on each supported macOS release, including the system Reduce Motion path.
- Consider optional password-protected archive exports if migration needs outgrow owner-only local archive files.
- Design true shared Pinboard collaboration separately from private iCloud archive sync.
@@ -24,4 +25,5 @@ This roadmap keeps future work aligned with the project's constraints: small exe
- Keep measuring binary size after each feature.
- Avoid continuous background file scans.
- Revisit polling intervals only with measured idle wakeup evidence.
- Keep image decoding lazy and cache bounded.
- Track asynchronous preview latency and viewport materialization under large histories.
- Keep card-thumbnail decoding and user-triggered image transforms off the main thread, with bounded queues and caches.

View File

@@ -1,137 +1,93 @@
# Manual Smoke Test Checklist
Use this checklist before a release or after changes to panel, pasteboard, settings, permissions, storage, launch-at-login, or packaging behavior.
Use this checklist before a release or after changes to the shelf, pasteboard, settings, permissions, storage, launch-at-login, or packaging behavior.
## Setup
1. Build the app:
1. Run `./scripts/check.sh`, quit any running copy, and open `build/ClipBored.app`.
2. With fresh preferences, confirm the setup assistant appears before the shelf and covers the open shortcut, Keep History, menu-bar/Dock presence, launch at login, iCloud sync, and Accessibility.
3. Finish setup, relaunch, and confirm the assistant does not return.
4. Confirm the menu-bar icon appears when `Show ClipBored in the menu bar` is enabled.
```bash
./scripts/check.sh
```
## Capture And Preview Loading
2. Quit any running ClipBored copy.
3. Open `build/ClipBored.app`.
4. With a fresh preferences profile, confirm the setup assistant appears before the clipboard panel; choose the open shortcut, Keep History retention, menu-bar/Dock presence, launch-at-login, iCloud sync, and Accessibility option, then finish setup.
5. Reopen ClipBored and confirm the setup assistant does not reappear.
6. Confirm ClipBored appears in the menu bar when `Show ClipBored in the menu bar` is enabled.
1. Copy plain text, code, a URL, a color, an image, audio, video, rich text, a PDF, one Finder file, and multiple Finder files. Confirm each appears with the correct kind and grouped files remain one clip.
2. Confirm a new card appears immediately even when its preview is not cached. Keep searching and scrolling while the thumbnail loads, then confirm the card updates in place without changing selection or scroll position.
3. Confirm URL and video thumbnails appear when source data is available, and that a missing or failed thumbnail leaves a usable fallback card.
4. Enable `Search in image labels`, copy an image containing readable text, and confirm the OCR text is searchable after processing finishes.
5. Disable a content kind in Settings > Capture, copy that kind again, and confirm it is skipped. Re-enable it afterward.
6. Copy from an ignored source app and confirm the capture status reports that the item was skipped.
## Capture
## Shelf Chrome, Search, And Categories
1. Copy plain text from TextEdit, Notes, or a browser.
2. Open the panel with the configured open shortcut.
3. Confirm the copied text appears in Most Recent.
4. Copy a URL and confirm it appears as a Link with a browser-style site preview; if the source provides a local preview image, confirm the Link card uses that image preview instead and that tall shelves give the preview more room.
5. Copy an image and confirm it appears as an Image with a thumbnail and a centered dimension overlay.
6. Enable `Search in image labels`, copy an image containing readable text, and confirm searching for that text finds the Image.
7. Copy a sound clip and confirm it appears as Audio with an album-style artwork tile.
8. Copy a movie or video clip and confirm it appears as Video with a player-style preview and centered format pill.
9. Copy a PDF or PDF selection and confirm it appears as a PDF.
10. Copy one Finder file and confirm it appears as a File.
11. Copy multiple Finder files at once and confirm they appear as one grouped File item with the file count.
12. Copy formatted text from a browser or Mail message and confirm it appears as Rich Text rather than flattened plain text.
13. Disable Images, Audio, Video, Rich Text, PDFs, or Files in Settings > Capture, copy that type again, and confirm it is not captured.
1. Open the shelf and confirm the collapsed search icon is at the toolbar's leading edge. Activate it and confirm the same liquid-glass pill expands smoothly to the right toward Clear History and Settings without moving its left edge or icon, with text and caret aligned after the icon.
2. With an empty query, confirm search is a circular magnifying-glass button. Click it or press `Command + F` and confirm it expands to an aligned search field without moving the card list.
3. Click a card or category while the empty search field is focused and confirm search collapses again. Enter a query and confirm it remains expanded after focus moves elsewhere; clearing the idle query collapses it.
4. Press `Command + F` repeatedly and confirm it keeps focus in the same search field.
5. Type a query and confirm results update immediately. Press `Esc` once to clear an active query and again to close the shelf.
6. Type a structured query such as `pinboard:"Client Work" type:image,pdf device:<part of this Mac's name>` and confirm the terms are combined with the text query.
7. Confirm the category icon rail scrolls vertically when its icons exceed the available height, while the card list scrolls independently beside it.
8. With sparse history, confirm built-in type/sort categories with zero matches are absent. Clipboard remains available, and empty custom Pinboards remain visible.
9. Click Text and confirm it replaces the current category filter. Command-click Links and confirm the list becomes the union of Text and Links; Command-click either selected chip again to remove it from the union.
10. Hover several category chips without clicking and confirm neither the active filters nor the visible clips change.
11. Tab to search, toolbar controls, chips, and cards. Confirm focus is visible and VoiceOver labels explain each action. Use Left/Right and Home/End on category icons, and Up/Down/Page/Home/End navigation on cards; Left/Right must not move card selection.
## Panel
## Cards, Selection, And Actions
1. Open the panel and confirm the search field is focused.
2. Type a query and confirm results filter immediately.
3. Press `Command + F` to focus search, press it again to show filters, then add Type, Device, and Pinboard filters and confirm they appear as structured tokens. Type a structured query such as `pinboard:"Client Work","Read Later" type:image,pdf device:<part of this Mac's name>` and confirm only clips from those collections, content types, and the copied-on Mac remain.
4. Clear the search field, press `Space`, and confirm a selected web link opens in ClipBored's built-in browser preview while non-link previewable clips open in Quick Look instead of inserting a blank query.
5. Use arrow keys to move selection while the search field is focused.
6. Tab to the search controls, collection `+`, toolbar buttons, and collection chips; confirm VoiceOver help explains the matching actions and shortcuts. Press `Space` or `Return` on a focused chip and confirm it is selected and the visible focus state is clear. Use Left/Right, Home, and End to move through the chip rail, including custom collections and Stack when present, and confirm VoiceOver help mentions those keys plus Pinboard/Stack context actions where available.
7. Tab to cards; confirm the focused card gets a clear focus border, `Return` pastes or copies it, `Command + C` copies it, `Shift + Return` and `Command + Shift + V` paste or copy it as plain text, and `Space` or `Command + Y` opens the built-in browser for web links or Quick Look for text, files, and media.
8. With a card focused, press `Command + F` and confirm focus returns to search; press `Command + E` on a text/code clip and confirm the edit dialog opens. On a Mac with Writing Tools available, confirm the dialog offers Writing Tools and keeps the saved clip as plain text. Press `Command + R` and confirm the rename dialog opens.
9. With a card focused, use Left/Right, Page Up/Page Down, Home, End, `Command + Up`, and `Command + Down`; confirm selection and focus move together across the shelf. Select two clips, press `Delete`, confirm both disappear, then press `Command + Z` and confirm both return selected.
10. With a card or collection chip focused, type a normal character and confirm focus returns to search with that character inserted and results filtered.
11. Use a mouse wheel or two-finger vertical scroll over the card shelf and a crowded collection rail; confirm each pans horizontally, clamps at both ends, and shows subtle edge fades only where more content is hidden, with collection-chip labels fading cleanly instead of hard-clipping at the utility buttons.
12. Right-click a filtered result and choose Show in Clipboard, or press `Command + G` with the result card focused, and confirm search clears while the same card stays selected in Most Recent. Press `Command + O` on a focused link, file, or media card and confirm it opens.
13. Press `Esc` once with a non-empty search while the search field, a card, or a collection chip is focused and confirm search clears without closing the panel.
14. Press `Esc` again and confirm the panel closes.
15. Reopen the panel, change sort segments, and confirm each segment updates results.
16. 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.
17. Press `Command + N` or the shelf pencil button, enter text, and confirm a new text clip appears selected in the active shelf; on a Mac with Writing Tools available, confirm the new-text dialog offers Writing Tools. Repeat while Client Work is selected and confirm the clip is created in that collection.
18. Return to Clipboard, select a card, use its Collect button to choose Client Work, and confirm the Client Work chip count increases.
19. 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. Confirm collection-assigned clips stay in Client Work even as normal clipboard history rolls over.
20. Create a second collection, then press `Command + Right` and `Command + Left`; confirm selection moves between collections and wraps around.
21. Right-click the Client Work chip, choose Edit Collection..., rename it, change its color, and confirm the chip and assigned card headers update.
22. Right-click a custom Pinboard chip, choose Export Pinboard..., save a `.clipboredarchive`, import it into a fresh test profile from Settings > Data, and confirm only that Pinboard's clips, empty Pinboard state, and color are restored.
23. Confirm collection chips with 0 clips do not show a visible count pill, while chips with clips still show their counts.
24. 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.
25. Right-click an image card or focus its action rail, choose `Rotate Image`, and confirm the preview updates while the title, Pinboard, source app, and searchable image text remain. Then choose `Extract Text` on an image containing text and confirm the card shows the extracted text and `Copy Plain Text` can copy it.
26. Double-click an item and confirm it attempts to paste or falls back to copy without creating a duplicate history entry.
27. Right-click a card, use Capture Rules to ignore its source app, copy from that app again, and confirm the new item is skipped.
28. Drag an unassigned card onto the renamed collection chip and confirm the chip count increases and the card appears when that collection is selected.
29. Open the panel and confirm it appears as a vertical side shelf with compact horizontal rows; hover a row and confirm it expands in place. On a multi-display setup, open from the global shortcut with the pointer on each display and confirm the shelf opens on that display; click the menu-bar icon and confirm it opens on the menu-bar icon's display. Switch Spaces or use a full-screen app and confirm the shelf opens on the active Space instead of following every desktop.
30. Switch Settings > General > Panel > Shelf side between Left and Right. Reopen the panel after each change and confirm it slides from the configured side and keeps the category row centered.
31. Select a file, rich text, or URL card, then hover or keyboard-focus it and confirm the action rail exposes `Paste Plain Text`, the corner source/kind badge remains visible, and on a narrow shelf secondary actions collapse behind `More` instead of overflowing the card.
32. Confirm card footers do not show `Unknown` for clips without a source app, confirm used clips show their usage count beside the source app, and confirm clips synced or imported from another Mac show that copied-on device in the footer.
33. Confirm card headers use readable relative ages such as `3 minutes ago` or `2 hours ago`, including when viewing a named collection.
34. Confirm the selected card shows a green corner Stack control, the hover/focus action rail does not duplicate Stack, and clips added to Stack keep a visible corner indicator when selection moves away.
35. Confirm text cards use a quiet paper-style body, single-line text cards do not repeat the same text in both title and body, and multi-line text cards show the remaining lines below the first line with a subtle bottom fade.
36. Confirm the Pinned empty state points to the Pin action instead of a plain-key shortcut.
37. Confirm each card's source or type badge reads as an attached header-corner tile instead of a small floating icon.
38. Confirm built-in collection chips use recognizable glyphs, while custom collection chips keep color-dot swatches.
39. Confirm cards from known apps show app identity in the header tile, falling back to source initials when an app icon is unavailable.
40. Confirm single-file and PDF cards show a centered document-cover preview with a file-type pill, while multi-file cards show a stacked file preview.
41. Confirm the shelf chrome uses one row with compact search, collection chips, and separate soft utility icon buttons instead of a heavy grouped block; typing a search expands the search field without pushing cards out of view.
42. Confirm the top collection rail reads as a quiet translucent strip, the selected collection is a subtle pill, and the active card visibly floats above neighboring cards while the rest stay docked in the shelf.
43. Copy a color swatch from a design tool and confirm it appears as a centered paint-chip Color card, can be filtered with the Colors chip, and copies back as both a color and hex text.
44. Copy a code snippet from an editor and confirm it appears as a Code card, remains visible in the Text chip, can be isolated with the Code chip or `type:code`, and copies back as plain text.
45. Copy a video/movie clip and confirm it appears as a Video card, uses a movie-frame thumbnail when available, filters with the Videos chip or `type:video`, `type:movie`, and `mp4`, previews/opens as a temp movie, and copies back as movie data.
46. Press `Shift + Command + C`, confirm the Stack chip appears active with 0 clips, copy two text snippets, and confirm Stack count increments and the Stack view shows them in copy order. Press `Shift + Command + C` again and confirm capture stops.
47. Filter to a few clips, right-click a card or the Stack chip, choose Add Visible Clips to Stack, and confirm only the visible clips are queued once in shelf order.
48. With multiple text-like clips in Stack, choose Copy Stack as Text or Paste Stack as Text and confirm the queued text is written in stack order with blank lines between clips and consumed from Stack.
49. Command-click non-adjacent cards and Shift-click a range; confirm the status count changes to selected clips, selected cards remain highlighted while hovering, and the card menu offers Paste Selection, Copy Selection, Paste Selection as Text, Copy Selection as Text, and Add Selection to Stack.
50. With a card focused, press `Shift` with Left/Right, Page Up/Page Down, Home, or End to extend the range, then press `Command + A` and confirm every visible card is selected without changing the active card.
1. Confirm cards form one vertical list beside the category rail, with full-color kind or Pinboard headers and readable alignment at both left and right shelf positions.
2. Hover an unselected card and confirm only that card's preview expands. The selected/focused card, selected range, visible results, and keyboard navigation must not change, and the content must remain unobscured.
3. Move focus with the keyboard while another card is hovered. Confirm focus moves from the keyboard selection and stale hover expansion clears.
4. Confirm card and category transitions feel continuous rather than snapping. Enable macOS System Settings > Accessibility > Display > Reduce Motion and confirm panel, search, category, and card transitions become immediate; restore the original system setting afterward.
5. Right-click representative text, link, image, file, and media cards. Confirm applicable commands appear in the context menu: paste/copy, plain-text variants, preview/open, edit/rename, pin/collect, Stack, image rotate/extract text, capture rules, show in Clipboard, and delete.
6. With a card focused, confirm `Return` pastes, `Shift + Return` uses plain text, `Command + C` copies, `Space` or `Command + Y` previews, `Command + O` opens applicable clips, `Command + E` edits text/code, and `Command + R` renames without changing the payload.
7. Press `Command + 1` through `Command + 9` and confirm the matching numbered cards are used; add `Shift` and confirm plain-text output.
8. Command-click non-adjacent cards and Shift-click a range. Confirm context-menu batch actions use the selected set and `Command + A` selects all visible cards.
9. Delete multiple selected clips, press `Command + Z`, and confirm the batch returns selected.
10. From a filtered result, choose Show in Clipboard or press `Command + G`; confirm search clears and the same clip stays selected in Clipboard.
11. Double-click a card and confirm paste or copy fallback occurs without creating a duplicate history item.
12. Confirm text cards do not repeat a one-line title in the body, multi-line text shows the remaining lines, files/PDFs use document previews, and missing source apps do not display `Unknown`.
13. On multiple displays and Spaces, confirm the shortcut opens on the pointer's active display/Space and the menu-bar icon opens on its display. Switch Settings > General > Shelf side and verify both left and right placement.
## Pinboards And Stack
1. Press `Shift + Command + N` or use the category-row add button to create an empty color-coded Pinboard named `Client Work`; confirm its labeled chip remains visible with no count pill.
2. Assign an existing card through Collect or drag it onto the Pinboard. Confirm its count, color, and assignment survive relaunch and normal history pruning.
3. Right-click the Pinboard chip to edit its name/color, export it, and delete it. Import the archive into a fresh profile and confirm clips plus empty Pinboard metadata are restored.
4. Press `Shift + Command + C`, copy two clips, and confirm Stack capture records them in copy order. Toggle capture off again.
5. From a card or Stack context menu, test Add Visible Clips to Stack, next-item paste/copy, Stack-as-text, and Clear Stack. Confirm queue order and duplicate protection.
## Copy And Paste
1. Select a text item and press the Copy button. Confirm the system clipboard contains that text.
2. Select a URL item and confirm the system clipboard contains both string and URL data by pasting into a browser address bar.
3. Select one-file and multi-file File items and paste into Finder or an app that accepts file references. Confirm all files are preserved for the multi-file item.
4. Select an audio item and paste into an app that accepts sound pasteboard data.
5. Select a Video item and paste into an app that accepts movie pasteboard data.
6. Select a PDF item and paste into Preview, Finder, or an app that accepts PDF pasteboard data.
7. Select a rich text item and paste into TextEdit rich text mode or Mail. Confirm basic formatting is preserved and plain-text paste still works in a text-only field.
8. Press `Command + 1` through `Command + 9` on visible numbered cards and confirm the matching card is pasted or copied; add `Shift` and confirm URL/rich items paste as plain text only.
9. Without Accessibility permission, confirm paste actions copy and show the permission fallback status.
10. With Accessibility permission granted, confirm paste returns focus to the previous app and inserts the selected item.
1. Copy/paste text, URL, image, audio, video, PDF, rich text, and file clips into suitable apps. Confirm original pasteboard representations are preserved.
2. Paste a multi-file clip into Finder and confirm all file references are present.
3. Use plain-text paste on URL and rich-text clips and confirm formatting and non-text representations are omitted.
4. Without Accessibility permission, confirm paste falls back to copying and Settings > Privacy reports the permission requirement.
5. With Accessibility permission, confirm paste returns focus to the previous app and inserts the selected clip.
## Settings
1. Open Settings with `Command + ,`.
2. Change Shelf side, Keep History, history length, default sort, polling profile, cache limit, ignored apps, and allowed content types; quit and reopen the app; confirm settings persist.
3. Change the open-panel shortcut and confirm the old shortcut no longer opens the panel and the new shortcut does.
4. Toggle `Pause clipboard capture` or press `Command + T`, copy text, and confirm paused capture does not record it; press `Command + T` again and confirm capture resumes.
5. Toggle `Exclude likely secrets`, copy a representative token, and confirm it is not recorded.
6. Toggle `Hide panel from screen sharing and recordings`, open the clipboard panel, and confirm it is omitted from a macOS screenshot or screen recording; disable it and confirm capture works normally again.
7. Use `Open Accessibility Settings` and confirm System Settings opens to the permission area or fallback settings app.
8. Use Settings > Data > `Export Archive...`, save a `.clipboredarchive`, then import it into a fresh or cleared test profile and confirm text, Pinboard assignments, image previews, and PDF/audio/video/rich-text clips reappear; confirm external file-reference clips still point at their original paths.
9. In an ad-hoc local build, turn on Settings > Data > `Sync history with iCloud` and confirm the status reports iCloud Sync unavailable. In a signed build with an iCloud entitlement, use `Sync Now`, then restore into a fresh test profile and confirm text, Pinboard assignments, and app-managed attachments reappear.
10. Use `Clear Clipboard History` and `Clear Thumbnail Cache`; confirm each shows a warning confirmation before deleting data.
1. With the ClipBored pane open, open Settings with `Command + ,` and confirm a resizable window with six clean selector tabs: `General`, `Shortcuts`, `Capture`, `Privacy`, `Performance`, and `Data`. Close the pane, switch to another app, and confirm `Command + ,` opens that app's settings instead of ClipBored.
2. Resize to the practical minimum. Visit every tab and confirm content stays anchored to the top-left, scrolls vertically when needed, and has no horizontal clipping, zero-sized controls, or duplicate selector labels.
3. In General, change shelf side, Keep History, history length, default sort, launch-at-login, and menu-bar/Dock presence. Relaunch and confirm persistence.
4. In Shortcuts, change the open shortcut, close Settings while a field is focused, and confirm the edited binding is committed and the old shortcut no longer opens the shelf.
5. In Capture, pause/resume capture, toggle likely-secret exclusion and image-label search, edit ignored apps, and toggle allowed content types. Confirm at least one content type must remain enabled and status feedback stays inside this tab.
6. In Privacy, test clear-on-quit, screen-capture hiding, Accessibility settings, permission refresh, and paste-status feedback.
7. In Performance, change the polling profile and image-cache cap. Confirm capture continues at the selected cadence, UI input remains responsive while polling, and the values persist.
8. In Data, export/import an archive, exercise available iCloud controls, open the history folder, and confirm destructive clear actions require confirmation and show success or error feedback in the Data page.
9. Change one control and confirm unrelated tabs do not jump, reset, or flash; long status messages should wrap instead of widening the window.
## Storage And Privacy
1. Open the data folder from Settings > Data.
2. Confirm `history.sqlite` exists after capture.
3. Copy unique text and confirm `strings ~/Library/Application\ Support/ClipBored/history.sqlite | grep "unique text"` does not find it.
4. Copy uniquely identifiable rich text/audio/video/PDF data and confirm `strings ~/Library/Application\ Support/ClipBored/attachments/* | grep "unique text"` does not find it.
5. Export an archive with unique test content and confirm the archive file is treated as sensitive backup material because it is portable and not encrypted by ClipBored.
6. If iCloud Sync is enabled in a signed build, confirm `ClipBored.clipboredarchive` in the app-private iCloud container is treated as sensitive backup material because it is portable and not encrypted by ClipBored.
7. If `history-encryption.key` exists, confirm it is readable only by the current user.
8. Confirm image files are under `images/` and rich text/audio/video/PDF attachments are under `attachments/`.
9. Confirm app storage is local to `~/Library/Application Support/ClipBored` when iCloud Sync is off.
10. Open or reveal an encrypted image/audio/video/PDF, then quit ClipBored and confirm `/tmp/ClipBored/Previews` is removed.
11. Use `Clear Clipboard History` and confirm saved history, app-managed attachments, temporary previews, and `history-encryption.key` are removed when that fallback key exists.
12. Confirm quitting with `Clear history on quit` enabled removes history and app-managed cache/attachment files.
1. Open the data folder and confirm `history.sqlite`, `images/`, and `attachments/` appear as applicable.
2. Copy unique text and managed attachment data; confirm `strings` does not expose the test content from the SQLite database or encrypted sidecars.
3. Treat exported and iCloud `.clipboredarchive` files as sensitive because they are portable and not encrypted by ClipBored.
4. Open or reveal encrypted media, quit ClipBored, and confirm temporary preview files under `/tmp/ClipBored/Previews` are removed.
5. Clear history and confirm saved rows, managed attachments, temporary previews, and the fallback encryption key are removed when present. Clear the thumbnail cache separately and confirm history remains.
6. Enable `Clear history on quit`, relaunch, and confirm history and managed cache/attachment files were removed.
## Launch And Lifecycle
1. Enable Launch at Login, log out and back in, and confirm ClipBored starts.
2. Disable Launch at Login and confirm it no longer starts after the next login.
3. Right-click the menu-bar icon and confirm the status menu opens with capture state, clip count, Show Clipboard, Settings, Pause/Resume Capture, Pause for 5 Minutes, Pause for 30 Minutes, Pause for 1 Hour, and Quit.
4. Control-click the menu-bar icon and confirm the same status menu opens without toggling the panel.
5. Choose `Pause for 5 Minutes`, confirm the status detail shows a timed pause and copied text is not recorded, then choose `Resume Capture`.
6. Toggle manual Pause/Resume Capture from the status menu and confirm the status row changes.
7. Quit ClipBored from the menu bar and confirm no `ClipBored` process remains.
1. Enable and disable Launch at Login and verify the next login behavior each time.
2. Right-click or Control-click the menu-bar icon and confirm the menu includes capture state/count, Show Clipboard, New Collection, Stack Capture, Settings, pause/resume options, and Quit.
3. Test a timed pause, manual pause, and resume; confirm copied items are skipped only while capture is paused.
4. Quit from the menu-bar menu and confirm no `ClipBored` process remains.

View File

@@ -1,11 +1,15 @@
1. When hovering over a copied item, the options that appear on hover obscure the text.
2. When hovering over an item, key board arrow navigation breaks.
3. There needs to be some kind of animation? or smooothening effect when switching categories.
4. The search bar is broken when compressed, and clicking elsewhere should compress it again.
5. The search filter should just be the categories selected. Select categories with cmd.
6. There're a lot of categories, if there's nothing in them, don't show the categories by default.
7. There is a resize bar at the top lip of the window, don't bother with it. it doesn't allow resizing anywa.yz
8. Get rid of new text clip button. Don't need it.
9. Get rid of compact mode.
10. Get rid of close window. Don't need it. We use the macro from the keyboard.
11. Get rid of the status bar.
# UI Cleanup Resolution
All eleven findings from the original cleanup list are resolved in the current shelf design.
1. [x] **Hover controls obscured clip text.** Hover now expands the preview visually; commands are exposed through the context menu and keyboard, so no hover rail covers content.
2. [x] **Hover broke keyboard navigation.** Hover state is independent from keyboard focus and selection, and arrow navigation clears stale hover ownership without changing the wrong clip.
3. [x] **Category changes felt abrupt.** Search, category, card, and panel changes use short eased transitions and become immediate when macOS Reduce Motion is enabled.
4. [x] **The collapsed search control was broken.** Search has one aligned container: it expands on click, typing, or `Command + F`, and collapses after click-away only when the query is empty.
5. [x] **Filtering was split across categories and a search menu.** Category chips are the primary visual filters; a click replaces the filter and Command-click builds a union. `Command + F` only focuses search.
6. [x] **Empty built-in categories added noise.** Built-in type/sort chips are created only when they have matches or are selected; empty custom Pinboards remain visible by design.
7. [x] **A nonfunctional resize lip was visible.** The shelf has no resize handle and uses its fixed side-shelf frame.
8. [x] **New Text Clip was unnecessary.** The panel, menu-bar menu, and shortcut map no longer expose a new-text action.
9. [x] **The alternate compact mode was unnecessary.** Settings exposes one Side Shelf layout; row sizing adapts automatically to available space instead of presenting a mode toggle.
10. [x] **The panel did not need a close control.** There is no close button in shelf chrome; `Esc` and the configured global shortcut dismiss it.
11. [x] **The persistent status bar consumed space.** The shelf flows directly from its toolbar into the side-rail card list; feedback is shown in the relevant menu or Settings page.