Skip to content

Add full clip context menu actions - #42

Closed
alvst wants to merge 6 commits into
momenbasel:mainfrom
alvst:codex/clip-context-menu
Closed

Add full clip context menu actions#42
alvst wants to merge 6 commits into
momenbasel:mainfrom
alvst:codex/clip-context-menu

Conversation

@alvst

@alvst alvst commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

What changed

  • Adds a native right-click menu for clip cards with SF Symbol icons and macOS-style separators/shortcuts.
  • Adds dynamic Paste to , Paste as Plain Text, Copy, Rename, Delete, Preview, and Share actions.
  • Adds a colored Pin submenu and Create Pinboard….
  • Adds real clip-payload editing for text, rich text, links, and colors; edits update matching history and pinboard copies and refresh the clipboard without creating a duplicate history entry.
  • Shows native Writing Tools only for eligible text clips when macOS 15.2+ reports it is available.

Why

This implements the complete contextual workflow requested for a clip, including the system-provided Writing Tools path rather than an imitation.

Validation

  • swift build
  • VERSION=1.0.0 BUILD=1 ./scripts/build_app.sh
  • git diff --check
  • Demo-mode UI verification of the menu, Edit dialog, Preview, dynamic paste target, and Writing Tools availability gating.

Screenshots

Demo mode — Pesty window only.

Pesty clip context menu with paste target, Writing Tools, Pin, Preview, and Share

Pesty Pin submenu with colored pinboards and Create Pinboard

Follow-up: editor polish

  • Replaces the small alert editor with a resizable native editing panel suitable for long clips.
  • Adds accessible rich-text controls, clear Save/Cancel actions, and Writing Tools access when available.
  • Uses a stable sheet surface with high-contrast toolbar controls and normal proportional statistics text.

Validation: swift build, git diff --check.

@alvst
alvst marked this pull request as ready for review July 26, 2026 22:05
@momenbasel

Copy link
Copy Markdown
Owner

A lot of good work here, and two blockers.

The edit path can make CloudKit delete the clip you just edited. addCaptured dedupes by sameContent before inserting; updateTextContent / updateColorContent do not. Concretely: history holds B("hello", newer) and A("world", older). Edit A to "hello" and save - updateContent rewrites A in place without bumping createdAt, so there are now two clips with the same content key. Sync pushes A. The other device sees the local duplicate B is newer and drops the incoming record, then reconcileShadow finds no local record named A and enqueues a deleteRecord for it. That deletion comes back and removes A from the machine you edited it on. Route edits through the same sameContent check, and decide the tie-break deliberately - bumping createdAt alone just moves the loss onto the other clip.

The editor has no working Cmd-C / Cmd-V / Cmd-Z / Cmd-A / Cmd-F. Pesty never installs a main menu - Main.swift builds NSApplication by hand and the only NSMenu is the status item. NSTextView gets those commands from the Edit menu key equivalents, so with NSApp.mainMenu == nil they are all dead, and allowsUndo / usesFindBar are inert. A minimal main menu is the real fix and it unblocks several other things (it also fixes Cmd-Q and makes the Settings window closable with Cmd-W).

Smaller ones:

  • saveButton.keyEquivalent = "\r" never fires, because the editable text view owns Return and inserts a newline. Bind Save to Cmd-Return.
  • previewedItemID is set but never cleared, and the preview window has no delegate - so after closing a preview, editing that same clip silently re-opens it.
  • The pinboard branch of updateContent assumes pinboard copies share the history item UUID. 1.2.0 changed that: each container mints its own UUID. So edits stop propagating to anything pinned on 1.2.0+ while still propagating for older items - two behaviours with no way to tell which you have.
  • renameItem pre-fills with displayTitle rather than customTitle ?? "", so pressing OK unchanged bakes an auto-generated title in permanently.
  • Editing a clip silently overwrites the live system clipboard, which nothing in the UI suggests.

My real ask: split this. Six commits and ~921 lines bundle five independent features plus two infrastructure fixes. The two infrastructure fixes are the strongest part and could land today on their own - the key-monitor scoping (guard event.window === barController?.window, which fixes typing into the Rename and Create Pinboard alerts) and pasteTarget. I would merge both this week.

