aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-04-09 04:31:21 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-04-09 04:32:45 +0300
commit5460a9601ea8dd3d05e64e97040c71c6be29172b (patch)
treea3bfa468c2112ab2eb843c7a1bef46fe3b901599
parentb5b61c14841fda60f289cfd9c782580ea099a2a2 (diff)
fix(tabs): keep default numeric labels auto-generated
-rw-r--r--src/app/input.rs87
-rw-r--r--src/ui.rs43
-rw-r--r--src/workspace.rs4
3 files changed, 119 insertions, 15 deletions
diff --git a/src/app/input.rs b/src/app/input.rs
index ada8e6d9..d1c20d8d 100644
--- a/src/app/input.rs
+++ b/src/app/input.rs
@@ -940,15 +940,18 @@ fn open_rename_active_tab(state: &mut AppState, replace_on_type: bool) {
}
}
-fn open_new_tab_dialog(state: &mut AppState) {
- let next_tab_name = state
+fn next_new_tab_default_name(state: &AppState) -> String {
+ state
.active
.and_then(|i| state.workspaces.get(i))
.map(|ws| (ws.tabs.len() + 1).to_string())
- .unwrap_or_else(|| "1".to_string());
+ .unwrap_or_else(|| "1".to_string())
+}
+
+fn open_new_tab_dialog(state: &mut AppState) {
state.creating_new_tab = true;
state.requested_new_tab_name = None;
- state.name_input = next_tab_name;
+ state.name_input = next_new_tab_default_name(state);
state.name_input_replace_on_type = true;
state.mode = Mode::RenameTab;
}
@@ -1049,16 +1052,20 @@ fn apply_rename_action(state: &mut AppState, action: ModalAction) {
}
Mode::RenameTab if state.creating_new_tab => {
state.request_new_tab = true;
- state.requested_new_tab_name = if new_name.is_empty() {
- None
- } else {
- Some(new_name)
- };
+ let default_name = next_new_tab_default_name(state);
+ state.requested_new_tab_name =
+ if new_name.is_empty() || new_name == default_name {
+ None
+ } else {
+ Some(new_name)
+ };
}
Mode::RenameTab => {
- if !new_name.is_empty() {
- if let Some(ws) = state.active.and_then(|i| state.workspaces.get_mut(i)) {
- if let Some(tab) = ws.active_tab_mut() {
+ if let Some(ws) = state.active.and_then(|i| state.workspaces.get_mut(i)) {
+ if let Some(tab) = ws.active_tab_mut() {
+ let keep_auto_name =
+ tab.is_auto_named() && new_name == tab.number.to_string();
+ if !new_name.is_empty() && !keep_auto_name {
tab.set_custom_name(new_name);
state.mark_session_dirty();
}
@@ -3427,6 +3434,62 @@ mod tests {
}
#[test]
+ fn saving_new_tab_dialog_with_default_name_keeps_tab_auto_named() {
+ let mut state = state_with_workspaces(&["test"]);
+ open_new_tab_dialog(&mut state);
+
+ handle_rename_key(
+ &mut state,
+ KeyEvent::new(KeyCode::Enter, KeyModifiers::empty()),
+ );
+
+ assert_eq!(state.mode, Mode::Terminal);
+ assert!(!state.creating_new_tab);
+ assert!(state.request_new_tab);
+ assert!(state.requested_new_tab_name.is_none());
+ }
+
+ #[test]
+ fn closing_first_auto_tab_resets_remaining_auto_tab_and_next_prompt() {
+ let mut state = state_with_workspaces(&["test"]);
+ open_new_tab_dialog(&mut state);
+ handle_rename_key(
+ &mut state,
+ KeyEvent::new(KeyCode::Enter, KeyModifiers::empty()),
+ );
+
+ state.workspaces[0].test_add_tab(state.requested_new_tab_name.as_deref());
+ state.request_new_tab = false;
+ state.requested_new_tab_name = None;
+
+ state.workspaces[0].close_tab(0);
+ state.workspaces[0].switch_tab(0);
+
+ assert_eq!(state.workspaces[0].tabs[0].display_name(), "1");
+ assert!(state.workspaces[0].tabs[0].custom_name.is_none());
+
+ open_new_tab_dialog(&mut state);
+ assert_eq!(state.name_input, "2");
+ }
+
+ #[test]
+ fn renaming_auto_tab_to_its_default_number_keeps_it_auto_named() {
+ let mut state = state_with_workspaces(&["test"]);
+ state.workspaces[0].test_add_tab(None);
+ state.workspaces[0].switch_tab(1);
+
+ open_rename_active_tab(&mut state, false);
+ handle_rename_key(
+ &mut state,
+ KeyEvent::new(KeyCode::Enter, KeyModifiers::empty()),
+ );
+
+ assert_eq!(state.mode, Mode::Terminal);
+ assert!(state.workspaces[0].tabs[1].custom_name.is_none());
+ assert_eq!(state.workspaces[0].tabs[1].display_name(), "2");
+ }
+
+ #[test]
fn confirm_close_keyboard_actions_are_direct_not_focused() {
let mut state = state_with_workspaces(&["a", "b"]);
state.mode = Mode::ConfirmClose;
diff --git a/src/ui.rs b/src/ui.rs
index f7d90408..33ba4e0b 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -558,10 +558,17 @@ fn render_tab_bar(app: &AppState, frame: &mut Frame, area: Rect) {
};
let active = idx == ws.active_tab;
let style = if active {
+ let base = Style::default().fg(p.panel_bg).bg(p.accent);
+ if tab.is_auto_named() {
+ base.add_modifier(Modifier::DIM)
+ } else {
+ base.add_modifier(Modifier::BOLD)
+ }
+ } else if tab.is_auto_named() {
Style::default()
- .fg(p.panel_bg)
- .bg(p.accent)
- .add_modifier(Modifier::BOLD)
+ .fg(p.overlay0)
+ .bg(p.surface0)
+ .add_modifier(Modifier::DIM)
} else {
Style::default().fg(p.overlay1).bg(p.surface0)
};
@@ -3139,6 +3146,36 @@ mod tests {
}
#[test]
+ fn tab_bar_dims_auto_named_tabs_and_emphasizes_custom_tabs() {
+ let mut app = crate::app::state::AppState::test_new();
+ let mut ws = Workspace::test_new("test");
+ let custom_tab = ws.test_add_tab(Some("logs"));
+ ws.switch_tab(custom_tab);
+
+ app.workspaces = vec![ws];
+ app.active = Some(0);
+ app.selected = 0;
+ app.mode = Mode::Terminal;
+
+ compute_view(&mut app, Rect::new(0, 0, 80, 20));
+
+ let backend = TestBackend::new(80, 20);
+ let mut terminal = Terminal::new(backend).unwrap();
+ terminal.draw(|frame| render(&app, frame)).unwrap();
+ let buffer = terminal.backend().buffer();
+
+ let auto_rect = app.view.tab_hit_areas[0];
+ let custom_rect = app.view.tab_hit_areas[1];
+ let auto_style = buffer[(auto_rect.x + 1, auto_rect.y)].style();
+ let custom_style = buffer[(custom_rect.x + 1, custom_rect.y)].style();
+
+ assert_eq!(auto_style.fg, Some(app.palette.overlay0));
+ assert!(auto_style.add_modifier.contains(Modifier::DIM));
+ assert_eq!(custom_style.fg, Some(app.palette.panel_bg));
+ assert!(custom_style.add_modifier.contains(Modifier::BOLD));
+ }
+
+ #[test]
fn all_workspaces_agent_panel_entries_use_workspace_and_optional_tab_labels() {
let mut app = crate::app::state::AppState::test_new();
let mut first = Workspace::test_new("one");
diff --git a/src/workspace.rs b/src/workspace.rs
index 02f971b0..447e6061 100644
--- a/src/workspace.rs
+++ b/src/workspace.rs
@@ -105,6 +105,10 @@ impl Tab {
.unwrap_or_else(|| self.number.to_string())
}
+ pub fn is_auto_named(&self) -> bool {
+ self.custom_name.is_none()
+ }
+
pub fn set_custom_name(&mut self, name: String) {
self.custom_name = Some(name);
}