feat(cli): mouse text selection and copy in VP mode - #6937
Conversation
First step of the VP mouse-selection feature: the design doc, submitted ahead of implementation so the approach can be reviewed before code lands. Proposes an application-level selection and copy system for VP mode, where SGR mouse tracking currently suppresses native terminal selection. Evaluates two routes and commits to exposing the Ink renderer cell grid via the existing Ink patch, which makes coordinate-to-text mapping uniform across all content types. Implementation follows as staged milestones on this branch.
|
文档审计结论:当前设计建议先修订,再进入 M0/实现阶段。 Route B 的方向可以继续评估,但文档目前把“拿到 cell grid”直接等同于“能忠实恢复可复制文本”,中间还缺少两类关键数据;按现有描述实现,会无法满足文档自己定义的 fidelity goal。 阻塞项
与当前代码不一致的勘误
以上基于当前 PR commit |
|
补充做了实现可行性审计,直接核对了 技术结论Route B 的基础方向可行,但当前文档描述的 Route B 不足以实现全部 Goals,暂时不能按现方案批准。 更精确地说:
因此我建议保留 Route B,但先把方案改成 frame-buffer contract + transform contract,而不是“expose 从 Ink 源码得到的可实施边界
建议的可落地方案方案 B1:推荐的最小 v1如果目标是尽快、安全落地,我建议第一版明确收窄为:
这条路径确实是 small, well-scoped,Route B 相比 Route A 有明显收益,也符合仓库的 Simplicity First。双击词、三击视觉行、copy-on-select、Esc clear 都可在此基础上实现。 方案 B2:保留文档全部 Goals若必须第一版就支持软换行恢复、gutter exclusion、跨屏拖动,则至少还要增加:
此时 Route B 仍然技术可行,但它已经是 renderer semantic extension,不应再描述为仅“expose grid”的小 patch,也应按 core change 的风险来评估。 坐标公式建议不要使用 并先验证 M0 建议改成真正的 go/no-go 验收M0 不应只验证
最终建议:选择 Route B,但先决定 B1 还是 B2。B1 可以实施且风险合理;B2 也能做,但当前文档低估了 Ink patch 的语义扩展和虚拟化状态管理,不能按现方案直接开工。 |
|
进一步比较了其他可选路线。结论不是“当前 Route B 已经是最好方案”,而是要先按产品目标分层:
可选方案对比
方案 1:Terminal-native Copy ModeGemini CLI 当前实现已经验证了这条路线:alternate buffer 下按 建议行为:
这条方案不能满足文档里“soft-wrapped lines rejoin”的承诺,因为 Ink 已经把视觉 wrap 输出成换行,终端只能复制视觉文本;也不能提供应用自己的 selection highlight。但如果真实用户诉求是“让我能复制屏幕上的内容”,它比完整 Route B 简单和稳健得多,符合 Simplicity First。 方案 2:Raw frame buffer(当前 Route B 的合理边界)若产品明确要求鼠标按下即由应用拖选、松开 copy-on-select,则 raw frame buffer 仍然是比 Route A 更好的起点。应把 v1 限定为当前可见 viewport,复制视觉行,并在 scroll/resize/streaming 时清除 selection。这样 Ink patch 只需要 frame snapshot + pre-serialization selection transform + invalidation,风险可以控制。 这不是最终 fidelity 方案,但它是可验证、可分阶段交付的应用内 selection MVP。 方案 3:Semantic frame buffer(完整目标的推荐方案)如果 PR 的 Goals 都是硬要求,我建议把 Route B 重写成 Route C:在 Ink 的 最小 cell/span contract 类似: type SelectableCell = {
value: string;
width: 1 | 2;
sourceId?: string;
logicalOffset?: number;
selectable: boolean;
breakAfter: 'none' | 'soft' | 'hard';
};
这实际上是 Route A 与 Route B 的正确结合:语义来自 source/render node,最终坐标来自 compositor。它比 raw grid patch 深,但比长期维护 per-renderer serialization 更可靠。 推荐决策我建议 maintainer 先回答一个问题:本 PR 必须解决的是“复制可用”,还是“完整应用内 selection UX”?
不建议继续当前文档的中间状态:它按 raw grid 的改动规模估算成本,却按 semantic frame buffer 的能力承诺结果。这是当前方案最大的结构性问题。 |
|
基于当前 Qwen/Ink 代码结构,对完整目标做了工作量与代码量估算。以下是净新增生产逻辑 / 测试代码的区间,置信度约 ±30%;工作日包含实现、单测、E2E、终端兼容验证和一轮 review 修正,不包含等待 maintainer 的时间。 总量判断完整最终目标预计:
根据作者确认,实施拆成 2 个 PR:当前 #6937 完成完整的 Claude-like 视觉选择;soft-wrap 与 gutter semantic fidelity 合并到一个后续 PR。 PR 1 — 当前 #6937:完整视觉选择合并原计划的 foundation 与 rich visual selection,范围包括:
估算:
该 PR 交付完整的 Claude-like 视觉选择体验,但复制语义仍按屏幕视觉 cell:暂不承诺 soft-wrap rejoin 和 gutter/decoration exclusion。 这会使当前 PR 超过此前建议的 1,000 行附近控制线,并触及 Ink patch、CLI UI、config 与 cross-package contract。它是
当前 PR 的合并门槛
PR 2 — 后续 PR:Semantic copy fidelity合并原计划的 soft-wrap fidelity 与 gutter/decoration exclusion,范围包括:
估算:
将 3/4 合并是合理的,因为 hard/soft break 与
后续 PR 的合并门槛
当前设计文档需要同步修改
最终拆分结论:
这样可以保证当前 PR 已经解决“不改变 Static 用户习惯”的核心目标,同时把需要更深 Ink semantic contract 和跨 renderer 产品规则的部分集中到一个后续 PR。 |
Rework the design after the implementation-feasibility audit against the Ink 7 renderer source. Key corrections and scope changes: - Two-PR plan. PR 1 (this branch) delivers the Ink frame-buffer foundation and a visible-region visual-selection MVP (visual-cell copy, clear on scroll). PR 2 adds cross-screen selection and semantic copy fidelity (soft-wrap rejoin, gutter exclusion), which need renderer semantic metadata. - Highlight goes through a bidirectional frame controller with a pre-serialization transform and throttled repaint; the earlier read-only accessor plus post-commit callback could not highlight the current frame. - Immutable cell/style allocation to avoid leaking the highlight onto other on-screen occurrences of the same text via shared cached styled chars. - Corrected coordinate formula (frame is not unconditionally top-anchored), OSC 52 behavior (over-cap skips and returns false; surface a copy failure), and the note that release already has non-selection consumers. - Merge gate and milestones aligned to the two-PR split.
|
感谢这轮非常扎实的实现可行性审计——直接核对 Ink 7 renderer 源码的几条结论都成立,已按此修订设计文档(commit 一、接受并已修订的技术结论阻塞项
源码级要点也已并入设计: 勘误: 二、一处需要澄清
这个"确认"我们这边没有记录,可能是推断。实际决定确实是 2 个 PR,但边界与该评论的 PR1 不同:
也就是把该评论 PR1 里的跨屏累积挪到了 PR 2,让首个 PR 更小、更易收敛,符合 Simplicity First。PR 1 不混入任何语义 fidelity 工作;M0–M4 各自独立 commit;新增 Ink API 在本 PR 内有真实 consumer;PR 描述会点明 downstream consumers 与 mouse subscribers。 三、产品取舍关于"只要复制可用 vs 完整应用内选择":产品目标是应用内鼠标选择(拖选 + 高亮 + copy-on-select),不是仅恢复复制,因此不走纯 terminal-native Copy Mode。不过 Copy Mode / mouse toggle 作为逃生阀保留(见设计的 escape hatch),覆盖 patch 缺陷、无障碍与偏好原生选择的用户。 四、M0 go/no-go 门槛已采纳你建议的验收项作为 PR 1 merge gate:press 后首帧高亮无延迟且不成 render loop;屏幕多处相同文本只高亮目标 cell(防 设计已按上述修订,接下来从 M0(Ink frame controller)开始提实现 commit。若对 B1 边界或 Ink patch 加深仍有意见,欢迎在合入 M0 前指出。 |
First implementation milestone of VP-mode mouse text selection: the Ink frame-buffer foundation and its read-side wrapper, with the design's M0 go/no-go criteria proven by tests. Extend the Ink patch into a small bidirectional frame controller. The renderer now retains the composited cell grid, applies a selection-range background highlight before serialization (allocating new cells so the shared cached styled chars are never mutated), and publishes an immutable frame each render. setSelection deduplicates and schedules exactly one repaint through Ink's own throttle; getFrameController(stdout) exposes the bridge to the application. Add ScreenBuffer, the read-side wrapper the selection state machine (M1) will build on: getCellAt / lineText / dimensions plus setSelection and subscribe. Tests cover addressable cells, wide-character spacer handling, pre-serialization highlight and clear, no highlight leakage onto identical on-screen text, and single-frame-per-change dedup (no render loop). The package.json export additions from the existing patch are preserved.
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
Turn mouse press/drag/release in the VP history viewport into a text selection, highlighted live and copied on release. - SelectionState: anchor/focus model in composited-frame coordinates, normalized to reading order, with collapsed/empty queries. - selection-text: extract the visual text of a range, emitting a wide glyph once (skipping its spacer) and trimming per-line trailing padding. B1 fidelity — visual cells as shown; soft-wrap rejoin and gutter exclusion are PR 2. - selection-coords: map a terminal cell to frame grid coordinates via the frame anchor (bottom-pinned on overflow), plus viewport bounds/clamp. - TextSelectionController: headless controller that subscribes button-level mouse events, drives the state machine, highlights the range through the frame controller (setSelection), and copies on release. Ignores the scrollbar column and presses outside the viewport; clears on any scroll (visible-region only in B1). - VirtualizedList/ScrollableList expose getViewportRect(); MainContent mounts the controller in the VP branch. Copy-failure feedback, key preemption (Esc/Ctrl+Shift+C), streaming/resize invalidation, and word/line selection follow in M3/M4.
Add ui.textSelection.enabled (default true) and ui.textSelection.copyOnSelect (default true) and wire them into the selection controller: selection is only active when enabled, and release copies only when copyOnSelect is on. Disabling enabled leaves the mouse to the terminal for those who prefer it.
- Double-click selects a word, triple-click selects a line, driven by a multi-click detector in the controller; word/line spans come from the composited frame (non-whitespace run / first-to-last-content cell) and are copied on select. - Invalidate the selection when the content scrolls, streams (scrollHeight changes), or the terminal resizes (frame height changes / resize event), detected via a frame subscription with a baseline captured at selection start. Our own highlight renders leave those unchanged, so there is no render loop. - Document mouse selection in the keyboard-shortcuts reference and the ui.textSelection settings in the settings reference; refresh the useTerminalBuffer description and regenerate the IDE settings schema. A single click already clears a selection, so Esc-to-clear, the manual copy keybinding, the footer "copied" toast, and a drag discoverability hint are deferred follow-ups within this PR.
Code Review — VP mode mouse text selection and copy (PR 1)Reviewed the full diff (design doc + M0–M4 implementation). This is a well-structured, well-tested foundation. Notes below, most important first. OverviewAdds application-level mouse text selection + copy to VP mode (
Strengths
Issues & risks1. 2. PR body & design doc oversell what ships.
3. Copy failures are silently swallowed. 4. Mouse arbitration is scrollbar-only. 5. Invalidation subscription can silently fail to attach. Minor / nits
Test coverageUnit coverage of the pure modules is solid, including CJK/wide-char and backward-drag normalization. Missing: the E2E tmux harness and any test that exercises SecurityNo concerns — copies the user's own on-screen text through existing clipboard infra, VP-gated. Overall: the foundation and the Ink patch are in good shape. The main things to resolve before this leaves draft are the |
Real-terminal testing surfaced a MaxListenersExceededWarning (11 resize listeners) on startup: the selection controller added a stdout 'resize' listener on top of the existing ones. A resize reflows content, which changes the frame/scroll height already watched by the frame subscription, so the explicit listener was redundant. Remove it to stay under the max-listeners cap; invalidation on resize still works via the subscription.
wenshao
left a comment
There was a problem hiding this comment.
Well-structured foundation for VP-mode mouse selection. The Ink patch (immutable frame snapshot + pre-serialization highlight + throttled repaint) is minimal and additive, and the pure selection modules have solid unit coverage including CJK/wide-char edge cases. Five suggestions inline, mostly around edge cases in the selection controller (use-text-selection.tsx) and a settings description that overstates the effect of disabling the feature.
— qwen-latest-series-invite-beta-v77 via Qwen Code /review
Native TUI post-fix verificationVerified commit
The focused selection suite passed 5 files / 31 tests, and the local build and bundle completed successfully. CI separately passed the Ubuntu Node 22 full test job and Web Shell E2E smoke. Shift/Option native terminal selection is not claimable from a detached tmux harness: the GUI terminal owns that behavior before input reaches tmux, and injecting modifier-bearing SGR bytes would test application input rather than the host-native fallback. One independent observation: the first launch printed a |
|
Corrective E2E update for The earlier tmux E2E report was invalid because the injected mouse row was manually offset to reach the intended text. That proved the highlight path but masked the actual user-facing coordinate bug. I reproduced the failure with an unmodified non-debug The corrective commit now clears/homes the alternate screen before the first frame, suppresses the legitimate VP resize-listener warning for the lifetime of the VP UI, invalidates selections only when the history viewport changes, and fixes release-coordinate, wide-character spacer, and post-drag click-state edge cases. Fresh verification used a real non-debug
Focused selection/rendering tests pass 8 files / 67 tests, CLI typecheck and build pass. The clean Node 22 PR verifier passed stages 1-17; its serialized CLI suite passed 13,567 tests and hit two unrelated 5-second multi-workspace timeouts. Those two tests passed together in 68ms when rerun directly under Node 22. A GUI terminal mouse pass remains a manual acceptance check; detached tmux cannot observe host-terminal selection behavior. This comment supersedes the previous E2E report. |
|
@wenshao Follow-up to the broad review at #6937 (comment): All actionable findings were rechecked against the current head and addressed:
The arbitration and readiness concerns do not require additional code on the current implementation:
The remaining notes are non-blocking: frame publication/retention is the intended B1 cost, repaint re-entrancy is deduplicated and covered by the own-repaint test, and the design's “≈500ms” describes the interaction approximately while the implementation intentionally uses 400ms. The patch is also exercised by clean |
|
Thanks for the PR! Template looks good ✓ — all required sections present with substantive content. Problem: observed UX gap, not theoretical. VP mode enables SGR mouse tracking ( Direction: aligned. Restoring text selection in VP mode is a real user need — every terminal app that takes over mouse tracking faces this. The approach of implementing selection at the application level on top of Ink's composited cell grid is the right layer. CHANGELOG has no direct reference but the area (terminal UX, mouse interactions) is clearly in scope. Size: all production code is in
The 1000+ production line advisory applies — this is a large PR. The design doc's deliberate two-PR split (visible-region MVP now, semantic copy fidelity later) keeps scope disciplined for PR 1. Approach: the modular decomposition is clean — selection state machine, coordinate mapping, span detection, text extraction, and the headless React controller each have a clear single responsibility. The Ink patch (frame controller + pre-serialization highlight) is minimal and additive. The ClickableThinkMessage fix (press/release tracking to distinguish click from drag) is a necessary companion change. Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ — 所有必需章节均有实质内容。 问题:已观测到的 UX 缺口,非理论性。VP 模式启用 SGR 鼠标追踪( 方向:对齐。在 VP 模式中恢复文本选择是真实用户需求——所有接管鼠标追踪的终端应用都面临这个问题。基于 Ink 合成 cell 网格实现应用层选择的方案是正确的层次。CHANGELOG 无直接参考但该领域(终端 UX、鼠标交互)显然在范围内。 规模:所有生产代码均在
1000+ 生产行大 PR 建议适用——这是一个较大的 PR。设计文档有意拆分为两个 PR(当前为可见区域 MVP,后续为语义复制保真度),使 PR 1 的范围保持严谨。 方案:模块分解清晰——选择状态机、坐标映射、跨度检测、文本提取和无头 React 控制器各有明确的单一职责。Ink patch(帧控制器 + 序列化前高亮)最小且为增量添加。ClickableThinkMessage 修复(按压/释放跟踪以区分点击和拖动)是必要的伴随变更。VP 模式下对 stdout 设置 进入代码审查 🔍 — Qwen Code · qwen3.7-max Reviewed at |
Code ReviewReviewed the full diff across 24 files (design doc, 7 selection modules, Ink patch, 4 component integrations, settings/schema updates). No critical blockers found. The implementation is clean, well-decomposed, and follows project conventions. The architecture is sound: Ink's frame controller exposes an immutable cell snapshot, the selection hook maps terminal coordinates through the frame anchor, and highlighting is applied pre-serialization in the renderer (never mutating cached Key design choices verified:
One non-blocking nit: Real-Scenario TestingUnable to run tmux-based interactive testing in this CI environment — no pre-installed dependencies and the npm install/build cycle exceeds the available time budget. Deferring to the author's extensive evidence:
中文说明代码审查审查了 24 个文件的完整 diff(设计文档、7 个选择模块、Ink patch、4 个组件集成、settings/schema 更新)。 未发现关键阻塞问题。 实现干净、分解良好,遵循项目约定。 架构合理:Ink 的 frame controller 暴露不可变的 cell 快照,选择 hook 通过 frame anchor 映射终端坐标,高亮在序列化前应用于渲染器(不会修改缓存的 已验证的关键设计决策:
一个非阻塞建议: Ink patch 中的 真实场景测试无法在当前 CI 环境中运行基于 tmux 的交互测试——没有预安装的依赖,npm install/build 周期超出可用时间预算。依赖作者的充分证据:
— Qwen Code · qwen3.7-max Reviewed at |
|
Confidence: 4/5 — Clean review across all stages. One non-blocking nit (hardcoded selection color). This is a solid, well-architected feature PR. The problem is real (VP mode's SGR tracking breaks native selection), the approach is the right one (application-level selection on top of Ink's composited cell grid), and the implementation is clean — modular selection logic, minimal Ink patch, correct lifecycle management. The author has done thorough testing across physical mouse, cmux GUI, real TTY tmux, and 67 unit tests. Maintainer The scope is deliberately conservative (visible-region MVP, semantic copy deferred to PR 2). Every change in the diff earns its place — no drive-by refactors or scope creep. The ClickableThinkMessage press/release fix is a necessary companion change that prevents drag-select from toggling Thought blocks. The one nit: Approving. ✅ 中文说明置信度:4/5 — 各阶段审查均干净。一个非阻塞建议(硬编码选择颜色)。 这是一个结构良好的功能 PR。问题是真实的(VP 模式的 SGR 追踪破坏了原生选择),方法是正确的(基于 Ink 合成 cell 网格的应用层选择),实现干净——模块化的选择逻辑、最小化的 Ink patch、正确的生命周期管理。作者已在物理鼠标、cmux GUI、真实 TTY tmux 和 67 个单元测试上进行了充分测试。Maintainer 范围刻意保守(可见区域 MVP,语义复制延迟到 PR 2)。diff 中的每个更改都有其必要性——没有顺手重构或范围蔓延。ClickableThinkMessage 按压/释放修复是防止拖拽选择切换 Thought 块的必要伴随更改。 一个建议: 批准 ✅ — Qwen Code · qwen3.7-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
|
Thanks for the review and approval. The hardcoded selection background on light terminal themes is a valid non-blocking follow-up. PR 1 scope is now frozen after the completed review rounds, so I am deferring theme-aware selection styling to a follow-up PR rather than widening this one. The current dark-theme behavior has been validated in the attached real-terminal evidence. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline. Not reviewed: reverse audit of chunk 5 round 2 — the subagent failed to execute. Not reviewed: reverse audit — its prompt was built, but no agent was launched with it that opened its brief, so the reverse-audit pass did not run.
— qwen3.7-max via Qwen Code /review
|
Qwen Code review did not complete successfully. Qwen review exited with status 1. See workflow logs. |
|
Follow-up on the failed |
What this PR does
Adds mouse-driven text selection and copy to VP mode, the alternate-screen viewport enabled by
ui.useTerminalBuffer. Click-drag selects visible text with a live highlight, double-click selects a word, triple-click selects a visual line, and releasing a non-empty selection copies it to the system clipboard. Selection is active whenever VP mode is active.The implementation extends the existing Ink patch with a small frame controller. Ink exposes an immutable snapshot of the composited cell grid for coordinate queries, while the CLI sends a selection range back to Ink for pre-serialization highlighting and throttled repaint. This keeps selection behavior uniform across markdown, diffs, tables, and tool output without adding per-renderer serialization logic.
Selection is cleared when the visible frame changes through scrolling, resizing, or streaming. Clipboard failures are reported through debug logging. Holding the terminal's native selection modifier, commonly Shift or Option, remains the fallback for native terminal selection.
Why it's needed
VP mode enables SGR mouse tracking for scrolling and interactive UI behavior. A button-held drag is therefore consumed by the CLI instead of reaching the terminal's native selection mechanism, so users cannot normally select and copy output with the mouse. This restores that expected interaction while preserving VP mode behavior.
Reviewer Test Plan
How to verify
Enable
ui.useTerminalBuffer, start an interactive session with multi-line output, and confirm that dragging highlights the visible cells and copies the selected text on release. Confirm that double-click selects a word and triple-click selects a visual line. Scroll, resize the terminal, or cause the visible output to change and confirm the old highlight is cleared. Native terminal selection remains available through the terminal's modifier key.Evidence (Before & After)
Metric-backed real-CLI capture: under the same drag gesture, the pre-fix build produced 1 unintended Thought toggle and the fixed build produced 0. The fixed run also demonstrates click-to-expand, drag-select inside expanded Thought content, and scroll-to-clear.
Before: mouse dragging in VP mode is consumed by CLI mouse tracking and does not create a copyable selection.
After: the first tmux report was invalid because it manually compensated for a terminal-row offset. Reproduction with an unmodified
npm run devsession showed the real failure: Ink entered the alternate screen without clearing and homing it, so visible rows and mouse coordinates diverged. The corrected non-debug 100x28 TTY run used the actual visible coordinates and an actual model response: drag selection over Chinese wide characters and double-click word selection remained highlighted for more than 10 seconds, prompt input outside the history preserved the selection, and wheel scrolling cleared it. Clipboard copy was also verified. Automated tests cover release coordinates, wide-character spacer cells, drag/click state, viewport-only invalidation, clipboard rejection, and selection-highlight repaint stability. A separate Node 22 cmux GUI regression pass used OS-level click and drag events, and the author also completed a physical-mouse acceptance pass: click-to-expand/collapse remained intact while dragging inside Thought content selected text without toggling the block.Tested on
Environment (optional)
macOS ARM64. Corrective verification used a real non-debug
npm run devsession in tmux at 100x28. The focused selection/rendering suite passed 8 files / 67 tests; CLI typecheck and the CLI build passed. The clean Node 22verify:prrun passed stages 1-17, then the serialized CLI suite passed 13,567 tests and timed out on 2 unrelated multi-workspace tests at their 5-second limit; both timed-out tests passed together in 68ms when rerun directly under Node 22. Remote CI will rerun after push.Risk & Scope
Linked Issues
中文说明
本 PR 做了什么
为 VP 模式(由
ui.useTerminalBuffer开启的 alternate-screen 视口)增加鼠标文本选择与复制。拖拽会实时高亮可见文本,双击选择单词,三击选择视觉行,松开非空选区后复制到系统剪贴板。VP 模式启用时该功能始终生效。实现扩展了现有 Ink patch,加入一个小型 frame controller。Ink 暴露合成后 cell 网格的不可变快照供坐标查询,CLI 再把选区范围传回 Ink,在序列化前完成高亮并通过 Ink 自身的节流机制重绘。这样 markdown、diff、表格和工具输出可以使用统一的选择逻辑,不需要逐个渲染器维护文本序列化。
滚动、终端 resize 或 streaming 导致可见 frame 变化时,旧选区会被清除。剪贴板失败会写入 debug 日志。终端原生选择仍可通过终端的修饰键(通常是 Shift 或 Option)使用。
为什么需要
VP 模式会启用 SGR 鼠标追踪来处理滚动和交互式 UI。按住鼠标拖动时,事件会被 CLI 消费,终端原生选择机制无法收到拖动事件,因此用户通常不能用鼠标选择和复制输出。本功能在保留 VP 模式行为的同时恢复这一常用交互。
评审测试计划
如何验证
开启
ui.useTerminalBuffer,启动包含多行输出的交互会话,确认拖拽会高亮可见 cell,并在松开时复制文本;确认双击选词、三击选择视觉行。滚动、resize 或让可见输出发生变化,确认旧高亮被清除。终端修饰键仍可用于原生选择。证据(Before & After)
Before:VP 模式下鼠标拖动被 CLI 鼠标追踪消费,无法形成可复制选区。
After:第一次 tmux 报告不成立,因为测试人为补偿了终端行偏移。使用未经修改的
npm run dev会话重新复现后确认了真实故障:Ink 进入 alternate screen 时没有清屏归位,导致可见行和鼠标坐标错位。修正后的 non-debug 100x28 TTY 测试使用实际可见坐标和真实模型回复:拖拽选择中文宽字符、双击选词的高亮均持续超过 10 秒;在 history 外的输入框键入内容不会清除选区;滚轮滚动会清除选区;剪贴板复制也已验证。自动化测试覆盖松开坐标、宽字符占位 cell、拖拽后的点击状态、仅视口内容失效、剪贴板失败和选区自身重绘稳定性。另在 Node 22 的 cmux GUI 中使用 OS 级点击与拖动完成独立回归,作者也完成了物理鼠标验收:单击展开/收起保持正常,在 Thought 正文内拖选时能够选择文本且不会切换折叠状态。测试平台
运行环境(可选)
macOS ARM64。修正验证使用 tmux 中真实的 non-debug
npm run dev会话,终端尺寸为 100x28。聚焦 selection/rendering 套件 8 个文件 / 67 个测试通过,CLI typecheck 和 CLI build 通过。干净的 Node 22verify:pr已通过第 1-17 阶段;串行 CLI 全套通过 13,567 个测试,另有 2 个无关的 multi-workspace 测试触发 5 秒超时;在 Node 22 下单独合并重跑这两个用例时,它们共用 68ms 并通过。推送后远端 CI 会重新运行。风险与范围