feat: stack preview cards for successive captures + optional manual dismiss#83
feat: stack preview cards for successive captures + optional manual dismiss#83iOSDevSK wants to merge 1 commit into
Conversation
…smiss Screenshots taken in quick succession now stack up in the screen corner (newest at the bottom, up to 5 cards) instead of replacing each other - like CleanShot X. Each card keeps its own auto-dismiss timer, delete/edit/pin/close actions, and drag-out support; cards are ~30% larger for easier targets. Adds a "Keep visible until closed" toggle in Capture settings: when enabled, cards never auto-dismiss and stay until closed via their X button. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Someone is attempting to deploy a commit to the knox projects Team on Vercel. A member of the Team first needs to authorize it. |
| /// When true, preview overlay cards stay visible until manually closed | ||
| /// (no auto-dismiss timer). | ||
| static var overlayNeverDismiss: Bool { | ||
| get { UserDefaults.standard.object(forKey: overlayNeverDismissKey) as? Bool ?? false } |
There was a problem hiding this comment.
Nit: bool(forKey:) is simpler here and avoids the object(forKey:) as? Bool bridge.
| get { UserDefaults.standard.object(forKey: overlayNeverDismissKey) as? Bool ?? false } | |
| get { UserDefaults.standard.bool(forKey: overlayNeverDismissKey) } |
| dismissTask = Task { | ||
| private func scheduleDismiss(for id: UUID) { | ||
| cancelDismiss(for: id) | ||
| guard !AppPreferences.overlayNeverDismiss else { return } |
There was a problem hiding this comment.
One behavioral edge case: if a card already has a dismiss task scheduled and the user later enables “Keep visible until closed”, those already-scheduled tasks will still fire. Might be worth cancelling outstanding dismiss tasks when overlayNeverDismiss flips to true (UserDefaults change notification / onChange from prefs).
| let ext = item.url.pathExtension.lowercased() | ||
| let isVideo = ext == "mov" || ext == "mp4" | ||
| // Images load synchronously here; video thumbnails generate async in onAppear. | ||
| _thumbnail = State(initialValue: isVideo ? nil : NSImage(contentsOf: item.url)) |
There was a problem hiding this comment.
With stacking, this sync NSImage(contentsOf:) can happen multiple times back-to-back on the main thread. If you see UI hitching on rapid captures, consider loading images async (similar to the video thumbnail path) and setting thumbnail on the main actor.
What
Two quality-of-life changes to the floating preview overlay, matching CleanShot X behavior:
How it works
PreviewOverlaynow manages anitems: [Item]stack rendered by aPreviewStackView(VStack of cards) in a single panel that grows with the stack; per-item dismiss tasks replace the single global timerAppPreferences.overlayNeverDismiss+ toggle in Capture settings; the dismiss-delay slider is disabled while it's onScope
PreviewOverlay.swift,AppPreferences.swift,PreferencesView.swift(Capture tab),CHANGELOG.mdunder Unreleased. No version bump.Testing
make releasebuilds cleanNote: on the newest Xcode toolchains
mainitself doesn't compile — see #80 for minimal Swift 6 concurrency fixes. This PR is independent of it.🤖 Generated with Claude Code