Repository navigation
[Bug]: terminal Cmd+C copies blankness from an empty IME textarea #7677
Description
Before submitting
- I searched existing issues and did not find a duplicate of this diagnosis.
- I included enough detail to reproduce or investigate the problem.
User-facing report of the same symptom: #6173. This issue is the root-cause writeup.
Area
apps/web (desktop wraps this surface; Electron's Edit menu is the macOS trigger)
Steps to reproduce
- Open the integrated terminal in the desktop app on macOS.
- Select some terminal text (single line or multiline).
- Press Cmd+C, or use Edit → Copy.
- Paste into another app.
Expected behavior
The selected terminal text is on the clipboard.
Actual behavior
The clipboard is blank (multiline) or unchanged (short selections). Right-click Copy is also unreliable. Matches #6173.
Impact
Blocks work completely
Version or commit
Regressed in 1add47b32 (fix(web): add copying terminal selection with ctrl+c in the web app / #5638). Still present on main @ beab6886f.
Environment
macOS desktop app. Web is the same Ghostty canvas path; Electron { role: "editMenu" } makes Mac worse.
Root cause
The Ghostty terminal is a canvas plus a 1×1 empty IME textarea. There is no DOM selection of the visible text.
#5638 stopped calling writeTextToClipboard(getSelection()) on Cmd+C. It instead:
- Leaves the keydown default alive so a native
copyevent can fire. - On that event, always
preventDefault(),clipboardData?.setData(...), and cancels the deferrednavigator.clipboard.writeTextfallback. - Does not put the Ghostty selection into the textarea first.
Two things then write emptiness:
- Chromium/Electron copy of the focused empty textarea.
- On macOS, the application menu
{ role: "editMenu" }Copy accelerator (webContents.copy()/getSelectedText()), which often delivers acopyevent with noclipboardDataand then writes the empty DOM selection after renderer JS.
Because the handler claims the gesture even when clipboardData is null, writeText never runs, and the OS clipboard ends up blank.
Ghostty formatting is not the bug. ghostty_terminal_selection_format_buf on a cell-drag selection of hello\nworld returns hello\nworld. hasSelection() / getSelection() already use that string. The text is available; copy throws it away.
Analysis
Copy shortcut path (apps/web/src/terminal/ghostty/surface.ts):
isTerminalCopyShortcut+hasSelection()→ scheduleclipboard.writeText(selection)on a microtask, do notpreventDefault.inputcopylistener: ifhasSelection(),preventDefault, optionalsetData, bumpcopyShortcutToken(this cancels the microtask).
Desktop Edit menu (apps/desktop/src/window/DesktopApplicationMenu.ts):
{ role: "editMenu" }includes Copy with Cmd+C.- Renderer
keydown.preventDefaultdoes not stop that accelerator on macOS. - The accelerator copies whatever DOM selection exists. The IME textarea is empty.
Why multiline vs short looked different in #6173: a native empty copy sometimes clears the clipboard (writes "") and sometimes is a no-op (previous contents remain). Both are the empty-textarea path, not a Ghostty format split.
Right-click Copy in the custom terminal menu is a separate seam (writeTextToClipboard after await contextMenu.show). The Cmd+C blankness is the empty-textarea / cancelled-fallback race above.
Solution
- Before native copy runs, park the Ghostty selection in the hidden textarea and
select()it, so Electron Edit → Copy and a DOMcopyof the focused field have real text. - Only
preventDefault+ cancelwriteTextwhenclipboardDataexists andsetData("text/plain", selection)actually ran. Acopyevent with noclipboardDatamust leave the fallback alive and must not suppress the default copy of the primed textarea. - Clear the primed textarea on the next non-copy key, composition start, and
clearSelection(), so IME is not composing over leftover copy text.
Keep clipboard.writeText as the WebKit/no-copy-event path. Do not go back to racing an empty textarea against the Edit menu.
Tests to lock it:
applyTerminalCopyEvent: missingclipboardData→ do not claim the fallback.primeTerminalCopyInput: textarea value and selection cover the Ghostty string.- ABI: cell-drag selection via screen grid refs still formats with Ghostty copy semantics.
Activity