From b516dfb15b6dea90845c977f498df340fe5c0de7 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Apr 2026 20:11:11 +0800 Subject: [PATCH] fix(terminal): reconnect suppress next output Signed-off-by: fufesou --- flutter/lib/desktop/pages/terminal_page.dart | 7 ++----- flutter/lib/models/terminal_model.dart | 3 +-- src/flutter.rs | 1 + src/server/terminal_service.rs | 1 + 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/flutter/lib/desktop/pages/terminal_page.dart b/flutter/lib/desktop/pages/terminal_page.dart index 7c6bb9814..e63c3b8e0 100644 --- a/flutter/lib/desktop/pages/terminal_page.dart +++ b/flutter/lib/desktop/pages/terminal_page.dart @@ -58,8 +58,7 @@ class _TerminalPageState extends State super.initState(); // Listen for tab selection changes to request focus - _tabStateSubscription = - widget.tabController.state.listen(_onTabStateChanged); + _tabStateSubscription = widget.tabController.state.listen(_onTabStateChanged); // Use shared FFI instance from connection manager _ffi = TerminalConnectionManager.getConnection( @@ -146,9 +145,7 @@ class _TerminalPageState extends State // Use post-frame callback to ensure widget is fully laid out in focus tree WidgetsBinding.instance.addPostFrameCallback((_) { // Re-check conditions after frame: mounted, focusable, still selected, not already focused - if (!mounted || - !_terminalFocusNode.canRequestFocus || - _terminalFocusNode.hasFocus) return; + if (!mounted || !_terminalFocusNode.canRequestFocus || _terminalFocusNode.hasFocus) return; final state = widget.tabController.state.value; if (state.selected >= 0 && state.selected < state.tabs.length) { if (state.tabs[state.selected].key == widget.tabKey) { diff --git a/flutter/lib/models/terminal_model.dart b/flutter/lib/models/terminal_model.dart index b0e5f5f2b..d8d6d9a87 100644 --- a/flutter/lib/models/terminal_model.dart +++ b/flutter/lib/models/terminal_model.dart @@ -288,8 +288,7 @@ class TerminalModel with ChangeNotifier { // On reconnect, the server may replay recent output. That replay can include // terminal queries like DSR/DA; xterm answers them through onOutput as // "^[[1;1R^[[2;2R^[[>0;0;0c", which must not be sent back to the peer. - _suppressNextTerminalDataOutput = - message == 'Reconnected to existing terminal with pending output'; + _suppressNextTerminalDataOutput = evt['replay_in_next_data'] == true; // Fallback: if terminal view is not yet ready but already has valid // dimensions (e.g. layout completed before open response arrived), diff --git a/src/flutter.rs b/src/flutter.rs index c7e07f892..3d416469b 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1135,6 +1135,7 @@ impl InvokeUiSession for FlutterHandler { ("message", json!(&opened.message)), ("pid", json!(opened.pid)), ("service_id", json!(&opened.service_id)), + ("replay_in_next_data", json!(opened.replay_in_next_data)), ]; if !opened.persistent_sessions.is_empty() { event_data.push(("persistent_sessions", json!(opened.persistent_sessions))); diff --git a/src/server/terminal_service.rs b/src/server/terminal_service.rs index 85ba806e3..7be381f24 100644 --- a/src/server/terminal_service.rs +++ b/src/server/terminal_service.rs @@ -1111,6 +1111,7 @@ impl TerminalServiceProxy { } else { "Reconnected to existing terminal".to_string() }; + opened.replay_in_next_data = has_pending; opened.pid = session.pid; opened.service_id = self.service_id.clone(); if service.needs_session_sync {