Skip to content

Terminal: mouse text selection and copy - #21

Merged
wingleeio merged 2 commits into
zeronsh:mainfrom
neo773:feat/terminal-selection
Aug 6, 2026
Merged

wingleeio merged 2 commits into
zeronsh:mainfrom
neo773:feat/terminal-selection

Conversation

@neo773

@neo773 neo773 commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #20. The first of the three commits (15e424d, the light/dark palette) belongs to that PR and will drop out of this diff once it merges. The two commits to review here are 59f7c54 and d645210.

The terminal grid painted output but never let you take it back out — there was no way to highlight anything, so nothing could be copied.

What works now

  • Drag to select
  • Double-click for a word, triple-click for a line
  • Shift+click extends an existing selection
  • Cmd+C (or Ctrl+Shift+C) copies

How

Term already owns a selection and already knows how to keep its anchors on their text as output scrolls the grid underneath. So the emulator layer is a thin translation: viewport rows in, grid points out, selection_to_string for the copy. Cells carry a selected flag; the view merges selected runs into wash quads, painted over the cell background but under the glyphs so highlighted text stays readable.

Two details from reading zed's terminal

Zed embeds the same alacritty_terminal, and their mouse layer handles two things that are not obvious until they bite:

Overshoot has to force the selection side, not just clamp the cell. Drag past the bottom edge and drift left of where you started, and a side derived from x stops the selection mid-row instead of taking the last line whole. Clamping alone leaves that bug. Alacritty and zed both force Right past the right/bottom edge and Left above the top; so does this.

A press must not select on its own. Without a travel threshold, the click that focuses the panel leaves a one-cell selection behind whenever the hand moves a pixel. Harmless today, but it is exactly what clobbers the clipboard the moment anything copies on selection change. 2px, matching zed's SELECTION_DRAG_THRESHOLD and gpui's own div threshold.

Selection color

Deliberately achromatic — white at 22% on dark, black at 16% on light — rather than reusing Theme::selection, which is the composer's blue. A saturated veil sits on top of sixteen ANSI hues and drags every one toward itself: red output reads purple under selection, green reads teal. Neutral moves lightness only, so selected text keeps the colors the program asked for.

Not implemented: mouse reporting

Programs that ask for the mouse (vim, htop) still do not receive events. That was already true before this change — but it means selection now owns the mouse unconditionally instead of going dead inside those programs, which is the more useful behaviour while reporting is missing. Zed gates all of this behind mouse_mode(shift); when reporting lands here, that is the gate it has to go behind, and the check belongs in TerminalPanel::on_mouse_down.

Tests

12 new. Pointer hit-testing including overshoot sides and degenerate metrics; word and line granularity; a selection surviving the output that scrolls it; a click without a drag reporting no selection; the wash staying achromatic in both appearances.

comet-ui is 375/375 green, clippy and fmt clean. Verified by hand in the running app.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

neo773 and others added 2 commits August 6, 2026 07:32
The grid painted output but never let you take it back out — no way to
highlight anything, so nothing could be copied. This adds drag-to-select,
double-click word, triple-click line, shift-click extend, and Cmd+C.

`Term` already owns a selection and knows how to keep its anchors on their
text as output scrolls the grid, so the emulator layer is a thin translation:
viewport rows in, grid points out, `selection_to_string` for the copy. Cells
carry a `selected` flag and the view merges selected runs into wash quads,
painted over the cell background but under the glyphs so highlighted text
stays readable.

Two details from reading zed's terminal (which embeds the same
alacritty_terminal) that are not obvious until they bite:

Overshoot has to force the selection *side*, not just clamp the cell. Drag
past the bottom edge and drift left of where you started, and a side derived
from x stops the selection mid-row instead of taking the last line whole.
Clamping alone leaves that bug; alacritty and zed both force Right past the
right/bottom edge and Left above the top.

A press must not select on its own. Without a travel threshold the click that
focuses the panel leaves a one-cell selection behind whenever the hand moves a
pixel — harmless today, but it is the exact thing that clobbers the clipboard
the moment anything copies on selection change. 2px, matching zed's
SELECTION_DRAG_THRESHOLD and gpui's own div threshold.

