Merge pull request #905 from Hmbown/fix/jetbrains-default-mouse-off
fix(tui): default mouse capture off in JetBrains JediTerm (#878, #898)
This commit is contained in:
+105
-10
@@ -3466,6 +3466,16 @@ fn should_use_alt_screen(cli: &Cli, config: &Config) -> bool {
|
||||
}
|
||||
|
||||
fn should_use_mouse_capture(cli: &Cli, config: &Config, use_alt_screen: bool) -> bool {
|
||||
let terminal_emulator = std::env::var("TERMINAL_EMULATOR").ok();
|
||||
should_use_mouse_capture_with(cli, config, use_alt_screen, terminal_emulator.as_deref())
|
||||
}
|
||||
|
||||
fn should_use_mouse_capture_with(
|
||||
cli: &Cli,
|
||||
config: &Config,
|
||||
use_alt_screen: bool,
|
||||
terminal_emulator: Option<&str>,
|
||||
) -> bool {
|
||||
if !use_alt_screen || cli.no_mouse_capture {
|
||||
return false;
|
||||
}
|
||||
@@ -3476,11 +3486,26 @@ fn should_use_mouse_capture(cli: &Cli, config: &Config, use_alt_screen: bool) ->
|
||||
.tui
|
||||
.as_ref()
|
||||
.and_then(|tui| tui.mouse_capture)
|
||||
.unwrap_or_else(default_mouse_capture_enabled)
|
||||
.unwrap_or_else(|| default_mouse_capture_enabled(terminal_emulator))
|
||||
}
|
||||
|
||||
fn default_mouse_capture_enabled() -> bool {
|
||||
!cfg!(windows)
|
||||
/// Whether to enable terminal mouse capture by default for this platform/host.
|
||||
///
|
||||
/// Returns `false` on Windows (legacy console mouse-mode reporting is flaky;
|
||||
/// `--mouse-capture` opts in) and on JetBrains' JediTerm, which advertises
|
||||
/// mouse support but delivers SGR mouse-event escape sequences as raw text
|
||||
/// in the input stream — visible to users as garbled characters in the
|
||||
/// composer when they move the mouse over the TUI (#878, #898). The user
|
||||
/// can still opt back in with `[tui] mouse_capture = true` in
|
||||
/// `~/.deepseek/config.toml` or `--mouse-capture`.
|
||||
fn default_mouse_capture_enabled(terminal_emulator: Option<&str>) -> bool {
|
||||
if cfg!(windows) {
|
||||
return false;
|
||||
}
|
||||
if matches!(terminal_emulator, Some(t) if t.eq_ignore_ascii_case("JetBrains-JediTerm")) {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn is_zellij() -> bool {
|
||||
@@ -4236,7 +4261,7 @@ mod terminal_mode_tests {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(should_use_mouse_capture(&cli, &config, true));
|
||||
assert!(should_use_mouse_capture_with(&cli, &config, true, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4245,7 +4270,7 @@ mod terminal_mode_tests {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(!should_use_mouse_capture(&cli, &config, true));
|
||||
assert!(!should_use_mouse_capture_with(&cli, &config, true, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4253,7 +4278,7 @@ mod terminal_mode_tests {
|
||||
let cli = parse_cli(&["deepseek", "--no-mouse-capture"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(!should_use_mouse_capture(&cli, &config, true));
|
||||
assert!(!should_use_mouse_capture_with(&cli, &config, true, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4270,7 +4295,7 @@ mod terminal_mode_tests {
|
||||
..Config::default()
|
||||
};
|
||||
|
||||
assert!(!should_use_mouse_capture(&cli, &config, true));
|
||||
assert!(!should_use_mouse_capture_with(&cli, &config, true, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4278,7 +4303,7 @@ mod terminal_mode_tests {
|
||||
let cli = parse_cli(&["deepseek", "--mouse-capture"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(should_use_mouse_capture(&cli, &config, true));
|
||||
assert!(should_use_mouse_capture_with(&cli, &config, true, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4295,7 +4320,7 @@ mod terminal_mode_tests {
|
||||
..Config::default()
|
||||
};
|
||||
|
||||
assert!(should_use_mouse_capture(&cli, &config, true));
|
||||
assert!(should_use_mouse_capture_with(&cli, &config, true, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4303,7 +4328,77 @@ mod terminal_mode_tests {
|
||||
let cli = parse_cli(&["deepseek", "--mouse-capture"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(!should_use_mouse_capture(&cli, &config, false));
|
||||
assert!(!should_use_mouse_capture_with(&cli, &config, false, None));
|
||||
}
|
||||
|
||||
// Issue #878 / #898: JetBrains JediTerm advertises mouse support but
|
||||
// forwards SGR mouse-event escapes as raw input characters, producing
|
||||
// the "input box auto-fills with garbled characters when I move the
|
||||
// mouse" failure mode in PyCharm/IDEA terminals. Default the capture
|
||||
// off when we see TERMINAL_EMULATOR=JetBrains-JediTerm; explicit
|
||||
// config / --mouse-capture still wins.
|
||||
|
||||
#[test]
|
||||
fn mouse_capture_defaults_off_in_jetbrains_jediterm() {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(!should_use_mouse_capture_with(
|
||||
&cli,
|
||||
&config,
|
||||
true,
|
||||
Some("JetBrains-JediTerm"),
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jetbrains_default_off_is_case_insensitive() {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let config = Config::default();
|
||||
|
||||
// JetBrains has occasionally varied the casing across releases;
|
||||
// a case-insensitive match keeps the protection in place.
|
||||
assert!(!should_use_mouse_capture_with(
|
||||
&cli,
|
||||
&config,
|
||||
true,
|
||||
Some("jetbrains-jediterm"),
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mouse_capture_flag_overrides_jetbrains_default() {
|
||||
let cli = parse_cli(&["deepseek", "--mouse-capture"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(should_use_mouse_capture_with(
|
||||
&cli,
|
||||
&config,
|
||||
true,
|
||||
Some("JetBrains-JediTerm"),
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_mouse_capture_true_overrides_jetbrains_default() {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let config = Config {
|
||||
tui: Some(crate::config::TuiConfig {
|
||||
alternate_screen: None,
|
||||
mouse_capture: Some(true),
|
||||
terminal_probe_timeout_ms: None,
|
||||
status_items: None,
|
||||
osc8_links: None,
|
||||
}),
|
||||
..Config::default()
|
||||
};
|
||||
|
||||
assert!(should_use_mouse_capture_with(
|
||||
&cli,
|
||||
&config,
|
||||
true,
|
||||
Some("JetBrains-JediTerm"),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -394,7 +394,7 @@ If you are upgrading from older releases:
|
||||
`false`. When `true`, the notification body includes the elapsed
|
||||
duration and the turn's cost in the configured display currency.
|
||||
- `tui.alternate_screen` (string, optional): `auto`, `always`, or `never`. `auto` disables the alternate screen in Zellij; `--no-alt-screen` forces inline mode. Set `never` or run with `--no-alt-screen` when you want real terminal scrollback.
|
||||
- `tui.mouse_capture` (bool, optional, default `true` on non-Windows terminals and `false` on Windows when the alternate screen is active): enable internal mouse scrolling, transcript selection, and right-click context actions. TUI-owned drag selection copies only user/assistant transcript text. Set this to `false` or run with `--no-mouse-capture` for raw terminal selection; set it to `true` or run with `--mouse-capture` to opt in on Windows.
|
||||
- `tui.mouse_capture` (bool, optional, default `true` on non-Windows terminals when the alternate screen is active; `false` on Windows and inside JetBrains JediTerm — PyCharm/IDEA/CLion/etc. — where mouse-event escapes leak into the input stream as garbled text, see #878 / #898): enable internal mouse scrolling, transcript selection, and right-click context actions. TUI-owned drag selection copies only user/assistant transcript text. Set this to `false` or run with `--no-mouse-capture` for raw terminal selection; set it to `true` or run with `--mouse-capture` to opt in anywhere it's defaulted off.
|
||||
- `tui.terminal_probe_timeout_ms` (int, optional, default `500`): startup terminal-mode probe timeout in milliseconds. Values are clamped to `100..=5000`; timeout emits a warning and aborts startup instead of hanging indefinitely.
|
||||
- `tui.osc8_links` (bool, optional, default `true`): emit OSC 8 escape sequences around URLs in transcript output so terminals that support them (iTerm2, Terminal.app 13+, Ghostty, Kitty, WezTerm, Alacritty, recent gnome-terminal/konsole) render them as Cmd+click hyperlinks. Terminals without OSC 8 support render the plain URL and ignore the escape. Set `false` for terminals that misrender the sequence; selection/clipboard output always strips the escapes.
|
||||
- `hooks` (optional): lifecycle hooks configuration (see `config.example.toml`).
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ Run `deepseek --help` for the canonical list. Common flags:
|
||||
- `-c, --continue`: resume the most recent session in this workspace
|
||||
- `--max-subagents <N>`: clamp to `1..=20`
|
||||
- `--no-alt-screen`: run inline without the alternate screen buffer
|
||||
- `--mouse-capture` / `--no-mouse-capture`: opt in or out of internal mouse scrolling, transcript selection, and right-click context actions. Mouse capture is enabled by default on non-Windows terminals so drag selection copies only user/assistant transcript text; hold Shift while dragging or use `--no-mouse-capture` for raw terminal selection. On Windows it defaults off to avoid CMD/terminal mouse escape sequences being inserted into the prompt; use `--mouse-capture` to opt in.
|
||||
- `--mouse-capture` / `--no-mouse-capture`: opt in or out of internal mouse scrolling, transcript selection, and right-click context actions. Mouse capture is enabled by default on non-Windows terminals so drag selection copies only user/assistant transcript text; hold Shift while dragging or use `--no-mouse-capture` for raw terminal selection. It defaults off on Windows (CMD/terminal mouse-escape spam in the prompt) and inside JetBrains JediTerm — PyCharm/IDEA/CLion/etc. — where the terminal advertises mouse support but forwards SGR mouse events as raw text (#878, #898). Use `--mouse-capture` to opt in anywhere it's defaulted off.
|
||||
- `--profile <NAME>`: select config profile
|
||||
- `--config <PATH>`: config file path
|
||||
- `-v, --verbose`: verbose logging
|
||||
|
||||
Reference in New Issue
Block a user