From ce5383fdaab5f7367397157206c36e64ab141cc4 Mon Sep 17 00:00:00 2001 From: MaggieShan Date: Tue, 28 Apr 2026 19:29:21 -0400 Subject: [PATCH] fix ui jitter --- app/src/terminal/event.rs | 16 ---------------- app/src/terminal/model/session.rs | 7 +++++-- app/src/terminal/model_events.rs | 7 ------- app/src/terminal/view.rs | 31 +++++++++++++++++++------------ crates/remote_server/src/setup.rs | 4 ---- 5 files changed, 24 insertions(+), 41 deletions(-) diff --git a/app/src/terminal/event.rs b/app/src/terminal/event.rs index 2fe3361c..40d58433 100644 --- a/app/src/terminal/event.rs +++ b/app/src/terminal/event.rs @@ -112,13 +112,6 @@ pub enum Event { is_tagged_in: bool, }, Handler(HandlerEvent), - /// Emitted by the async remote server setup task to report intermediate - /// state changes (e.g. Checking → Installing → Initializing) so the - /// prompt can show stage-specific messages. - RemoteServerSetupStateChanged { - session_id: SessionId, - state: RemoteServerSetupState, - }, /// Emitted when the remote server binary has been successfully checked or /// installed and is ready. The session is initialized independently on /// `Bootstrapped`; when the remote server later connects, the client is @@ -468,15 +461,6 @@ impl Debug for Event { write!(f, "AgentTaggedInChanged(is_tagged_in: {is_tagged_in})") } Event::Handler(handler_event) => write!(f, "Handler({handler_event:?}))"), - Event::RemoteServerSetupStateChanged { - session_id, - ref state, - } => { - write!( - f, - "RemoteServerSetupStateChanged(session: {session_id:?}, state: {state:?})" - ) - } Event::RemoteServerReady { session_id } => { write!(f, "RemoteServerReady(session: {session_id:?})") } diff --git a/app/src/terminal/model/session.rs b/app/src/terminal/model/session.rs index b9911c47..d0b5c297 100644 --- a/app/src/terminal/model/session.rs +++ b/app/src/terminal/model/session.rs @@ -142,7 +142,7 @@ impl Sessions { #[cfg(feature = "local_tty")] if FeatureFlag::SshRemoteServer.is_enabled() { let mgr = RemoteServerManager::handle(ctx); - ctx.subscribe_to_model(&mgr, |sessions, event, _ctx| match event { + ctx.subscribe_to_model(&mgr, |sessions, event, ctx| match event { RemoteServerManagerEvent::SessionConnected { session_id: sid, host_id, @@ -158,6 +158,10 @@ impl Sessions { session.set_remote_host_id(None); } } + RemoteServerManagerEvent::SetupStateChanged { session_id, state } => { + sessions.set_remote_server_setup_state(*session_id, state.clone()); + ctx.notify(); + } RemoteServerManagerEvent::SessionConnecting { .. } | RemoteServerManagerEvent::SessionDeregistered { .. } | RemoteServerManagerEvent::SessionConnectionFailed { .. } @@ -167,7 +171,6 @@ impl Sessions { | RemoteServerManagerEvent::RepoMetadataSnapshot { .. } | RemoteServerManagerEvent::RepoMetadataUpdated { .. } | RemoteServerManagerEvent::RepoMetadataDirectoryLoaded { .. } - | RemoteServerManagerEvent::SetupStateChanged { .. } | RemoteServerManagerEvent::BinaryCheckComplete { .. } | RemoteServerManagerEvent::BinaryInstallComplete { .. } | RemoteServerManagerEvent::ClientRequestFailed { .. } diff --git a/app/src/terminal/model_events.rs b/app/src/terminal/model_events.rs index 145c7bfe..e17ead6b 100644 --- a/app/src/terminal/model_events.rs +++ b/app/src/terminal/model_events.rs @@ -117,13 +117,6 @@ impl ModelEventDispatcher { is_subshell, }) } - Event::RemoteServerSetupStateChanged { session_id, state } => { - self.sessions.update(ctx, |sessions, ctx| { - sessions.set_remote_server_setup_state(session_id, state.clone()); - ctx.notify(); - }); - return; - } Event::RemoteServerReady { session_id } => { log::info!("Remote server ready for session {session_id:?}"); return; diff --git a/app/src/terminal/view.rs b/app/src/terminal/view.rs index e8a47b0c..b205ed21 100644 --- a/app/src/terminal/view.rs +++ b/app/src/terminal/view.rs @@ -131,6 +131,9 @@ use crate::code_review::git_status_update::{ }; use crate::code_review::telemetry_event::CodeReviewPaneEntrypoint; use crate::projects::ProjectManagementModel; +use crate::remote_server::manager::{ + RemoteServerInitPhase, RemoteServerManager, RemoteServerManagerEvent, +}; use crate::settings::ai::FocusedTerminalInfo; use crate::settings_view::mcp_servers_page::MCPServersSettingsPage; use crate::terminal::cli_agent_sessions::event::{ @@ -4168,17 +4171,14 @@ impl TerminalView { // Forward RemoteServerManager setup events into the terminal event stream // so the ModelEventDispatcher can gate session initialization on them. - if crate::features::FeatureFlag::SshRemoteServer.is_enabled() { - use crate::remote_server::manager::{RemoteServerManager, RemoteServerManagerEvent}; + if FeatureFlag::SshRemoteServer.is_enabled() { let mgr_handle = RemoteServerManager::handle(ctx); ctx.subscribe_to_model(&mgr_handle, |me, _, event, ctx| match event { - RemoteServerManagerEvent::SetupStateChanged { session_id, state } => { - me.model.lock().event_proxy.send_terminal_event( - crate::terminal::event::Event::RemoteServerSetupStateChanged { - session_id: *session_id, - state: state.clone(), - }, - ); + RemoteServerManagerEvent::SetupStateChanged { .. } => { + // Sessions handles the state update directly via its own + // subscription to the manager. Notify the view so the + // loading footer re-renders with the updated message. + ctx.notify(); } RemoteServerManagerEvent::SessionConnected { session_id, .. } => { me.model.lock().event_proxy.send_terminal_event( @@ -4198,7 +4198,7 @@ impl TerminalView { .unwrap_or((None, None)); send_telemetry_from_ctx!( TelemetryEvent::RemoteServerInitialization { - phase: remote_server::manager::RemoteServerInitPhase::Initialize, + phase: RemoteServerInitPhase::Initialize, error: None, remote_os, remote_arch, @@ -11358,18 +11358,24 @@ impl TerminalView { }) } - /// Returns `true` when the pending session has a connecting remote-server setup state. + /// Returns `true` when the loading footer should be shown in place of the + /// input editor during the SSH remote-server setup flow. fn show_remote_server_loading_footer(&self, model: &TerminalModel, app: &AppContext) -> bool { if !FeatureFlag::SshRemoteServer.is_enabled() { return false; } + // Don't show the loading footer while the choice block is visible; + // the choice block replaces it. + if self.active_ssh_remote_server_choice_block().is_some() { + return false; + } let Some(pending_sid) = model.pending_session_id() else { return false; }; self.sessions .as_ref(app) .remote_server_setup_state(pending_sid) - .is_some_and(|state| state.is_connecting()) + .is_some_and(|state| state.is_in_progress()) } /// Renders a shimmering loading footer in place of the input editor @@ -11387,6 +11393,7 @@ impl TerminalView { .as_ref(app) .remote_server_setup_state(sid) .map(|state| match state { + RemoteServerSetupState::Checking => "Checking...".to_string(), RemoteServerSetupState::Installing { progress_percent: Some(p), } => format!("Installing... ({p}%)"), diff --git a/crates/remote_server/src/setup.rs b/crates/remote_server/src/setup.rs index 624df906..06a9c53d 100644 --- a/crates/remote_server/src/setup.rs +++ b/crates/remote_server/src/setup.rs @@ -37,10 +37,6 @@ impl RemoteServerSetupState { Self::Checking | Self::Installing { .. } | Self::Initializing ) } - - pub fn is_connecting(&self) -> bool { - matches!(self, Self::Installing { .. } | Self::Initializing) - } } /// Detected remote platform from `uname -sm` output.