[GH-ISSUE #744] Feature: Click count tracking + word/line selection primitives #973

Open
opened 2026-03-14 09:10:34 +03:00 by kerem · 4 comments
Owner

Originally created by @phall1 on GitHub (Feb 26, 2026).
Original GitHub issue: https://github.com/anomalyco/opentui/issues/744

Summary

The renderer's selection system currently operates at character-level granularity with coordinate-based APIs (startSelection, updateSelection, getSelection). There's no built-in support for multi-click selection (double-click word, triple-click line) or text-coordinate mapping, which means consumers can't implement standard text selection UX without reverse-engineering internal layout math.

Problem

MouseEvent has no clickCount property, and the renderer has no word/line-level selection methods. Implementing double-click-to-select-word requires:

  1. Userland click counting (easy, but should be framework-level)
  2. Mapping screen (x, y) → text content → word/line boundaries → back to screen (x, y) to call startSelection/updateSelection

Step 2 is the blocker. Text layout (wrapping, padding, scroll offsets) is internal to each renderable. There's no public API to go from a character index in text to a screen coordinate, so consumers end up either hardcoding layout assumptions or reaching into renderable internals — both fragile.

Requested APIs

1. clickCount on MouseEvent

Track consecutive clicks at the same position within a time threshold (~400ms). This is standard in every GUI framework (DOM, Qt, GTK, etc.).

class MouseEvent {
  readonly clickCount: number; // 1 = single, 2 = double, 3 = triple
  // ... existing fields
}

2. Word/line selection on renderer

Methods that handle the text↔coordinate mapping internally, where the layout knowledge already lives:

class CliRenderer {
  // Select the word at screen position (x, y)
  selectWord(x: number, y: number): void;

  // Select the full line at screen position y
  selectLine(x: number, y: number): void;

  // During drag: snap selection extent to nearest word boundary
  // (for word-snap dragging after double-click-drag)
  updateSelectionWordSnap(x: number, y: number): void;
}

The renderer already owns both the text layout and the selection state, so this is a natural extension of the existing selection system.

Use Case

Standard terminal text selection UX:

  • Single click + drag: character-level selection (already works )
  • Double click: select word under cursor
  • Triple click: select entire line (within a <box> persumably)
  • Double click + drag: extend selection with word-level snapping
Originally created by @phall1 on GitHub (Feb 26, 2026). Original GitHub issue: https://github.com/anomalyco/opentui/issues/744 ## Summary The renderer's selection system currently operates at character-level granularity with coordinate-based APIs (`startSelection`, `updateSelection`, `getSelection`). There's no built-in support for multi-click selection (double-click word, triple-click line) or text-coordinate mapping, which means consumers can't implement standard text selection UX without reverse-engineering internal layout math. ## Problem `MouseEvent` has no `clickCount` property, and the renderer has no word/line-level selection methods. Implementing double-click-to-select-word requires: 1. Userland click counting (easy, but should be framework-level) 2. Mapping screen `(x, y)` → text content → word/line boundaries → back to screen `(x, y)` to call `startSelection`/`updateSelection` Step 2 is the blocker. Text layout (wrapping, padding, scroll offsets) is internal to each renderable. There's no public API to go from a character index in text to a screen coordinate, so consumers end up either hardcoding layout assumptions or reaching into renderable internals — both fragile. ## Requested APIs ### 1. `clickCount` on `MouseEvent` Track consecutive clicks at the same position within a time threshold (~400ms). This is standard in every GUI framework (DOM, Qt, GTK, etc.). ```typescript class MouseEvent { readonly clickCount: number; // 1 = single, 2 = double, 3 = triple // ... existing fields } ``` ### 2. Word/line selection on renderer Methods that handle the text↔coordinate mapping internally, where the layout knowledge already lives: ```typescript class CliRenderer { // Select the word at screen position (x, y) selectWord(x: number, y: number): void; // Select the full line at screen position y selectLine(x: number, y: number): void; // During drag: snap selection extent to nearest word boundary // (for word-snap dragging after double-click-drag) updateSelectionWordSnap(x: number, y: number): void; } ``` The renderer already owns both the text layout and the selection state, so this is a natural extension of the existing selection system. ## Use Case Standard terminal text selection UX: - **Single click + drag**: character-level selection (already works ✅) - **Double click**: select word under cursor - **Triple click**: select entire line (within a `<box>` persumably) - **Double click + drag**: extend selection with word-level snapping
Author
Owner

@viralcodex commented on GitHub (Mar 5, 2026):

Hi @simonklee, would take it up on the weekend and contribute for this if needed, anything I should know before implementing? Please let me know 😄

<!-- gh-comment-id:4006968110 --> @viralcodex commented on GitHub (Mar 5, 2026): Hi @simonklee, would take it up on the weekend and contribute for this if needed, anything I should know before implementing? Please let me know 😄
Author
Owner

@simonklee commented on GitHub (Mar 5, 2026):

Hi @simonklee, would take it up on the weekend and contribute for this if needed, anything I should know before implementing? Please let me know 😄

Thanks for asking - i haven't looked at it, but let me try to get back at you. If i forget just ping me on discord or x :)

<!-- gh-comment-id:4006996384 --> @simonklee commented on GitHub (Mar 5, 2026): > Hi [@simonklee](https://github.com/simonklee), would take it up on the weekend and contribute for this if needed, anything I should know before implementing? Please let me know 😄 Thanks for asking - i haven't looked at it, but let me try to get back at you. If i forget just ping me on discord or x :)
Author
Owner

@viralcodex commented on GitHub (Mar 5, 2026):

@simonklee, will ping you on X by Friday night if it is fine.

<!-- gh-comment-id:4007083524 --> @viralcodex commented on GitHub (Mar 5, 2026): @simonklee, will ping you on X by Friday night if it is fine.
Author
Owner

@viralcodex commented on GitHub (Mar 9, 2026):

@simonklee , pinged you but I think you didn't see my message.

<!-- gh-comment-id:4022558327 --> @viralcodex commented on GitHub (Mar 9, 2026): @simonklee , pinged you but I think you didn't see my message.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/opentui#973
No description provided.