feat(selection): Add triple-click and selection improvements#115
feat(selection): Add triple-click and selection improvements#115sreya merged 14 commits intocoder:mainfrom
Conversation
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. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6ad518e433
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
lib/selection-manager.ts
Outdated
| ) { | ||
| // Clear same-cell selection from click-without-drag | ||
| this.selectionStart = null; | ||
| this.selectionEnd = null; | ||
| return; |
There was a problem hiding this comment.
Mark dirty rows when clearing same-cell selection
Clearing a click-without-drag selection here bypasses clearSelection(), so no dirty rows are recorded. Because hasSelection() now returns true for same-cell selections, a render can paint the highlight between mousedown and mouseup; after you null out the selection and return, the renderer has no reason to redraw that row, so the highlight can stick until some unrelated redraw (cursor blink/move, new text). Consider calling clearSelection() or at least markCurrentSelectionDirty() before nulling out the selection so the row is repainted.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
@0xBigBoss I noticed this as well while trying out your changes
Block cursors now re-draw the character underneath using the cursorAccent theme color, making text visible on the opaque cursor. Uses ctx.clip() to prevent wide characters from bleeding outside the cursor cell. Co-authored-by: Nathan Cooper <ncoop57@users.noreply.github.com>
… selection, and click handling - Add triple-click to select entire line (text content only, not full width) - Use event.detail for reliable double/triple-click detection - Expand word characters to include path separators like native Ghostty - Use scrollback-aware line retrieval in triple-click - Handle single-cell and single-character selections properly - Clear same-cell selection on mouseup via clearSelection() for proper dirty-row tracking - Add comprehensive tests for new selection features
6ad518e to
268dd2b
Compare
- Clear selection when user types by calling clearSelection() before firing the data event in the input callback (terminal.ts) - Add tests for scrollback line content accuracy - Add test for selection clearing on input - Add test for triple-click scrollback line verification
- selectLines(): use viewportRowToAbsolute() instead of incorrect viewportY + row formula - getWordAtCell(): convert viewport row to absolute row and use getScrollbackLine() when in scrollback region
On mousedown, only set selectionStart without selectionEnd. This prevents the renderer from highlighting the clicked cell before any drag occurs. On mouseup, check for null selectionEnd (no drag) instead of comparing start==end coordinates. This also enables single-character selection by dragging to the same cell, which previously looked like click-without-drag.
…hold Replace dragMovedToNewCell flag with a 5px pixel-distance threshold for selection drag detection. This prevents jitter from creating accidental selections while allowing precise single-char selection via intentional drag. - Track mousedown pixel position (mouseDownX/mouseDownY) - Only begin selection once mouse moves ≥5px from mousedown - Use squared distance comparison (no sqrt needed) - Clear selection on mouseup if threshold was never met
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a3fb59aa94
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
lib/selection-manager.ts
Outdated
| if (!this.selectionEnd || !this.dragThresholdMet) { | ||
| this.clearSelection(); |
There was a problem hiding this comment.
Preserve drag selections when pointer leaves canvas early
This mouseup guard clears the selection whenever dragThresholdMet is false, but that flag is only set in the canvas mousemove handler; if the user drags quickly outside the canvas, the document-level mousemove path can update selectionEnd while dragThresholdMet stays false, so the completed drag is discarded on mouseup. In practice this breaks edge/outside drags (including auto-scroll selection) for fast pointer movement.
Useful? React with 👍 / 👎.
…arding fast edge drags
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bf1a07faba
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
lib/renderer.ts
Outdated
| // Set CSS size (what user sees) | ||
| this.canvas.style.width = `${cssWidth}px`; | ||
| this.canvas.style.height = `${cssHeight}px`; | ||
| this.canvas.style.cursor = 'text'; |
There was a problem hiding this comment.
Do not force text cursor on the canvas element
Setting the canvas cursor directly to text overrides the inherited cursor from the terminal container, so the link-hover cursor updates in Terminal.handleMouseMove (this.element.style.cursor = link ? 'pointer' : 'text') are no longer visible over the canvas. In practice this regresses hyperlink affordance: links still work, but users no longer get the pointer feedback when hovering them.
Useful? React with 👍 / 👎.
lib/selection-manager.ts
Outdated
| const absoluteRow = this.viewportRowToAbsolute(cell.row); | ||
| this.selectionStart = { col: cell.col, absoluteRow }; | ||
| this.selectionEnd = { col: cell.col, absoluteRow }; | ||
| this.selectionEnd = null; // Don't highlight until drag |
There was a problem hiding this comment.
Keep an initial selection end for edge-drag autoscroll
Initializing selectionEnd to null on mousedown breaks a real drag path: if the pointer leaves the canvas before an in-canvas move updates selectionEnd, document mousemove can enter auto-scroll mode and skip end updates while scrolling, and the auto-scroll extension path only runs when selectionEnd is already set. Mouseup then treats the action as a click and clears it, so fast edge drags (especially from top/bottom rows) can lose the selection entirely.
Useful? React with 👍 / 👎.
… for edge drag support - Set canvas cursor to 'text' at creation, but update to 'pointer' on link hover (both canvas and container) so hyperlink affordance works - Removed redundant cursor set from renderer.resize() - Restored selectionEnd = start on mousedown so auto-scroll/edge drag works - hasSelection() returns false while isSelecting && !dragThresholdMet to prevent flash-highlight on click
|
@codex review |
|
Codex Review: Didn't find any major issues. 🚀 ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
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