Skip to content

Add Edit menu so ⌘V/dictation work in Settings text fields#514

Merged
dhilgaertner merged 1 commit into
mainfrom
feature/crow-512-settings-edit-menu-paste
Jun 15, 2026
Merged

Add Edit menu so ⌘V/dictation work in Settings text fields#514
dhilgaertner merged 1 commit into
mainfrom
feature/crow-512-settings-edit-menu-paste

Conversation

@dhilgaertner

Copy link
Copy Markdown
Contributor

Closes #512

Problem

Text fields across the Settings screens couldn't accept a paste — ⌘V did nothing and dictation/voice insertion failed.

Root cause

setupMenu() in AppDelegate.swift built a custom NSApp.mainMenu containing only an App menu — no Edit menu. On macOS the standard cut:/copy:/paste:/selectAll:/undo selectors are registered and routed through the Edit menu's items; with no Edit menu, ⌘V in a plain SwiftUI TextField/TextEditor was silently dropped (dictation uses the same responder-chain path).

Fix

Add a standard Edit menu (Undo/Redo/Cut/Copy/Paste/Select All) wired to the first responder in setupMenu(). App-wide fix — restores cut/copy/paste/select-all/undo everywhere.

The terminal is unaffected: GhosttySurfaceView.performKeyEquivalent returns true for any Cmd/Ctrl key when the surface is focused, and in macOS the key window's view hierarchy receives performKeyEquivalent before the main menu — so a focused terminal intercepts ⌘V/⌘C before the Edit-menu item can fire.

Verification

  • make app builds clean; full test suite passes (226 tests).
  • Edit menu appears with Cut/Copy/Paste/Select All (+ Undo/Redo); ⌘V pastes in Settings fields; terminal paste still works.

🤖 Generated with Claude Code

Settings text fields couldn't accept a paste — ⌘V did nothing and
dictation/voice insertion failed. The app built a custom NSApp.mainMenu
with only an App menu, so the standard cut:/copy:/paste:/selectAll:/undo
selectors were never registered on the responder chain and ⌘V in plain
SwiftUI TextField/TextEditor was silently dropped.

Add a standard Edit menu (Undo/Redo/Cut/Copy/Paste/Select All) wired to
the first responder in setupMenu(). This is an app-wide fix. The terminal
is unaffected: GhosttySurfaceView.performKeyEquivalent intercepts ⌘V/⌘C
at the view level before the main menu sees them.

🐦‍⬛ Generated with Claude Code, orchestrated by Crow

Co-Authored-By: Claude <noreply@anthropic.com>
Crow-Session: 8CC01877-519E-428E-90B8-63D473C0D838
@dhilgaertner dhilgaertner requested a review from dgershman as a code owner June 15, 2026 14:49
@dhilgaertner dhilgaertner added the crow:merge Crow auto-merge on green label Jun 15, 2026

@dgershman dgershman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code & Security Review

Critical Issues

None.

Security Review

Strengths:

  • Pure UI/menu wiring. No input handling, IPC, network, or persistence changes — no new attack surface.
  • Menu items use target = nil (via addItem(withTitle:action:keyEquivalent:)), which routes selectors through the first-responder chain. Standard Cocoa pattern; no privileged code paths added.
  • App-wide fix with minimal blast radius — only adds a submenu to NSApp.mainMenu.

Concerns: None.

Code Quality

Strengths:

  • Excellent in-code comment (AppDelegate.swift:1083–1087) explains the root cause, the fix, and why the terminal is unaffected, citing #512. Future maintainers won't have to reverse-engineer this.
  • Verified the terminal-isn't-affected claim against Packages/CrowTerminal/Sources/CrowTerminal/GhosttySurfaceView.swift:255: performKeyEquivalent returns true for any Cmd/Ctrl keyDown while the surface is non-nil, so a focused terminal intercepts ⌘V/⌘C before the main menu sees them. ✅
  • Redo correctly uses uppercase "Z" as the key equivalent, which auto-adds the shift modifier — matches the macOS HIG.
  • Selector(("undo:")) / Selector(("redo:")) as string-based selectors is the standard Cocoa pattern for undo:/redo: (they aren't @objc on a concrete class — they're routed through the responder chain to whichever responder implements them, e.g. NSTextView via its UndoManager). Same approach Interface Builder emits.
  • #selector(NSText.cut(_:)) / copy / paste / selectAll correctly target methods that any AppKit text responder implements; the responder chain handles dispatch.
  • setupMenu() is invoked once from applicationDidFinishLaunching (AppDelegate.swift:1039), so no risk of duplicate menu items on re-entry.
  • Hard-coded English titles ("Undo", "Cut", etc.) are consistent with the existing App menu items in the same function ("Restart Manager", "Hide Crow", "Quit Crow") — no localization regression.

Consider (Green, non-blocking):

  • The standard macOS Edit menu typically also offers "Paste and Match Style" (⌘⇧⌥V) and a Find submenu. The minimum viable set here is fine for fixing the Settings paste bug; richer items can land as a follow-up if a use case appears.

Summary Table

Color Meaning Verdict effect
Red Must fix Request changes
Yellow Should fix Request changes
Green Consider Approve allowed

Recommendation: Approve — driven by [0 Red, 0 Yellow, 1 Green] findings.


🐦‍⬛ Reviewed by Crow via Claude Code

@dhilgaertner dhilgaertner merged commit 5f90415 into main Jun 15, 2026
2 checks passed
@dhilgaertner dhilgaertner deleted the feature/crow-512-settings-edit-menu-paste branch June 15, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

crow:merge Crow auto-merge on green

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Settings text fields can't paste (⌘V / dictation) — app has no Edit menu

2 participants