mirror of
https://github.com/rust-kotlin/ashell.git
synced 2026-09-22 08:01:00 +00:00
feat: enhance split pane focus UI and fix graceful exit tab cleanup
- Implement 4-sided inner absolute border for active pane focus - Fix 'exit' gracefully closing tabs not removing empty tab groups - Sync system monitoring automatically on active group switches
This commit is contained in:
+1
-39
@@ -639,45 +639,7 @@ impl Ashell {
|
||||
|| reason == "ssh session closed";
|
||||
// Auto-close the pane on graceful exit (e.g. user typed exit)
|
||||
if is_graceful_exit {
|
||||
if let Some(ix) = self.tabs.iter().position(|t| t.id == tab_id) {
|
||||
self.tabs[ix].backend.send(BackendCommand::Close);
|
||||
self.tabs.remove(ix);
|
||||
}
|
||||
if let Some(g) = self.tab_groups.iter_mut().find(|g| g.pane_root.contains(&tab_id)) {
|
||||
g.pane_root.remove_tab(&tab_id);
|
||||
}
|
||||
self.pane_root.remove_tab(&tab_id);
|
||||
self.sync_pane_root_to_group();
|
||||
if self.tabs.is_empty() || self.tab_groups.is_empty() {
|
||||
self.pane_root = PaneLayout::Single(String::new());
|
||||
self.focused_pane_path = vec![];
|
||||
self.active_tab = None;
|
||||
self.active_group = None;
|
||||
self.tab_groups.clear();
|
||||
self.tabs.clear();
|
||||
self.system_tab_id = None;
|
||||
self.cpu_history.clear();
|
||||
self.net_rx_history.clear();
|
||||
self.net_tx_history.clear();
|
||||
self.system_status = None;
|
||||
if let Some(handle) = self.sftp_handles.remove(&tab_id) {
|
||||
handle.close();
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
let was_active = self.active_tab.as_deref() == Some(tab_id.as_str());
|
||||
if was_active
|
||||
|| self.active_tab.as_ref().is_some_and(|active_id| !self.tabs.iter().any(|tab| &tab.id == active_id))
|
||||
{
|
||||
let first_id = self.pane_root.tab_ids().first().copied().map(String::from)
|
||||
.or_else(|| self.tabs.first().map(|t| t.id.clone()));
|
||||
if let Some(new_id) = first_id {
|
||||
self.active_tab = Some(new_id.clone());
|
||||
self.focus_pane_with_id(new_id);
|
||||
}
|
||||
}
|
||||
self.sync_system_tab_to_active_group();
|
||||
self.handle_tab_close(tab_id.clone());
|
||||
self.status = reason.into();
|
||||
self.remote_sample_in_flight = false;
|
||||
return changed;
|
||||
|
||||
+24
-16
@@ -1639,23 +1639,31 @@ impl Ashell {
|
||||
}
|
||||
})
|
||||
.unwrap_or(cx.theme().success);
|
||||
if is_focused {
|
||||
el = h_flex()
|
||||
.size_full()
|
||||
.child(
|
||||
div()
|
||||
.w(px(2.))
|
||||
.h_full()
|
||||
.flex_none()
|
||||
.bg(indicator_color),
|
||||
)
|
||||
.child(el.flex_1().min_w(px(0.)));
|
||||
} else {
|
||||
el = h_flex()
|
||||
.size_full()
|
||||
.child(div().w(px(2.)).h_full().flex_none())
|
||||
.child(el.opacity(0.85).flex_1().min_w(px(0.)));
|
||||
let has_multiple_panes = this.pane_root.tab_ids().len() > 1;
|
||||
|
||||
if !is_focused {
|
||||
el = el.opacity(0.85);
|
||||
}
|
||||
|
||||
if has_multiple_panes {
|
||||
if is_focused {
|
||||
el = div()
|
||||
.size_full()
|
||||
.relative()
|
||||
.child(div().absolute().top(px(1.)).left(px(1.)).right(px(1.)).h(px(1.)).bg(indicator_color))
|
||||
.child(div().absolute().bottom(px(1.)).left(px(1.)).right(px(1.)).h(px(1.)).bg(indicator_color))
|
||||
.child(div().absolute().left(px(1.)).top(px(1.)).bottom(px(1.)).w(px(1.)).bg(indicator_color))
|
||||
.child(div().absolute().right(px(1.)).top(px(1.)).bottom(px(1.)).w(px(1.)).bg(indicator_color))
|
||||
.p(px(4.))
|
||||
.child(el);
|
||||
} else {
|
||||
el = div()
|
||||
.size_full()
|
||||
.p(px(4.))
|
||||
.child(el);
|
||||
}
|
||||
}
|
||||
|
||||
el.into_any_element()
|
||||
}
|
||||
PaneLayout::Horizontal(children, ratio) => {
|
||||
|
||||
+7
-5
@@ -488,15 +488,19 @@ impl Ashell {
|
||||
}
|
||||
|
||||
pub(crate) fn close_tab(&mut self, id: String, cx: &mut Context<Self>) {
|
||||
self.handle_tab_close(id);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub(crate) fn handle_tab_close(&mut self, id: String) {
|
||||
let group_ix = self.tab_groups.iter().position(|g| g.pane_root.contains(&id));
|
||||
let Some(ref group) = group_ix.map(|i| self.tab_groups[i].clone()) else {
|
||||
// Fallback: find and close individual tab
|
||||
eprintln!("[close_tab] no group found for tab '{}', closing individually", id);
|
||||
eprintln!("[handle_tab_close] no group found for tab '{}', closing individually", id);
|
||||
if let Some(ix) = self.tabs.iter().position(|tab| tab.id == id) {
|
||||
self.tabs[ix].backend.send(BackendCommand::Close);
|
||||
self.tabs.remove(ix);
|
||||
}
|
||||
cx.notify();
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -504,7 +508,7 @@ impl Ashell {
|
||||
let pane_ids_str: Vec<&str> = pane_ids.iter().map(|s| *s).collect();
|
||||
let is_group_close = pane_ids.len() <= 1;
|
||||
eprintln!(
|
||||
"[close_tab] id='{}' group_panes={:?} is_group_close={}",
|
||||
"[handle_tab_close] id='{}' group_panes={:?} is_group_close={}",
|
||||
id, pane_ids_str, is_group_close
|
||||
);
|
||||
|
||||
@@ -573,7 +577,6 @@ impl Ashell {
|
||||
for (_, handle) in self.sftp_handles.drain() {
|
||||
handle.close();
|
||||
}
|
||||
cx.notify();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -606,7 +609,6 @@ impl Ashell {
|
||||
}
|
||||
}
|
||||
self.sync_system_tab_to_active_group();
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub(crate) fn focus_terminal(
|
||||
|
||||
Reference in New Issue
Block a user