mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
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:
1 parent
53eb2e9f27
commit
0130a2fa40
13 files changed
+3256
-1021
No files matched your search
@@ -1,4 +1,5 @@
|
||||
use pretty_assertions::assert_eq;
|
||||
use ratatui::style::Color;
|
||||
use ratatui::style::Stylize;
|
||||
use ratatui::text::Line;
|
||||
use ratatui::text::Span;
|
||||
@@ -382,34 +383,20 @@ fn blockquote_heading_inherits_heading_style() {
|
||||
fn blockquote_with_code_block() {
|
||||
let md = "> ```\n> code\n> ```\n";
|
||||
let text = render_markdown_text(md);
|
||||
let lines: Vec<String> = text
|
||||
.lines
|
||||
.iter()
|
||||
.map(|l| {
|
||||
l.spans
|
||||
.iter()
|
||||
.map(|s| s.content.clone())
|
||||
.collect::<String>()
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(lines, vec!["> code".to_string()]);
|
||||
assert_eq!(text.lines, [Line::from_iter(["> ", "", "code"]).cyan()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blockquote_with_multiline_code_block() {
|
||||
let md = "> ```\n> first\n> second\n> ```\n";
|
||||
let text = render_markdown_text(md);
|
||||
let lines: Vec<String> = text
|
||||
.lines
|
||||
.iter()
|
||||
.map(|l| {
|
||||
l.spans
|
||||
.iter()
|
||||
.map(|s| s.content.clone())
|
||||
.collect::<String>()
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(lines, vec!["> first", "> second"]);
|
||||
assert_eq!(
|
||||
text.lines,
|
||||
[
|
||||
Line::from_iter(["> ", "", "first"]).cyan(),
|
||||
Line::from_iter(["> ", "", "second"]).cyan(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -453,6 +440,12 @@ fn nested_blockquote_with_inline_and_fenced_code() {
|
||||
"> > echo \"hello from a quote\"".to_string(),
|
||||
]
|
||||
);
|
||||
|
||||
// Fenced code inside nested blockquotes should keep code styling so copy logic can treat it as
|
||||
// preformatted.
|
||||
for idx in [4usize, 5usize] {
|
||||
assert_eq!(text.lines[idx].style.fg, Some(Color::Cyan));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -654,7 +647,7 @@ fn link() {
|
||||
#[test]
|
||||
fn code_block_unhighlighted() {
|
||||
let text = render_markdown_text("```rust\nfn main() {}\n```\n");
|
||||
let expected = Text::from_iter([Line::from_iter(["", "fn main() {}"])]);
|
||||
let expected = Text::from_iter([Line::from_iter(["", "fn main() {}"]).cyan()]);
|
||||
assert_eq!(text, expected);
|
||||
}
|
||||
|
||||
@@ -663,8 +656,8 @@ fn code_block_multiple_lines_root() {
|
||||
let md = "```\nfirst\nsecond\n```\n";
|
||||
let text = render_markdown_text(md);
|
||||
let expected = Text::from_iter([
|
||||
Line::from_iter(["", "first"]),
|
||||
Line::from_iter(["", "second"]),
|
||||
Line::from_iter(["", "first"]).cyan(),
|
||||
Line::from_iter(["", "second"]).cyan(),
|
||||
]);
|
||||
assert_eq!(text, expected);
|
||||
}
|
||||
@@ -674,9 +667,9 @@ fn code_block_indented() {
|
||||
let md = " function greet() {\n console.log(\"Hi\");\n }\n";
|
||||
let text = render_markdown_text(md);
|
||||
let expected = Text::from_iter([
|
||||
Line::from_iter([" ", "function greet() {"]),
|
||||
Line::from_iter([" ", " console.log(\"Hi\");"]),
|
||||
Line::from_iter([" ", "}"]),
|
||||
Line::from_iter([" ", "function greet() {"]).cyan(),
|
||||
Line::from_iter([" ", " console.log(\"Hi\");"]).cyan(),
|
||||
Line::from_iter([" ", "}"]).cyan(),
|
||||
]);
|
||||
assert_eq!(text, expected);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user