Not implemented: mouse *reporting*. Programs that ask for the mouse (vim,
htop) still do not receive events — that was already true, and selection now
owns the mouse unconditionally rather than going dead inside those programs.
When reporting lands, `mouse_mode(shift)` is the gate it has to go behind, the
way zed does it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
It was borrowing Theme::selection — the composer's blue. That is the right
tone over prose, and the wrong one over a terminal: a saturated veil sits on
top of sixteen ANSI hues and drags every one toward itself, so red output
reads purple under selection and green reads teal. It undoes the per-hue
tuning the palette work exists to provide.

Neutral instead — white at 22% on dark, black at 16% on light. Lightness
moves, hue does not, so selected text keeps the colors the program asked for.
Light carries a little more ink because an equal alpha of black on near-white
reads fainter than white on near-black.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@wingleeio
wingleeio force-pushed the feat/terminal-selection branch from d645210 to d3b26ad Compare August 6, 2026 07:35

@wingleeio wingleeio left a comment

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.

Reviewed both commits line-by-line and verified locally after rebasing onto main: no network/filesystem/process/unsafe code, no dependency changes; the only clipboard interaction is a write on an explicit copy keystroke (no copy-on-select). Scanned the full diff for hidden/invisible Unicode (bidi overrides, zero-width) — none; only em-dashes/arrows in doc comments. 376/376 tests pass, zero clippy warnings in the changed files, fmt clean. Rebased the branch onto main (dropping the stacked #20 commit) via maintainer edit — content is byte-identical to the reviewed diff. Safe to merge.

@wingleeio
wingleeio merged commit 2b0dc84 into zeronsh:main Aug 6, 2026
1 check passed
guilhermexp referenced this pull request in guilhermexp/comet Aug 30, 2026
* Terminal: mouse text selection and copy

The grid painted output but never let you take it back out — no way to
highlight anything, so nothing could be copied. This adds drag-to-select,
double-click word, triple-click line, shift-click extend, and Cmd+C.

`Term` already owns a selection and knows how to keep its anchors on their
text as output scrolls the grid, so the emulator layer is a thin translation:
viewport rows in, grid points out, `selection_to_string` for the copy. Cells
carry a `selected` flag and the view merges selected runs into wash quads,
painted over the cell background but under the glyphs so highlighted text
stays readable.

Two details from reading zed's terminal (which embeds the same
alacritty_terminal) that are not obvious until they bite:

Overshoot has to force the selection *side*, not just clamp the cell. Drag
past the bottom edge and drift left of where you started, and a side derived
from x stops the selection mid-row instead of taking the last line whole.
Clamping alone leaves that bug; alacritty and zed both force Right past the
right/bottom edge and Left above the top.

A press must not select on its own. Without a travel threshold the click that
focuses the panel leaves a one-cell selection behind whenever the hand moves a
pixel — harmless today, but it is the exact thing that clobbers the clipboard
the moment anything copies on selection change. 2px, matching zed's
SELECTION_DRAG_THRESHOLD and gpui's own div threshold.

Not implemented: mouse *reporting*. Programs that ask for the mouse (vim,
htop) still do not receive events — that was already true, and selection now
owns the mouse unconditionally rather than going dead inside those programs.
When reporting lands, `mouse_mode(shift)` is the gate it has to go behind, the
way zed does it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Terminal: give the selection its own achromatic wash

It was borrowing Theme::selection — the composer's blue. That is the right
tone over prose, and the wrong one over a terminal: a saturated veil sits on
top of sixteen ANSI hues and drags every one toward itself, so red output
reads purple under selection and green reads teal. It undoes the per-hue
tuning the palette work exists to provide.

Neutral instead — white at 22% on dark, black at 16% on light. Lightness
moves, hue does not, so selected text keeps the colors the program asked for.
Light carries a little more ink because an equal alpha of black on near-white
reads fainter than white on near-black.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.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