From 6580a79d404201a515bc2d445c0e136bcc5a9fcb Mon Sep 17 00:00:00 2001 From: TZGyn Date: Fri, 9 Oct 2026 17:58:38 +0800 Subject: [PATCH] Select items by clicking the card instead of copying them Copying was a side effect of a click, which made it impossible to build a selection with the mouse and surprising when the intent was to pick something up rather than copy it immediately. A click now selects (or deselects), and copying is an explicit action from the selection bar or the right-click menu. --- AGENTS.md | 23 ++++++++++++----------- README.md | 21 +++++++++------------ Sources/Prompt/PanelView.swift | 17 +++++++++++------ 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7758a56..c8ce578 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,12 +33,11 @@ clicking an item copies it back to the clipboard. Saves on every mutation. - `Sources/Prompt/PanelView.swift` — the panel UI. Header (section switcher, search, add photos, close), item list, composer. Composer `# Name` creates/ - switches sections. Click = copy; the row checkbox selects, Cmd+click toggles - multi-select, Shift+click selects a range, and a selection bar offers Copy as - List / Copy as Lines; right-click = context menu acting on the selection - (Copy, Copy as List, Copy as Lines, Done, Edit, Open, Merge, Move To, - Delete). Esc clears the selection before it hides the - panel. Drop target for images/files. + switches sections. Click = select (Shift+click for a range); the selection + bar offers Copy (single item), Copy as List and Copy as Lines; right-click = + context menu acting on the whole selection (Copy, Copy as List, Copy as + Lines, Done, Edit, Open, Merge, Move To, Delete). Esc clears the selection + before it hides the panel. Drop target for images/files. - `Sources/Prompt/PanelController.swift` — owns the floating `NSPanel` (`.nonactivatingPanel`, `.floating`, right-edge docked, frame autosaved as `PromptPanel`), the Cmd+V image-paste monitor, and `EditWindowController` @@ -172,11 +171,13 @@ brew upgrade --cask prompt dispatches `paste:` to the responder chain itself for text, and adds the clipboard image to the store when there is no text. `Prompt --diag` dumps the menu so the wiring can be checked without a keyboard. -- **A row is not a `Button`** because it contains a checkbox `Button` for - selecting the item, and nested buttons never receive clicks. The row uses - `contentShape` + `onTapGesture` for click-to-copy instead. Selection is - driven by the checkbox, Cmd+click (toggle) and Shift+click (range); the - context menu acts on the whole selection via `effectiveIDs(for:)`. +- **A row is not a `Button`** because it contains a checkbox `Button`, and + nested buttons never receive clicks. The row uses `contentShape` + + `onTapGesture` so a click anywhere on the card selects it. Selection is + driven by the card click, the checkbox, Cmd+click (toggle) and Shift+click + (range); the context menu acts on the whole selection via `effectiveIDs(for:)`. + Copying is an explicit action (selection bar or context menu), never a side + effect of a click — that was changed deliberately. - **Never bind bare keys as `.keyboardShortcut` in the panel** (Space, Delete, plain Cmd+C). SwiftUI key equivalents are matched before text input and would steal characters from the composer TextField. Only Cmd-modified diff --git a/README.md b/README.md index a22eca6..54cbfc3 100644 --- a/README.md +++ b/README.md @@ -2,25 +2,22 @@ A native macOS app for keeping prompts and photos, in the spirit of [Copper](https://shadcn.com/copper) by shadcn: a floating panel that sits next -to your work. Click anything to copy it back to your clipboard. +to your work. Select what you need and send it back to your clipboard. -- **Checkbox to select** — every item has a checkbox; tick several and act on - them at once (copy, delete, move, merge). Clicking the row itself still - copies that item. -- **Click to copy** — text notes go back as text, photos go back as images. +- **Click to select** — clicking a card selects it (Shift+click for a range), + and the selection bar copies a single item as-is, or the whole selection as a + numbered list or as plain newline-separated lines. +- **Text or photo** — copying sends text back as text and a photo back as an + image. - **Capture from anywhere** — select text in any app and tap **Left Shift twice**. - **Photos** — paste (Cmd+V), drag & drop, or add via the photo button. - **Workspaces** — type `# Name` in the composer, or use "New Workspace" in the Cmd+K switcher (which opens a name modal); Cmd+K switches, and the up/down arrows in the switcher reorder them. (The code and the saved JSON still call these `sections` so existing data keeps working.) -- **Copy as List / Copy as Lines** — select with the checkboxes (or Cmd+click, - or Shift+click for a range), then copy the selection as a numbered list or as - plain newline-separated text, from the selection bar, the right-click menu, or - Shift+Cmd+C / Option+Cmd+C. The same menu deletes, moves or merges the - selection. -- **Merge Notes, Mark as Done, Edit, Move To** — right-click any item. -- **Search** — Cmd+F searches across all sections. +- **Right-click for actions** — Copy, Copy as List, Copy as Lines, Mark as Done, + Edit, Open, Merge Notes, Move To, Delete — each acting on the whole selection. +- **Search** — Cmd+F searches across all workspaces. - **Sync (optional)** — generate an 8-digit code (`1234-5678`) on one Mac and enter it on the other; no account, no login. The code is the only secret. - **Local & private** — everything lives in diff --git a/Sources/Prompt/PanelView.swift b/Sources/Prompt/PanelView.swift index 06e64f9..85a4af1 100644 --- a/Sources/Prompt/PanelView.swift +++ b/Sources/Prompt/PanelView.swift @@ -242,7 +242,7 @@ struct PanelView: View { } .contentShape(Rectangle()) .onTapGesture { handleClick(item) } - .help("Click to copy · checkbox to tick off · Cmd+click to select several · Shift+click for a range · right-click for more") + .help("Click to select · Shift+click for a range · right-click to copy, delete and more") .contextMenu { contextMenu(for: item) } } @@ -561,6 +561,12 @@ struct PanelView: View { .font(.caption) .foregroundStyle(.secondary) Spacer() + if selection.count == 1, let id = selection.first, let item = store.item(id) { + Button("Copy") { copy(item) } + .font(.caption) + .buttonStyle(.plain) + .foregroundStyle(Color.accentColor) + } Button("Copy as List") { copySelection(asList: true) } .font(.caption) .buttonStyle(.plain) @@ -669,17 +675,16 @@ struct PanelView: View { // MARK: - Actions + /// A click anywhere on the card selects it (Shift+click selects a range). + /// Copying is then an action on the selection — from the selection bar or + /// the right-click menu — rather than the side effect of a click. private func handleClick(_ item: PromptItem) { let modifiers = NSApp.currentEvent?.modifierFlags ?? [] - if modifiers.contains(.command) { - toggleSelection(item.id) - return - } if modifiers.contains(.shift), let anchor = lastSelectedID, anchor != item.id { selectRange(from: anchor, to: item.id) return } - copy(item) + toggleSelection(item.id) } private func toggleSelection(_ id: UUID) {