Detailed changes
@@ -1013,7 +1013,7 @@ pub struct AcpThread {
session_id: acp::SessionId,
work_dirs: Option<PathList>,
parent_session_id: Option<acp::SessionId>,
- title: SharedString,
+ title: Option<SharedString>,
provisional_title: Option<SharedString>,
entries: Vec<AgentThreadEntry>,
plan: Plan,
@@ -1176,7 +1176,7 @@ impl Error for LoadError {}
impl AcpThread {
pub fn new(
parent_session_id: Option<acp::SessionId>,
- title: impl Into<SharedString>,
+ title: Option<SharedString>,
work_dirs: Option<PathList>,
connection: Rc<dyn AgentConnection>,
project: Entity<Project>,
@@ -1203,7 +1203,7 @@ impl AcpThread {
shared_buffers: Default::default(),
entries: Default::default(),
plan: Default::default(),
- title: title.into(),
+ title,
provisional_title: None,
project,
running_turn: None,
@@ -1259,10 +1259,10 @@ impl AcpThread {
&self.project
}
- pub fn title(&self) -> SharedString {
- self.provisional_title
+ pub fn title(&self) -> Option<SharedString> {
+ self.title
.clone()
- .unwrap_or_else(|| self.title.clone())
+ .or_else(|| self.provisional_title.clone())
}
pub fn has_provisional_title(&self) -> bool {
@@ -1387,8 +1387,8 @@ impl AcpThread {
if let acp::MaybeUndefined::Value(title) = info_update.title {
let had_provisional = self.provisional_title.take().is_some();
let title: SharedString = title.into();
- if title != self.title {
- self.title = title;
+ if self.title.as_ref() != Some(&title) {
+ self.title = Some(title);
cx.emit(AcpThreadEvent::TitleUpdated);
} else if had_provisional {
cx.emit(AcpThreadEvent::TitleUpdated);
@@ -1676,8 +1676,8 @@ impl AcpThread {
pub fn set_title(&mut self, title: SharedString, cx: &mut Context<Self>) -> Task<Result<()>> {
let had_provisional = self.provisional_title.take().is_some();
- if title != self.title {
- self.title = title.clone();
+ if self.title.as_ref() != Some(&title) {
+ self.title = Some(title.clone());
cx.emit(AcpThreadEvent::TitleUpdated);
if let Some(set_title) = self.connection.set_title(&self.session_id, cx) {
return set_title.run(title, cx);
@@ -4297,7 +4297,7 @@ mod tests {
let thread = cx.new(|cx| {
AcpThread::new(
None,
- "Test",
+ None,
Some(work_dirs),
self.clone(),
project,
@@ -4999,7 +4999,7 @@ mod tests {
// Initial title is the default.
thread.read_with(cx, |thread, _| {
- assert_eq!(thread.title().as_ref(), "Test");
+ assert_eq!(thread.title(), None);
});
// Setting a provisional title updates the display title.
@@ -5007,7 +5007,10 @@ mod tests {
thread.set_provisional_title("Hello, can you helpβ¦".into(), cx);
});
thread.read_with(cx, |thread, _| {
- assert_eq!(thread.title().as_ref(), "Hello, can you helpβ¦");
+ assert_eq!(
+ thread.title().as_ref().map(|s| s.as_str()),
+ Some("Hello, can you helpβ¦")
+ );
});
// The provisional title should NOT have propagated to the connection.
@@ -5024,7 +5027,10 @@ mod tests {
});
task.await.expect("set_title should succeed");
thread.read_with(cx, |thread, _| {
- assert_eq!(thread.title().as_ref(), "Helping with Rust question");
+ assert_eq!(
+ thread.title().as_ref().map(|s| s.as_str()),
+ Some("Helping with Rust question")
+ );
});
assert_eq!(
set_title_calls.borrow().as_slice(),
@@ -5088,7 +5094,10 @@ mod tests {
result.expect("session info update should succeed");
thread.read_with(cx, |thread, _| {
- assert_eq!(thread.title().as_ref(), "Helping with Rust question");
+ assert_eq!(
+ thread.title().as_ref().map(|s| s.as_str()),
+ Some("Helping with Rust question")
+ );
assert!(
!thread.has_provisional_title(),
"session info title update should clear provisional title"
@@ -665,11 +665,10 @@ mod test_support {
cx: &mut gpui::App,
) -> Entity<AcpThread> {
let action_log = cx.new(|_| ActionLog::new(project.clone()));
- let thread_title = title.unwrap_or_else(|| SharedString::new_static("Test"));
let thread = cx.new(|cx| {
AcpThread::new(
None,
- thread_title,
+ title,
Some(work_dirs),
self.clone(),
project,
@@ -662,14 +662,16 @@ impl NativeAgent {
let Some(session) = self.sessions.get(session_id) else {
return;
};
- let thread = thread.downgrade();
- let acp_thread = session.acp_thread.downgrade();
- cx.spawn(async move |_, cx| {
- let title = thread.read_with(cx, |thread, _| thread.title())?;
- let task = acp_thread.update(cx, |acp_thread, cx| acp_thread.set_title(title, cx))?;
- task.await
- })
- .detach_and_log_err(cx);
+
+ if let Some(title) = thread.read(cx).title() {
+ let acp_thread = session.acp_thread.downgrade();
+ cx.spawn(async move |_, cx| {
+ let task =
+ acp_thread.update(cx, |acp_thread, cx| acp_thread.set_title(title, cx))?;
+ task.await
+ })
+ .detach_and_log_err(cx);
+ }
}
fn handle_thread_token_usage_updated(
@@ -3122,7 +3122,7 @@ async fn test_title_generation(cx: &mut TestAppContext) {
fake_model.send_last_completion_stream_text_chunk("Hey!");
fake_model.end_last_completion_stream();
cx.run_until_parked();
- thread.read_with(cx, |thread, _| assert_eq!(thread.title(), "New Thread"));
+ thread.read_with(cx, |thread, _| assert_eq!(thread.title(), None));
// Ensure the summary model has been invoked to generate a title.
summary_model.send_last_completion_stream_text_chunk("Hello ");
@@ -3131,7 +3131,9 @@ async fn test_title_generation(cx: &mut TestAppContext) {
summary_model.end_last_completion_stream();
send.collect::<Vec<_>>().await;
cx.run_until_parked();
- thread.read_with(cx, |thread, _| assert_eq!(thread.title(), "Hello world"));
+ thread.read_with(cx, |thread, _| {
+ assert_eq!(thread.title(), Some("Hello world".into()))
+ });
// Send another message, ensuring no title is generated this time.
let send = thread
@@ -3145,7 +3147,9 @@ async fn test_title_generation(cx: &mut TestAppContext) {
cx.run_until_parked();
assert_eq!(summary_model.pending_completions(), Vec::new());
send.collect::<Vec<_>>().await;
- thread.read_with(cx, |thread, _| assert_eq!(thread.title(), "Hello world"));
+ thread.read_with(cx, |thread, _| {
+ assert_eq!(thread.title(), Some("Hello world".into()))
+ });
}
#[gpui::test]
@@ -1312,7 +1312,7 @@ impl Thread {
pub fn to_db(&self, cx: &App) -> Task<DbThread> {
let initial_project_snapshot = self.initial_project_snapshot.clone();
let mut thread = DbThread {
- title: self.title(),
+ title: self.title().unwrap_or_default(),
messages: self.messages.clone(),
updated_at: self.updated_at,
detailed_summary: self.summary.clone(),
@@ -2491,8 +2491,8 @@ impl Thread {
}
}
- pub fn title(&self) -> SharedString {
- self.title.clone().unwrap_or("New Thread".into())
+ pub fn title(&self) -> Option<SharedString> {
+ self.title.clone()
}
pub fn is_generating_summary(&self) -> bool {
@@ -42,7 +42,6 @@ pub struct UnsupportedVersion;
pub struct AcpConnection {
id: AgentId,
- display_name: SharedString,
telemetry_id: SharedString,
connection: Rc<acp::ClientSideConnection>,
sessions: Rc<RefCell<HashMap<acp::SessionId, AcpSession>>>,
@@ -167,7 +166,6 @@ impl AgentSessionList for AcpSessionList {
pub async fn connect(
agent_id: AgentId,
project: Entity<Project>,
- display_name: SharedString,
command: AgentServerCommand,
default_mode: Option<acp::SessionModeId>,
default_model: Option<acp::ModelId>,
@@ -177,7 +175,6 @@ pub async fn connect(
let conn = AcpConnection::stdio(
agent_id,
project,
- display_name,
command.clone(),
default_mode,
default_model,
@@ -194,7 +191,6 @@ impl AcpConnection {
pub async fn stdio(
agent_id: AgentId,
project: Entity<Project>,
- display_name: SharedString,
command: AgentServerCommand,
default_mode: Option<acp::SessionModeId>,
default_model: Option<acp::ModelId>,
@@ -364,7 +360,6 @@ impl AcpConnection {
auth_methods,
command,
connection,
- display_name,
telemetry_id,
sessions,
agent_capabilities: response.agent_capabilities,
@@ -660,7 +655,7 @@ impl AgentConnection for AcpConnection {
let thread: Entity<AcpThread> = cx.new(|cx| {
AcpThread::new(
None,
- self.display_name.clone(),
+ None,
Some(work_dirs),
self.clone(),
project,
@@ -718,7 +713,6 @@ impl AgentConnection for AcpConnection {
let mcp_servers = mcp_servers_for_project(&project, cx);
let action_log = cx.new(|_| ActionLog::new(project.clone()));
- let title = title.unwrap_or_else(|| self.display_name.clone());
let thread: Entity<AcpThread> = cx.new(|cx| {
AcpThread::new(
None,
@@ -801,7 +795,6 @@ impl AgentConnection for AcpConnection {
let mcp_servers = mcp_servers_for_project(&project, cx);
let action_log = cx.new(|_| ActionLog::new(project.clone()));
- let title = title.unwrap_or_else(|| self.display_name.clone());
let thread: Entity<AcpThread> = cx.new(|cx| {
AcpThread::new(
None,
@@ -296,11 +296,6 @@ impl AgentServer for CustomAgentServer {
cx: &mut App,
) -> Task<Result<Rc<dyn AgentConnection>>> {
let agent_id = self.agent_id();
- let display_name = delegate
- .store
- .read(cx)
- .agent_display_name(&agent_id)
- .unwrap_or_else(|| agent_id.0.clone());
let default_mode = self.default_mode(cx);
let default_model = self.default_model(cx);
let is_registry_agent = is_registry_agent(agent_id.clone(), cx);
@@ -376,7 +371,6 @@ impl AgentServer for CustomAgentServer {
let connection = crate::acp::connect(
agent_id,
project,
- display_name,
command,
default_mode,
default_model,
@@ -44,7 +44,6 @@ pub struct AgentDiffPane {
thread: Entity<AcpThread>,
focus_handle: FocusHandle,
workspace: WeakEntity<Workspace>,
- title: SharedString,
_subscriptions: Vec<Subscription>,
}
@@ -113,7 +112,6 @@ impl AgentDiffPane {
this.handle_acp_thread_event(event, cx)
}),
],
- title: SharedString::default(),
multibuffer,
editor,
thread,
@@ -121,7 +119,6 @@ impl AgentDiffPane {
workspace,
};
this.update_excerpts(window, cx);
- this.update_title(cx);
this
}
@@ -231,17 +228,9 @@ impl AgentDiffPane {
}
}
- fn update_title(&mut self, cx: &mut Context<Self>) {
- let new_title = self.thread.read(cx).title();
- if new_title != self.title {
- self.title = new_title;
- cx.emit(EditorEvent::TitleChanged);
- }
- }
-
fn handle_acp_thread_event(&mut self, event: &AcpThreadEvent, cx: &mut Context<Self>) {
if let AcpThreadEvent::TitleUpdated = event {
- self.update_title(cx)
+ cx.emit(EditorEvent::TitleChanged);
}
}
@@ -534,13 +523,17 @@ impl Item for AgentDiffPane {
fn tab_content(&self, params: TabContentParams, _window: &Window, cx: &App) -> AnyElement {
let title = self.thread.read(cx).title();
- Label::new(format!("Review: {}", title))
- .color(if params.selected {
- Color::Default
- } else {
- Color::Muted
- })
- .into_any_element()
+ Label::new(if let Some(title) = title {
+ format!("Review: {}", title)
+ } else {
+ "Review".to_string()
+ })
+ .color(if params.selected {
+ Color::Default
+ } else {
+ Color::Muted
+ })
+ .into_any_element()
}
fn telemetry_event_text(&self) -> Option<&'static str> {
@@ -26,7 +26,6 @@ use zed_actions::agent::{
ResolveConflictedFilesWithAgent, ResolveConflictsWithAgent, ReviewBranchDiff,
};
-use crate::ui::{AcpOnboardingModal, ClaudeCodeOnboardingModal, HoldForDefault};
use crate::{
AddContextServer, AgentDiffPane, ConversationView, CopyThreadToClipboard, CycleStartThreadIn,
Follow, InlineAssistant, LoadThreadFromClipboard, NewTextThread, NewThread,
@@ -42,6 +41,10 @@ use crate::{
Agent, AgentInitialContent, ExternalSourcePrompt, NewExternalAgentThread,
NewNativeAgentThreadFromSummary,
};
+use crate::{
+ DEFAULT_THREAD_TITLE,
+ ui::{AcpOnboardingModal, ClaudeCodeOnboardingModal, HoldForDefault},
+};
use crate::{
ExpandMessageEditor, ThreadHistoryView,
text_thread_history::{TextThreadHistory, TextThreadHistoryEvent},
@@ -92,7 +95,6 @@ use zed_actions::{
const AGENT_PANEL_KEY: &str = "agent_panel";
const RECENTLY_UPDATED_MENU_LIMIT: usize = 6;
-const DEFAULT_THREAD_TITLE: &str = "New Thread";
fn read_serialized_panel(
workspace_id: workspace::WorkspaceId,
@@ -775,11 +777,7 @@ impl AgentPanel {
SerializedActiveThread {
session_id: thread.session_id().0.to_string(),
agent_type: self.selected_agent_type.clone(),
- title: if title.as_ref() != DEFAULT_THREAD_TITLE {
- Some(title.to_string())
- } else {
- None
- },
+ title: title.map(|t| t.to_string()),
work_dirs: work_dirs.map(|dirs| dirs.serialize()),
}
});
@@ -3221,7 +3219,7 @@ impl AgentPanel {
.map(|r| r.read(cx).title_editor.clone())
{
if is_generating_title {
- Label::new("New Threadβ¦")
+ Label::new(DEFAULT_THREAD_TITLE)
.color(Color::Muted)
.truncate()
.with_animation(
@@ -80,6 +80,8 @@ pub(crate) use thread_history::ThreadHistory;
pub(crate) use thread_history_view::*;
use zed_actions;
+pub const DEFAULT_THREAD_TITLE: &str = "New Thread";
+
actions!(
agent,
[
@@ -4,6 +4,7 @@ use std::path::PathBuf;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
+use crate::DEFAULT_THREAD_TITLE;
use crate::ThreadHistory;
use acp_thread::MentionUri;
use agent_client_protocol as acp;
@@ -192,7 +193,7 @@ pub struct EntryMatch {
fn session_title(title: Option<SharedString>) -> SharedString {
title
.filter(|title| !title.is_empty())
- .unwrap_or_else(|| SharedString::new_static("New Thread"))
+ .unwrap_or_else(|| SharedString::new_static(DEFAULT_THREAD_TITLE))
}
#[derive(Debug, Clone)]
@@ -1098,11 +1099,11 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
if let Some(agent_panel) = workspace.panel::<AgentPanel>(cx)
&& let Some(thread) = agent_panel.read(cx).active_agent_thread(cx)
+ && let Some(title) = thread.read(cx).title()
{
- let thread = thread.read(cx);
mentions.insert(MentionUri::Thread {
- id: thread.session_id().clone(),
- name: thread.title().into(),
+ id: thread.read(cx).session_id().clone(),
+ name: title.to_string(),
});
}
@@ -40,6 +40,7 @@ use parking_lot::RwLock;
use project::{AgentId, AgentServerStore, Project, ProjectEntryId};
use prompt_store::{PromptId, PromptStore};
+use crate::DEFAULT_THREAD_TITLE;
use crate::message_editor::SessionCapabilities;
use rope::Point;
use settings::{NotifyWhenAgentWaiting, Settings as _, SettingsStore};
@@ -551,7 +552,7 @@ impl ConversationView {
(
Some(thread.session_id().clone()),
thread.work_dirs().cloned(),
- Some(thread.title()),
+ thread.title(),
)
})
.unwrap_or((None, None, None));
@@ -1106,9 +1107,12 @@ impl ConversationView {
&self.workspace
}
- pub fn title(&self, _cx: &App) -> SharedString {
+ pub fn title(&self, cx: &App) -> SharedString {
match &self.server_state {
- ServerState::Connected(_) => "New Thread".into(),
+ ServerState::Connected(view) => view
+ .active_view()
+ .and_then(|v| v.read(cx).thread.read(cx).title())
+ .unwrap_or_else(|| DEFAULT_THREAD_TITLE.into()),
ServerState::Loading(_) => "Loadingβ¦".into(),
ServerState::LoadError { error, .. } => match error {
LoadError::Unsupported { .. } => {
@@ -1350,8 +1354,9 @@ impl ConversationView {
);
}
AcpThreadEvent::TitleUpdated => {
- let title = thread.read(cx).title();
- if let Some(active_thread) = self.thread_view(&thread_id) {
+ if let Some(title) = thread.read(cx).title()
+ && let Some(active_thread) = self.thread_view(&thread_id)
+ {
let title_editor = active_thread.read(cx).title_editor.clone();
title_editor.update(cx, |editor, cx| {
if editor.text(cx) != title {
@@ -3708,7 +3713,7 @@ pub(crate) mod tests {
cx.new(|cx| {
AcpThread::new(
None,
- name,
+ Some(name.into()),
None,
connection,
project,
@@ -3908,7 +3913,7 @@ pub(crate) mod tests {
Task::ready(Ok(cx.new(|cx| {
AcpThread::new(
None,
- "AuthGatedAgent",
+ None,
Some(work_dirs),
self,
project,
@@ -3982,7 +3987,7 @@ pub(crate) mod tests {
let action_log = cx.new(|_| ActionLog::new(project.clone()));
AcpThread::new(
None,
- "SaboteurAgentConnection",
+ None,
Some(work_dirs),
self,
project,
@@ -4052,7 +4057,7 @@ pub(crate) mod tests {
let action_log = cx.new(|_| ActionLog::new(project.clone()));
AcpThread::new(
None,
- "RefusalAgentConnection",
+ None,
Some(work_dirs),
self,
project,
@@ -4132,7 +4137,7 @@ pub(crate) mod tests {
let thread = cx.new(|cx| {
AcpThread::new(
None,
- "CwdCapturingConnection",
+ None,
Some(work_dirs),
self.clone(),
project,
@@ -4167,7 +4172,7 @@ pub(crate) mod tests {
let thread = cx.new(|cx| {
AcpThread::new(
None,
- "CwdCapturingConnection",
+ None,
Some(work_dirs),
self.clone(),
project,
@@ -6109,7 +6114,7 @@ pub(crate) mod tests {
assert_eq!(editor.text(cx), "My Custom Title");
});
thread.read_with(cx, |thread, _cx| {
- assert_eq!(thread.title().as_ref(), "My Custom Title");
+ assert_eq!(thread.title(), Some("My Custom Title".into()));
});
}
@@ -6195,7 +6200,7 @@ pub(crate) mod tests {
cx.new(|cx| {
AcpThread::new(
parent_session_id,
- "Test Thread",
+ None,
None,
connection,
project,
@@ -6703,7 +6708,7 @@ pub(crate) mod tests {
let thread = cx.new(|cx| {
AcpThread::new(
None,
- "CloseCapableConnection",
+ Some("CloseCapableConnection".into()),
Some(work_dirs),
self,
project,
@@ -1,4 +1,4 @@
-use crate::SelectPermissionGranularity;
+use crate::{DEFAULT_THREAD_TITLE, SelectPermissionGranularity};
use std::cell::RefCell;
use acp_thread::ContentBlock;
@@ -405,7 +405,11 @@ impl ThreadView {
let can_edit = thread.update(cx, |thread, cx| thread.can_set_title(cx));
let editor = cx.new(|cx| {
let mut editor = Editor::single_line(window, cx);
- editor.set_text(thread.read(cx).title(), window, cx);
+ if let Some(title) = thread.read(cx).title() {
+ editor.set_text(title, window, cx);
+ } else {
+ editor.set_text(DEFAULT_THREAD_TITLE, window, cx);
+ }
editor.set_read_only(!can_edit);
editor
});
@@ -1052,7 +1056,7 @@ impl ThreadView {
.ok();
}
});
- if is_first_message {
+ if is_first_message && thread.read_with(cx, |thread, _cx| thread.title().is_none())? {
let text: String = contents
.iter()
.filter_map(|block| match block {
@@ -1537,7 +1541,7 @@ impl ThreadView {
EditorEvent::Blurred => {
if title_editor.read(cx).text(cx).is_empty() {
title_editor.update(cx, |editor, cx| {
- editor.set_text("New Thread", window, cx);
+ editor.set_text(DEFAULT_THREAD_TITLE, window, cx);
});
}
}
@@ -4656,7 +4660,10 @@ impl ThreadView {
.language_for_name("Markdown");
let thread = self.thread.read(cx);
- let thread_title = thread.title().to_string();
+ let thread_title = thread
+ .title()
+ .unwrap_or_else(|| DEFAULT_THREAD_TITLE.into())
+ .to_string();
let markdown = thread.to_markdown(cx);
let project = workspace.read(cx).project().clone();
@@ -7068,7 +7075,7 @@ impl ThreadView {
let thread_title = thread
.as_ref()
- .map(|t| t.read(cx).title())
+ .and_then(|t| t.read(cx).title())
.filter(|t| !t.is_empty());
let tool_call_label = tool_call.label.read(cx).source().to_string();
let has_tool_call_label = !tool_call_label.is_empty();
@@ -1,3 +1,4 @@
+use crate::DEFAULT_THREAD_TITLE;
use crate::SendImmediately;
use crate::ThreadHistory;
use crate::{
@@ -387,7 +388,7 @@ impl MessageEditor {
};
let thread_title = title
.filter(|title| !title.is_empty())
- .unwrap_or_else(|| SharedString::new_static("New Thread"));
+ .unwrap_or_else(|| SharedString::new_static(DEFAULT_THREAD_TITLE));
let uri = MentionUri::Thread {
id: session_id,
name: thread_title.to_string(),
@@ -1,5 +1,7 @@
use crate::thread_history::ThreadHistory;
-use crate::{AgentPanel, ConversationView, RemoveHistory, RemoveSelectedThread};
+use crate::{
+ AgentPanel, ConversationView, DEFAULT_THREAD_TITLE, RemoveHistory, RemoveSelectedThread,
+};
use acp_thread::AgentSessionInfo;
use chrono::{Datelike as _, Local, NaiveDate, TimeDelta, Utc};
use editor::{Editor, EditorEvent};
@@ -16,14 +18,12 @@ use ui::{
WithScrollbar, prelude::*,
};
-const DEFAULT_TITLE: &SharedString = &SharedString::new_static("New Thread");
-
-pub(crate) fn thread_title(entry: &AgentSessionInfo) -> &SharedString {
+pub(crate) fn thread_title(entry: &AgentSessionInfo) -> SharedString {
entry
.title
- .as_ref()
+ .clone()
.filter(|title| !title.is_empty())
- .unwrap_or(DEFAULT_TITLE)
+ .unwrap_or_else(|| DEFAULT_THREAD_TITLE.into())
}
pub struct ThreadHistoryView {
@@ -203,7 +203,7 @@ impl ThreadHistoryView {
let mut candidates = Vec::with_capacity(entries.len());
for (idx, entry) in entries.iter().enumerate() {
- candidates.push(StringMatchCandidate::new(idx, thread_title(entry)));
+ candidates.push(StringMatchCandidate::new(idx, &thread_title(entry)));
}
const MAX_MATCHES: usize = 100;
@@ -429,7 +429,7 @@ impl ThreadHistoryView {
(_, None) => "β".to_string(),
};
- let title = thread_title(entry).clone();
+ let title = thread_title(entry);
let full_date = entry_time
.map(|time| {
EntryTimeFormat::DateAndTime.format_timestamp(time.timestamp(), self.local_timezone)
@@ -678,7 +678,7 @@ impl HistoryEntryElement {
impl RenderOnce for HistoryEntryElement {
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
let id = ElementId::Name(self.entry.session_id.0.clone().into());
- let title = thread_title(&self.entry).clone();
+ let title = thread_title(&self.entry);
let formatted_time = self
.entry
.updated_at
@@ -21,6 +21,8 @@ use ui::{App, Context, SharedString};
use util::ResultExt as _;
use workspace::PathList;
+use crate::DEFAULT_THREAD_TITLE;
+
pub fn init(cx: &mut App) {
SidebarThreadMetadataStore::init_global(cx);
@@ -134,7 +136,9 @@ impl ThreadMetadata {
pub fn from_thread(thread: &Entity<acp_thread::AcpThread>, cx: &App) -> Self {
let thread_ref = thread.read(cx);
let session_id = thread_ref.session_id().clone();
- let title = thread_ref.title();
+ let title = thread_ref
+ .title()
+ .unwrap_or_else(|| DEFAULT_THREAD_TITLE.into());
let updated_at = Utc::now();
let agent_id = thread_ref.connection().agent_id();
@@ -987,7 +991,7 @@ mod tests {
cx.new(|cx| {
acp_thread::AcpThread::new(
Some(regular_session_id.clone()),
- "Subagent Thread",
+ Some("Subagent Thread".into()),
None,
connection.clone(),
project.clone(),
@@ -5,7 +5,9 @@ use agent_ui::thread_metadata_store::{SidebarThreadMetadataStore, ThreadMetadata
use agent_ui::threads_archive_view::{
ThreadsArchiveView, ThreadsArchiveViewEvent, format_history_entry_timestamp,
};
-use agent_ui::{Agent, AgentPanel, AgentPanelEvent, NewThread, RemoveSelectedThread};
+use agent_ui::{
+ Agent, AgentPanel, AgentPanelEvent, DEFAULT_THREAD_TITLE, NewThread, RemoveSelectedThread,
+};
use chrono::Utc;
use editor::Editor;
use feature_flags::{AgentV2FeatureFlag, FeatureFlagViewExt as _};
@@ -559,7 +561,9 @@ impl Sidebar {
let icon = thread_view_ref.agent_icon;
let icon_from_external_svg = thread_view_ref.agent_icon_from_external_svg.clone();
- let title = thread.title();
+ let title = thread
+ .title()
+ .unwrap_or_else(|| DEFAULT_THREAD_TITLE.into());
let is_native = thread_view_ref.as_native_thread(cx).is_some();
let is_title_generating = is_native && thread.has_provisional_title();
let session_id = thread.session_id().clone();
@@ -2709,9 +2713,9 @@ impl Sidebar {
let label: SharedString = if is_active {
self.active_draft_text(cx)
- .unwrap_or_else(|| "New Thread".into())
+ .unwrap_or_else(|| DEFAULT_THREAD_TITLE.into())
} else {
- "New Thread".into()
+ DEFAULT_THREAD_TITLE.into()
};
let workspace = workspace.clone();
@@ -5122,7 +5126,7 @@ mod tests {
let connection_b2 = StubAgentConnection::new();
connection_b2.set_next_prompt_updates(vec![acp::SessionUpdate::AgentMessageChunk(
- acp::ContentChunk::new("New thread".into()),
+ acp::ContentChunk::new(DEFAULT_THREAD_TITLE.into()),
)]);
open_thread_with_connection(&panel_b, connection_b2, cx);
send_message(&panel_b, cx);