Skip to content

feat(ui): Add y shortcut to yank selection to clipboard#262

Open
amix wants to merge 1 commit into
modem-dev:mainfrom
amix:amix/fix-copy-selection
Open

feat(ui): Add y shortcut to yank selection to clipboard#262
amix wants to merge 1 commit into
modem-dev:mainfrom
amix:amix/fix-copy-selection

Conversation

@amix

@amix amix commented May 9, 2026

Copy link
Copy Markdown

Context

Press y to copy the active terminal text selection to the clipboard. Uses pbcopy on macOS and falls back to OSC52 elsewhere. Action also surfaces in the Navigate menu and the help dialog.

What was changed

  • New y keybinding wired through useAppKeyboardShortcuts, the Navigate menu, and the help dialog
  • New src/ui/lib/clipboard.ts with yankActiveSelection (selection read + copy + status mapping) and copyTextToClipboard (pbcopy → OSC52 fallback)
  • New useTransientNotice hook hoisted to AppHost so yank acknowledgements and the startup update notice share one status-bar slot and one dismiss timer instead of competing
  • useStartupUpdateNotice refactored to publish through the shared channel

Out of scope

  • No clipboard read; yank only copies what the user has selected.

Press y to copy the active terminal text selection to the clipboard.
Uses pbcopy on macOS and falls back to OSC52 elsewhere. The action
also appears in the Navigate menu and the help dialog.

Routes the acknowledgement through a new useTransientNotice channel
so the yank message and the startup update notice share one
status-bar slot with one dismiss timer instead of competing.
@amix amix marked this pull request as ready for review May 9, 2026 15:59
@greptile-apps

greptile-apps Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a y keybinding to yank the active terminal selection to the clipboard, with a pbcopy-first/OSC52-fallback strategy on macOS. It also hoists transient status-bar notice state into a new useTransientNotice hook at the AppHost level so both the yank acknowledgement and the startup update notice share a single channel.

  • src/ui/lib/clipboard.ts: New module with copyTextToClipboard (pbcopy → OSC52 fallback) and yankActiveSelection (reads renderer selection, invokes copy, returns a YankResult status key).
  • src/ui/hooks/useTransientNotice.ts: New shared notice channel with last-writer-wins timer semantics; useStartupUpdateNotice is refactored to publish through it instead of managing its own state.
  • Wiring: yankSelection callback threaded through AppuseAppKeyboardShortcuts, the Navigate menu, and the help dialog; showNotice is now a required App prop supplied by AppHost.

Confidence Score: 5/5

Safe to merge. The yank feature is self-contained, the clipboard fallback chain is correct, and the notice-channel refactor is well-isolated in AppHost.

All changed paths are well-tested end-to-end (unit tests for clipboard, integration test for the full yank flow). The useTransientNotice refactor cleanly centralises timer ownership without changing observable behaviour for either the yank notice or the startup update notice. No data loss or correctness risk was found in the new code.

No files require special attention.

Important Files Changed

Filename Overview
src/ui/lib/clipboard.ts New clipboard module: pbcopy-first/OSC52-fallback logic is correct; null status from spawnSync (e.g. ENOENT) correctly falls through to OSC52.
src/ui/hooks/useTransientNotice.ts New hook cleanly manages a last-writer-wins notice channel; useEffect cleanup correctly calls clearDismissTimer on unmount.
src/ui/hooks/useStartupUpdateNotice.ts Refactored to publish through showNotice; own timer management removed cleanly; showNotice in dep array is safe because it is a stable useCallback reference.
src/ui/hooks/useAppKeyboardShortcuts.ts y keybinding added to both handlePagerShortcut and handleAppShortcut; missing return in pager path already tracked in prior thread.
src/ui/AppHost.tsx useTransientNotice hoisted here; showNotice forwarded to both useStartupUpdateNotice and App; notice state correctly survives App key-remounts.
src/ui/App.tsx showNotice added as a required prop; yankSelection callback wired correctly; CLIPBOARD_NOTICE_MESSAGES exhaustively covers all YankResult values.
src/ui/lib/clipboard.test.ts Good unit coverage: macOS pbcopy success/failure, Linux OSC52 path, and no-clipboard path all tested with injected mocks.
src/ui/AppHost.interactions.test.tsx New integration test verifies end-to-end yank flow including OSC52 invocation and status-bar notice; uses platform override correctly with try/finally cleanup.

