Compare commits

..

2 Commits

Author SHA1 Message Date
Omer Strulovich
17484b3f9e Add terminal title spinner while running
Co-authored-by: Codex <noreply@openai.com>
2026-03-05 14:40:04 -05:00
Omer Strulovich
1d4c6ca84e Add /title terminal title override
Co-authored-by: Codex <noreply@openai.com>
2026-03-05 14:39:37 -05:00
4 changed files with 21 additions and 46 deletions

View File

@@ -738,12 +738,10 @@ fn decorate_title_context(
fn compute_title_context(
title_override: Option<String>,
thread_name: Option<String>,
task_running: bool,
tick: u128,
) -> Option<String> {
let context = title_override
.or(thread_name)
.as_deref()
.map(str::trim)
.filter(|name| !name.is_empty())
@@ -1792,7 +1790,6 @@ impl App {
let task_running = app.chat_widget.is_task_running();
let title_context = compute_title_context(
app.chat_widget.title_override(),
app.chat_widget.thread_name(),
task_running,
title_animation_tick(),
);
@@ -1903,7 +1900,6 @@ impl App {
let task_running = app.chat_widget.is_task_running();
let title_context = compute_title_context(
app.chat_widget.title_override(),
app.chat_widget.thread_name(),
task_running,
title_animation_tick(),
);
@@ -3814,33 +3810,25 @@ mod tests {
#[test]
fn decorate_title_context_adds_spinner_while_running() {
assert_eq!(
decorate_title_context(Some("Working".to_string()), true, 0),
Some("⠋ - Working".to_string())
decorate_title_context(Some("Named thread".to_string()), true, 0),
Some("⠋ - Named thread".to_string())
);
assert_eq!(
decorate_title_context(Some("Working".to_string()), true, 9),
Some("⠏ - Working".to_string())
decorate_title_context(Some("Named thread".to_string()), true, 9),
Some("⠏ - Named thread".to_string())
);
assert_eq!(decorate_title_context(None, true, 0), Some("".to_string()));
}
#[test]
fn title_context_uses_thread_name_when_idle() {
assert_eq!(
compute_title_context(None, Some("named thread".to_string()), false, 0),
Some("named thread".to_string())
);
fn title_context_defaults_to_none_without_manual_title() {
assert_eq!(compute_title_context(None, false, 0), None);
}
#[test]
fn title_context_prefers_manual_title_when_idle() {
assert_eq!(
compute_title_context(
Some("manual title".to_string()),
Some("named thread".to_string()),
false,
0,
),
compute_title_context(Some("manual title".to_string()), false, 0),
Some("manual title".to_string())
);
}

View File

@@ -17,11 +17,7 @@ use std::collections::HashSet;
// Hide alias commands in the default popup list so each unique action appears once.
// `quit` is an alias of `exit`, so we skip `quit` here.
// `approvals` is an alias of `permissions`.
const ALIAS_COMMANDS: &[SlashCommand] = &[
SlashCommand::Quit,
SlashCommand::Approvals,
SlashCommand::Title,
];
const ALIAS_COMMANDS: &[SlashCommand] = &[SlashCommand::Quit, SlashCommand::Approvals];
/// A selectable item in the popup: either a built-in command or a user prompt.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -482,11 +478,11 @@ mod tests {
}
#[test]
fn title_hidden_in_empty_filter_but_shown_for_prefix() {
fn title_shown_in_empty_filter_and_for_prefix() {
let mut popup = CommandPopup::new(Vec::new(), CommandPopupFlags::default());
popup.on_composer_text_change("/".to_string());
let items = popup.filtered_items();
assert!(!items.contains(&CommandItem::Builtin(SlashCommand::Title)));
assert!(items.contains(&CommandItem::Builtin(SlashCommand::Title)));
popup.on_composer_text_change("/ti".to_string());
let items = popup.filtered_items();

View File

@@ -69,25 +69,14 @@ mod tests {
}
#[test]
fn debug_command_still_resolves_for_dispatch() {
let cmd = find_builtin_command("debug-config", all_enabled_flags());
assert_eq!(cmd, Some(SlashCommand::DebugConfig));
}
#[test]
fn clear_command_resolves_for_dispatch() {
assert_eq!(
find_builtin_command("clear", all_enabled_flags()),
Some(SlashCommand::Clear)
);
}
#[test]
fn title_command_resolves_for_dispatch() {
assert_eq!(
find_builtin_command("title", all_enabled_flags()),
Some(SlashCommand::Title)
);
fn known_commands_resolve_for_dispatch() {
for (name, expected) in [
("debug-config", SlashCommand::DebugConfig),
("clear", SlashCommand::Clear),
("title", SlashCommand::Title),
] {
assert_eq!(find_builtin_command(name, all_enabled_flags()), Some(expected));
}
}
#[test]

View File

@@ -80,7 +80,9 @@ fn format_terminal_title(context: Option<&str>) -> String {
.filter(|text| !text.is_empty());
match context {
Some(context) if has_spinner_prefix(&context) => format!("{DEFAULT_TERMINAL_TITLE} {context}"),
Some(context) if has_spinner_prefix(&context) => {
format!("{DEFAULT_TERMINAL_TITLE} {context}")
}
Some(context) => format!("{DEFAULT_TERMINAL_TITLE} - {context}"),
None => DEFAULT_TERMINAL_TITLE.to_string(),
}