momenbasel added a commit that referenced this pull request Aug 12, 2026
…et robustly (#66)

Two infrastructure fixes extracted from #42:

- The local key monitor now ignores events destined for any window other
  than the bar panel, so typing into the Rename / Create Pinboard alerts
  (and any future editor or Settings surface shown while the bar is up)
  reaches that window instead of being swallowed into search.

- Pasting resolves its target through lastActiveApp -> previousApp ->
  frontmost, skipping Pesty itself and terminated apps. previousApp is
  only captured inside showBar(), so the menu-bar and reopen paths used
  to paste into a stale target. showBar() also keeps lastActiveApp in
  sync when it captures the frontmost app.

Co-authored-by: momenbasel <momenbasel@users.noreply.github.com>
Co-authored-by: Alvie Stoddard <alviestoddard@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

@momenbasel momenbasel left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintainer pass — infrastructure extracted and merged; requesting changes on the remainder.

Action taken: per the Aug 11 review's split request, the two infrastructure fixes landed on main today via #66 with Co-authored-by credit — the event.window === barController?.window key-monitor scoping (which fixes typing into the Rename / Create Pinboard alerts) and the pasteTarget chain (lastActiveApp → previousApp → frontmost, skipping Pesty/terminated apps) with showBar() keeping lastActiveApp in sync. Please rebase; those hunks will drop out.

What still blocks the rest (unchanged since the review — last commit here predates it):

  1. Edit-path CloudKit data loss: updateTextContent/updateColorContent skip the sameContent dedupe, so an edit that collides with an existing clip creates two records with one content key, and the sync reconciliation deletes the edited record from the very device that edited it. Route edits through the dedupe and pick the tie-break deliberately.
  2. The editor is dead without a main menu: NSApp.mainMenu is nil, so ⌘C/⌘V/⌘Z/⌘A/⌘F never reach the NSTextView and allowsUndo/usesFindBar are inert. A minimal main menu is the real fix (and also fixes ⌘Q / ⌘W app-wide).
  3. Smaller, all still open: Save bound to Return conflicts with typing newlines (bind ⌘↩); previewedItemID never cleared and no preview-window delegate; updateContent assumes pinboard copies share the history UUID (false since 1.2.0 minted per-container UUIDs); renameItem pre-fills displayTitle instead of customTitle ?? "", baking auto-titles in; editing silently overwrites the live clipboard with no UI hint.

The Preview/Share/Edit/menu work itself is genuinely good — split it into reviewable pieces on top of current main and each one can land quickly.

— automated maintainer pass on behalf of @momenbasel

@momenbasel

Copy link
Copy Markdown
Owner

Superseded by #78, which reimplements this on current main with both blockers fixed: edits bump createdAt and dedupe same-content clips explicitly so CloudKit reconciliation can never delete the record that was just edited, and a minimal main menu is installed at launch so the editor's ⌘Z/⌘C/⌘V/⌘A/⌘F, ⌘Q, and ⌘W all work. All five smaller review items are addressed too (⌘↩ save, preview delegate, rename pre-fill, edit scope by record UUID, and no silent clipboard overwrite), and it builds on the #66 infrastructure rather than duplicating it. Your work is credited there via Co-authored-by — thank you! Closing this one in favor of #78. — automated maintainer pass on behalf of @momenbasel

@momenbasel momenbasel closed this Aug 12, 2026
momenbasel added a commit that referenced this pull request Aug 12, 2026
The context menu now offers Paste to <target app>, Paste as Plain
Text, Copy, Edit, Writing Tools, Rename, a Pin submenu with colored
pinboard dots, Preview, Share, and Delete. Editing opens a native
rich-text editor panel with formatting controls, live stats, undo,
find, and Writing Tools; colors get a color-well editor.

Edits can no longer make CloudKit delete the clip that was just
edited: updateContent bumps createdAt and removes same-content
duplicates in the same container as an explicit, deliberate delete, so
the edited record is always the newest copy of its content key and
remote dedupe can never drop it into a reconciliation delete. Edits
update the existing record in place - no delete and recreate, no
dropped fields - and pinboard copies with their own UUIDs are left
untouched while legacy shared-id copies stay consistent.

A minimal main menu is installed at launch so the editor's Cmd-Z/X/C/
V/A/F shortcuts, Cmd-Q, and Cmd-W actually work; the app stays an
accessory so no menu bar appears. Save in the editor is bound to
Cmd-Return since Return types a newline, Rename pre-fills the custom
title rather than baking in the auto-generated one, the preview window
clears its tracked item via a window delegate when closed, and editing
no longer silently overwrites the live system clipboard.

Supersedes #42. Builds on the key-monitor scoping and pasteTarget
infrastructure from #66.

Co-authored-by: momenbasel <momenbasel@users.noreply.github.com>
Co-authored-by: Alvie Stoddard <alviestoddard@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants