[Bug] Fullscreen TUI renderer disables mouse text selection; incompatible with clickable prompts
Bug Description
# tui: fullscreen breaks mouse text selection; no mouse setting recovers it cleanly
Version: 2.1.220 (Claude Code)
Platform: Windows 11, Windows Terminal
Trigger setting: "tui": "fullscreen" (set for me automatically — see note at the end)
## Summary
With the fullscreen renderer, text can no longer be selected with the mouse: the TUI
holds mouse tracking, so drag events go to the app instead of the terminal. Switching
back with /tui default restores selection and scrolling completely.
The catch is that the clickable UI goes away with it. Observed behaviour, with no
mouse env vars set in either case:
| Renderer | Clickable prompts | Mouse text selection |
|---|---|---|
| fullscreen | yes | no |
| default | no | yes |
So the two features are mutually exclusive, and the switch between them is the
renderer setting — not either of the variables named after the mouse. There is no
configuration in which a user gets both.
The two mouse env vars that look like the fix are both dead ends, and one of them is
actively misleading. I lost time on this because the obvious-sounding setting made
things worse, and nothing in /tui, /config, or the settings docs connects the
renderer to mouse behaviour.
## Reproduction
1. Claude Code 2.1.220, "tui": "fullscreen" in ~/.claude/settings.json.
2. Try to select any transcript text by click-dragging → nothing is selected; the drag
is consumed by the app.
3. Run /tui default, restart → selection and wheel scrolling behave normally again.
## Why the env vars don't help
Mouse mode comes from exactly two variables:
``js
function mouseMode() {
if (CLAUDE_CODE_DISABLE_MOUSE !== undefined) return CLAUDE_CODE_DISABLE_MOUSE ? "off" : "full";
if (CLAUDE_CODE_DISABLE_MOUSE_CLICKS !== undefined) return CLAUDE_CODE_DISABLE_MOUSE_CLICKS ? "scroll" : "full";
return "full";
}
`
Under tui: fullscreen that gives three states, none of them good:
| Setting | Mode | Clickable prompts | Text selection | Wheel |
|---|---|---|---|---|
| (none — default) | full | yes | **no** | scrolls transcript |
| CLAUDE_CODE_DISABLE_MOUSE_CLICKS=1 | scroll | no | **no** | scrolls transcript |
| CLAUDE_CODE_DISABLE_MOUSE=1 | off | no | yes | **cycles prompt history — see below** |
### DISABLE_MOUSE_CLICKS is named for the wrong outcome
The name reads as "turn the new click feature off and give me my terminal back." What
it actually does is keep mouse capture on for scroll while ignoring clicks — so
selection stays broken *and* clicking stops working. It is the worst of the three
states, and its name is what makes users pick it. I set it for exactly that reason and
only learned the truth by reading strings out of the binary.
### DISABLE_MOUSE=1 makes the wheel edit the composer
With mouse mode off and the fullscreen renderer, the wheel does something actively
wrong rather than nothing:
- Scrolling **cycles through previous prompts in the input box** instead of scrolling.
- The transcript cannot be scrolled with the mouse at all; PageUp/PageDown only.
- Shift and Ctrl do not change this.
Presumably the terminal translates wheel events into Up/Down once mouse reporting is
off (normal in the alternate screen buffer), and the input component consumes them as
history navigation. Whatever the cause, scrolling by reflex silently changes what is
in the composer.
## The clickable UI is a liability, not a convenience
Beyond the selection trade-off: a click is how you focus a window. Coming back to the
terminal from another application, my first click lands wherever the pointer happens to
be — and if a question or permission prompt is on screen, that click **answers it**. I
have submitted answers I never intended to give this way, and there is no undo: the
turn proceeds on the wrong choice.
Selecting text is something I do constantly and deliberately. Clicking an option is
something I would do maybe once a day, and the keyboard already handles it faster.
The feature trades a high-frequency, intentional interaction for a low-frequency one,
and adds a class of silent misfires that did not exist before.
At minimum, the first click after the terminal window gains focus should be swallowed
rather than treated as an answer — standard focus-click behaviour in most desktop UIs.
## Requests
1. **Let drags over plain text fall through to the terminal in fullscreen mode.**
Clickable prompts need press/release on a widget; text regions do not need to be
captured. This is the fix that matters: today users must give up either clickable
prompts or mouse text selection, and selection is the one that existed first and
that muscle memory expects.
2. **Rename or re-document CLAUDE_CODE_DISABLE_MOUSE_CLICKS`.…
Note:** Content was truncated.
3 Comments
Same symptom here on Linux (code-server / xterm.js), Claude Code 2.1.220, and I measured what each setting actually emits — posting it because the "scroll-only" mouse mode does not restore selection, which is not obvious from its name or from the docs.
Sequences emitted by the fullscreen TUI
Captured by running the TUI under
script(1)in a fresh pty and grepping the raw stream for DEC private mode sets. No input is sent to the TUI.| Setting | Sequences emitted | Plain drag selects? |
|---|---|---|
|
CLAUDE_CODE_DISABLE_MOUSE=1| (none) | ✅ yes ||
CLAUDE_CODE_DISABLE_MOUSE_CLICKS=1|?1000h ?1006h| ❌ no || neither |
?1000h ?1002h ?1003h ?1006h| ❌ no ||
tuinotfullscreen| (none) | ✅ yes, regardless of the variables |?1000halone is enough: any conformant terminal handsmousedownto the application and stops creating a selection. SoCLAUDE_CODE_DISABLE_MOUSE_CLICKS=1— documented as disabling click/drag/hover while keeping wheel scroll — still costs you text selection. It changes what the TUI does with the events, not whether the terminal surrenders them.The two variables are also not interchangeable in precedence:
CLAUDE_CODE_DISABLE_MOUSEis evaluated first, andCLAUDE_CODE_DISABLE_MOUSE_CLICKSis only consulted when the first one is unset. Swapping one for the other looks like a rename and silently changes behaviour — that is exactly how this regressed for us, three weeks after we had fixed it.Verified on the terminal side too
Loaded the exact
@xterm/xtermbuild shipped by code-server in a standalone page and drove it with real browser mouse events (not synthetic ones), with a control row to validate the harness:| Mouse tracking | plain drag |
Shift+ drag ||---|---|---|
| none | ✅ selection | ✅ selection |
|
?1000h ?1006h| ❌ nothing | ✅ selection ||
?1000h ?1002h ?1003h ?1006h| ❌ nothing | ✅ selection |Workaround worth documenting
Shift+ drag always selects, whatever the TUI has armed — xterm.js (and xterm itself) force a local selection while Shift is held. That is a zero-config escape hatch for anyone hitting this, and it is currently not mentioned anywhere in the docs.Suggestions
CLAUDE_CODE_DISABLE_MOUSE=1is the only setting that restores terminal text selection, and thatCLAUDE_CODE_DISABLE_MOUSE_CLICKS=1does not (see also #70539 and #71687).Shift+drag in the fullscreen TUI docs.mouse: off | scroll | fullkey insettings.jsonwould be less error-prone than two environment variables whose names suggest they are variants of each other while one is a strict superset of the other's effect.Same behaviour on macOS, so this is not specific to Windows or Windows Terminal.
Claude Code 2.1.224,
"tui": "fullscreen", the integrated terminal in Zed 1.15.0, macOS (Darwin 23.6.0). Selection by drag is gone, and the three states match your table exactly.Two things to add.
Option+drag does not recover selection under
CLAUDE_CODE_DISABLE_MOUSE_CLICKS=1. On macOS that is the usual terminal-level override when an application is holding mouse tracking, and it is the first thing a macOS reader will reach for on finding this thread. It does nothing here, so thescrollstate has no escape on this platform either.The first-click request cannot be left to terminals, because host behaviour varies. The click-to-focus hazard reproduces here, and whether it can reproduce at all depends on the host application rather than on Claude Code. The macOS convention is that a click on an inactive window raises it and goes no further, and Terminal.app is reported to hold to that, while Ghostty overrides AppKit's
acceptsFirstMousetotruedeliberately — https://github.com/ghostty-org/ghostty/discussions/2593 quotes their own source, and a spot-check in that thread puts VS Code and JetBrains on the same side. Zed passes the click through too. So users on one OS get different exposure depending on where they run Claude Code, which argues for swallowing the first click in the app rather than waiting for terminals to converge.Confirming the naming point from the other direction as well: I set
DISABLE_MOUSE_CLICKSon the strength of its 2.1.195 changelog entry — "disable mouse click/drag/hover in fullscreen mode while keeping wheel scroll" — which reads as exactly the fix and describes the wrong outcome. Reverted the same day.Still the case on 2.1.246, macOS 26.5, VS Code integrated terminal. Adding the bundle-level detail and a rebuttal of the rationale used to close #72173, because this keeps being closed as "working as intended" and pushed back onto users.
What
CLAUDE_CODE_DISABLE_MOUSE_CLICKSactually does (2.1.246 bundle)DISABLE_MOUSE_CLICKS→ mouse mode"scroll";DISABLE_MOUSE→"off"; default"full".ESC[?1000h ESC[?1006his written for"scroll"and"full"— the terminal cannot tell them apart.if (getMouseMode() === "scroll" && (m.button & 3) === 0) continue;— click events are discarded after the terminal has already routed them to the app.So in scroll mode the terminal still owns nothing: xterm.js sees mouse tracking on and hands every drag to Claude Code. The variable cannot preserve native selection; the docs now say as much ("Claude Code still captures the mouse … You still need to hold your terminal's key"), which makes the name misleading and #72173's original purpose unmet.
The "mutually exclusive at the protocol level" claim in #72173 is not correct
They can, and have for decades: alternate scroll mode (
DECSET 1007). With mouse tracking off and the alternate screen active, the terminal translates wheel notches into cursor-up/down key sequences. xterm (?1007h), iTerm2, mintty, Windows Terminal, and xterm.js (VS Code — it does this unconditionally in the alt buffer, no?1007hneeded) all implement it. It is howless,vim,man, andtmuxscroll with the wheel while leaving click-drag to the terminal.Claude Code already receives these events: #70724 documented that with
CLAUDE_CODE_DISABLE_MOUSE=1the wheel "navigates prompt history instead of scrolling the viewport" — those were the terminal's alternate-scroll arrow keys, mis-routed to the input box. That issue was closed as stale.Concrete ask
"scroll"(and"off") mode, don't emit?1000h. Emit?1007hinstead, and treat cursor-key bursts from the terminal as viewport scroll (heuristic: rapid Up/Down repeats while the input box has no history to navigate, or unconditionally in transcript mode —lesssemantics). Native click-drag, native Cmd+C, wheel scrolling: all work at once, as in every other alt-screen TUI.DISABLE_MOUSE_CLICKSas "ignore clicks" rather than implying it preserves selection, and documentCLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1as the only setting that gives back native selection and wheel scrolling (it gates mouse tracking onaltScreenActive) — it was undocumented for months (#56881) and gives up fullscreen entirely.Related: #72173 (closed "intended"), #72498 (dup), #71438, #72681, #70857, #77116, #70724 (closed stale), #56881.