Reviews (2): Last reviewed commit: "feat(ui): add y shortcut to yank selecti..." | Re-trigger Greptile

Comment on lines +183 to 186
if (key.name === "y" || key.sequence === "y") {
yankSelection();
}
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Every other branch in handlePagerShortcut ends with an explicit return. The y handler omits it, which is harmless today because it is the last branch, but will silently fall through if a future case is appended below it.

Suggested change
if (key.name === "y" || key.sequence === "y") {
yankSelection();
}
};
if (key.name === "y" || key.sequence === "y") {
yankSelection();
return;
}
};
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ui/hooks/useAppKeyboardShortcuts.ts
Line: 183-186

Comment:
Every other branch in `handlePagerShortcut` ends with an explicit `return`. The `y` handler omits it, which is harmless today because it is the last branch, but will silently fall through if a future case is appended below it.

```suggestion
    if (key.name === "y" || key.sequence === "y") {
      yankSelection();
      return;
    }
  };
```

How can I resolve this? If you propose a fix, please make it concise.

Comment thread src/ui/lib/clipboard.ts
Comment on lines +36 to +39
const result = spawnSync("pbcopy", [], {
input: text,
stdio: ["pipe", "ignore", "ignore"],
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 spawnSync has no timeout option, so if pbcopy ever stalls (e.g. macOS clipboard service unresponsive), the call blocks the Node.js main thread indefinitely, freezing the terminal UI with no escape path. A short timeout lets the OSC52 fallback kick in instead.

Suggested change
const result = spawnSync("pbcopy", [], {
input: text,
stdio: ["pipe", "ignore", "ignore"],
});
const result = spawnSync("pbcopy", [], {
input: text,
stdio: ["pipe", "ignore", "ignore"],
timeout: 2000,
});
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ui/lib/clipboard.ts
Line: 36-39

Comment:
`spawnSync` has no `timeout` option, so if `pbcopy` ever stalls (e.g. macOS clipboard service unresponsive), the call blocks the Node.js main thread indefinitely, freezing the terminal UI with no escape path. A short timeout lets the OSC52 fallback kick in instead.

```suggestion
      const result = spawnSync("pbcopy", [], {
        input: text,
        stdio: ["pipe", "ignore", "ignore"],
        timeout: 2000,
      });
```

How can I resolve this? If you propose a fix, please make it concise.

@benvinegar

Copy link
Copy Markdown
Member

Hey can you help me understand the use case? With iTerm or Ghostty I just drag-select and copy using the native terminal.

@stefanprodan

stefanprodan commented May 10, 2026

Copy link
Copy Markdown

Hey can you help me understand the use case? With iTerm or Ghostty I just drag-select and copy using the native terminal.

I find it very annoying that Cmd+C does not work. What's the point of allowing text selection if it's not possible to copy it?

@benvinegar

Copy link
Copy Markdown
Member

I find it very annoying that Cmd+C does not work.

Okay sounds like we just need to fix that then. Maybe related to #312.

@benvinegar

Copy link
Copy Markdown
Member

Closing this as superseded by the copy-selection work that landed in #306. The original copying gap tracked in #312 is now closed: diff/pager views support mouse-drag selection and clipboard copy via OSC 52, with copy-decoration handling in the current render path.

A future keyboard yank command could still be proposed separately, but this branch predates the current copy-selection implementation and would need a fresh design on top of it.

This comment was generated by Pi using OpenAI GPT-5

@benvinegar benvinegar closed this Jun 13, 2026
@benvinegar

Copy link
Copy Markdown
Member

Reopening — #306 fixed mouse-drag copy, but it did not add the keyboard y yank shortcut this PR proposes. Keeping this open to evaluate the keyboard-driven copy workflow separately.

This comment was generated by Pi using OpenAI GPT-5

@benvinegar benvinegar reopened this Jun 14, 2026
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.

3 participants