diff --git a/src/backend/ssh.rs b/src/backend/ssh.rs index 2951780..f80ed42 100644 --- a/src/backend/ssh.rs +++ b/src/backend/ssh.rs @@ -115,7 +115,8 @@ async fn run_ssh( tab_id: tab_id.clone(), }); - let mut exit_reason = "ssh session closed".to_string(); + let mut exit_reason = "ssh connection lost (unknown)".to_string(); + let mut is_graceful_close = false; loop { tokio::select! { @@ -132,6 +133,7 @@ async fn run_ssh( } Some(BackendCommand::Close) | None => { let _ = channel.eof().await; + exit_reason = "ssh session closed".to_string(); break; } } @@ -144,7 +146,15 @@ async fn run_ssh( bytes: data.to_vec(), }); } + Some(ChannelMsg::ExitStatus { exit_status: _ }) => { + is_graceful_close = true; + } Some(ChannelMsg::Close) => { + if is_graceful_close { + exit_reason = "ssh session closed".to_string(); + } else { + exit_reason = "ssh connection lost (abrupt close)".to_string(); + } break; } None => { diff --git a/src/session/mod.rs b/src/session/mod.rs index c6bfc58..531c9fd 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -431,17 +431,53 @@ impl Ashell { return; }; + // Close old backend self.tabs[ix].backend.send(BackendCommand::Close); - self.tabs.remove(ix); - if self.active_tab.as_deref() == Some(progress.tab_id.as_str()) { - self.active_tab = self - .tabs - .get(ix) - .or_else(|| self.tabs.get(ix.saturating_sub(1))) - .map(|tab| tab.id.clone()); + + // Spawn new backend + let backend = ssh::spawn_ssh_terminal( + self.runtime.handle(), + progress.tab_id.clone(), + session.clone(), + DEFAULT_COLS, + DEFAULT_ROWS, + self.events_tx.clone(), + ); + + // Replace tab state in-place to reuse the UI component + self.tabs[ix] = TerminalTab::new_ssh( + progress.tab_id.clone(), + &session, + backend, + self.events_tx.clone(), + ); + + // Restart SFTP if applicable + if let Some(group) = self.tab_groups.iter_mut().find(|g| g.pane_root.contains(&progress.tab_id)) { + if let Some(old_handle) = self.sftp_handles.remove(&group.id) { + old_handle.close(); + } + let sftp_handle = crate::sftp::spawn_sftp( + self.runtime.handle(), + group.id.clone(), + session.clone(), + self.events_tx.clone(), + ); + self.sftp_handles.insert(group.id.clone(), sftp_handle); + + if let Some(sftp) = group.sftp.as_mut() { + sftp.status = rust_i18n::t!("sftp_connecting").to_string(); + } } - self.connection_progress = None; - self.open_ssh_session(session, cx); + + self.connection_progress = Some(ConnectionProgress { + tab_id: progress.tab_id.clone(), + title: t!("connecting").into(), + lines: vec![t!("starting_connection").into()], + failed: false, + }); + self.status = "ssh tab retrying".into(); + cx.notify(); } pub(crate) fn cancel_connection_progress(&mut self, cx: &mut Context) {