Prefill rename prompt with current thread name (#18057)

Addresses #12178

Problem: The TUI /rename prompt opened blank even when the current
thread already had a custom name, making small edits awkward.

Solution: Let custom prompts receive initial text and prefill /rename
with the existing thread name while preserving the empty prompt for
unnamed threads.

Testing: Manually verified that the feature works by using `/rename`
with unnamed and already-named threads.
This commit is contained in:
Eric Traut
2026-04-16 09:01:45 -07:00
committed by GitHub
parent 9c6d038622
commit 9c56e89e4f
4 changed files with 55 additions and 6 deletions

View File

@@ -41,15 +41,22 @@ impl CustomPromptView {
pub(crate) fn new(
title: String,
placeholder: String,
initial_text: String,
context_label: Option<String>,
on_submit: PromptSubmitted,
) -> Self {
let mut textarea = TextArea::new();
if !initial_text.is_empty() {
textarea.set_text_clearing_elements(&initial_text);
textarea.set_cursor(initial_text.len());
}
Self {
title,
placeholder,
context_label,
on_submit,
textarea: TextArea::new(),
textarea,
textarea_state: RefCell::new(TextAreaState::default()),
complete: false,
}

View File

@@ -5279,11 +5279,8 @@ impl ChatWidget {
fn show_rename_prompt(&mut self) {
let tx = self.app_event_tx.clone();
let has_name = self
.thread_name
.as_ref()
.is_some_and(|name| !name.is_empty());
let title = if has_name {
let existing_name = self.thread_name.as_deref().filter(|name| !name.is_empty());
let title = if existing_name.is_some() {
"Rename thread"
} else {
"Name thread"
@@ -5291,6 +5288,7 @@ impl ChatWidget {
let view = CustomPromptView::new(
title.to_string(),
"Type a name and press Enter".to_string(),
/*initial_text*/ existing_name.unwrap_or_default().to_string(),
/*context_label*/ None,
Box::new(move |name: String| {
let Some(name) = crate::legacy_core::util::normalize_thread_name(&name) else {
@@ -10599,6 +10597,7 @@ impl ChatWidget {
let view = CustomPromptView::new(
"Custom review instructions".to_string(),
"Type instructions and press Enter".to_string(),
/*initial_text*/ String::new(),
/*context_label*/ None,
Box::new(move |prompt: String| {
let trimmed = prompt.trim().to_string();

View File

@@ -0,0 +1,10 @@
---
source: tui/src/chatwidget/tests/slash_commands.rs
assertion_line: 135
expression: popup
---
▌ Rename thread
▌ Current project title
Press enter to confirm or esc to go back

View File

@@ -124,6 +124,39 @@ async fn inline_slash_command_is_available_from_local_recall_after_dispatch() {
assert_eq!(chat.bottom_pane.composer_text(), "/rename Better title");
}
#[tokio::test]
async fn slash_rename_prefills_existing_thread_name() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
chat.thread_name = Some("Current project title".to_string());
chat.dispatch_command(SlashCommand::Rename);
let popup = render_bottom_popup(&chat, /*width*/ 80);
assert_chatwidget_snapshot!("slash_rename_prefilled_prompt", popup);
chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
assert_matches!(
rx.try_recv(),
Ok(AppEvent::CodexOp(Op::SetThreadName { name })) if name == "Current project title"
);
}
#[tokio::test]
async fn slash_rename_without_existing_thread_name_starts_empty() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
chat.dispatch_command(SlashCommand::Rename);
let popup = render_bottom_popup(&chat, /*width*/ 80);
assert!(popup.contains("Name thread"));
assert!(popup.contains("Type a name and press Enter"));
chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
assert_matches!(rx.try_recv(), Err(TryRecvError::Empty));
}
#[tokio::test]
async fn usage_error_slash_command_is_available_from_local_recall() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.3-codex")).await;