mirror of
https://github.com/rust-kotlin/ashell.git
synced 2026-09-22 00:00:59 +00:00
fix: restore connection progress dialog and support Enter to retry
This commit is contained in:
+128
@@ -260,6 +260,8 @@ pub(crate) struct Ashell {
|
||||
pub(crate) tabs_scroll_handle: gpui::ScrollHandle,
|
||||
pub(crate) selector_scroll_handle: gpui::ScrollHandle,
|
||||
pub(crate) saved_scroll_handle: gpui::ScrollHandle,
|
||||
pub(crate) connection_scroll_handle: gpui::ScrollHandle,
|
||||
pub(crate) connection_progress: Option<ConnectionProgress>,
|
||||
pub(crate) pending_sftp_path_sync: Option<String>,
|
||||
pub(crate) sftp_context_menu: Option<SftpContextMenuState>,
|
||||
pub(crate) sftp_creating_folder: bool,
|
||||
@@ -326,6 +328,14 @@ pub(crate) enum SelectorEntry {
|
||||
Saved(String),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ConnectionProgress {
|
||||
pub(crate) tab_id: String,
|
||||
pub(crate) title: SharedString,
|
||||
pub(crate) lines: Vec<SharedString>,
|
||||
pub(crate) failed: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct SftpContextMenuState {
|
||||
pub(crate) remote_path: String,
|
||||
@@ -607,6 +617,8 @@ impl Ashell {
|
||||
tabs_scroll_handle: gpui::ScrollHandle::new(),
|
||||
selector_scroll_handle: gpui::ScrollHandle::new(),
|
||||
saved_scroll_handle: gpui::ScrollHandle::new(),
|
||||
connection_scroll_handle: gpui::ScrollHandle::new(),
|
||||
connection_progress: None,
|
||||
pending_sftp_path_sync: Some("/".into()),
|
||||
sftp_context_menu: None,
|
||||
sftp_creating_folder: false,
|
||||
@@ -799,6 +811,14 @@ impl Ashell {
|
||||
tab.backend_initialized = true;
|
||||
tab.status = text.clone();
|
||||
}
|
||||
if let Some(progress) = self.connection_progress.as_mut() {
|
||||
if progress.tab_id == tab_id {
|
||||
progress.lines.push(text.clone().into());
|
||||
let _idx = progress.lines.len().saturating_sub(1);
|
||||
self.connection_scroll_handle
|
||||
.set_offset(gpui::point(px(0.), px(-99999.0)));
|
||||
}
|
||||
}
|
||||
self.status = text.into();
|
||||
}
|
||||
BackendEvent::Connected { tab_id } => {
|
||||
@@ -809,6 +829,13 @@ impl Ashell {
|
||||
}
|
||||
self.sync_system_tab_to_active_group();
|
||||
self.request_active_system_snapshot();
|
||||
if self
|
||||
.connection_progress
|
||||
.as_ref()
|
||||
.is_some_and(|progress| progress.tab_id == tab_id && !progress.failed)
|
||||
{
|
||||
self.connection_progress = None;
|
||||
}
|
||||
}
|
||||
BackendEvent::PromptRequest {
|
||||
tab_id,
|
||||
@@ -914,6 +941,16 @@ impl Ashell {
|
||||
if self.system_tab_id.as_deref() == Some(tab_id.as_str()) {
|
||||
self.system_status = Some(reason.clone().into());
|
||||
}
|
||||
if let Some(progress) = self.connection_progress.as_mut() {
|
||||
if progress.tab_id == tab_id {
|
||||
progress.lines.push(reason.clone().into());
|
||||
let _idx = progress.lines.len().saturating_sub(1);
|
||||
self.connection_scroll_handle
|
||||
.set_offset(gpui::point(px(0.), px(-99999.0)));
|
||||
progress.title = t!("connection_failed").into();
|
||||
progress.failed = true;
|
||||
}
|
||||
}
|
||||
self.status = reason.into();
|
||||
}
|
||||
BackendEvent::TransferProgress {
|
||||
@@ -1086,6 +1123,97 @@ impl Ashell {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub(crate) fn retry_connection_progress(&mut self, cx: &mut Context<Self>) {
|
||||
let Some(progress) = self.connection_progress.clone() else {
|
||||
return;
|
||||
};
|
||||
self.connection_progress = None;
|
||||
let mut retry_tabs = Vec::new();
|
||||
for (ix, tab) in self.tabs.iter().enumerate() {
|
||||
if !tab.connected && tab.session.is_some() && tab.id == progress.tab_id {
|
||||
retry_tabs.push((ix, tab.id.clone(), tab.session.clone().unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
if retry_tabs.is_empty() {
|
||||
cx.notify();
|
||||
return;
|
||||
}
|
||||
|
||||
for (ix, tab_id, session) in retry_tabs {
|
||||
// Close old backend
|
||||
self.tabs[ix].send_backend(crate::terminal::BackendCommand::Close);
|
||||
|
||||
// Spawn new backend
|
||||
let backend = crate::backend::ssh::spawn_ssh_terminal(
|
||||
self.runtime.handle(),
|
||||
tab_id.clone(),
|
||||
session.clone(),
|
||||
self.tabs[ix].cols,
|
||||
self.tabs[ix].rows,
|
||||
self.events_tx.clone(),
|
||||
);
|
||||
|
||||
// Replace tab state
|
||||
self.tabs[ix].set_backend(backend);
|
||||
self.tabs[ix].connected = false;
|
||||
self.tabs[ix].status = "connecting".into();
|
||||
self.tabs[ix].disconnected_reason = None;
|
||||
self.tabs[ix].backend_initialized = false;
|
||||
|
||||
// Restart SFTP for the group containing this tab
|
||||
if let Some(group) = self
|
||||
.tab_groups
|
||||
.iter()
|
||||
.find(|g| g.pane_root.contains(&tab_id))
|
||||
{
|
||||
let group_id = group.id.clone();
|
||||
let group_session = self
|
||||
.tabs
|
||||
.iter()
|
||||
.find(|t| group.pane_root.contains(&t.id) && t.session.is_some())
|
||||
.and_then(|t| t.session.clone());
|
||||
|
||||
if let Some(session) = group_session {
|
||||
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,
|
||||
self.events_tx.clone(),
|
||||
);
|
||||
self.sftp_handles.insert(group_id.clone(), sftp_handle);
|
||||
|
||||
if let Some(group) = self.tab_groups.iter_mut().find(|g| g.id == group_id) {
|
||||
if let Some(sftp) = group.sftp.as_mut() {
|
||||
sftp.status = rust_i18n::t!("sftp_connecting").to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 tabs retrying".into();
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub(crate) fn cancel_connection_progress(&mut self, cx: &mut Context<Self>) {
|
||||
if let Some(progress) = &self.connection_progress {
|
||||
let tab_id = progress.tab_id.clone();
|
||||
self.connection_progress = None;
|
||||
self.handle_tab_close(tab_id);
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub(crate) fn save_layout_state(&self, window: &mut gpui::Window, cx: &gpui::App) {
|
||||
if self.is_layout_reset {
|
||||
tracing::info!("[ui] layout was reset, skipping save layout state.");
|
||||
|
||||
+109
@@ -2903,6 +2903,115 @@ impl Render for Ashell {
|
||||
),
|
||||
)
|
||||
})
|
||||
.when_some(self.connection_progress.clone(), |this, progress| {
|
||||
this.child(
|
||||
div()
|
||||
.absolute()
|
||||
.top_0()
|
||||
.left_0()
|
||||
.right_0()
|
||||
.bottom_0()
|
||||
.bg(gpui::Hsla {
|
||||
h: 0.0,
|
||||
s: 0.0,
|
||||
l: 0.0,
|
||||
a: 0.48,
|
||||
})
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.child(
|
||||
div()
|
||||
.w(px(420.))
|
||||
.p_5()
|
||||
.rounded_lg()
|
||||
.border_1()
|
||||
.border_color(cx.theme().border)
|
||||
.bg(cx.theme().popover)
|
||||
.shadow_lg()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_4()
|
||||
.child(
|
||||
Button::new("ssh-connect-progress")
|
||||
.primary()
|
||||
.loading(!progress.failed)
|
||||
.label(progress.title.clone()),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.relative()
|
||||
.min_h(px(0.))
|
||||
.max_h(px(220.))
|
||||
.child(
|
||||
div()
|
||||
.id("connection-progress-scroll")
|
||||
.max_h(px(220.))
|
||||
.overflow_hidden()
|
||||
.overflow_y_scroll()
|
||||
.track_scroll(&self.connection_scroll_handle)
|
||||
.child(
|
||||
v_flex().gap_2().children(
|
||||
progress.lines.iter().cloned().map(|line| {
|
||||
div()
|
||||
.text_size(rems(1.0))
|
||||
.text_color(if progress.failed {
|
||||
cx.theme().danger
|
||||
} else {
|
||||
cx.theme().muted_foreground
|
||||
})
|
||||
.child(line)
|
||||
}),
|
||||
),
|
||||
)
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.absolute()
|
||||
.top_0()
|
||||
.right_0()
|
||||
.bottom_0()
|
||||
.w(px(16.))
|
||||
.child(
|
||||
Scrollbar::vertical(&self.connection_scroll_handle)
|
||||
.scrollbar_show(ScrollbarShow::Scrolling)
|
||||
)
|
||||
)
|
||||
)
|
||||
.when(progress.failed, |this| {
|
||||
this.child(
|
||||
h_flex()
|
||||
.justify_end()
|
||||
.gap_2()
|
||||
.child(
|
||||
Button::new("ssh-connect-progress-retry")
|
||||
.primary()
|
||||
.label(t!("retry").to_string())
|
||||
.on_click(cx.listener(
|
||||
|this, _, _, cx| {
|
||||
this.retry_connection_progress(
|
||||
cx,
|
||||
)
|
||||
},
|
||||
)),
|
||||
)
|
||||
.child(
|
||||
Button::new("ssh-connect-progress-close")
|
||||
.label(t!("cancel").to_string())
|
||||
.on_click(cx.listener(
|
||||
|this, _, _, cx| {
|
||||
this.cancel_connection_progress(
|
||||
cx,
|
||||
)
|
||||
},
|
||||
)),
|
||||
),
|
||||
)
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
.on_prepaint({
|
||||
let view = cx.entity().clone();
|
||||
move |_, window, cx| {
|
||||
|
||||
@@ -494,6 +494,12 @@ impl Ashell {
|
||||
self.events_tx.clone(),
|
||||
));
|
||||
self.active_tab = Some(id.clone());
|
||||
self.connection_progress = Some(crate::app::ConnectionProgress {
|
||||
tab_id: id.clone(),
|
||||
title: rust_i18n::t!("connecting").into(),
|
||||
lines: vec![rust_i18n::t!("starting_connection").into()],
|
||||
failed: false,
|
||||
});
|
||||
self.pane_root = PaneLayout::Single(id.clone());
|
||||
self.focused_pane_path = vec![];
|
||||
let group_id = Uuid::new_v4().to_string();
|
||||
|
||||
@@ -119,6 +119,15 @@ impl Ashell {
|
||||
&& !event.keystroke.modifiers.alt
|
||||
&& !event.keystroke.modifiers.platform
|
||||
{
|
||||
if let Some(progress) = &self.connection_progress {
|
||||
if progress.failed {
|
||||
self.retry_connection_progress(cx);
|
||||
window.prevent_default();
|
||||
cx.stop_propagation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let active_id = self.active_tab.clone();
|
||||
if let Some(active_id) = active_id {
|
||||
let is_disconnected = self
|
||||
|
||||
Reference in New Issue
Block a user