Fix ui jitter during remote server initialization (#9342)

## Description
* Fixes
[APP-4307](https://linear.app/warpdotdev/issue/APP-4307/fix-blocklist-ui-flicker-when-initializing-remote-server)
* The issue: during the initialization flow there would be significant
ui jitter where the entire blocklist is moving up and down - this is
because there is a gap between when the command/init shell finishes and
when we render the loading footer (which looks like the output grid
cursor appearing, then disappearing, then the loading footer appearing)
* The fix is to render the footer as we're checking for if the binary is
installed so that there is no gap for when the cursor disappears and the
loading footer appears
* The original reason this was done was to ensure that the loading
footer didn't appear before or with the choice block - I've updated it
to have an explicit check instead
* This also updates where we're handling `RemoteServerSetupStateChanged`
such that `session` is able to handle and update itself, and the
terminal view is only in charge of rerendering the footer

## Testing
https://www.loom.com/share/d4f157b4afd94f648f61ab1afbcee249

## Agent Mode
- [x] Warp Agent Mode - This PR was created via Warp's AI Agent Mode
This commit is contained in:
Maggie Shan
2026-04-29 09:53:27 -04:00
committed by GitHub
parent a6d1ece15e
commit b7b327510d
5 changed files with 24 additions and 41 deletions
+19 -12
View File
@@ -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,
@@ -11359,18 +11359,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
@@ -11388,6 +11394,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}%)"),