fix(tui): defensive mouse-report sanitizer and move provider-wait logging off render path

Strip SGR mouse coordinate tails even when mouse capture is disabled, covering
orphaned terminal reporting state after crashes or focus races (#3063/#3067).

Move provider-wait incident logging from footer render to the main tick loop
so stall diagnostics do not fire on every redraw (#3095 harvest note).

Co-authored-by: Hunter Bown <101357273+Hmbown@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Hunter B
2026-06-11 19:41:31 -07:00
parent 385a07f32a
commit e1a5f5c464
3 changed files with 25 additions and 6 deletions
+24 -5
View File
@@ -3821,9 +3821,6 @@ impl App {
}
fn strip_raw_mouse_reports_from_input(&mut self) {
if !self.use_mouse_capture {
return;
}
if let Some((input, cursor_position)) =
strip_raw_mouse_report_runs(&self.input, self.cursor_position)
{
@@ -5653,12 +5650,34 @@ mod tests {
}
#[test]
fn composer_keeps_mouse_like_text_when_mouse_capture_is_disabled() {
fn composer_strips_raw_sgr_mouse_report_when_mouse_capture_is_disabled() {
let mut app = App::new(test_options(false), &Config::default());
app.insert_str("[<35;44;18M");
assert_eq!(app.input, "[<35;44;18M");
assert_eq!(app.input, "");
assert_eq!(app.cursor_position, 0);
}
#[test]
fn composer_strips_tail_only_mouse_report_burst_when_mouse_capture_is_disabled() {
let mut app = App::new(test_options(false), &Config::default());
app.insert_str("draft ");
app.insert_str(";76;20M35;74;22M35;73;23M");
assert_eq!(app.input, "draft ");
assert_eq!(app.cursor_position, "draft ".chars().count());
}
#[test]
fn composer_keeps_coordinate_like_text_when_mouse_capture_is_disabled() {
let mut app = App::new(test_options(false), &Config::default());
app.insert_str("Size 12;34M");
assert_eq!(app.input, "Size 12;34M");
assert_eq!(app.cursor_position, "Size 12;34M".chars().count());
}
#[test]
-1
View File
@@ -93,7 +93,6 @@ pub(crate) fn render_footer(f: &mut Frame, area: Rect, app: &mut App) {
if let Some(reason) = stall_reason(app) {
label = format!("{label} ({reason})");
}
maybe_log_provider_wait_incident(app);
props.state_label = label;
props.state_color = palette::DEEPSEEK_SKY;
+1
View File
@@ -2759,6 +2759,7 @@ async fn run_event_loop(
// window. Triggers a redraw if the prompt was visible.
app.tick_quit_armed();
app.tick_receipt();
crate::tui::footer_ui::maybe_log_provider_wait_incident(app);
// While the user is drag-selecting past the transcript edge, advance
// the viewport on a fixed cadence and extend the selection head so a
// long passage can be selected in one drag (#1163).