feat(tui2): add multi-click transcript selection (#8471)

Support multi-click transcript selection using transcript/viewport
coordinates
(wrapped visual line index + content column), not terminal buffer
positions.

Gestures:
- double click: select word-ish token under cursor
- triple click: select entire wrapped line
- quad click: select paragraph (contiguous non-empty wrapped lines)
- quint+ click: select the entire history cell (all wrapped lines
belonging to a
  single `HistoryCell`, including blank lines inside the cell)

Selection expansion rebuilds the wrapped transcript view from
`HistoryCell::display_lines(width)` so boundaries match on-screen
wrapping during
scroll/resize/streaming reflow. Click grouping is resilient to minor
drag jitter
(some terminals emit tiny Drag events during clicks) and becomes more
tolerant as
the sequence progresses so quad/quint clicks are practical.

Tests cover expansion (word/line/paragraph/cell), sequence resets
(timing, motion,
line changes, real drags), drag jitter, and behavior on spacer lines
between
history cells (paragraph/cell selection prefers the cell above).
This commit is contained in:
Josh McKinney authored and GitHub committed 2025-12-23 21:05:06 +00:00
1 parent 53eb2e9f27
commit 0130a2fa40
13 files changed
+3256 -1021

No files matched your search

+9 -1
View File
@@ -468,11 +468,19 @@ where
.indent_stack
.iter()
.any(|ctx| ctx.prefix.iter().any(|s| s.content.contains('>')));
let style = if blockquote_active {
let mut style = if blockquote_active {
self.styles.blockquote
} else {
line.style
};
// Code blocks are "preformatted": we want them to keep code styling even when they appear
// within other structures like blockquotes (which otherwise apply a line-level style).
//
// This matters for copy fidelity: downstream copy logic uses code styling as a cue to
// preserve indentation and to fence code runs with Markdown markers.
if self.in_code_block {
style = style.patch(self.styles.code);
}
let was_pending = self.pending_marker_line;
self.current_initial_indent = self.prefix_spans(was_pending);