-
Notifications
You must be signed in to change notification settings - Fork 52
feat(selection): Add triple-click and selection improvements #115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Add triple-click support to SelectionManager for selecting entire lines, matching standard terminal behavior. Detection uses click timing within 500ms threshold to distinguish from double-click word selection.
The previous implementation tried to track triple-clicks via the dblclick event, but browsers only fire dblclick on the second click (with detail=2), not the third. Triple-clicks never triggered line selection. Fix: Use the click event with event.detail: - detail === 2: double-click -> select word - detail >= 3: triple-click -> select line This is the standard browser API for detecting multi-clicks.
Double-click now selects entire paths (e.g., ~/foo/bar.ts) instead of breaking at slashes. Added characters to word boundary: - / (path separator) - . (file extensions) - ~ (home directory) - : (line numbers like file.ts:42) - @ (emails/usernames) - + (common in URLs/paths) This matches native Ghostty terminal behavior.
Match native Ghostty behavior: triple-click now selects only the actual line content, excluding trailing whitespace/empty cells. Previously it selected the entire terminal width which showed as a full-width highlight.
Triple-click was using viewport-relative getLine() to compute line length, which reads the wrong line when the viewport is scrolled into scrollback. Now uses the same scrollback-aware pattern as getSelection() - checking absoluteRow against scrollbackLength to choose between getScrollbackLine() and getLine().
When a line has only one character at column 0, the previous code set selectionEnd.col = 0, making start == end. hasSelection() then returned false, preventing the selection from being rendered or copied. Fix: use endCol + 1 to ensure start != end, and skip selection entirely for empty lines (endCol = -1). The extra column is harmless because getSelection() trims trailing empty cells.
…highlight The previous fix used endCol + 1 to avoid hasSelection() returning false for single-char lines, but this caused the renderer to highlight an extra trailing cell. Fix: modify hasSelection() to distinguish between drag selections (where same start/end means no drag happened) and programmatic selections (where same start/end is valid for single-char content like triple-click). - During drag (isSelecting=true): same-cell = no selection (unchanged) - Programmatic (isSelecting=false): same-cell = valid selection (new) This allows single-char line selections without highlighting extra cells.
A click without drag was leaving a one-cell selection that persisted after mouseup, which differs from native terminal behavior. Fix: in mouseup handler, check if start equals end (no drag happened) and clear the selection coordinates. This ensures clicks don't create selections while preserving programmatic selections (triple-click on single-char line, select() API). Simplified hasSelection() since same-cell from click-without-drag is now cleared in mouseup - any remaining same-cell selection is valid.
Changed selection rendering from solid color replacement to a semi-transparent overlay (40% opacity) that preserves original text colors. This matches VS Code's editor selection behavior and improves readability. - Selection background now overlays on top of cell background - Text keeps original colors unless selectionForeground is defined - Added TODO for configurable opacity via theme.selectionOpacity
Additional changes for discussionWe have a few more changes in our fork that enable VS Code webview integration. These are potentially VS Code-specific, so I wanted to gauge interest before submitting a separate PR. Compare: main...0xBigBoss:ghostty-web:feat/integration-hooks Changes included:
Would these be useful upstream, or are they too VS Code-specific? Happy to submit a separate PR if there's interest. |
Summary
This PR adds comprehensive selection improvements to ghostty-web:
event.detailfor reliable double/triple-click detection instead of custom timing logic/,.,-,_,~for selecting file paths (matching native Ghostty)Test plan
/usr/local/bin