From 9fd84bf55577e09e3344a82095e1e06af605386f Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Fri, 31 Jul 2026 22:59:25 +0800 Subject: [PATCH] fix(ui): don't let a bare shell name outrank the cwd/repo title pane_title_of() picked any non-empty PaneRecord.title, but an idle terminal's foreground process is just the shell itself (zsh, bash, ...). That made almost every idle-shell workspace show up as "zsh" in the sidebar instead of the far more useful cwd/repo-derived name, which defeats the "cwd as final fallback" intent of this change. Skip bare shell process names when picking a pane title, so the process-name fallback only kicks in for genuinely distinctive foreground processes (nvim, an agent, ...). --- crates/tty7-core/src/core/shells.rs | 43 +++++++++++++++++++++++++++++ src/ui/machine_mirror.rs | 9 +++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/crates/tty7-core/src/core/shells.rs b/crates/tty7-core/src/core/shells.rs index f91ab190..c84bc9cc 100644 --- a/crates/tty7-core/src/core/shells.rs +++ b/crates/tty7-core/src/core/shells.rs @@ -27,6 +27,37 @@ pub struct ShellInventory { pub default_name: String, } +/// Common interactive-shell process names, lowercase and without a +/// platform-specific extension. Anything that lands in here is what a pane +/// looks like at an idle prompt — not distinctive enough to stand in for a +/// pane's cwd/repo name when picking a display title. +const BARE_SHELL_NAMES: &[&str] = &[ + "sh", + "bash", + "zsh", + "fish", + "dash", + "ksh", + "tcsh", + "csh", + "nu", + "elvish", + "xonsh", + "pwsh", + "powershell", + "cmd", + "wsl", +]; + +/// True when `name` is a bare interactive-shell process name (e.g. the +/// foreground process of an idle terminal), so callers that want a +/// *distinctive* title should skip it and fall back to something else. +pub fn is_bare_shell_name(name: &str) -> bool { + let lower = name.trim().to_ascii_lowercase(); + let lower = lower.strip_suffix(".exe").unwrap_or(&lower); + BARE_SHELL_NAMES.contains(&lower) +} + pub fn inventory() -> ShellInventory { let configured = crate::core::config::shell_command(); ShellInventory { @@ -409,4 +440,16 @@ mod tests { assert!(!default_shell_name(None).is_empty()); assert!(!default_shell_name(Some(" ")).is_empty()); } + + #[test] + fn bare_shell_names_are_recognized_case_and_extension_insensitively() { + assert!(is_bare_shell_name("zsh")); + assert!(is_bare_shell_name("bash")); + assert!(is_bare_shell_name("PowerShell")); + assert!(is_bare_shell_name("pwsh.exe")); + assert!(is_bare_shell_name("CMD.EXE")); + assert!(!is_bare_shell_name("nvim")); + assert!(!is_bare_shell_name("claude")); + assert!(!is_bare_shell_name("")); + } } diff --git a/src/ui/machine_mirror.rs b/src/ui/machine_mirror.rs index f81cad5a..012fc902 100644 --- a/src/ui/machine_mirror.rs +++ b/src/ui/machine_mirror.rs @@ -276,7 +276,7 @@ fn pane_title_of<'a>(ws: &Workspace, panes: &'a [PaneRecord]) -> Option<&'a str> .flat_map(|t| t.root.pane_ids()) .filter_map(|id| panes.iter().find(|p| p.id == id)) .map(|p| p.title.trim()) - .find(|title| !title.is_empty()) + .find(|title| !title.is_empty() && !tty7_core::core::shells::is_bare_shell_name(title)) } pub fn subject_path_of(ws: &Workspace, panes: &[PaneRecord]) -> Option { @@ -530,6 +530,13 @@ mod tests { "a live process name is more distinctive than the cwd group" ); + panes[0].title = "zsh".into(); + assert_eq!( + display_name_of(&ws, &panes), + "tty7", + "an idle shell prompt is not distinctive — cwd/repo name should win" + ); + ws.name = Some(" Release prep ".into()); assert_eq!(display_name_of(&ws, &panes), "Release prep");