[Bug] Fullscreen TUI renderer disables mouse text selection; incompatible with clickable prompts

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 3 comments · opened Jul 31, 2026

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.

View original on GitHub ↗

3 Comments

dede34fr · 20 days ago

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 |
| tui not fullscreen | (none) | ✅ yes, regardless of the variables |

?1000h alone is enough: any conformant terminal hands mousedown to the application and stops creating a selection. So CLAUDE_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_MOUSE is evaluated first, and CLAUDE_CODE_DISABLE_MOUSE_CLICKS is 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/xterm build 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

  1. Document that CLAUDE_CODE_DISABLE_MOUSE=1 is the only setting that restores terminal text selection, and that CLAUDE_CODE_DISABLE_MOUSE_CLICKS=1 does not (see also #70539 and #71687).
  2. Mention Shift+drag in the fullscreen TUI docs.
  3. Longer term: a single mouse: off | scroll | full key in settings.json would 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.
ymendel · 14 days ago

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 the scroll state 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 acceptsFirstMouse to true deliberately — 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_CLICKS on 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.

rpennell · 5 days ago

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_CLICKS actually does (2.1.246 bundle)

  • DISABLE_MOUSE_CLICKS → mouse mode "scroll"; DISABLE_MOUSE"off"; default "full".
  • On alternate-screen entry the same ESC[?1000h ESC[?1006h is written for "scroll" and "full" — the terminal cannot tell them apart.
  • The only difference is in the input path: 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

terminals can't deliver wheel events to an app without also capturing clicks

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 ?1007h needed) all implement it. It is how less, vim, man, and tmux scroll with the wheel while leaving click-drag to the terminal.

Claude Code already receives these events: #70724 documented that with CLAUDE_CODE_DISABLE_MOUSE=1 the 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

  1. In "scroll" (and "off") mode, don't emit ?1000h. Emit ?1007h instead, 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 — less semantics). Native click-drag, native Cmd+C, wheel scrolling: all work at once, as in every other alt-screen TUI.
  2. Until then, rename/document DISABLE_MOUSE_CLICKS as "ignore clicks" rather than implying it preserves selection, and document CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1 as the only setting that gives back native selection and wheel scrolling (it gates mouse tracking on altScreenActive) — 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.