aboutsummaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-14 05:30:20 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-14 05:30:20 +0300
commit226e87306deaba017d36583f51ae34e42b303aad (patch)
tree2a1155689b75007c15d93735aedfa58db7a05edf /src/ui
parent17544c36ee397233e2f7b3319d86e3b517e3b5d0 (diff)
fix: preserve tab identity across restored sessions
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/mobile.rs68
1 files changed, 62 insertions, 6 deletions
diff --git a/src/ui/mobile.rs b/src/ui/mobile.rs
index b8f23073..a9dc977f 100644
--- a/src/ui/mobile.rs
+++ b/src/ui/mobile.rs
@@ -290,7 +290,7 @@ fn render_header_status(
} else {
state_dot(state, seen, p)
};
- let tab_label = format!("tab {}/{}", ws.active_tab + 1, ws.tabs.len());
+ let tab_label = mobile_tab_status(ws);
let row1 = Rect::new(area.x, area.y, area.width, 1);
let tab_w = (tab_label.chars().count() as u16 + 1).min(area.width);
let name_w = area.width.saturating_sub(tab_w);
@@ -329,6 +329,17 @@ fn render_header_status(
}
}
+fn mobile_tab_status(ws: &crate::workspace::Workspace) -> String {
+ let tab_number = ws
+ .public_tab_number(ws.active_tab)
+ .unwrap_or(ws.active_tab + 1);
+ if ws.tabs.len() <= 1 {
+ format!("tab {tab_number}")
+ } else {
+ format!("tab {tab_number} · {}/{}", ws.active_tab + 1, ws.tabs.len())
+ }
+}
+
fn render_switch_button(app: &AppState, frame: &mut Frame, area: Rect) {
if area.width == 0 || area.height == 0 {
return;
@@ -471,10 +482,9 @@ fn render_mobile_switcher_content(
),
]);
let detail = format!(
- " {} · tab {}/{}",
+ " {} · {}",
ws.branch().unwrap_or_else(|| "shell".into()),
- ws.active_tab + 1,
- ws.tabs.len()
+ mobile_tab_status(ws)
);
render_two_line_item(
frame,
@@ -515,9 +525,9 @@ fn render_mobile_switcher_content(
let active = idx == ws.active_tab;
let bg = mobile_item_bg(false, active, p);
let label = if tab.is_auto_named() {
- format!("tab {}", idx + 1)
+ format!("tab {}", tab.display_name())
} else {
- format!("{} · {}", idx + 1, tab.display_name())
+ format!("{} · {}", tab.number, tab.display_name())
};
let title = Line::from(vec![
Span::styled(" ", Style::default().bg(bg)),
@@ -965,6 +975,52 @@ mod tests {
assert_eq!(mobile_agent_detail(&entry), " idle · pi");
}
+ #[test]
+ fn mobile_tab_status_uses_public_tab_number_and_position() {
+ let mut workspace = crate::workspace::Workspace::test_new("mobile-tabs");
+ let removed_tab = workspace.test_add_tab(None);
+ workspace.test_add_tab(None);
+ assert!(workspace.close_tab(removed_tab));
+ workspace.active_tab = 1;
+
+ assert_eq!(mobile_tab_status(&workspace), "tab 3 · 2/2");
+ }
+
+ #[test]
+ fn mobile_switcher_uses_stable_public_tab_number_for_auto_tab_labels() {
+ let mut app = crate::app::state::AppState::test_new();
+ let mut workspace = crate::workspace::Workspace::test_new("mobile-tabs");
+ let removed_tab = workspace.test_add_tab(None);
+ workspace.test_add_tab(None);
+ assert!(workspace.close_tab(removed_tab));
+ app.workspaces = vec![workspace];
+ app.ensure_test_terminals();
+ app.active = Some(0);
+ app.selected = 0;
+ app.view.mobile_header_rect = Rect::new(0, 0, 40, 2);
+ app.view.terminal_area = Rect::new(0, 2, 40, 18);
+
+ let backend = ratatui::backend::TestBackend::new(40, 20);
+ let mut terminal = ratatui::Terminal::new(backend).unwrap();
+ terminal
+ .draw(|frame| {
+ render_mobile_panel(
+ &app,
+ &TerminalRuntimeRegistry::new(),
+ frame,
+ Rect::new(0, 0, 40, 20),
+ )
+ })
+ .unwrap();
+
+ let row = (0..40)
+ .map(|x| terminal.backend().buffer()[(x, 10)].symbol())
+ .collect::<String>();
+
+ assert!(row.contains("tab 3"), "mobile tab row: {row:?}");
+ assert!(!row.contains("tab 2"), "mobile tab row: {row:?}");
+ }
+
#[cfg(unix)]
#[tokio::test]
async fn mobile_header_uses_live_root_runtime_cwd_for_workspace_label() {